5#include <boost/container/allocator.hpp>
6#include <boost/container/node_allocator.hpp>
7#include <boost/heap/binomial_heap.hpp>
8#include <boost/heap/fibonacci_heap.hpp>
9#include <boost/heap/pairing_heap.hpp>
50inline bool subproblem_lt(
Subproblem left,
Subproblem right) {
return uint64_t(left) < uint64_t(right); };
66namespace search_states {
69#if !defined(NDEBUG) && !defined(COUNTERS)
79template<
typename Actual>
140 template<
typename PlanTable>
144 return actual().is_bottom(PT, G, M, CF, CE);
147 template<
typename PlanTable>
151 return actual().is_top(PT, G, M, CF, CE);
160 double decrease_g(
const Actual *new_parent,
double new_g)
const {
return actual().decrease_g(new_parent, new_g); }
170 template<
typename Callback,
typename PlanTable>
173 actual().for_each_successor(std::forward<Callback>(callback), PT, G, M, CF, CE);
178 template<
typename Callback>
180 actual().template for_each_subproblem<Callback>(std::forward<Callback>(callback), G);
185 void dump(std::ostream &out)
const { out <<
actual() << std::endl; }
191template<
typename Actual>
192typename Base<Actual>::state_counters_t
237 template<
typename PlanTable>
257 template<
typename PlanTable>
283 template<
typename PlanTable>
300 template<
typename PlanTable>
306 subproblems[0] = All;
321 template<
typename PlanTable>
328 template<
typename PlanTable>
335 double g()
const {
return g_; }
348 template<
typename PlanTable>
355 template<
typename PlanTable>
372 template<
typename Callback>
382 if (this->
size() != other.
size())
return false;
384 auto this_it = this->
cbegin();
385 auto other_it = other.
cbegin();
386 for (; this_it != this->
cend(); ++this_it, ++other_it) {
387 if (*this_it != *other_it)
396 return std::lexicographical_compare(
cbegin(),
cend(), other.
cbegin(), other.
cend(), subproblem_lt);
401 out <<
"g = " << S.
g() <<
", [";
402 for (
auto it = S.
cbegin(); it != S.
cend(); ++it) {
403 if (it != S.
cbegin()) out <<
", ";
404 it->print_fixed_length(out, 15);
409 void dump(std::ostream &out)
const { out << *
this << std::endl; }
439 template<
bool IsConst>
444 using reference = std::conditional_t<is_const, const Subproblem&, Subproblem&>;
445 using pointer = std::conditional_t<is_const, const Subproblem*, Subproblem*>;
497 template<
typename PlanTable>
511 template<
typename PlanTable>
523 template<
typename PlanTable>
549 template<
typename PlanTable>
555 double g()
const {
return g_; }
562 template<
typename PlanTable>
569 template<
typename PlanTable>
577 template<
typename Callback>
596 if (this->
size() != other.
size())
return false;
600 std::size_t i = 0, j = 0;
601 while (i !=
size()) {
603 if (
T.is_subset(
X)) {
609 if (
T != O)
return false;
624 out <<
"g = " << S.
g() <<
", [";
625 for (
auto it = S.
cbegin(); it != S.
cend(); ++it) {
626 if (it != S.
cbegin()) out <<
", ";
627 it->print_fixed_length(out, 15);
632 void dump(std::ostream &out)
const { out << *
this << std::endl; }
698 template<
typename It>
723 std::copy(other.
begin(), other.
end(), this->begin());
732 {
swap(*
this, other); }
755 template<
typename PlanTable>
761 double g()
const {
return g_; }
780 template<
typename Callback>
798 auto this_it = this->
cbegin();
799 auto other_it = other.
cbegin();
800 for (; this_it != this->
cend(); ++this_it, ++other_it) {
801 if (*this_it != *other_it)
814 uint8_t *datasource_to_subproblem)
const
817 for (std::size_t idx = 0; idx != G.
num_sources(); ++idx) {
818 new (&datasource_to_subproblem[idx]) int8_t(idx);
823 for (
unsigned join_idx : *
this) {
825 const auto &sources = join.
sources();
826 const unsigned left_idx = datasource_to_subproblem[sources[0].get().id()];
827 const unsigned right_idx = datasource_to_subproblem[sources[1].get().id()];
828 subproblems[left_idx] |= subproblems[right_idx];
829 for (
auto id : subproblems[right_idx])
830 datasource_to_subproblem[
id] = left_idx;
835 for (std::size_t idx = 0; idx != G.
num_sources(); ++idx) {
836 Subproblem S = subproblems[datasource_to_subproblem[idx]];
837 M_insist(not S.
empty(),
"reverse index must never point to an empty subproblem");
841 unsigned num_subproblems_nonempty = 0;
842 for (std::size_t idx = 0; idx != G.
num_sources(); ++idx) {
844 if (S.
empty())
continue;
845 ++num_subproblems_nonempty;
846 M_insist((S & combined).empty(),
"current subproblem must be disjoint from all others");
850 M_insist(All == combined,
"must not loose a relation");
858 out <<
"g = " << S.
g() <<
", [";
859 for (
auto it = S.
cbegin(); it != S.
cend(); ++it) {
860 if (it != S.
cbegin()) out <<
", ";
866 void dump(std::ostream &out)
const { out << *
this << std::endl; }
878 template<
bool IsConst>
883 using pointer_type = std::conditional_t<is_const, const EdgePtrBottomUp*, EdgePtrBottomUp*>;
884 using reference_type = std::conditional_t<is_const, const EdgePtrBottomUp&, EdgePtrBottomUp&>;
1017 template<
typename PlanTable>
1024 double g()
const {
return g_; }
1040 template<
typename Callback>
1043 uint8_t datasource_to_subproblem[G.
num_sources()];
1047 if (not it->empty())
1061 auto ptr = this_joins;
1062 for (
auto it = this->
cbegin(); it->parent(); ++it, ++ptr)
1063 *ptr = it->join_id();
1067 for (
auto it = other.
cbegin(); it->parent(); ++it) {
1068 auto pos = std::find(this_joins,
end, it->join_id());
1082 uint8_t *datasource_to_subproblem)
const
1085 for (std::size_t idx = 0; idx != G.
num_sources(); ++idx) {
1086 new (&datasource_to_subproblem[idx]) int8_t(idx);
1092 while (runner->
parent()) {
1094 const auto &sources = join.
sources();
1095 const unsigned left_idx = datasource_to_subproblem[sources[0].get().id()];
1096 const unsigned right_idx = datasource_to_subproblem[sources[1].get().id()];
1097 subproblems[left_idx] |= subproblems[right_idx];
1098 for (
auto id : subproblems[right_idx])
1099 datasource_to_subproblem[
id] = left_idx;
1101 runner = runner->
parent();
1105 for (std::size_t idx = 0; idx != G.
num_sources(); ++idx) {
1106 Subproblem S = subproblems[datasource_to_subproblem[idx]];
1107 M_insist(not S.
empty(),
"reverse index must never point to an empty subproblem");
1111 for (std::size_t idx = 0; idx != G.
num_sources(); ++idx) {
1113 if (S.
empty())
continue;
1114 M_insist((S & combined).empty(),
"current subproblem must be disjoint from all others");
1117 M_insist(All == combined,
"must not loose a relation");
1125 out <<
"g = " << S.
g() <<
", [";
1126 for (
auto it = S.
cbegin(); it->parent(); ++it) {
1127 if (it != S.
cbegin()) out <<
", ";
1128 out << it->join_id();
1133 void dump(std::ostream &out)
const { out << *
this << std::endl; }
1146struct hash<
m::pe::hs::search_states::SubproblemsArray>
1153 hash = hash ^ uint64_t(s);
1154 hash = hash * 1181783497276652981UL + 4292484099903637661UL;
1161struct hash<
m::pe::hs::search_states::SubproblemTableBottomUp>
1168 hash = hash ^ uint64_t(s);
1169 hash = hash * 1181783497276652981UL + 4292484099903637661UL;
1176struct hash<
m::pe::hs::search_states::EdgesBottomUp>
1182 for (
unsigned join_idx : state) {
1183 hash = hash ^ join_idx;
1184 hash = hash * 1181783497276652981UL + 4292484099903637661UL;
1191struct hash<
m::pe::hs::search_states::EdgePtrBottomUp>
1196 unsigned *ptr = joins;
1197 for (
auto it = state.
cbegin(); it->parent(); ++it, ++ptr)
1198 *ptr = it->join_id();
1199 std::sort(joins, joins + state.
num_joins());
1204 for (
auto it = joins; it != joins + state.
num_joins(); ++it) {
1206 hash = hash * 1181783497276652981UL + 4292484099903637661UL;
1222namespace expansions {
1230 template<
typename State,
typename PlanTable>
1233 return State::Bottom(PT, G, M, CF, CE);
1236 template<
typename State,
typename PlanTable>
1240 return state.is_top(PT, G, M, CF, CE);
1243 template<
typename State,
typename PlanTable>
1256 template<
typename Callback,
typename PlanTable>
1264 bool has_successor =
false;
1271 for (
auto outer_it = state.
cbegin(), outer_end = std::prev(state.
cend()); outer_it != outer_end; ++outer_it)
1273 const auto neighbors = M.
neighbors(*outer_it);
1274 for (
auto inner_it = std::next(outer_it); inner_it != state.
cend(); ++inner_it) {
1275 M_insist(subproblem_lt(*outer_it, *inner_it),
"subproblems must be sorted");
1276 M_insist((*outer_it & *inner_it).empty(),
"subproblems must not overlap");
1278 if (subproblem_lt(*inner_it, marked))
1281 if (neighbors & *inner_it) {
1283 const Subproblem joined = *outer_it | *inner_it;
1288 for (
auto it = state.
cbegin(); it != state.
cend(); ++it) {
1289 if (it == outer_it)
continue;
1290 else if (it == inner_it)
new (ptr++)
Subproblem(joined);
1293 M_insist(std::is_sorted(subproblems, subproblems + state.
size() - 1, subproblem_lt));
1297 if (not PT[joined].model) {
1298 auto &model_left = *PT[*outer_it].model;
1299 auto &model_right = *PT[*inner_it].model;
1300 PT[joined].model = CE.
estimate_join(G, model_left, model_right, condition);
1311 state.
g() + action_cost,
1317 callback(std::move(S));
1319 has_successor =
true;
1325 M_insist(has_successor,
"must expand to at least one successor");
1329 template<
typename Callback,
typename PlanTable>
1342 if (L.is_subset(
X))
continue;
1347 while (not N.
empty()) {
1348 auto it = N.
begin();
1359 if (subproblem_lt(joined, state.
marker()))
continue;
1365 for (std::size_t j = 0; j != G.
num_sources(); ++j)
1366 table[j] = joined[j] ? joined : state[j];
1369 for (std::size_t i : joined)
1375 PT.update(G, CE, CF, L, R, condition);
1379 const double action_cost = total_cost - (PT[L].cost + PT[R].cost);
1384 state.
g() + action_cost,
1390 callback(std::move(S));
1395 template<
typename Callback,
typename PlanTable>
1401 M_insist(std::is_sorted(state.
cbegin(), state.
cend()),
"joins must be sorted by index");
1405 uint8_t datasource_to_subproblem[G.
num_sources()];
1411 bool join_matrix[size_of_join_matrix];
1412 std::fill_n(join_matrix, size_of_join_matrix,
false);
1413 auto joined = [&G, size_of_join_matrix](
bool *matrix,
unsigned row,
unsigned col) ->
bool& {
1414 const unsigned x = std::min(row, col);
1415 const unsigned y = std::max(row, col);
1418 M_insist(y * (y - 1) / 2 + x < size_of_join_matrix);
1419 return matrix[ y * (y - 1) / 2 + x ];
1423 unsigned *joins =
static_cast<unsigned*
>(alloca(
sizeof(
unsigned[state.
num_joins() + 1])));
1424 std::copy(state.
cbegin(), state.
cend(), joins + 1);
1430 while (j < state.
num_joins()
and joins[j] == joins[j+1]) {
1439 M_insist(j == state.
num_joins() or joins[j] < joins[j+1],
"invalid position of j");
1441 const Join &join = *G.
joins()[joins[j]];
1442 const auto &sources = join.
sources();
1445 const unsigned left = datasource_to_subproblem[sources[0].get().id()];
1446 const unsigned right = datasource_to_subproblem[sources[1].get().id()];
1447 if (left == right)
goto next;
1450 if (
bool &was_joined_before = joined(join_matrix, left, right); was_joined_before)
1453 was_joined_before =
true;
1458 M_insist(not subproblems[left].empty());
1459 M_insist(not subproblems[right].empty());
1463 PT.update(G, CE, CF, subproblems[left], subproblems[right], condition);
1466 const double total_cost = CF.
calculate_join_cost(G, PT, CE, subproblems[left], subproblems[right], condition);
1467 const double action_cost = total_cost - (PT[subproblems[left]].cost + PT[subproblems[right]].cost);
1472 callback(std::move(S));
1478 while (j < state.
num_joins()
and joins[j] == joins[j+1]) {
1487 template<
typename Callback,
typename PlanTable>
1496 uint8_t datasource_to_subproblem[G.
num_sources()];
1502 bool join_matrix[size_of_join_matrix];
1503 std::fill_n(join_matrix, size_of_join_matrix,
false);
1504 auto joined = [&G, size_of_join_matrix](
bool *matrix,
unsigned row,
unsigned col) ->
bool& {
1505 const unsigned x = std::min(row, col);
1506 const unsigned y = std::max(row, col);
1509 M_insist(y * (y - 1) / 2 + x < size_of_join_matrix);
1510 return matrix[ y * (y - 1) / 2 + x ];
1514 unsigned joins_taken[state.
num_joins()];
1516 unsigned *ptr = joins_taken;
1517 for (
auto it = state.
cbegin(); it->parent(); ++it, ++ptr)
1518 *ptr = it->join_id();
1519 std::sort(joins_taken, joins_taken + state.
num_joins());
1523 unsigned *sweepline = joins_taken;
1524 for (std::size_t j = 0; j != G.
num_joins(); ++j) {
1525 if (sweepline != joins_taken + state.
num_joins()
and j == *sweepline) {
1532 const auto &sources = join.
sources();
1535 const unsigned left = datasource_to_subproblem[sources[0].get().id()];
1536 const unsigned right = datasource_to_subproblem[sources[1].get().id()];
1537 if (left == right)
continue;
1540 if (
bool &was_joined_before = joined(join_matrix, left, right); was_joined_before)
1543 was_joined_before =
true;
1546 M_insist(not subproblems[left].empty());
1547 M_insist(not subproblems[right].empty());
1552 PT.update(G, CE, CF, subproblems[left], subproblems[right], condition);
1555 const double total_cost = CF.
calculate_join_cost(G, PT, CE, subproblems[left], subproblems[right], condition);
1556 const double action_cost = total_cost - (PT[subproblems[left]].cost + PT[subproblems[right]].cost);
1560 state.
g() + action_cost,
1563 callback(std::move(S));
1571 template<
typename State,
typename PlanTable>
1574 return State::Top(PT, G, M, CF, CE);
1577 template<
typename State,
typename PlanTable>
1581 return state.is_bottom(PT, G, M, CF, CE);
1584 template<
typename State,
typename PlanTable>
1598 template<
typename Callback,
typename PlanTable>
1606 bool has_successor =
false;
1613 if (subproblem_lt(S2, S1))
swap(S1, S2);
1620 auto left_it = state.
cbegin(), left_end = state.
cend();
1621 auto right_it = partitions, right_end = partitions + 2;
1622 auto out = subproblems;
1624 M_insist(out <= subproblems + state.
size() + 1,
"out of bounds");
1625 M_insist(left_it <= left_end,
"out of bounds");
1626 M_insist(right_it <= right_end,
"out of bounds");
1627 if (left_it == left_end) {
1628 if (right_it == right_end)
break;
1629 *out++ = *right_it++;
1630 }
else if (*left_it == (S1|S2)) [[unlikely]] {
1632 }
else if (right_it == right_end) {
1633 *out++ = *left_it++;
1634 }
else if (subproblem_lt(*left_it, *right_it)) {
1635 *out++ = *left_it++;
1637 *out++ = *right_it++;
1644 M_insist(std::is_sorted(subproblems, subproblems + state.
size() + 1, subproblem_lt));
1650 double action_cost = 0;
1651 if ((S1|S2) != All) {
1652 if (not PT[S1|S2].model)
1661 state.
g() + action_cost,
1667 callback(std::move(S));
1669 has_successor =
true;
1674 if (not S.is_singleton()) {
1681 M_insist(has_successor,
"must expand to at least one successor");
1698namespace heuristics {
1704template<
typename PlanTable,
typename State,
typename Expand>
1725template<
typename PlanTable,
typename State,
typename Expand>
1728template<
typename PlanTable,
typename State>
1735 static constexpr bool is_admissible =
false;
1742 double distance = 0;
1743 if (not state.is_top(PT, G, M, CF, CE)) {
1744 state.for_each_subproblem([&](
const Subproblem S) {
1752template<
typename PlanTable,
typename State>
1757 static constexpr bool is_admissible =
true;
1765 if (state.is_top(PT, G, M, CF, CE))
1767 double distance = 0;
1768 state.for_each_subproblem([&](
const Subproblem S) {
1770 if (not PT[S].model)
1771 PT[S].model = CE.estimate_join_all(G, PT, S, condition);
1772 distance += CE.predict_cardinality(*PT[S].model);
1779template<
typename PlanTable,
typename State,
typename Expand>
1780struct sum :
sum<PlanTable, State, typename Expand::direction>
1782 using sum<PlanTable, State,
typename Expand::direction>::sum;
1786template<
typename PlanTable,
typename State,
typename Expand>
1789template<
typename PlanTable,
typename State>
1801 double distance = 0;
1802 state.for_each_subproblem([&](
const Subproblem S) {
1804 if (not PT[S].model)
1805 PT[S].model = CE.estimate_join_all(G, PT, S, condition);
1806 distance += 2 * std::sqrt(CE.predict_cardinality(*PT[S].model));
1813template<
typename PlanTable,
typename State,
typename Expand>
1816 using sqrt_sum<PlanTable, State,
typename Expand::direction>::sqrt_sum;
1820template<
typename PlanTable,
typename State,
typename Expand>
1823template<
typename PlanTable,
typename State>
1834 double distance = 0;
1836 std::size_t num_sources = 0;
1838 if (not state.is_top(PT, G, M, CF, CE)) {
1839 state.for_each_subproblem([&](
Subproblem S) {
1842 std::sort(cardinalities, cardinalities + num_sources, std::greater<double>());
1843 for (std::size_t i = 0; i != num_sources - 1; ++i)
1844 distance += (i+1) * cardinalities[i];
1845 distance += (num_sources-1) * cardinalities[num_sources - 1];
1852template<
typename PlanTable,
typename State,
typename Expand>
1855 using scaled_sum<PlanTable, State,
typename Expand::direction>::scaled_sum;
1865template<
typename PlanTable,
typename State,
typename Expand>
1868template<
typename PlanTable,
typename State>
1878 if (state.size() > 1) {
1879 double distance = 1;
1880 for (
auto s : state)
1888template<
typename PlanTable,
typename State,
typename Expand>
1891 using product<PlanTable, State,
typename Expand::direction>::product;
1895template<
typename PlanTable,
typename State>
1907 if (state.is_top(PT, G, M, CF, CE))
return 0;
1909 double distance = 0;
1910 for (
auto s : state)
1914 if (state.size() == 2)
1918 double min_additional_costs = std::numeric_limits<
decltype(min_additional_costs)>::infinity();
1921 for (
auto outer_it = state.cbegin(); outer_it != state.cend(); ++outer_it) {
1922 const auto neighbors = M.
neighbors(*outer_it);
1923 for (
auto inner_it = std::next(outer_it); inner_it != state.cend(); ++inner_it) {
1924 M_insist(uint64_t(*inner_it) > uint64_t(*outer_it),
"subproblems must be sorted");
1925 M_insist((*outer_it & *inner_it).empty(),
"subproblems must not overlap");
1926 if (neighbors & *inner_it) {
1927 const Subproblem joined = *outer_it | *inner_it;
1928 if (not PT[joined].model) {
1929 PT[joined].model = CE.
estimate_join(G, *PT[*outer_it].model, *PT[*inner_it].model,
1933 const double total_cost = CF.
calculate_join_cost(G, PT, CE, *outer_it, *inner_it, condition);
1934 const double action_cost = total_cost - (PT[*outer_it].cost + PT[*inner_it].cost);
1937 if (additional_costs < min_additional_costs) {
1938 min_subproblem_left = *outer_it;
1939 min_subproblem_right = *inner_it;
1940 min_additional_costs = additional_costs;
1945 distance += min_additional_costs;
1954template<
typename PlanTable,
typename State,
typename Expand>
1957template<
typename PlanTable,
typename State>
1971 std::size_t num_nodes = 0;
1972 state.for_each_subproblem([&](
Subproblem S) {
1981 if (All != (left|right)) {
1982 const double old_cost_left = std::exchange(PT[left].cost, 0);
1983 const double old_cost_right = std::exchange(PT[right].cost, 0);
1985 PT[left].cost = old_cost_left;
1986 PT[right].cost = old_cost_right;
1988 }, PT, G, M, CF, CE, nodes, nodes + num_nodes);
1995template<
typename PlanTable,
typename State>
2006 std::vector<Subproblem> worklist;
2008 worklist.insert(worklist.end(), state.cbegin(), state.cend());
2014 }, PT, G, M, CF, CE, std::move(worklist));
2020template<
typename PlanTable,
typename State,
typename Expand>
2021struct GOO :
GOO<PlanTable, State, typename Expand::direction>
2023 using GOO<PlanTable, State,
typename Expand::direction>::GOO;
2027template<
typename PlanTable,
typename State,
typename Expand>
2030template<
typename PlanTable,
typename State>
2041 if (state.size() <= 2)
return 0;
2043 double cardinalities[state.size()];
2046 double *ptr = cardinalities;
2050 std::sort(cardinalities, end);
2054 if (not PT[All].model) {
2059 double Cprod = std::reduce(cardinalities, end, 1., std::multiplies<double>{});
2063 const std::size_t num_joins_remaining = state.size() - 1;
2064 const double avg_sel = std::pow(sel_remaining, 1. / num_joins_remaining);
2067 double accumulated_cost = 0;
2068 for (
auto ptr = cardinalities; ptr < end - 1; ++ptr) {
2069 const double card = ptr[0] * ptr[1] *
avg_sel;
2070 accumulated_cost += card;
2072 for (
auto runner = ptr; runner != end - 1
and runner[0] > runner[1]; ++runner)
2073 swap(runner[0], runner[1]);
2076 return accumulated_cost;
2080template<
typename PlanTable,
typename State,
typename Expand>
2083 using avg_sel<PlanTable, State,
typename Expand::direction>::avg_sel;
2097 template<
typename Cmp>
2100 template<
typename T,
typename...
Options>
2103 template<
typename T>
2113template<
long Num,
long Denom = 1>
2144template<
typename T,
typename...
Ts>
2155#define DEFINE_SEARCH(NAME, ...) \
2156 using NAME = combine<monotone<true>, Fibonacci_heap, __VA_ARGS__>
2182template<
typename PlanTable,
typename State>
2191 template<
typename,
typename,
typename>
typename Heuristic,
2201 using base_type::operator();
2203 template<
typename PlanTable>
#define DEFINE_SEARCH(NAME,...)
#define M_unreachable(MSG)
heuristic search state expansions namespace
heuristic search states namespace
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
void swap(PlanTableBase< Actual > &first, PlanTableBase< Actual > &second)
An adjacency matrix for a given query graph.
bool is_connected(SmallBitset S) const
Returns true iff the subproblem S is connected.
SmallBitset neighbors(SmallBitset S) const
Computes the neighbors of S.
std::unique_ptr< DataModel > estimate_join_all(const QueryGraph &G, const PlanTable &PT, Subproblem to_join, const cnf::CNF &condition) const
Compute a DataModel for the result of joining all DataSources in to_join by condition.
virtual std::unique_ptr< DataModel > estimate_join(const QueryGraph &G, const DataModel &left, const DataModel &right, const cnf::CNF &condition) const =0
Form a new DataModel by joining two DataModels.
virtual std::size_t predict_cardinality(const DataModel &data) const =0
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.
A Join in a QueryGraph combines DataSources by a join condition.
const sources_t & sources() const
Returns a reference to the joined DataSources.
void partition(const AdjacencyMatrix &M, Callback &&callback, const Subproblem S) const
Singleton class representing options provided as command line argument to the binaries.
The query graph represents all data sources and joins in a graph structure.
std::size_t num_joins() const
Returns the number of Joins in this graph.
const auto & joins() const
std::size_t num_sources() const
Returns the number of DataSources in this graph.
Implements a small and efficient set over integers in the range of 0 to 63 (including).
bool is_singleton() const
static SmallBitset All(std::size_t n)
Factory method for creating a SmallBitset with first n bits set.
bool empty() const
Returns true if there are no elements in this SmallBitset.
static SmallBitset Singleton(std::size_t n)
Factory method for creating a Singleton Smallbitset with n -th bit set.
Relies on the rules of aggregate initialization
A CNF represents a conjunction of cnf::Clauses.
A helper class to define CRTP class hierarchies.
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.
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.
Computes the join order using heuristic search.
void operator()(enumerate_tag, PlanTable &PT, const QueryGraph &G, const CostFunction &CF) const
boost::container::node_allocator< T > allocator_type
boost::heap::compare< Cmp > compare
boost::heap::fibonacci_heap< T, Options... > heap_type
static constexpr bool PerformAnytimeSearch
std::ratio< Num, Denom > BeamWidth
Combines multiple configuration parameters into a single configuration type.
static constexpr bool PerformCostBasedPruning
static constexpr bool Lazy
static constexpr bool IsMonotone
static constexpr bool PerformWeightedSearch
void operator()(const EdgesBottomUp &state, Callback &&callback, PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &, const CostFunction &CF, const CardinalityEstimator &CE) const
void operator()(const SubproblemsArray &state, Callback &&callback, PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE) const
static bool is_goal(const State &state, const PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE)
void operator()(const EdgePtrBottomUp &state, Callback &&callback, PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE) const
void operator()(const SubproblemTableBottomUp &state, Callback &&callback, PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE) const
static bool is_goal(const State &state, const PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE)
void reset_marked(State &state, const PlanTable &, const QueryGraph &, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &)
static State Start(PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE)
static bool is_goal(const State &state, const PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE)
void operator()(const SubproblemsArray &state, Callback &&callback, PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE) const
static bool is_goal(const State &state, const PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE)
static State Start(PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE)
void reset_marked(State &state, const PlanTable &, const QueryGraph &G, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &)
GOO(PlanTable &, const QueryGraph &, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &)
double operator()(const state_type &state, PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE) const
double operator()(const state_type &state, PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE) const
GOO(PlanTable &, const QueryGraph &, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &)
Inspired by GOO: Greedy Operator Ordering.
avg_sel(PlanTable &, const QueryGraph &, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &)
double operator()(const state_type &state, PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &CE) const
bottomup_lookahead_cheapest(PlanTable &, const QueryGraph &, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &)
double operator()(const state_type &state, PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE) const
product(PlanTable &, const QueryGraph &, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &)
double operator()(const state_type &state, PlanTable &PT, const QueryGraph &, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &CE) const
This heuristic estimates the distance from a state to the nearest goal state as the product of the si...
scaled_sum(PlanTable &, const QueryGraph &, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &)
double operator()(const state_type &state, PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE) const
double operator()(const state_type &state, PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &CE) const
sqrt_sum(PlanTable &, const QueryGraph &, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &)
double operator()(const state_type &state, PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE) const
sum(PlanTable &, const QueryGraph &, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &)
sum(PlanTable &, const QueryGraph &, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &)
double operator()(const state_type &state, PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE) const
This heuristic estimates the distance from a state to the nearest goal state as the sum of the sizes ...
zero(PlanTable &, const QueryGraph &, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &)
double operator()(const state_type &, PlanTable &, const QueryGraph &, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &) const
static constexpr bool is_admissible
unsigned num_states_constructed
unsigned num_states_expanded
unsigned num_states_generated
unsigned num_states_disposed
A state in the search space.
void for_each_subproblem(Callback &&callback, const QueryGraph &G) const
static void INCREMENT_NUM_STATES_GENERATED()
static void INCREMENT_NUM_STATES_CONSTRUCTED()
static void INCREMENT_NUM_STATES_EXPANDED()
static state_counters_t STATE_COUNTERS()
static void RESET_STATE_COUNTERS()
void for_each_successor(Callback &&callback, PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE) const
Returns true iff this and other have the exact same Subproblems.
static void INCREMENT_NUM_STATES_DISPOSED()
bool is_top(const PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE) const
double decrease_g(const Actual *new_parent, double new_g) const
Reduces the g value of the state.
static state_counters_t STATE_COUNTERS(state_counters_t new_counters)
bool operator<(const Base &other) const
const Actual * parent() const
static unsigned NUM_STATES_EXPANDED()
double g() const
Returns the cost to reach this state from the initial state.
bool is_bottom(const PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE) const
static unsigned NUM_STATES_CONSTRUCTED()
bool operator!=(const Base &other) const
static unsigned NUM_STATES_GENERATED()
static unsigned NUM_STATES_DISPOSED()
static state_counters_t state_counters_
bool operator==(const Base &other) const
M_LCOV_EXCL_START void dump(std::ostream &out) const
Scratchpad(const Scratchpad &)=delete
bool owned_by(const EdgePtrBottomUp *state) const
void make_owner(const EdgePtrBottomUp *state, const QueryGraph &G)
bool is_allocated() const
Scratchpad(Scratchpad &&other)
const EdgePtrBottomUp * owner
Scratchpad(const QueryGraph &G)
uint8_t * datasource_to_subproblem
Scratchpad & operator=(Scratchpad other)
friend void swap(Scratchpad &first, Scratchpad &second)
static constexpr bool is_const
reference_type operator*() const
the_iterator operator++(int)
bool operator!=(the_iterator other) const
std::conditional_t< is_const, const EdgePtrBottomUp &, EdgePtrBottomUp & > reference_type
the_iterator & operator++()
bool operator==(the_iterator other) const
the_iterator(pointer_type state)
std::conditional_t< is_const, const EdgePtrBottomUp *, EdgePtrBottomUp * > pointer_type
pointer_type operator->() const
void compute_datasource_to_subproblem_index(const QueryGraph &G, Subproblem *subproblems, uint8_t *datasource_to_subproblem) const
Computes the Subproblems produced by the Joins of this state in subproblems.
const_iterator cend() const
bool operator!=(const EdgePtrBottomUp &other) const
friend void swap(EdgePtrBottomUp &first, EdgePtrBottomUp &second)
typename base_type::size_type size_type
the_iterator< false > iterator
const_iterator begin() const
bool operator==(const EdgePtrBottomUp &other) const
Returns true iff this and other have the exact same joins.
EdgePtrBottomUp(EdgePtrBottomUp &&other)
Move c'tor.
const_iterator end() const
void for_each_subproblem(Callback &&callback, const QueryGraph &G) const
the_iterator< true > const_iterator
bool is_goal(const PlanTable &, const QueryGraph &G, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &) const
void decrease_g(double new_g) const
const EdgePtrBottomUp * parent() const
const EdgePtrBottomUp * parent_
unsigned num_joins_remaining(const QueryGraph &G) const
EdgePtrBottomUp(const QueryGraph &)
Creates an initial state.
static Scratchpad scratchpad_
unsigned num_joins() const
static EdgePtrBottomUp CreateInitial(const QueryGraph &G, const AdjacencyMatrix &)
EdgePtrBottomUp(const EdgePtrBottomUp &)=delete
Copy c'tor.
EdgePtrBottomUp & operator=(EdgePtrBottomUp other)
Assignment.
void dump(std::ostream &out) const
M_LCOV_EXCL_START friend std::ostream & operator<<(std::ostream &out, const EdgePtrBottomUp &S)
const_iterator cbegin() const
EdgePtrBottomUp(const EdgePtrBottomUp *parent, unsigned num_joins, double g, unsigned join_id)
Creates a state with actual costs g and given subproblems.
EdgesBottomUp(size_type num_joins_to_goal, size_type num_joins, unsigned *joins)
Creates an initial state with the given subproblems.
boost::container::node_allocator< unsigned > allocator_type
EdgesBottomUp(size_type num_joins_to_goal, double g, size_type num_joins, unsigned *joins)
Creates a state with actual costs g and given subproblems.
bool operator==(const EdgesBottomUp &other) const
Returns true iff this and other have the exact same joins.
EdgesBottomUp(const EdgesBottomUp &other)
Copy c'tor.
typename base_type::size_type size_type
const_iterator cbegin() const
size_type num_joins_
number of joins performed to reach this state
static allocator_type & get_allocator()
returns a reference to the class-wide allocator
double g_
the cost to reach this state from the initial state
bool is_goal(const PlanTable &, const QueryGraph &, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &) const
void dump(std::ostream &out) const
M_LCOV_EXCL_START friend std::ostream & operator<<(std::ostream &out, const EdgesBottomUp &S)
void for_each_subproblem(Callback &&callback, const QueryGraph &G) const
unsigned operator[](std::size_t idx) const
EdgesBottomUp(EdgesBottomUp &&other)
Move c'tor.
size_type num_joins() const
void decrease_g(double new_g) const
size_type num_joins_to_goal_
number of joins necessary to reach goal
size_type num_joins_to_goal() const
size_type num_subproblems(const QueryGraph &G) const
void compute_datasource_to_subproblem_index(const QueryGraph &G, Subproblem *subproblems, uint8_t *datasource_to_subproblem) const
Computes the Subproblems produced by the Joins of this state in subproblems.
unsigned * joins_
array of IDs of joins performed
static EdgesBottomUp CreateInitial(const QueryGraph &G, const AdjacencyMatrix &)
bool operator!=(const EdgesBottomUp &other) const
EdgesBottomUp & operator=(EdgesBottomUp other)
Assignment.
EdgesBottomUp(size_type num_joins_to_goal, double g, It begin, It end)
Creates a state with actual costs g and subproblems in range [begin; end).
friend void swap(EdgesBottomUp &first, EdgesBottomUp &second)
const unsigned * const_iterator
const_iterator cend() const
const_iterator begin() const
static allocator_type allocator_
class-wide allocator, used by all instances
const_iterator end() const
bool operator==(the_iterator other) const
static constexpr bool is_const
the_iterator(Subproblem *table, size_type size, size_type idx, size_type unique)
reference operator*() const
pointer operator->() const
the_iterator & operator++()
bool operator!=(the_iterator other) const
std::conditional_t< is_const, const Subproblem &, Subproblem & > reference
the_iterator operator++(int)
void dump(std::ostream &out) const
the_iterator< true > const_iterator
const Subproblem & operator[](std::size_t idx) const
SubproblemTableBottomUp()=default
double g_
the cost to reach this state from the initial state
bool operator!=(const SubproblemTableBottomUp &other) const
const_iterator cbegin() const
size_type size_
number of subproblems in this state
~SubproblemTableBottomUp()
D'tor.
boost::container::node_allocator< Subproblem > allocator_type
Subproblem marker_
the last formed subproblem
SubproblemTableBottomUp(const SubproblemTableBottomUp &)=delete
Copy c'tor.
SubproblemTableBottomUp(const PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE)
Creates an initial state.
static allocator_type & get_allocator()
returns a reference to the class-wide allocator
static allocator_type allocator_
class-wide allocator, used by all instances
const_iterator cend() const
bool is_goal(const PlanTable &, const QueryGraph &, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &) const
friend void swap(SubproblemTableBottomUp &first, SubproblemTableBottomUp &second)
SubproblemTableBottomUp(const PlanTable &, const QueryGraph &G, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &, double g, size_type size, Subproblem marker, Subproblem *table)
Creates a state with cost g and given table.
SubproblemTableBottomUp(const SubproblemTableBottomUp &other, const PlanTable &, const QueryGraph &G, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &)
static unsigned num_partitions(const PlanTable &, const QueryGraph &G, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &)
bool operator==(const SubproblemTableBottomUp &other) const
Returns true iff this and other have the exact same Subproblems.
Subproblem & operator[](std::size_t idx)
void decrease_g(double new_g) const
const_iterator begin() const
the_iterator< false > iterator
unsigned partition_id(const PlanTable &, const QueryGraph &, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &) const
Subproblem marker() const
Subproblem * table_
array of subproblems
M_LCOV_EXCL_START friend std::ostream & operator<<(std::ostream &out, const SubproblemTableBottomUp &S)
bool operator<(const SubproblemTableBottomUp &) const
SubproblemTableBottomUp(SubproblemTableBottomUp &&other)
Move c'tor.
SubproblemTableBottomUp & operator=(SubproblemTableBottomUp other)
Assignment.
const_iterator end() const
typename base_type::size_type size_type
void for_each_subproblem(Callback &&callback, const QueryGraph &) const
M_LCOV_EXCL_START friend std::ostream & operator<<(std::ostream &out, const SubproblemsArray &S)
const_iterator cend() const
friend void swap(SubproblemsArray &first, SubproblemsArray &second)
double g_
the cost to reach this state from the initial state
Subproblem mark(const Subproblem new_marked)
const_iterator end() const
static SubproblemsArray Bottom(const PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE)
typename base_type::size_type size_type
static allocator_type & get_allocator()
returns a reference to the class-wide allocator
SubproblemsArray & operator=(SubproblemsArray other)
Assignment.
const_iterator cbegin() const
Subproblem * subproblems_
array of subproblems
SubproblemsArray(const PlanTable &, const QueryGraph &G, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &, const SubproblemsArray *parent, double g, size_type size, Subproblem marked, Subproblem *subproblems)
Creates a state with cost g and given subproblems.
void for_each_subproblem(Callback &&callback, const QueryGraph &) const
Subproblem operator[](std::size_t idx) const
const SubproblemsArray * parent_
SubproblemsArray(const SubproblemsArray &)=delete
Copy c'tor.
static SubproblemsArray Top(const PlanTable &PT, const QueryGraph &G, const AdjacencyMatrix &M, const CostFunction &CF, const CardinalityEstimator &CE)
static allocator_type allocator_
class-wide allocator, used by all instances
unsigned partition_id(const PlanTable &, const QueryGraph &, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &) const
SubproblemsArray()=default
const_iterator begin() const
bool is_bottom(const PlanTable &, const QueryGraph &G, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &) const
static unsigned num_partitions(const PlanTable &, const QueryGraph &G, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &)
void decrease_g(const SubproblemsArray *new_parent, double new_g) const
const SubproblemsArray * parent() const
size_type size_
number of subproblems in this state
bool is_top(const PlanTable &, const QueryGraph &, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &) const
Subproblem marked() const
bool operator==(const SubproblemsArray &other) const
Returns true iff this and other have the exact same Subproblems.
Subproblem marked_
marked subproblem, used to avoid redundant paths
void dump(std::ostream &out) const
bool operator!=(const SubproblemsArray &other) const
SubproblemsArray(const SubproblemsArray &other, const PlanTable &, const QueryGraph &, const AdjacencyMatrix &, const CostFunction &, const CardinalityEstimator &)
~SubproblemsArray()
D'tor.
boost::container::node_allocator< Subproblem > allocator_type
SubproblemsArray(SubproblemsArray &&other)
Move c'tor.
bool operator<(const SubproblemsArray &other) const
uint64_t operator()(const m::pe::hs::search_states::EdgePtrBottomUp &state) const
uint64_t operator()(const m::pe::hs::search_states::EdgesBottomUp &state) const
uint64_t operator()(const m::pe::hs::search_states::SubproblemTableBottomUp &state) const
uint64_t operator()(const m::pe::hs::search_states::SubproblemsArray &state) const