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();
52template<
typename State>
53std::array<Subproblem, 2> delta(
const State &before_join,
const State &after_join)
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();
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) {
69 *out_it++ = *before_it++;
75template<
typename PlanTable,
typename State>
81 const State *parent = state.parent();
82 if (not parent)
return;
83 reconstruct_plan_bottom_up(*parent, PT, G, CE, CF);
84 const auto D = delta(*parent, state);
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);
89template<
typename PlanTable,
typename State>
93 const State *current = &goal;
96 while (current->parent()) {
97 const State *parent = current->parent();
98 const auto D = delta(*current, *parent);
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);
106template<
typename PlanTable>
107void reconstruct_saved_plan(
const std::vector<std::pair<Subproblem, Subproblem>> &saved_plan, PlanTable &PT,
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);
122template<
typename PlanTable,
typename State>
128 std::size_t num_nodes = 0;
138 plan.emplace_back(std::pair(left, right));
139 if (All != (left|right)) {
141 const double old_cost_left = std::exchange(PT[left].cost, 0);
142 const double old_cost_right = std::exchange(PT[right].cost, 0);
144 PT[left].cost = old_cost_left;
145 PT[right].cost = old_cost_right;
147 }, PT, G, M, CF, CE, nodes, nodes + num_nodes);
159namespace search_states {
175 typename SearchAlgorithm,
176 template<
typename,
typename,
typename>
typename Heuristic,
177 ai::SearchConfigConcept StaticConfig
183 State::RESET_STATE_COUNTERS();
185 if constexpr (StaticConfig::PerformCostBasedPruning) {
187 std::cout <<
"initial upper bound is " << config.
upper_bound << std::endl;
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);
196 const State &goal = S.search(
197 std::move(initial_state),
208 if constexpr (std::is_base_of_v<expansions::TopDown, Expand>) {
209 reconstruct_plan_top_down(goal, PT, G, CE, CF);
211 static_assert(std::is_base_of_v<expansions::BottomUp, Expand>,
"unexpected expansion");
212 reconstruct_plan_bottom_up(goal, PT, G, CE, CF);
214 }
catch (std::logic_error) {
219 if constexpr (StaticConfig::PerformCostBasedPruning) {
220 if (options::initialize_upper_bound) {
223 std::cout <<
"search did not reach a goal state, fall back to plan found during initialization"
225 reconstruct_saved_plan(config.
initial_plan, PT, G, CE, CF);
228 if (not options::initialize_upper_bound) {
231 std::cout <<
"search did not reach a goal state, fall back to DPccp" << std::endl;
240 M_insist(SearchAlgorithm::use_anytime_search,
"exception can only be thrown during anytime search");
244 using partition_type =
typename SearchAlgorithm::state_manager_type::map_type;
245 const auto &SM = S.state_manager();
246 const auto &partitions = SM.partitions();
256 auto find_partition_closest_to_goal = [](
auto partitions) ->
const partition_type & {
257 auto it = std::find_if_not(
260 [](
const partition_type &partition) ->
bool { return partition.empty(); }
262 M_insist(it != partitions.end(),
"all partitions empty");
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());
270 return find_partition_closest_to_goal(partitions);
273 using entry_type = SearchAlgorithm::state_manager_type::map_type::value_type;
277 if constexpr (SearchAlgorithm::use_weighted_search)
280 return e.first.g() + e.second.h;
282 const double f_best = f(*best);
283 const double f_next = f(next);
284 return f_best <= f_next ? best : &next;
287 auto &last_state_found = std::accumulate(
288 last_partition.begin(),
289 last_partition.end(),
290 &*last_partition.begin(),
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);
302 reconstruct_plan_top_down(last_state_found, PT, G, CE, CF);
303 }
else if constexpr (std::is_base_of_v<expansions::BottomUp, Expand>) {
309 reconstruct_plan_bottom_up(last_state_found, PT, G, CE, CF);
313 std::size_t num_nodes = 0;
314 last_state_found.for_each_subproblem([&](
Subproblem S) {
326 static_assert(std::is_base_of_v<expansions::TopDown, Expand>);
330 std::vector<Subproblem> worklist;
332 worklist.insert(worklist.end(), last_state_found.cbegin(), last_state_found.cend());
336 PT.update(G, CE, CF, left, right, condition);
341 reconstruct_plan_top_down(last_state_found, PT, G, CE, CF);
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()
366 template<
typename,
typename,
typename>
typename Heuristic,
367 template<
typename,
typename,
typename,
typename,
typename...>
typename Search,
370bool heuristic_search_helper(
const char *vertex_str,
const char *expand_str,
const char *heuristic_str,
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 ))
381 if constexpr (StaticConfig::PerformCostBasedPruning) {
382 if (options::initialize_upper_bound) {
389 double cost_initial_plan = 0;
390 State initial_state = expansions::BottomUpComplete::template Start<State>(PT, G, M, CF, CE);
392 return cost_initial_plan;
395 }
else if (options::initialize_upper_bound) {
396 std::cerr <<
"WARNING: option --hs-init-upper-bound has no effect for the chosen search configuration"
400 if constexpr (StaticConfig::PerformWeightedSearch) {
402 }
else if (options::weighting_factor != 1.f) {
403 std::cerr <<
"WARNING: option --hs-wf has no effect for the chosen search configuration"
407 if constexpr (StaticConfig::PerformAnytimeSearch) {
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"
414 using H = Heuristic<PlanTable, State, Expand>;
416 using SearchAlgorithm = Search<
417 State, Expand, H, StaticConfig,
426 SearchAlgorithm S(PT, G, M, CF, CE);
435 >(PT, G, M, CF, CE, S, config);
445template<
typename PlanTable>
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, \
459 config::STATIC_CONFIG \
460 >(#STATE, #EXPAND, #HEURISTIC, #STATIC_CONFIG, PT, G, M, CF, CE)) \
462 goto matched_heuristic_search; \
468 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, zero, AStar_with_cbp )
470 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, zero, beam_search_with_cbp )
471 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, zero, dynamic_beam_search )
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 )
484 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, sum, dynamic_beam_search )
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 )
493 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, scaled_sum, beam_search )
494 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, scaled_sum, dynamic_beam_search )
498 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete, avg_sel, beam_search )
512 HEURISTIC_SEARCH( SubproblemsArray, BottomUpComplete,
GOO, weighted_anytimeAStar_with_cbp )
523 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, zero, dynamic_beam_search )
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)
537 HEURISTIC_SEARCH( SubproblemsArray, TopDownComplete, sum, dynamic_beam_search )
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)
555 throw std::invalid_argument(
"illegal search configuration");
556#undef HEURISTIC_SEARCH
558matched_heuristic_search:;
561 auto plan_cost = [&PT]() ->
double {
563 const Subproblem right = PT.get_final().right;
564 return PT[left].cost + PT[right].cost;
567 const double hs_cost = plan_cost();
570 const double dp_cost = plan_cost();
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;
589void register_heuristic_search_plan_enumerator()
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"
604 "the heuristic search vertex to use",
605 [] (
const char *str) { options::vertex = str; }
611 "the vertex expansion to use",
612 [] (
const char *str) { options::expand = str; }
618 "the heuristic function to use",
619 [] (
const char *str) { options::heuristic = str; }
625 "the search method to use",
626 [] (
const char *str) { options::search = str; }
632 "the weighting factor for the heuristic value (defaults to 1)",
633 [] (
float wf) { options::weighting_factor = wf; }
638 "--hs-init-upper-bound",
639 "greedily compute an initial upper bound for cost-based pruning",
640 [] (
bool) { options::initialize_upper_bound =
true; }
646 "the expansion budget to use for Anytime A*",
647 [] (uint64_t
n) { options::expansion_budget =
n; }
#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.
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 streq(const char *first, const char *second)
command-line options for the HeuristicSearchPlanEnumerator
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.
void register_plan_enumerator(ThreadSafePooledString name, std::unique_ptr< pe::PlanEnumerator > PE, const char *description=nullptr)
Registers a new PlanEnumerator with the given name.
Database & get_database_in_use()
Returns a reference to the Database that is currently in use, if any.
ThreadSafePooledString pool(const char *str) const
Creates an internalized copy of the string str by adding it to the internal StringPool.
static Catalog & Get()
Return a reference to the single Catalog instance.
m::ArgParser & arg_parser()
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.
static Options & Get()
Return a reference to the single Options instance.
This table represents all explored plans with their sub-plans, estimated size, cost,...
This table represents all explored plans with their sub-plans, estimated size, cost,...
The query graph represents all data sources and joins in a graph structure.
std::size_t num_sources() const
Returns the number of DataSources in this graph.
AdjacencyMatrix & adjacency_matrix()
Implements a small and efficient set over integers in the range of 0 to 63 (including).
static SmallBitset All(std::size_t n)
Factory method for creating a SmallBitset with first n bits set.
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.
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
static Scratchpad scratchpad_
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