AI 24/25 Project Software
Documentation for the AI 24/25 course programming project software
Loading...
Searching...
No Matches
dominance_pruning.h
1#ifndef PDBS_DOMINANCE_PRUNING_H
2#define PDBS_DOMINANCE_PRUNING_H
3
4#include "downward/pdbs/types.h"
5
6#include "downward/pdbs/pattern_database.h"
7
8#include "downward/utils/countdown_timer.h"
9#include "downward/utils/hash.h"
10#include "downward/utils/logging.h"
11
12#include <cassert>
13#include <vector>
14
15namespace utils {
16class LogProxy;
17}
18
19namespace pdbs {
20class Pruner {
21 /*
22 Algorithm for pruning dominated patterns.
23
24 "patterns" is the vector of patterns used.
25 Each pattern is a vector of variable IDs.
26
27 "pattern_cliques" is the vector of pattern cliques.
28
29 The algorithm works by setting a "current pattern collection"
30 against which other patterns and collections can be tested for
31 dominance efficiently.
32
33 "variable_to_pattern_id" encodes the relevant information about the
34 current clique. For every variable v, variable_to_pattern_id[v] is
35 the id of the pattern containing v in the current clique,
36 or -1 if the variable is not contained in the current
37 clique. (Note that patterns in a pattern clique must be
38 disjoint, which is verified by an assertion in debug mode.)
39
40 To test if a given pattern v_1, ..., v_k is dominated by the
41 current clique, we check that all entries variable_to_pattern_id[v_i]
42 are equal and different from -1.
43
44 "dominated_patterns" is a vector<bool> that can be used to
45 quickly test whether a given pattern is dominated by the current
46 clique. This is precomputed for every pattern whenever the
47 current clique is set.
48 */
49
50 const PatternCollection& patterns;
51 const std::vector<PatternClique>& pattern_cliques;
52 const int num_variables;
53
54 std::vector<int> variable_to_pattern_id;
55 std::vector<bool> dominated_patterns;
56
57 void set_current_clique(int clique_id)
58 {
59 /*
60 Set the current pattern collection to be used for
61 is_pattern_dominated() or is_collection_dominated(). Compute
62 dominated_patterns based on the current pattern collection.
63 */
64 variable_to_pattern_id.assign(num_variables, -1);
65 assert(variable_to_pattern_id == std::vector<int>(num_variables, -1));
66 for (PatternID pattern_id : pattern_cliques[clique_id]) {
67 for (int variable : patterns[pattern_id]) {
68 assert(variable_to_pattern_id[variable] == -1);
69 variable_to_pattern_id[variable] = pattern_id;
70 }
71 }
72
73 dominated_patterns.clear();
74 dominated_patterns.reserve(patterns.size());
75 for (size_t i = 0; i < patterns.size(); ++i) {
76 dominated_patterns.push_back(is_pattern_dominated(i));
77 }
78 }
79
80 bool is_pattern_dominated(int pattern_id) const
81 {
82 /*
83 Check if the pattern with the given pattern_id is dominated
84 by the current pattern clique.
85 */
86 const Pattern& pattern = patterns[pattern_id];
87 assert(!pattern.empty());
88 PatternID clique_pattern_id = variable_to_pattern_id[pattern[0]];
89 if (clique_pattern_id == -1) {
90 return false;
91 }
92 int pattern_size = pattern.size();
93 for (int i = 1; i < pattern_size; ++i) {
94 if (variable_to_pattern_id[pattern[i]] != clique_pattern_id) {
95 return false;
96 }
97 }
98 return true;
99 }
100
101 bool is_clique_dominated(int clique_id) const
102 {
103 /*
104 Check if the collection with the given collection_id is
105 dominated by the current pattern collection.
106 */
107 for (PatternID pattern_id : pattern_cliques[clique_id]) {
108 if (!dominated_patterns[pattern_id]) {
109 return false;
110 }
111 }
112 return true;
113 }
114
115public:
116 Pruner(
117 const PatternCollection& patterns,
118 const std::vector<PatternClique>& pattern_cliques,
119 int num_variables)
120 : patterns(patterns)
121 , pattern_cliques(pattern_cliques)
122 , num_variables(num_variables)
123 {
124 }
125
126 std::vector<bool>
127 get_pruned_cliques(const utils::CountdownTimer& timer, utils::LogProxy& log)
128 {
129 int num_cliques = pattern_cliques.size();
130 std::vector<bool> pruned(num_cliques, false);
131 /*
132 Already pruned cliques are not used to prune other
133 cliques. This makes things faster and helps handle
134 duplicate cliques in the correct way: the first copy
135 will survive and prune all duplicates.
136 */
137 for (int c1 = 0; c1 < num_cliques; ++c1) {
138 if (!pruned[c1]) {
139 set_current_clique(c1);
140 for (int c2 = 0; c2 < num_cliques; ++c2) {
141 if (c1 != c2 && !pruned[c2] && is_clique_dominated(c2))
142 pruned[c2] = true;
143 }
144 }
145 if (timer.is_expired()) {
146 /*
147 Since after each iteration, we determined if a given
148 clique is pruned or not, we can just break the
149 computation here if reaching the time limit and use all
150 information we collected so far.
151 */
152 if (log.is_at_least_normal()) {
153 log << "Time limit reached. Abort dominance pruning."
154 << std::endl;
155 }
156 break;
157 }
158 }
159
160 return pruned;
161 }
162};
163
164/*
165 Clique superset dominates clique subset iff for every pattern
166 p_subset in subset there is a pattern p_superset in superset where
167 p_superset is a superset of p_subset.
168*/
169template <typename T>
170extern void prune_dominated_cliques(
171 PatternCollection& patterns,
172 std::vector<T>& pdbs,
173 std::vector<PatternClique>& pattern_cliques,
174 int num_variables,
175 double max_time,
176 utils::LogProxy& log)
177{
178 utils::CountdownTimer timer(max_time);
179
180 int num_patterns = patterns.size();
181 int num_cliques = pattern_cliques.size();
182
183 std::vector<bool> pruned = Pruner(patterns, pattern_cliques, num_variables)
184 .get_pruned_cliques(timer, log);
185
186 std::vector<PatternClique> remaining_pattern_cliques;
187 std::vector<bool> is_remaining_pattern(num_patterns, false);
188 int num_remaining_patterns = 0;
189 for (size_t i = 0; i < pattern_cliques.size(); ++i) {
190 if (!pruned[i]) {
191 PatternClique& clique = pattern_cliques[i];
192 for (PatternID pattern_id : clique) {
193 if (!is_remaining_pattern[pattern_id]) {
194 is_remaining_pattern[pattern_id] = true;
195 ++num_remaining_patterns;
196 }
197 }
198 remaining_pattern_cliques.push_back(std::move(clique));
199 }
200 }
201
202 PatternCollection remaining_patterns;
203 std::vector<T> remaining_pdbs;
204 remaining_patterns.reserve(num_remaining_patterns);
205 remaining_pdbs.reserve(num_remaining_patterns);
206 std::vector<PatternID> old_to_new_pattern_id(num_patterns, -1);
207 for (PatternID old_pattern_id = 0; old_pattern_id < num_patterns;
208 ++old_pattern_id) {
209 if (is_remaining_pattern[old_pattern_id]) {
210 PatternID new_pattern_id = remaining_patterns.size();
211 old_to_new_pattern_id[old_pattern_id] = new_pattern_id;
212 remaining_patterns.push_back(std::move(patterns[old_pattern_id]));
213 remaining_pdbs.push_back(std::move(pdbs[old_pattern_id]));
214 }
215 }
216 for (PatternClique& clique : remaining_pattern_cliques) {
217 for (size_t i = 0; i < clique.size(); ++i) {
218 PatternID old_pattern_id = clique[i];
219 PatternID new_pattern_id = old_to_new_pattern_id[old_pattern_id];
220 assert(new_pattern_id != -1);
221 clique[i] = new_pattern_id;
222 }
223 }
224
225 int num_pruned_collections = num_cliques - remaining_pattern_cliques.size();
226 int num_pruned_patterns = num_patterns - num_remaining_patterns;
227
228 patterns.swap(remaining_patterns);
229 pdbs.swap(remaining_pdbs);
230 pattern_cliques.swap(remaining_pattern_cliques);
231
232 if (log.is_at_least_normal()) {
233 log << "Pruned " << num_pruned_collections << " of " << num_cliques
234 << " pattern cliques" << std::endl;
235 log << "Pruned " << num_pruned_patterns << " of " << num_patterns
236 << " PDBs" << std::endl;
237 log << "Dominance pruning took " << timer.get_elapsed_time()
238 << std::endl;
239 }
240}
241} // namespace pdbs
242
243#endif