mutable
A Database System for Research and Fast Prototyping
Loading...
Searching...
No Matches
HeuristicSearchPlanEnumerator.cpp
Go to the documentation of this file.
2
3#include <cstring>
4#include <execution>
5#include <functional>
6#include <memory>
8#include <mutable/Options.hpp>
9#include <mutable/util/fn.hpp>
10
11
12using namespace m;
13using namespace m::pe;
14using namespace m::pe::hs;
15
16
17/*======================================================================================================================
18 * Options
19 *====================================================================================================================*/
20
21namespace {
22
24namespace options {
25
27const char *vertex = "SubproblemsArray";
29const char *expand = "BottomUpComplete";
31const char *heuristic = "zero";
33const char *search = "AStar";
35float weighting_factor = 1.f;
37bool initialize_upper_bound = false;
39uint64_t expansion_budget = std::numeric_limits<uint64_t>::max();
40
41}
42
43}
44
45
46/*======================================================================================================================
47 * Helper functions
48 *====================================================================================================================*/
49
50namespace {
51
52template<typename State>
53std::array<Subproblem, 2> delta(const State &before_join, const State &after_join)
54{
55 std::array<Subproblem, 2> delta;
56 M_insist(before_join.size() == after_join.size() + 1);
57 auto after_it = after_join.cbegin();
58 auto before_it = before_join.cbegin();
59 auto out_it = delta.begin();
60
61 while (out_it != delta.end()) {
62 M_insist(before_it != before_join.cend());
63 if (after_it == after_join.cend()) {
64 *out_it++ = *before_it++;
65 } else if (*before_it == *after_it) {
66 ++before_it;
67 ++after_it;
68 } else {
69 *out_it++ = *before_it++;
70 }
71 }
72 return delta;
73}
74
75template<typename PlanTable, typename State>
76void reconstruct_plan_bottom_up(const State &state, PlanTable &PT, const QueryGraph &G,const CardinalityEstimator &CE,
77 const CostFunction &CF)
78{
79 static cnf::CNF condition; // TODO: use join condition
80
81 const State *parent = state.parent();
82 if (not parent) return;
83 reconstruct_plan_bottom_up(*parent, PT, G, CE, CF); // solve recursively
84 const auto D = delta(*parent, state); // find joined subproblems
85 M_insist(not PT.has_plan(D[0] | D[1]), "PlanTable must not contain plans for intermediate results");
86 PT.update(G, CE, CF, D[0], D[1], condition); // update plan table
87}
88
89template<typename PlanTable, typename State>
90void reconstruct_plan_top_down(const State &goal, PlanTable &PT, const QueryGraph &G, const CardinalityEstimator &CE,
91 const CostFunction &CF)
92{
93 const State *current = &goal;
94 static cnf::CNF condition; // TODO: use join condition
95
96 while (current->parent()) {
97 const State *parent = current->parent();
98 const auto D = delta(*current, *parent); // find joined subproblems
99 M_insist(not PT.has_plan(D[0] | D[1]), "PlanTable must not contain plans for intermediate results");
100 PT.update(G, CE, CF, D[0], D[1], condition); // update plan table
101 current = parent; // advance
102 }
103}
104
105/* Reconstruct a saved (partial) plan. */
106template<typename PlanTable>
107void reconstruct_saved_plan(const std::vector<std::pair<Subproblem, Subproblem>> &saved_plan, PlanTable &PT,
108 const QueryGraph &G, const CardinalityEstimator &CE, const CostFunction &CF)
109{
110 static cnf::CNF condition; // TODO: use join condition
111 for (auto [left, right]: saved_plan) {
112 M_insist(not PT.has_plan(left | right), "PlanTable must not contain plans for intermediate results");
113 PT.update(G, CE, CF, left, right, condition);
114 }
115};
116}
117
118namespace m::pe::hs {
119
120/* Run GOO from the starting state 'state' until all relations of the query graph are joined. Return the cost of the
121 * resulting (partial) plan. We save the found (partial) plan in the provided `plan`. */
122template<typename PlanTable, typename State>
123double goo_path_completion(const State &state, PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M,
124 const CardinalityEstimator &CE, const CostFunction &CF, binary_plan_type &plan)
125{
126 /*----- Initialize nodes. -----*/
127 m::pe::GOO::node nodes[G.num_sources()];
128 std::size_t num_nodes = 0;
129 state.for_each_subproblem([&](Subproblem S) {
130 new (&nodes[num_nodes++]) m::pe::GOO::node(S, M.neighbors(S));
131 }, G);
132
133 /*----- Greedily enumerate all joins. -----*/
134 const Subproblem All = Subproblem::All(G.num_sources());
135 double cost = 0;
136 m::pe::GOO{}.for_each_join([&](Subproblem left, Subproblem right) {
137 static cnf::CNF condition; // TODO: use join condition
138 plan.emplace_back(std::pair(left, right));
139 if (All != (left|right)) {
140 /* We only consider the cost of the current join and discard the accumulated costs of the inputs. */
141 const double old_cost_left = std::exchange(PT[left].cost, 0);
142 const double old_cost_right = std::exchange(PT[right].cost, 0);
143 cost += CF.calculate_join_cost(G, PT, CE, left, right, condition);
144 PT[left].cost = old_cost_left;
145 PT[right].cost = old_cost_right;
146 }
147 }, PT, G, M, CF, CE, nodes, nodes + num_nodes);
148
149 return cost;
150}
151}
152
153
154/*======================================================================================================================
155 * Heuristic Search States: class/static fields
156 *====================================================================================================================*/
157
158namespace m::pe::hs {
159namespace search_states {
160
164EdgePtrBottomUp::Scratchpad EdgePtrBottomUp::scratchpad_;
165
166}
167}
168
169namespace m::pe::hs {
170
171template<
172 typename PlanTable,
173 typename State,
174 typename Expand,
175 typename SearchAlgorithm,
176 template<typename, typename, typename> typename Heuristic,
177 ai::SearchConfigConcept StaticConfig
178>
179bool heuristic_search(PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF,
180 const CardinalityEstimator &CE, SearchAlgorithm &S,
182{
183 State::RESET_STATE_COUNTERS();
184
185 if constexpr (StaticConfig::PerformCostBasedPruning) {
187 std::cout << "initial upper bound is " << config.upper_bound << std::endl;
188 }
189
190 try {
191 State initial_state = Expand::template Start<State>(PT, G, M, CF, CE);
192 using H = Heuristic<PlanTable, State, Expand>;
193 H h(PT, G, M, CF, CE);
194
195 /*----- Run the search algorithm. -----*/
196 const State &goal = S.search(
197 /* initial_state= */ std::move(initial_state),
198 /* expand= */ Expand{},
199 /* heuristic= */ h,
200 /* config= */ config,
201 /*----- Context -----*/
202 PT, G, M, CF, CE
203 );
204 if (Options::Get().statistics)
205 S.dump(std::cout);
206
207 /*----- Reconstruct the plan from the found path to goal. -----*/
208 if constexpr (std::is_base_of_v<expansions::TopDown, Expand>) {
209 reconstruct_plan_top_down(goal, PT, G, CE, CF);
210 } else {
211 static_assert(std::is_base_of_v<expansions::BottomUp, Expand>, "unexpected expansion");
212 reconstruct_plan_bottom_up(goal, PT, G, CE, CF);
213 }
214 } catch (std::logic_error) {
215 /*----- Handle incomplete search not finding a plan. -----*/
216 /* Any incplete search may *not* find a plan and hence throw a `std::logic_error`. In this case, we can attempt
217 * to use a plan we found during initialization of the search. If we do no have such a plan, we resort to a
218 * complete search, e.g. `DPccp`. */
219 if constexpr (StaticConfig::PerformCostBasedPruning) {
220 if (options::initialize_upper_bound) {
221 /* Fall back to initial plan from upper bound initialization. */
222 if (not Options::Get().quiet)
223 std::cout << "search did not reach a goal state, fall back to plan found during initialization"
224 << std::endl;
225 reconstruct_saved_plan(config.initial_plan, PT, G, CE, CF);
226 }
227 }
228 if (not options::initialize_upper_bound) {
229 /* No plan available from upper bound initialization. Fall back to DPccp. */
230 if (not Options::Get().quiet)
231 std::cout << "search did not reach a goal state, fall back to DPccp" << std::endl;
232 DPccp{}(G, CF, PT);
233 }
235 /*--- No plan was found within the given budget for A* ⇒ use GOO to complete the *nearest* partial solution --*/
236 /* Find the set of states that is closest to the goal state, concerning path length. Among these states chose
237 * the state X with the lowest f-value (f(X)=g(X)+h(X)). In the case that this state is a goal state, the found
238 * path to this state is returned. Otherwise, use GOO from this state to find a plan. */
239
240 M_insist(SearchAlgorithm::use_anytime_search, "exception can only be thrown during anytime search");
241 if (Options::Get().statistics)
242 S.dump(std::cout);
243
244 using partition_type = typename SearchAlgorithm::state_manager_type::map_type;
245 const auto &SM = S.state_manager();
246 const auto &partitions = SM.partitions();
247
248 /*----- Find the `last_state_found' as the starting state for path completion -----*/
249 /* Find the partition closest to the goal, i.e. the partition that minimizes the difference between the number
250 * of subproblems included in the states of the partition and the number of subproblems of the goal. Thus, the
251 * direction of traversal to finding this partition depends on the chosen search direction. For bottom-up
252 * search, traverse the partitions starting at the partition with each state only consisting of one subproblem,
253 * i.e. the goal of bottom-up Search. For top-down search, traverse the partitions in the reverse direction,
254 * starting at the partition with each state only consisting of base relations, i.e. the goal of top-down
255 * search. In the partition closest to the goal, choose the state X with ths smallest unweighted f(X). */
256 auto find_partition_closest_to_goal = [](auto partitions) -> const partition_type & {
257 auto it = std::find_if_not(
258 partitions.begin(),
259 partitions.end(),
260 [](const partition_type &partition) -> bool { return partition.empty(); }
261 );
262 M_insist(it != partitions.end(), "all partitions empty");
263 return *it;
264 };
265
266 auto &last_partition = [&find_partition_closest_to_goal](auto partitions) -> const partition_type & {
267 if constexpr (std::is_base_of_v<expansions::TopDown, Expand>)
268 return find_partition_closest_to_goal(partitions.reverse());
269 else
270 return find_partition_closest_to_goal(partitions);
271 }(partitions);
272
273 using entry_type = SearchAlgorithm::state_manager_type::map_type::value_type;
274 auto min_state = [&config](const entry_type *best, const entry_type &next) -> const entry_type* {
276 auto f = [&config](const entry_type &e) {
277 if constexpr (SearchAlgorithm::use_weighted_search)
278 return e.first.g() + e.second.h / config.weighting_factor;
279 else
280 return e.first.g() + e.second.h;
281 };
282 const double f_best = f(*best);
283 const double f_next = f(next);
284 return f_best <= f_next ? best : &next;
285 };
286
287 auto &last_state_found = std::accumulate(
288 /* first= */ last_partition.begin(),
289 /* last= */ last_partition.end(),
290 /* init= */ &*last_partition.begin(),
291 /* op= */ min_state
292 )->first;
293
294 /*----- Check whether the 'last_state_found' is a goal state already -----*/
295 /* This may happen when a goal state is added to the state manager during vertex expansion but it is never
296 * popped from the queue and expanded itself. If the `last_state_found` is a goal state, we can directly
297 * reconstruct the path. */
298 if (Expand::is_goal(last_state_found, PT, G, M, CF, CE)) {
299 if (std::is_base_of_v<expansions::BottomUp, Expand>)
300 reconstruct_plan_bottom_up(last_state_found, PT, G, CE, CF);
301 else
302 reconstruct_plan_top_down(last_state_found, PT, G, CE, CF);
303 } else if constexpr (std::is_base_of_v<expansions::BottomUp, Expand>) {
304 /*----- BottomUp -----*/
305 /* The `last_state_found` is *not* a goal state. We need to *finish* the partial solution using a greedy
306 * approach. */
307
308 /* Reconstruct partial plan found so far. */
309 reconstruct_plan_bottom_up(last_state_found, PT, G, CE, CF);
310
311 /* Initialize nodes with the subproblems of the starting state */
312 pe::GOO::node nodes[G.num_sources()];
313 std::size_t num_nodes = 0;
314 last_state_found.for_each_subproblem([&](Subproblem S) {
315 nodes[num_nodes++] = pe::GOO::node(S, M.neighbors(S));
316 }, G);
317
318 /* Run GOO from the starting state and update the PlanTable accordingly */
319 pe::GOO{}.compute_plan(PT, G, M, CF, CE, nodes, nodes + num_nodes);
320
321 M_insist(PT.has_plan(Subproblem::All(G.num_sources())), "No full plan found");
322
323
324 } else {
325 /*----- TopDown -----*/
326 static_assert(std::is_base_of_v<expansions::TopDown, Expand>);
327 static cnf::CNF condition;
328
329 /* Initialize worklist with the subproblems of the starting state. */
330 std::vector<Subproblem> worklist;
331 worklist.reserve(G.num_sources());
332 worklist.insert(worklist.end(), last_state_found.cbegin(), last_state_found.cend());
333
334 /* Compute the remaining plan with TDGOO. */
335 auto update_PT = [&](Subproblem left, Subproblem right) {
336 PT.update(G, CE, CF, left, right, condition); // TODO: use actual condition
337 };
338 pe::TDGOO{}.for_each_join(update_PT, PT, G, M, CF, CE, std::move(worklist));
339
340 /* To complete the plan found by TDGOO, fill the PlanTable with the joins initially found by the search */
341 reconstruct_plan_top_down(last_state_found, PT, G, CE, CF);
342
343 M_insist(PT.has_plan(Subproblem::All(G.num_sources())), "No full plan found");
344 }
345 }
346#ifdef COUNTERS
347 if (Options::Get().statistics) {
348 std::cout << "Vertices generated: " << State::NUM_STATES_GENERATED()
349 << "\nVertices expanded: " << State::NUM_STATES_EXPANDED()
350 << "\nVertices constructed: " << State::NUM_STATES_CONSTRUCTED()
351 << "\nVertices disposed: " << State::NUM_STATES_DISPOSED()
352 << std::endl;
353 }
354#endif
355 return true;
356}
357
358}
359
360namespace {
361
362template<
363 typename PlanTable,
364 typename State,
365 typename Expand,
366 template<typename, typename, typename> typename Heuristic,
367 template<typename, typename, typename, typename, typename...> typename Search,
368 ai::SearchConfigConcept StaticConfig
369>
370bool heuristic_search_helper(const char *vertex_str, const char *expand_str, const char *heuristic_str,
371 const char *search_str, PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M,
372 const CostFunction &CF, const CardinalityEstimator &CE)
373{
374 if (streq(options::vertex, vertex_str ) and
375 streq(options::expand, expand_str ) and
376 streq(options::heuristic, heuristic_str) and
377 streq(options::search, search_str ))
378 {
380
381 if constexpr (StaticConfig::PerformCostBasedPruning) {
382 if (options::initialize_upper_bound) {
383 /*----- Run GOO to compute upper bound of plan cost. -----*/
384 /* Obtain the sum of the costs of the left and the right subplan of the resulting initial plan. The
385 * costs considered for the search do not include the cardinality of the result set. Save the initial
386 * plan. */
387 config.upper_bound = [&]() {
388 config.initial_plan->reserve(G.num_sources()-1);
389 double cost_initial_plan = 0;
390 State initial_state = expansions::BottomUpComplete::template Start<State>(PT, G, M, CF, CE);
391 cost_initial_plan = goo_path_completion(initial_state, PT, G, M, CE, CF, config.initial_plan);
392 return cost_initial_plan;
393 }();
394 }
395 } else if (options::initialize_upper_bound) {
396 std::cerr << "WARNING: option --hs-init-upper-bound has no effect for the chosen search configuration"
397 << std::endl;
398 }
399
400 if constexpr (StaticConfig::PerformWeightedSearch) {
401 config.weighting_factor = options::weighting_factor;
402 } else if (options::weighting_factor != 1.f) {
403 std::cerr << "WARNING: option --hs-wf has no effect for the chosen search configuration"
404 << std::endl;
405 }
406
407 if constexpr (StaticConfig::PerformAnytimeSearch) {
408 config.expansion_budget = options::expansion_budget;
409 } else if (options::expansion_budget != std::numeric_limits<uint64_t>::max()) {
410 std::cerr << "WARNING: option --hs-budget has no effect for the chosen search configuration"
411 << std::endl;
412 }
413
414 using H = Heuristic<PlanTable, State, Expand>;
415
416 using SearchAlgorithm = Search<
417 State, Expand, H, StaticConfig,
418 /*----- context -----*/
419 PlanTable&,
420 const QueryGraph&,
421 const AdjacencyMatrix&,
422 const CostFunction&,
424 >;
425
426 SearchAlgorithm S(PT, G, M, CF, CE);
427
429 PlanTable,
430 State,
431 Expand,
432 SearchAlgorithm,
433 Heuristic,
434 StaticConfig
435 >(PT, G, M, CF, CE, S, config);
436 }
437 return false;
438
439}
440
441}
442
443namespace m::pe::hs {
444
445template<typename PlanTable>
446void HeuristicSearch::operator()(enumerate_tag, PlanTable &PT, const QueryGraph &G, const CostFunction &CF) const
447{
448 Catalog &C = Catalog::Get();
450 const AdjacencyMatrix &M = G.adjacency_matrix();
451
452
453#define HEURISTIC_SEARCH(STATE, EXPAND, HEURISTIC, STATIC_CONFIG) \
454 if (heuristic_search_helper<PlanTable, \
455 search_states::STATE, \
456 expansions::EXPAND, \
457 heuristics::HEURISTIC, \
458 ai::genericAStar, \
459 config::STATIC_CONFIG \
460 >(#STATE, #EXPAND, #HEURISTIC, #STATIC_CONFIG, PT, G, M, CF, CE)) \
461 { \
462 goto matched_heuristic_search; \
463 }
464
465 // bottom-up
466 // zero
467 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, zero, AStar )
468 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, zero, AStar_with_cbp )
469 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, zero, beam_search )
470 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, zero, beam_search_with_cbp )
471 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, zero, dynamic_beam_search )
472 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, zero, anytimeAStar )
473 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, zero, weighted_anytimeAStar )
474 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, zero, anytimeAStar_with_cbp )
475 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, zero, weighted_anytimeAStar_with_cbp )
476
477
478 // sum
479 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, sum, AStar )
480 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, sum, AStar_with_cbp )
481 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, sum, weighted_AStar )
482 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, sum, lazyAStar )
483 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, sum, beam_search )
484 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, sum, dynamic_beam_search )
485 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, sum, anytimeAStar )
486 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, sum, weighted_anytimeAStar )
487 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, sum, anytimeAStar_with_cbp )
488 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, sum, weighted_anytimeAStar_with_cbp )
489
490
491 // scaled_sum
492 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, scaled_sum, AStar )
493 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, scaled_sum, beam_search )
494 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, scaled_sum, dynamic_beam_search )
495
496 // avg_sel
497 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, avg_sel, AStar )
498 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, avg_sel, beam_search )
499
500 // product
501 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, product, AStar )
502
503 // GOO
504 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, GOO, AStar )
505 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, GOO, AStar_with_cbp )
506 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, GOO, weighted_AStar )
507 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, GOO, beam_search )
508 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, GOO, dynamic_beam_search )
509 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, GOO, anytimeAStar )
510 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, GOO, weighted_anytimeAStar )
511 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, GOO, anytimeAStar_with_cbp )
512 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, GOO, weighted_anytimeAStar_with_cbp )
513
514
515 // HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, bottomup_lookahead_cheapest, AStar )
516 // HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, perfect_oracle, AStar )
517
518 // top-down
519 // zero
520 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, zero, AStar )
521 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, zero, AStar_with_cbp )
522 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, zero, beam_search )
523 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, zero, dynamic_beam_search )
524 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, zero, anytimeAStar )
525 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, zero, weighted_anytimeAStar )
526 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, zero, anytimeAStar_with_cbp )
527 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, zero, weighted_anytimeAStar_with_cbp)
528
529 // sqrt_sum
530 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, sqrt_sum, AStar )
531
532 // sum
533 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, sum, AStar )
534 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, sum, AStar_with_cbp )
535 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, sum, weighted_AStar )
536 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, sum, beam_search )
537 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, sum, dynamic_beam_search )
538 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, sum, anytimeAStar )
539 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, sum, weighted_anytimeAStar )
540 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, sum, anytimeAStar_with_cbp )
541 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, sum, weighted_anytimeAStar_with_cbp)
542
543 // GOO
544 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, GOO, AStar )
545 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, GOO, AStar_with_cbp )
546 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, GOO, weighted_AStar )
547 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, GOO, beam_search )
548 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, GOO, dynamic_beam_search )
549 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, GOO, anytimeAStar )
550 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, GOO, weighted_anytimeAStar )
551 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, GOO, anytimeAStar_with_cbp )
552 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, GOO, weighted_anytimeAStar_with_cbp)
553
554
555 throw std::invalid_argument("illegal search configuration");
556#undef HEURISTIC_SEARCH
557
558matched_heuristic_search:;
559#ifndef NDEBUG
560 if (Options::Get().statistics) {
561 auto plan_cost = [&PT]() -> double {
562 const Subproblem left = PT.get_final().left;
563 const Subproblem right = PT.get_final().right;
564 return PT[left].cost + PT[right].cost;
565 };
566
567 const double hs_cost = plan_cost();
568 DPccp dpccp;
569 dpccp(G, CF, PT);
570 const double dp_cost = plan_cost();
571
572 std::cout << "AI: " << hs_cost << ", DP: " << dp_cost << ", Δ " << hs_cost / dp_cost << 'x' << std::endl;
573 if (hs_cost > dp_cost)
574 std::cout << "WARNING: Suboptimal solution!" << std::endl;
575 }
576#endif
577}
578
579// explicit template instantiation
580template void HeuristicSearch::operator()(enumerate_tag, PlanTableSmallOrDense &PT, const QueryGraph &G, const CostFunction &CF) const;
582
583}
584
585
586namespace {
587
588__attribute__((constructor(202)))
589void register_heuristic_search_plan_enumerator()
590{
591 Catalog &C = Catalog::Get();
593 C.pool("HeuristicSearch"),
594 std::make_unique<HeuristicSearch>(),
595 "uses heuristic search to find a plan; "
596 "found plans are optimal when the search method is optimal and the heuristic is admissible"
597 );
598
599 /*----- Command-line arguments -----------------------------------------------------------------------------------*/
600 C.arg_parser().add<const char*>(
601 /* group= */ "HeuristicSearch",
602 /* short= */ nullptr,
603 /* long= */ "--hs-vertex",
604 /* description= */ "the heuristic search vertex to use",
605 [] (const char *str) { options::vertex = str; }
606 );
607 C.arg_parser().add<const char*>(
608 /* group= */ "HeuristicSearch",
609 /* short= */ nullptr,
610 /* long= */ "--hs-expand",
611 /* description= */ "the vertex expansion to use",
612 [] (const char *str) { options::expand = str; }
613 );
614 C.arg_parser().add<const char*>(
615 /* group= */ "HeuristicSearch",
616 /* short= */ nullptr,
617 /* long= */ "--hs-heuristic",
618 /* description= */ "the heuristic function to use",
619 [] (const char *str) { options::heuristic = str; }
620 );
621 C.arg_parser().add<const char*>(
622 /* group= */ "HeuristicSearch",
623 /* short= */ nullptr,
624 /* long= */ "--hs-search",
625 /* description= */ "the search method to use",
626 [] (const char *str) { options::search = str; }
627 );
628 C.arg_parser().add<float>(
629 /* group= */ "HeuristicSearch",
630 /* short= */ nullptr,
631 /* long= */ "--hs-wf",
632 /* description= */ "the weighting factor for the heuristic value (defaults to 1)",
633 [] (float wf) { options::weighting_factor = wf; }
634 );
635 C.arg_parser().add<bool>(
636 /* group= */ "HeuristicSearch",
637 /* short= */ nullptr,
638 /* long= */ "--hs-init-upper-bound",
639 /* description= */ "greedily compute an initial upper bound for cost-based pruning",
640 [] (bool) { options::initialize_upper_bound = true; }
641 );
642 C.arg_parser().add<uint64_t>(
643 /* group= */ "HeuristicSearch",
644 /* short= */ nullptr,
645 /* long= */ "--hs-budget",
646 /* description= */ "the expansion budget to use for Anytime A*",
647 [] (uint64_t n) { options::expansion_budget = n; }
648 );
649}
650
651}
#define HEURISTIC_SEARCH(STATE, EXPAND, HEURISTIC, STATIC_CONFIG)
__attribute__((constructor(202))) static void register_interpreter()
void add(const char *group_name, const char *short_name, const char *long_name, const char *description, Callback &&callback)
Adds a new group option to the ArgParser.
Definition: ArgParser.hpp:84
#define M_insist(...)
Definition: macro.hpp:129
‍heuristic search namespace
bool heuristic_search(PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE, Search &S, const ai::SearchConfiguration< StaticConfig > &config)
double goo_path_completion(const State &state, PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CardinalityEstimator &CE, const CostFunction &CF, binary_plan_type &plan)
std::vector< std::pair< Subproblem, Subproblem > > binary_plan_type
‍plan enumerator namespace
Bool< L > uint8_t n
Definition: WasmUtil.hpp:1318
std::size_t bool
Definition: WasmDSL.hpp:528
‍mutable namespace
Definition: Backend.hpp:10
bool streq(const char *first, const char *second)
Definition: fn.hpp:29
and
Definition: enum_ops.hpp:12
‍command-line options for the HeuristicSearchPlanEnumerator
Definition: V8Engine.cpp:44
An adjacency matrix for a given query graph.
SmallBitset neighbors(SmallBitset S) const
Computes the neighbors of S.
The catalog contains all Databases and keeps track of all meta information of the database system.
Definition: Catalog.hpp:215
void register_plan_enumerator(ThreadSafePooledString name, std::unique_ptr< pe::PlanEnumerator > PE, const char *description=nullptr)
Registers a new PlanEnumerator with the given name.
Definition: Catalog.hpp:431
Database & get_database_in_use()
Returns a reference to the Database that is currently in use, if any.
Definition: Catalog.hpp:295
ThreadSafePooledString pool(const char *str) const
Creates an internalized copy of the string str by adding it to the internal StringPool.
Definition: Catalog.hpp:274
static Catalog & Get()
Return a reference to the single Catalog instance.
m::ArgParser & arg_parser()
Definition: Catalog.hpp:253
double calculate_join_cost(const QueryGraph &G, const PlanTable &PT, const CardinalityEstimator &CE, Subproblem left, Subproblem right, const cnf::CNF &condition) const
Returns the total cost of performing a Join operation.
std::unique_ptr< CardinalityEstimator > cardinality_estimator(std::unique_ptr< CardinalityEstimator > CE)
Sets the CardinalityEstimator of this Database.
Definition: Schema.hpp:946
bool statistics
Definition: Options.hpp:39
static Options & Get()
Return a reference to the single Options instance.
Definition: Options.cpp:9
bool quiet
Definition: Options.hpp:35
This table represents all explored plans with their sub-plans, estimated size, cost,...
Definition: PlanTable.hpp:284
This table represents all explored plans with their sub-plans, estimated size, cost,...
Definition: PlanTable.hpp:180
The query graph represents all data sources and joins in a graph structure.
Definition: QueryGraph.hpp:172
std::size_t num_sources() const
Returns the number of DataSources in this graph.
Definition: QueryGraph.hpp:224
AdjacencyMatrix & adjacency_matrix()
Definition: QueryGraph.hpp:277
Implements a small and efficient set over integers in the range of 0 to 63 (including).
Definition: ADT.hpp:26
static SmallBitset All(std::size_t n)
Factory method for creating a SmallBitset with first n bits set.
Definition: ADT.hpp:117
Relies on the rules of aggregate initialization
OptField< StaticConfig::PerformCostBasedPruning, std::vector< std::pair< Subproblem, Subproblem > > > initial_plan
Initial plan for the query.
OptField< StaticConfig::PerformCostBasedPruning, double > upper_bound
Upper bound on the cost of plans to consider.
OptField< StaticConfig::PerformAnytimeSearch, uint64_t > expansion_budget
Budget for the maximum number of expansions.
OptField< StaticConfig::PerformWeightedSearch, float > weighting_factor
The weighting factor for the heuristic.
A CNF represents a conjunction of cnf::Clauses.
Definition: CNF.hpp:134
Computes the join order using connected subgraph complement pairs (CCP).
Greedy operator ordering.
void for_each_join(Callback &&callback, PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &, const CardinalityEstimator &CE, node *begin, node *end) const
Enumerate the sequence of joins that yield the smallest subproblem in each step.
void compute_plan(PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE, node *begin, node *end) const
Top-down version of greedy operator ordering.
void for_each_join(Callback &&callback, PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &, const CardinalityEstimator &CE, std::vector< Subproblem > subproblems) const
Enumerate the sequence of graph cuts that yield the smallest subproblems in each step.
void operator()(enumerate_tag, PlanTable &PT, const QueryGraph &G, const CostFunction &CF) const
boost::container::node_allocator< unsigned > allocator_type
static allocator_type allocator_
‍class-wide allocator, used by all instances
boost::container::node_allocator< Subproblem > allocator_type
static allocator_type allocator_
‍class-wide allocator, used by all instances
static allocator_type allocator_
‍class-wide allocator, used by all instances
boost::container::node_allocator< Subproblem > allocator_type