41#include <unordered_map>
49#define CHECK_FOR_INTERRUPTS() provsql_tdkc_poll()
60inline std::size_t bag_index(
bag_t b)
62 return static_cast<std::underlying_type<bag_t>::type
>(b);
66constexpr std::int8_t UNASSIGNED = -2;
67constexpr std::int8_t DONE = -1;
72 std::vector<Atom> atoms;
73 std::uint64_t full = 0;
74 std::vector<std::uint64_t> atoms_of_var;
79 std::vector<DisjunctInfo> disjuncts;
90 std::vector<std::int8_t> st;
94 return w == o.w && st == o.st;
113 std::vector<std::vector<DCode> > homs;
122 std::map<unsigned long, bool> gate_val;
123 std::vector<unsigned long> susp;
126 if (sat != o.sat || gate_val != o.gate_val || susp != o.susp)
130 return homs == o.homs;
136 std::size_t operator()(
const State &s)
const noexcept {
137 std::uint64_t h = 1469598103934665603ull;
138 auto mix = [&](std::uint64_t x) {
140 h *= 1099511628211ull;
142 mix(s.sat ? 0x9e3779b9ull : 0x1234567ull);
143 for (
const auto &[g, v] : s.gate_val)
144 mix((g << 1) | (v ? 1u : 0u));
145 for (
unsigned long g : s.susp)
146 mix(g * 0x9e3779b97f4a7c15ull);
148 for (
const auto &codes : s.homs) {
150 for (
const auto &c : codes) {
152 for (std::int8_t v : c.st)
153 mix(
static_cast<std::uint64_t
>(
static_cast<std::uint8_t
>(v)));
156 return static_cast<std::size_t
>(h);
161inline int positionIn(
const std::vector<unsigned long> &domain,
unsigned long v)
163 return static_cast<int>(
164 std::lower_bound(domain.begin(), domain.end(), v) - domain.begin());
168void canonicalize(std::vector<DCode> &codes)
170 std::sort(codes.begin(), codes.end());
171 codes.erase(std::unique(codes.begin(), codes.end()), codes.end());
175State trivialState(
const QueryCtx &q)
179 s.homs.resize(q.disjuncts.size());
180 for (std::size_t d = 0; d < q.disjuncts.size(); ++d)
182 DCode{std::vector<std::int8_t>(q.disjuncts[d].n_vars, UNASSIGNED), 0});
198bool closeDisjunct(
const DisjunctInfo &di,
199 std::vector<DCode> &codes,
201 const std::vector<unsigned long> &domain,
202 const std::map<unsigned, unsigned long> *head_pin =
nullptr)
205 std::vector<std::size_t> cand;
206 for (std::size_t ai = 0; ai < di.atoms.size(); ++ai)
207 if (di.atoms[ai].relation_id == fact.
relation_id &&
208 di.atoms[ai].vars.size() == fact.
elements.size())
213 std::vector<int> pos(fact.
elements.size());
214 for (std::size_t i = 0; i < fact.
elements.size(); ++i)
215 pos[i] = positionIn(domain, fact.
elements[i]);
217 std::set<DCode> seen(codes.begin(), codes.end());
218 std::vector<DCode> work(codes.begin(), codes.end());
221 while (!work.empty()) {
222 DCode c = std::move(work.back());
224 for (std::size_t ai : cand) {
225 if ((c.w >> ai) & 1u)
227 const Atom &a = di.atoms[ai];
230 for (std::size_t i = 0; i < a.
vars.size(); ++i) {
231 const unsigned v = a.
vars[i];
232 const std::int8_t p =
static_cast<std::int8_t
>(pos[i]);
237 auto pit = head_pin->find(v);
238 if (pit != head_pin->end() && fact.
elements[i] != pit->second) {
243 if (c2.st[v] == UNASSIGNED)
245 else if (c2.st[v] != p) {
252 c2.w |= (std::uint64_t{1} << ai);
253 if (seen.insert(c2).second) {
261 codes.assign(seen.begin(), seen.end());
267State closeWithFact(
const QueryCtx &q,
const State &s,
const Fact &fact,
268 const std::vector<unsigned long> &domain,
269 const std::map<unsigned, unsigned long> *head_pin =
nullptr)
274 for (std::size_t d = 0; d < q.disjuncts.size(); ++d)
275 if (closeDisjunct(q.disjuncts[d], out.homs[d], fact, domain, head_pin))
289State forgetLift(
const QueryCtx &q,
const State &s,
290 const std::vector<unsigned long> &from_domain,
291 const std::vector<unsigned long> &to_domain)
293 if (s.sat || from_domain == to_domain)
295 std::vector<int> map(from_domain.size());
296 for (std::size_t i = 0; i < from_domain.size(); ++i) {
297 auto it = std::lower_bound(to_domain.begin(), to_domain.end(),
299 map[i] = (it != to_domain.end() && *it == from_domain[i])
300 ?
static_cast<int>(it - to_domain.begin()) : -1;
305 out.homs.resize(q.disjuncts.size());
306 for (std::size_t d = 0; d < q.disjuncts.size(); ++d) {
307 const DisjunctInfo &di = q.disjuncts[d];
308 std::vector<DCode> &dst = out.homs[d];
309 for (
const DCode &c : s.homs[d]) {
312 c2.st.resize(di.n_vars);
314 for (
unsigned v = 0; v < di.n_vars; ++v) {
315 const std::int8_t st = c.st[v];
316 if (st == UNASSIGNED || st == DONE) {
319 const int np = map[
static_cast<std::size_t
>(st)];
321 c2.st[v] =
static_cast<std::int8_t
>(np);
322 }
else if ((c.w & di.atoms_of_var[v]) == di.atoms_of_var[v]) {
331 dst.push_back(std::move(c2));
343State
join(
const QueryCtx &q,
const State &s1,
const State &s2)
345 if (s1.sat || s2.sat)
349 out.homs.resize(q.disjuncts.size());
350 for (std::size_t d = 0; d < q.disjuncts.size(); ++d) {
351 const DisjunctInfo &di = q.disjuncts[d];
352 std::vector<DCode> &dst = out.homs[d];
353 for (
const DCode &c1 : s1.homs[d])
354 for (
const DCode &c2 : s2.homs[d]) {
357 c.st.resize(di.n_vars);
359 for (
unsigned v = 0; v < di.n_vars; ++v) {
360 const std::int8_t a = c1.st[v];
361 const std::int8_t b = c2.st[v];
365 else if (b == UNASSIGNED)
367 else if (a == DONE && b == DONE)
369 else if (a == DONE || b == DONE) {
384 dst.push_back(std::move(c));
418unsigned essentialVarCount(
const CQ &cq,
const std::vector<bool> &appears,
421 std::vector<unsigned> V;
422 for (
unsigned v = 0; v < cq.
n_vars; ++v)
425 const unsigned nV =
static_cast<unsigned>(V.size());
426 static constexpr unsigned MAX_FD_VARS = 12;
427 if (nV <= 1 || nV > MAX_FD_VARS)
431 std::map<std::pair<unsigned, std::size_t>,
432 std::vector<const std::vector<unsigned long> *> > byrel;
438 std::map<std::tuple<unsigned, std::size_t, std::uint64_t, unsigned>,
bool> memo;
439 auto dataFD = [&](
unsigned rel, std::size_t arity,
440 std::uint64_t known,
unsigned c) ->
bool {
441 auto key = std::make_tuple(rel, arity, known, c);
442 auto it = memo.find(key);
443 if (it != memo.end())
446 auto rit = byrel.find({rel, arity});
447 if (rit != byrel.end()) {
448 std::map<std::vector<unsigned long>,
unsigned long> grp;
449 for (
const std::vector<unsigned long> *t : rit->second) {
450 std::vector<unsigned long> kv;
451 for (std::size_t p = 0; p < arity; ++p)
452 if (known & (std::uint64_t{1} << p))
453 kv.push_back((*t)[p]);
454 auto git = grp.find(kv);
455 if (git == grp.end())
456 grp.emplace(std::move(kv), (*t)[c]);
457 else if (git->second != (*t)[c]) {
467 std::unordered_map<unsigned, unsigned> bitOf;
468 for (
unsigned i = 0; i < nV; ++i)
470 const std::uint32_t fullMask = (std::uint32_t{1} << nV) - 1;
473 auto closure = [&](std::uint32_t det) -> std::uint32_t {
478 const std::size_t arity = a.
vars.size();
479 std::uint64_t knownPos = 0;
480 for (std::size_t p = 0; p < arity; ++p)
481 if (det & (std::uint32_t{1} << bitOf[a.
vars[p]]))
482 knownPos |= (std::uint64_t{1} << p);
483 for (std::size_t p = 0; p < arity; ++p) {
484 if (knownPos & (std::uint64_t{1} << p))
486 const unsigned bit = bitOf[a.
vars[p]];
487 if (dataFD(a.
relation_id, arity, knownPos,
static_cast<unsigned>(p))) {
488 det |= (std::uint32_t{1} << bit);
489 knownPos |= (std::uint64_t{1} << p);
499 for (std::uint32_t s = 0; s <= fullMask; ++s) {
500 const unsigned pc =
static_cast<unsigned>(__builtin_popcount(s));
503 if (closure(s) == fullMask)
513 q.disjuncts.resize(ucq.
disjuncts.size());
515 for (std::size_t d = 0; d < ucq.
disjuncts.size(); ++d) {
517 DisjunctInfo &di = q.disjuncts[d];
520 if (cq.
atoms.empty())
522 if (cq.
atoms.size() > 64)
524 di.full = (cq.
atoms.size() == 64)
526 : ((std::uint64_t{1} << cq.
atoms.size()) - 1);
527 di.atoms_of_var.assign(di.n_vars, 0);
528 std::vector<bool> appears(di.n_vars,
false);
529 for (std::size_t ai = 0; ai < cq.
atoms.size(); ++ai)
530 for (
unsigned v : cq.
atoms[ai].vars) {
533 di.atoms_of_var[v] |= (std::uint64_t{1} << ai);
539 stats.
n_enumerating[d] = essentialVarCount(cq, appears, enc);
553 default:
return true;
572 unsigned max_treewidth,
573 std::size_t max_states,
575 const std::unordered_map<
unsigned long,
577 const std::map<
unsigned,
578 unsigned long> *head_pin)
584 const std::vector<SliceGate> &slice = enc.
slice;
585 const std::size_t D = q.disjuncts.size();
586 auto isElem = [&](
unsigned long v) {
return v < E; };
587 auto gidx = [&](
unsigned long v) {
return static_cast<std::size_t
>(v - E); };
593 std::unordered_map<unsigned long, bag_t> elim_local;
594 std::unique_ptr<TreeDecomposition> built_td;
596 if (tdp ==
nullptr) {
598 unsigned max_degree = 0;
602 if (built_td->getTreewidth() > max_treewidth)
604 tdp = built_td.get();
607 const std::unordered_map<unsigned long, bag_t> &elim =
608 shared_elim ? *shared_elim : elim_local;
611 const std::size_t nb_bags = td.
getNbBags();
613 auto bagIndex = [](
bag_t b) {
614 return static_cast<std::underlying_type<bag_t>::type
>(b);
618 std::vector<std::vector<unsigned long> > dom(nb_bags), edom(nb_bags);
619 for (std::size_t b = 0; b < nb_bags; ++b) {
621 dom[b].push_back(
static_cast<std::underlying_type<gate_t>::type
>(g));
622 std::sort(dom[b].begin(), dom[b].end());
623 dom[b].erase(std::unique(dom[b].begin(), dom[b].end()), dom[b].end());
624 for (
unsigned long v : dom[b])
626 edom[b].push_back(v);
631 std::vector<std::vector<std::size_t> > facts_at_bag(nb_bags);
632 for (std::size_t fi = 0; fi < enc.
facts.size(); ++fi) {
634 std::vector<unsigned long> cl = f.
elements;
636 cl.push_back(E + f.
gate);
637 bag_t best = elim.at(cl[0]);
638 for (
unsigned long v : cl)
639 if (bagIndex(elim.at(v)) < bagIndex(best))
641 facts_at_bag[bagIndex(best)].push_back(fi);
645 using Table = std::unordered_map<State, gate_t, StateHash>;
646 using Accumulator = std::unordered_map<State, std::vector<gate_t>, StateHash>;
647 const gate_t invalid{
static_cast<std::underlying_type<gate_t>::type
>(-1)};
650 std::vector<gate_t> ev_in(slice.size(), invalid), ev_not(slice.size(), invalid);
651 auto inGate = [&](std::size_t i) {
652 if (ev_in[i] == invalid)
657 auto notGate = [&](std::size_t i) {
658 if (ev_not[i] == invalid) {
660 dd.
addWire(ev_not[i], inGate(i));
665 if (a == true_gate)
return b;
666 if (b == true_gate)
return a;
673 auto finalize = [&](Accumulator &acc) {
675 t.reserve(acc.size());
676 for (
auto &e : acc) {
677 if (e.second.size() == 1)
678 t.emplace(e.first, e.second[0]);
684 t.emplace(e.first, o);
689 if (t.size() > max_states)
691 "joint DP state space exceeds the per-node bound (" +
692 std::to_string(max_states) +
")");
697 auto almost = [&](
const State &s) {
698 for (
const auto &[gv, gval] : s.gate_val) {
701 auto it = s.gate_val.find(E + c);
702 if (it == s.gate_val.end())
704 const bool cval = it->second;
706 if (gval && !cval)
return false;
708 if (!gval && cval)
return false;
710 if (gval == cval)
return false;
716 auto justify = [&](State &s) {
717 std::vector<unsigned long> keep;
718 for (
unsigned long g : s.susp) {
720 const bool gval = s.gate_val.at(g);
723 auto it = s.gate_val.find(E + c);
724 if (it == s.gate_val.end())
726 const bool cval = it->second;
734 s.susp = std::move(keep);
736 auto addSusp = [](State &s,
unsigned long g) {
737 auto it = std::lower_bound(s.susp.begin(), s.susp.end(), g);
738 if (it == s.susp.end() || *it != g)
739 s.susp.insert(it, g);
746 auto closeFact = [&](State s,
const Fact &f,
747 const std::vector<unsigned long> &ed) {
750 for (std::size_t d = 0; d < D; ++d)
751 if (closeDisjunct(q.disjuncts[d], s.homs[d], f, ed,
761 auto remapHoms = [&](
const State &in, State &out,
762 const std::vector<unsigned long> &ef,
763 const std::vector<unsigned long> &et) {
770 out.homs.assign(D, {});
771 std::vector<int> map(ef.size());
772 for (std::size_t i = 0; i < ef.size(); ++i) {
773 auto it = std::lower_bound(et.begin(), et.end(), ef[i]);
774 map[i] = (it != et.end() && *it == ef[i])
775 ?
static_cast<int>(it - et.begin()) : -1;
777 for (std::size_t d = 0; d < D; ++d) {
778 const DisjunctInfo &di = q.disjuncts[d];
779 std::vector<DCode> &dst = out.homs[d];
780 for (
const DCode &c : in.homs[d]) {
783 c2.st.resize(di.n_vars);
785 for (
unsigned v = 0; v < di.n_vars; ++v) {
786 const std::int8_t st = c.st[v];
787 if (st == UNASSIGNED || st == DONE)
790 const int np = map[
static_cast<std::size_t
>(st)];
792 c2.st[v] =
static_cast<std::int8_t
>(np);
793 else if ((c.w & di.atoms_of_var[v]) == di.atoms_of_var[v])
795 else { dead =
true;
break; }
799 dst.push_back(std::move(c2));
808 std::function<Table(
const Table &,
const std::vector<unsigned long> &,
809 const std::vector<unsigned long> &)> lift =
810 [&](
const Table &tab,
const std::vector<unsigned long> &from,
811 const std::vector<unsigned long> &to) -> Table {
814 std::vector<unsigned long> ef, et;
815 for (
unsigned long v : from)
if (isElem(v)) ef.push_back(v);
816 for (
unsigned long v : to)
if (isElem(v)) et.push_back(v);
817 std::vector<unsigned long> gforget, gintro;
818 for (
unsigned long v : from)
819 if (!isElem(v) && !std::binary_search(to.begin(), to.end(), v))
820 gforget.push_back(v);
821 for (
unsigned long v : to)
822 if (!isElem(v) && !std::binary_search(from.begin(), from.end(), v))
825 for (
const auto &[St, g] : tab) {
826 CHECK_FOR_INTERRUPTS();
828 cur.gate_val = St.gate_val;
832 for (
unsigned long v : gforget) {
834 cg = andGate(cg, St.gate_val.at(v) ? inGate(gidx(v)) : notGate(gidx(v)));
835 else if (std::binary_search(St.susp.begin(), St.susp.end(), v)) {
839 cur.gate_val.erase(v);
840 auto it = std::lower_bound(cur.susp.begin(), cur.susp.end(), v);
841 if (it != cur.susp.end() && *it == v)
847 base.gate_val = cur.gate_val;
848 base.susp = cur.susp;
849 remapHoms(St, base, ef, et);
850 std::vector<State> sts = {std::move(base)};
851 std::vector<gate_t> gs = {cg};
852 for (
unsigned long v : gintro) {
853 std::vector<State> ns;
854 std::vector<gate_t> n2;
855 for (std::size_t i = 0; i < sts.size(); ++i)
856 for (
int b = 0; b < 2; ++b) {
858 nv.gate_val[v] =
static_cast<bool>(b);
860 isStrong(slice[gidx(v)].type,
static_cast<bool>(b)))
862 ns.push_back(std::move(nv));
868 for (std::size_t i = 0; i < sts.size(); ++i) {
872 acc[std::move(sts[i])].push_back(gs[i]);
875 return finalize(acc);
878 auto join = [&](
const Table &t1,
const Table &t2) {
880 for (
const auto &[A, ga] : t1)
881 for (
const auto &[B, gb] : t2) {
882 CHECK_FOR_INTERRUPTS();
884 for (
const auto &[k, v] : A.gate_val) {
885 auto it = B.gate_val.find(k);
886 if (it != B.gate_val.end() && it->second != v) {
894 nv.gate_val = A.gate_val;
895 for (
const auto &[k, v] : B.gate_val)
898 for (
unsigned long x : A.susp)
899 if (std::binary_search(B.susp.begin(), B.susp.end(), x))
900 nv.susp.push_back(x);
904 if (A.sat || B.sat) {
909 nv.homs.assign(D, {});
911 for (std::size_t d = 0; d < D; ++d) {
912 const DisjunctInfo &di = q.disjuncts[d];
913 std::vector<DCode> &dst = nv.homs[d];
914 for (
const DCode &c1 : A.homs[d])
915 for (
const DCode &c2 : B.homs[d]) {
918 c.st.resize(di.n_vars);
920 for (
unsigned v = 0; v < di.n_vars; ++v) {
921 const std::int8_t a = c1.st[v], bb = c2.st[v];
923 if (a == UNASSIGNED) r = bb;
924 else if (bb == UNASSIGNED) r = a;
925 else if (a == DONE && bb == DONE) r = DONE;
926 else if (a == DONE || bb == DONE) { good =
false;
break; }
927 else if (a == bb) r = a;
928 else { good =
false;
break; }
933 if (c.w == di.full) sat =
true;
934 dst.push_back(std::move(c));
943 acc[std::move(nv)].push_back(andGate(ga, gb));
945 return finalize(acc);
948 auto applyFacts = [&](Table tab, std::size_t b) {
949 for (std::size_t fi : facts_at_bag[b]) {
952 for (
const auto &[St, g] : tab) {
955 (St.gate_val.count(E + f.
gate) &&
956 St.gate_val.at(E + f.
gate));
957 State ns = present ? closeFact(St, f, edom[b]) : St;
958 acc[std::move(ns)].push_back(g);
966 auto trivialTable = [&](
const std::vector<unsigned long> &d) {
968 z.homs.assign(D, {});
969 for (std::size_t i = 0; i < D; ++i)
971 DCode{std::vector<std::int8_t>(q.disjuncts[i].n_vars,
974 e.emplace(std::move(z), true_gate);
975 return lift(e, {}, d);
981 std::size_t next_child = 0;
983 bool has_table =
false;
984 explicit Frame(
bag_t b) : bag(b) {
987 std::vector<Frame> stack;
988 stack.push_back(Frame(td.
getRoot()));
989 std::size_t root_bag = bagIndex(td.
getRoot());
991 while (!stack.empty()) {
992 Frame &frame = stack.back();
993 const auto &children = td.getChildren(frame.bag);
994 if (frame.next_child < children.size()) {
995 stack.push_back(Frame(children[frame.next_child++]));
998 CHECK_FOR_INTERRUPTS();
999 const std::size_t b = bagIndex(frame.bag);
1000 Table table = frame.has_table ? std::move(frame.table) : trivialTable(dom[b]);
1001 table = applyFacts(std::move(table), b);
1002 if (stack.size() == 1) {
1003 root_table = std::move(table);
1007 Frame &parent = stack[stack.size() - 2];
1008 const std::size_t pb = bagIndex(parent.bag);
1009 Table lifted = lift(table, dom[b], dom[pb]);
1010 if (!parent.has_table) {
1011 parent.table = std::move(lifted);
1012 parent.has_table =
true;
1014 parent.table =
join(parent.table, lifted);
1020 Table top = lift(root_table, dom[root_bag], {});
1021 std::vector<gate_t> accepting;
1022 for (
const auto &[s, g] : top)
1024 accepting.push_back(g);
1026 if (accepting.empty()) {
1029 }
else if (accepting.size() == 1) {
1030 root = accepting[0];
1034 for (
gate_t g : accepting)
1039 result.
stats = std::move(stats);
1062 std::vector<unsigned> head_vars;
1063 std::vector<int> slot_of_var;
1064 std::size_t n_head()
const {
return head_vars.size(); }
1068constexpr unsigned long NO_VAL =
static_cast<unsigned long>(-1);
1073 std::vector<std::int8_t> st;
1074 std::uint64_t w = 0;
1075 std::vector<unsigned long> hval;
1078 return w == o.w && st == o.st && hval == o.hval;
1081 if (w != o.w)
return w < o.w;
1082 if (st != o.st)
return st < o.st;
1083 return hval < o.hval;
1090 std::vector<std::vector<ADCode> > homs;
1091 std::vector<std::vector<unsigned long> > done;
1094 return done == o.done && homs == o.homs;
1099 std::size_t operator()(
const AState &s)
const noexcept {
1100 std::uint64_t h = 1469598103934665603ull;
1101 auto mix = [&](std::uint64_t x) { h ^= x; h *= 1099511628211ull; };
1102 for (
const auto &t : s.done) {
1103 mix(0xD0E + t.size());
1104 for (
unsigned long e : t) mix(e * 0x9e3779b97f4a7c15ull);
1106 for (
const auto &codes : s.homs) {
1108 for (
const auto &c : codes) {
1110 for (std::int8_t v : c.st)
1111 mix(
static_cast<std::uint64_t
>(
static_cast<std::uint8_t
>(v)));
1112 for (
unsigned long e : c.hval) mix(e + 0x9e37);
1115 return static_cast<std::size_t
>(h);
1119inline void canonicalizeA(std::vector<ADCode> &codes)
1121 std::sort(codes.begin(), codes.end());
1122 codes.erase(std::unique(codes.begin(), codes.end()), codes.end());
1126inline void addDone(std::vector<std::vector<unsigned long> > &done,
1127 const std::vector<unsigned long> &t)
1129 auto it = std::lower_bound(done.begin(), done.end(), t);
1130 if (it == done.end() || *it != t)
1135inline std::vector<unsigned long> readHeadTuple(
1136 const ADCode &c,
const HeadInfo &hi,
1137 const std::vector<unsigned long> &domain)
1139 std::vector<unsigned long> t(hi.n_head());
1140 for (std::size_t i = 0; i < hi.n_head(); ++i) {
1141 const unsigned v = hi.head_vars[i];
1142 const std::int8_t s = c.st[v];
1144 t[i] = domain[
static_cast<std::size_t
>(s)];
1149 "compileAnswersOneDP: head variable unbound at completion "
1150 "(head must occur in every disjunct)");
1163void closeDisjunctA(
const DisjunctInfo &di,
const HeadInfo &hi,
1164 std::vector<ADCode> &codes,
const Fact &fact,
1165 const std::vector<unsigned long> &domain,
1166 std::vector<std::vector<unsigned long> > &completions)
1168 std::vector<std::size_t> cand;
1169 for (std::size_t ai = 0; ai < di.atoms.size(); ++ai)
1170 if (di.atoms[ai].relation_id == fact.
relation_id &&
1171 di.atoms[ai].vars.size() == fact.
elements.size())
1176 std::vector<int> pos(fact.
elements.size());
1177 for (std::size_t i = 0; i < fact.
elements.size(); ++i)
1178 pos[i] = positionIn(domain, fact.
elements[i]);
1180 std::set<ADCode> seen(codes.begin(), codes.end());
1181 std::vector<ADCode> work(codes.begin(), codes.end());
1183 while (!work.empty()) {
1184 ADCode c = std::move(work.back());
1186 for (std::size_t ai : cand) {
1187 if ((c.w >> ai) & 1u)
1189 const Atom &a = di.atoms[ai];
1192 for (std::size_t i = 0; i < a.
vars.size(); ++i) {
1193 const unsigned v = a.
vars[i];
1194 const std::int8_t p =
static_cast<std::int8_t
>(pos[i]);
1195 if (c2.st[v] == UNASSIGNED)
1197 else if (c2.st[v] != p) {
1204 c2.w |= (std::uint64_t{1} << ai);
1205 if (seen.insert(c2).second) {
1206 if (c2.w == di.full)
1207 completions.push_back(readHeadTuple(c2, hi, domain));
1215 for (
const ADCode &c : seen)
1218 canonicalizeA(codes);
1230AState forgetLiftA(
const QueryCtx &q,
const HeadInfo &hi,
const AState &s,
1231 const std::vector<unsigned long> &from_domain,
1232 const std::vector<unsigned long> &to_domain)
1234 if (from_domain == to_domain)
1236 std::vector<int> map(from_domain.size());
1237 for (std::size_t i = 0; i < from_domain.size(); ++i) {
1238 auto it = std::lower_bound(to_domain.begin(), to_domain.end(),
1240 map[i] = (it != to_domain.end() && *it == from_domain[i])
1241 ?
static_cast<int>(it - to_domain.begin()) : -1;
1246 out.homs.resize(q.disjuncts.size());
1247 for (std::size_t d = 0; d < q.disjuncts.size(); ++d) {
1248 const DisjunctInfo &di = q.disjuncts[d];
1249 std::vector<ADCode> &dst = out.homs[d];
1250 for (
const ADCode &c : s.homs[d]) {
1253 c2.st.resize(di.n_vars);
1256 for (
unsigned v = 0; v < di.n_vars; ++v) {
1257 const std::int8_t st = c.st[v];
1258 if (st == UNASSIGNED || st == DONE) {
1261 const int np = map[
static_cast<std::size_t
>(st)];
1263 c2.st[v] =
static_cast<std::int8_t
>(np);
1264 }
else if ((c.w & di.atoms_of_var[v]) == di.atoms_of_var[v]) {
1266 const int slot = hi.slot_of_var[v];
1268 c2.hval[
static_cast<std::size_t
>(slot)] =
1269 from_domain[
static_cast<std::size_t
>(st)];
1277 dst.push_back(std::move(c2));
1285AState joinA(
const QueryCtx &q,
const HeadInfo &hi,
1286 const std::vector<unsigned long> &domain,
1287 const AState &s1,
const AState &s2)
1291 for (
const auto &t : s2.done)
1292 addDone(out.done, t);
1293 out.homs.resize(q.disjuncts.size());
1294 for (std::size_t d = 0; d < q.disjuncts.size(); ++d) {
1295 const DisjunctInfo &di = q.disjuncts[d];
1296 std::vector<ADCode> &dst = out.homs[d];
1297 for (
const ADCode &c1 : s1.homs[d])
1298 for (
const ADCode &c2 : s2.homs[d]) {
1301 c.st.resize(di.n_vars);
1302 c.hval.assign(hi.n_head(), NO_VAL);
1304 for (
unsigned v = 0; v < di.n_vars; ++v) {
1305 const std::int8_t a = c1.st[v];
1306 const std::int8_t b = c2.st[v];
1308 if (a == UNASSIGNED)
1310 else if (b == UNASSIGNED)
1312 else if (a == DONE && b == DONE)
1314 else if (a == DONE || b == DONE) {
1325 const int slot = hi.slot_of_var[v];
1326 if (r == DONE && slot >= 0) {
1327 const unsigned long v1 = (a == DONE)
1328 ? c1.hval[
static_cast<std::size_t
>(slot)] : NO_VAL;
1329 const unsigned long v2 = (b == DONE)
1330 ? c2.hval[
static_cast<std::size_t
>(slot)] : NO_VAL;
1331 if (v1 != NO_VAL && v2 != NO_VAL && v1 != v2) {
1335 c.hval[
static_cast<std::size_t
>(slot)] =
1336 (v1 != NO_VAL) ? v1 : v2;
1342 addDone(out.done, readHeadTuple(c, hi, domain));
1344 dst.push_back(std::move(c));
1352AState trivialAState(
const QueryCtx &q,
const HeadInfo &hi)
1355 s.homs.resize(q.disjuncts.size());
1356 for (std::size_t d = 0; d < q.disjuncts.size(); ++d)
1357 s.homs[d].push_back(
1358 ADCode{std::vector<std::int8_t>(q.disjuncts[d].n_vars, UNASSIGNED), 0,
1359 std::vector<unsigned long>(hi.n_head(), NO_VAL)});
1368HeadInfo buildHeadInfo(
const QueryCtx &q,
const std::vector<unsigned> &head_vars)
1371 hi.head_vars = head_vars;
1373 for (
const auto &di : q.disjuncts)
1374 maxv = std::max(maxv, di.n_vars);
1375 hi.slot_of_var.assign(maxv, -1);
1376 for (std::size_t i = 0; i < head_vars.size(); ++i) {
1377 if (head_vars[i] >= maxv)
1379 hi.slot_of_var[head_vars[i]] =
static_cast<int>(i);
1381 for (
const auto &di : q.disjuncts)
1382 for (
unsigned hv : head_vars)
1383 if (hv >= di.n_vars || di.atoms_of_var[hv] == 0)
1385 "compileAnswersOneDP: a head variable does not occur in a disjunct");
1396 std::map<unsigned long, bool> gate_val;
1397 std::vector<unsigned long> susp;
1400 return gate_val == o.gate_val && susp == o.susp && core == o.core;
1405 std::size_t operator()(
const CState &s)
const noexcept {
1406 std::uint64_t h =
static_cast<std::uint64_t
>(AStateHash{}(s.core));
1407 auto mix = [&](std::uint64_t x) { h ^= x; h *= 1099511628211ull; };
1408 for (
const auto &[g, v] : s.gate_val)
1409 mix((g << 1) | (v ? 1u : 0u));
1410 for (
unsigned long g : s.susp)
1411 mix(g * 0x9e3779b97f4a7c15ull);
1412 return static_cast<std::size_t
>(h);
1431 unsigned max_treewidth, std::size_t max_states)
1437 const std::vector<SliceGate> &slice = enc.
slice;
1438 const std::size_t D = q.disjuncts.size();
1439 auto isElem = [&](
unsigned long v) {
return v < E; };
1440 auto gidx = [&](
unsigned long v) {
return static_cast<std::size_t
>(v - E); };
1444 unsigned max_degree = 0;
1447 std::unordered_map<unsigned long, bag_t> elim;
1451 const std::size_t nb_bags = td.
getNbBags();
1454 auto bagIdx = [](
bag_t b) {
1455 return static_cast<std::underlying_type<bag_t>::type
>(b);
1458 std::vector<std::vector<unsigned long> > dom(nb_bags), edom(nb_bags);
1459 for (std::size_t b = 0; b < nb_bags; ++b) {
1461 dom[b].push_back(
static_cast<std::underlying_type<gate_t>::type
>(g));
1462 std::sort(dom[b].begin(), dom[b].end());
1463 dom[b].erase(std::unique(dom[b].begin(), dom[b].end()), dom[b].end());
1464 for (
unsigned long v : dom[b])
1466 edom[b].push_back(v);
1468 std::vector<std::vector<std::size_t> > facts_at_bag(nb_bags);
1469 for (std::size_t fi = 0; fi < enc.
facts.size(); ++fi) {
1471 std::vector<unsigned long> cl = f.
elements;
1473 cl.push_back(E + f.
gate);
1474 bag_t best = elim.at(cl[0]);
1475 for (
unsigned long v : cl)
1476 if (bagIdx(elim.at(v)) < bagIdx(best))
1478 facts_at_bag[bagIdx(best)].push_back(fi);
1489 const unsigned long NV = E + slice.size();
1490 std::vector<unsigned long> uf(NV);
1491 for (
unsigned long v = 0; v < NV; ++v) uf[v] = v;
1492 std::function<
unsigned long(
unsigned long)> ufind =
1493 [&](
unsigned long x) {
while (uf[x] != x) { uf[x] = uf[uf[x]]; x = uf[x]; }
return x; };
1494 auto uunion = [&](
unsigned long a,
unsigned long b) { uf[ufind(a)] = ufind(b); };
1496 std::vector<unsigned long> cl = f.
elements;
1498 cl.push_back(E + f.
gate);
1499 for (std::size_t i = 1; i < cl.size(); ++i)
1500 uunion(cl[0], cl[i]);
1504 using Table = std::unordered_map<CState, gate_t, CStateHash>;
1505 using Accumulator = std::unordered_map<CState, std::vector<gate_t>, CStateHash>;
1506 const gate_t invalid{
static_cast<std::underlying_type<gate_t>::type
>(-1)};
1509 std::vector<gate_t> ev_in(slice.size(), invalid), ev_not(slice.size(), invalid);
1510 auto inGate = [&](std::size_t i) {
1511 if (ev_in[i] == invalid)
1516 auto notGate = [&](std::size_t i) {
1517 if (ev_not[i] == invalid) {
1519 dd.
addWire(ev_not[i], inGate(i));
1524 if (a == true_gate)
return b;
1525 if (b == true_gate)
return a;
1532 auto orGates = [&](
const std::vector<gate_t> &gs) {
1533 if (gs.size() == 1)
return gs[0];
1539 auto finalize = [&](Accumulator &acc) {
1541 t.reserve(acc.size());
1543 t.emplace(e.first, orGates(e.second));
1546 if (t.size() > max_states)
1548 "joint DP state space exceeds the per-node bound (" +
1549 std::to_string(max_states) +
")");
1554 auto almost = [&](
const std::map<unsigned long, bool> &gv) {
1555 for (
const auto &[g, gval] : gv) {
1558 auto it = gv.find(E + c);
1559 if (it == gv.end())
continue;
1560 const bool cval = it->second;
1562 if (gval && !cval)
return false;
1564 if (!gval && cval)
return false;
1566 if (gval == cval)
return false;
1572 auto justify = [&](std::map<unsigned long, bool> &gv,
1573 std::vector<unsigned long> &susp) {
1574 std::vector<unsigned long> keep;
1575 for (
unsigned long g : susp) {
1577 const bool gval = gv.at(g);
1580 auto it = gv.find(E + c);
1581 if (it == gv.end())
continue;
1582 const bool cval = it->second;
1587 if (!just) keep.push_back(g);
1589 susp = std::move(keep);
1591 auto addSusp = [](std::vector<unsigned long> &susp,
unsigned long g) {
1592 auto it = std::lower_bound(susp.begin(), susp.end(), g);
1593 if (it == susp.end() || *it != g) susp.insert(it, g);
1596 std::map<std::vector<unsigned long>, std::vector<gate_t> > answer_acc;
1600 auto applyFacts = [&](Table table, std::size_t b) {
1601 for (std::size_t fi : facts_at_bag[b]) {
1604 for (
const auto &[St, g] : table) {
1605 CHECK_FOR_INTERRUPTS();
1606 const bool present =
1608 (St.gate_val.count(E + f.
gate) &&
1609 St.gate_val.at(E + f.
gate));
1612 std::vector<std::vector<unsigned long> > comp;
1613 for (std::size_t d = 0; d < D; ++d)
1614 closeDisjunctA(q.disjuncts[d], hi, ns.core.homs[d],
1616 for (
const auto &t : comp)
1617 addDone(ns.core.done, t);
1619 acc[std::move(ns)].push_back(g);
1621 table = finalize(acc);
1629 std::function<Table(
const Table &,
const std::vector<unsigned long> &,
1630 const std::vector<unsigned long> &)> lift =
1631 [&](
const Table &tab,
const std::vector<unsigned long> &from,
1632 const std::vector<unsigned long> &to) -> Table {
1635 std::vector<unsigned long> ef, et;
1636 for (
unsigned long v : from)
if (isElem(v)) ef.push_back(v);
1637 for (
unsigned long v : to)
if (isElem(v)) et.push_back(v);
1638 std::vector<unsigned long> gforget, gintro;
1639 for (
unsigned long v : from)
1640 if (!isElem(v) && !std::binary_search(to.begin(), to.end(), v))
1641 gforget.push_back(v);
1642 for (
unsigned long v : to)
1643 if (!isElem(v) && !std::binary_search(from.begin(), from.end(), v))
1644 gintro.push_back(v);
1646 for (
const auto &[St, g] : tab) {
1647 CHECK_FOR_INTERRUPTS();
1648 std::map<unsigned long, bool> cur_gv = St.gate_val;
1649 std::vector<unsigned long> cur_susp = St.susp;
1652 for (
unsigned long v : gforget) {
1654 cg = andGate(cg, St.gate_val.at(v) ? inGate(gidx(v)) : notGate(gidx(v)));
1655 else if (std::binary_search(St.susp.begin(), St.susp.end(), v)) {
1660 auto it = std::lower_bound(cur_susp.begin(), cur_susp.end(), v);
1661 if (it != cur_susp.end() && *it == v)
1667 AState core = forgetLiftA(q, hi, St.core, ef, et);
1672 std::vector<std::vector<unsigned long> > keep;
1673 for (
const auto &tup : core.done) {
1674 const unsigned long cv = ufind(tup[0]);
1675 bool present =
false;
1676 for (
unsigned long u : to)
1677 if (ufind(u) == cv) { present =
true;
break; }
1679 answer_acc[tup].push_back(cg);
1681 keep.push_back(tup);
1683 core.done = std::move(keep);
1685 std::vector<CState> sts;
1686 std::vector<gate_t> gs;
1687 sts.push_back(CState{std::move(core), std::move(cur_gv),
1688 std::move(cur_susp)});
1690 for (
unsigned long v : gintro) {
1691 std::vector<CState> ns;
1692 std::vector<gate_t> n2;
1693 for (std::size_t i = 0; i < sts.size(); ++i)
1694 for (
int bv = 0; bv < 2; ++bv) {
1696 nv.gate_val[v] =
static_cast<bool>(bv);
1698 isStrong(slice[gidx(v)].type,
static_cast<bool>(bv)))
1699 addSusp(nv.susp, v);
1700 ns.push_back(std::move(nv));
1701 n2.push_back(gs[i]);
1703 sts = std::move(ns);
1706 for (std::size_t i = 0; i < sts.size(); ++i) {
1707 justify(sts[i].gate_val, sts[i].susp);
1708 if (!almost(sts[i].gate_val))
1710 acc[std::move(sts[i])].push_back(gs[i]);
1713 return finalize(acc);
1716 auto join = [&](
const Table &t1,
const Table &t2,
1717 const std::vector<unsigned long> &edomain) {
1719 for (
const auto &[A, ga] : t1)
1720 for (
const auto &[B, gb] : t2) {
1721 CHECK_FOR_INTERRUPTS();
1723 for (
const auto &[k, v] : A.gate_val) {
1724 auto it = B.gate_val.find(k);
1725 if (it != B.gate_val.end() && it->second != v) {
1733 nv.gate_val = A.gate_val;
1734 for (
const auto &[k, v] : B.gate_val)
1736 for (
unsigned long x : A.susp)
1737 if (std::binary_search(B.susp.begin(), B.susp.end(), x))
1738 nv.susp.push_back(x);
1739 justify(nv.gate_val, nv.susp);
1740 if (!almost(nv.gate_val))
1742 nv.core = joinA(q, hi, edomain, A.core, B.core);
1743 acc[std::move(nv)].push_back(andGate(ga, gb));
1745 return finalize(acc);
1748 auto trivialTable = [&](
const std::vector<unsigned long> &d) {
1750 z.core = trivialAState(q, hi);
1752 e.emplace(std::move(z), true_gate);
1753 return lift(e, {}, d);
1759 std::size_t next_child = 0;
1761 bool has_table =
false;
1762 explicit Frame(
bag_t b) : bag(b) {}
1765 std::size_t root_bag = bagIdx(td.
getRoot());
1767 std::vector<Frame> stack;
1768 stack.push_back(Frame(td.
getRoot()));
1769 while (!stack.empty()) {
1770 Frame &frame = stack.back();
1772 if (frame.next_child < children.size()) {
1773 stack.push_back(Frame(children[frame.next_child++]));
1776 CHECK_FOR_INTERRUPTS();
1777 const std::size_t b = bagIdx(frame.bag);
1778 Table table = frame.has_table ? std::move(frame.table) : trivialTable(dom[b]);
1779 table = applyFacts(std::move(table), b);
1780 if (stack.size() == 1) {
1781 root_table = std::move(table);
1785 Frame &parent = stack[stack.size() - 2];
1786 const std::size_t pb = bagIdx(parent.bag);
1787 Table lifted = lift(table, dom[b], dom[pb]);
1788 if (!parent.has_table) {
1789 parent.table = std::move(lifted);
1790 parent.has_table =
true;
1792 parent.table =
join(parent.table, lifted, edom[pb]);
1799 lift(root_table, dom[root_bag], {});
1802 result.
answers.reserve(answer_acc.size());
1803 for (
auto &entry : answer_acc)
1812 const std::vector<unsigned> &head_vars,
1813 unsigned max_treewidth, std::size_t max_states)
1821 buildQueryCtx(ucq, enc, q, stats);
1822 HeadInfo hi = buildHeadInfo(q, head_vars);
1829 return compileAnswersOneDPCorrelated(enc, ucq, q, hi, std::move(stats),
1830 max_treewidth, max_states);
1848 unsigned max_treewidth,
1849 std::size_t max_states,
1851 const std::unordered_map<unsigned long, bag_t> *shared_elim,
1852 const std::map<unsigned, unsigned long> *head_pin)
1857 dDNNF &dd = result.dd;
1858 Stats &stats = result.stats;
1867 buildQueryCtx(ucq, enc, q, stats);
1875 return mergedCompile(enc, q, stats, max_treewidth, max_states,
1876 shared_td, shared_elim, head_pin);
1885 std::unordered_map<unsigned long, bag_t> elim_local;
1886 std::unique_ptr<TreeDecomposition> built_td;
1888 if (tdp ==
nullptr) {
1890 unsigned max_degree = 0;
1895 if (built_td->getTreewidth() > max_treewidth)
1897 tdp = built_td.get();
1900 const std::unordered_map<unsigned long, bag_t> &elimination_bag =
1901 shared_elim ? *shared_elim : elim_local;
1904 const std::size_t nb_bags = td.
getNbBags();
1910 std::vector<std::vector<std::size_t> > facts_at_bag(nb_bags);
1911 for (std::size_t i = 0; i < enc.
facts.size(); ++i) {
1914 for (
unsigned long e : f.
elements) {
1915 bag_t be = elimination_bag.at(e);
1916 if (bag_index(be) < bag_index(best))
1919 facts_at_bag[bag_index(best)].push_back(i);
1923 std::vector<std::vector<unsigned long> > domains(nb_bags);
1924 for (std::size_t b = 0; b < nb_bags; ++b) {
1925 auto &dom = domains[b];
1928 dom.push_back(
static_cast<std::underlying_type<gate_t>::type
>(g));
1929 std::sort(dom.begin(), dom.end());
1930 dom.erase(std::unique(dom.begin(), dom.end()), dom.end());
1939 using Table = std::unordered_map<State, gate_t, StateHash>;
1940 using Accumulator = std::unordered_map<State, std::vector<gate_t>, StateHash>;
1942 const gate_t invalid_gate{
static_cast<std::underlying_type<gate_t>::type
>(-1)};
1946 std::vector<gate_t> ev_in(enc.
events.size(), invalid_gate);
1947 std::vector<gate_t> ev_not(enc.
events.size(), invalid_gate);
1948 auto inGate = [&](std::size_t e) {
1949 if (ev_in[e] == invalid_gate)
1954 auto notGate = [&](std::size_t e) {
1955 if (ev_not[e] == invalid_gate) {
1957 dd.
addWire(ev_not[e], inGate(e));
1973 auto finalize = [&](Accumulator &acc) {
1975 t.reserve(acc.size());
1976 for (
auto &entry : acc) {
1977 if (entry.second.size() == 1)
1978 t.emplace(entry.first, entry.second[0]);
1982 for (
gate_t c : entry.second)
1984 t.emplace(entry.first, g);
1988 stats.max_states = std::max(stats.max_states, t.size());
1989 if (t.size() > max_states)
1991 "joint DP state space exceeds the per-node bound (" +
1992 std::to_string(max_states) +
")");
1996 const State kTrivial = trivialState(q);
1997 auto trivialTable = [&]() {
1998 return Table{{kTrivial, true_gate}};
2000 auto isTrivial = [&](
const Table &t) {
2001 return t.size() == 1 && t.begin()->second == true_gate &&
2002 t.begin()->first == kTrivial;
2008 auto lift = [&](
const Table &t,
const std::vector<unsigned long> &from,
2009 const std::vector<unsigned long> &to) {
2013 for (
const auto &entry : t)
2014 acc[forgetLift(q, entry.first, from, to)].push_back(
2016 return finalize(acc);
2020 auto joinTables = [&](
const Table &t1,
const Table &t2) {
2026 for (
const auto &left : t1)
2027 for (
const auto &right : t2) {
2028 CHECK_FOR_INTERRUPTS();
2029 acc[
join(q, left.first, right.first)].push_back(
2030 andGate(left.second, right.second));
2032 return finalize(acc);
2036 auto applyFacts = [&](Table table, std::size_t b) {
2037 const auto &domain = domains[b];
2038 for (std::size_t fi : facts_at_bag[b]) {
2041 for (
const auto &entry : table) {
2042 CHECK_FOR_INTERRUPTS();
2043 State present = closeWithFact(q, entry.first, fact,
2046 acc[std::move(present)].push_back(entry.second);
2047 }
else if (present == entry.first) {
2050 acc[entry.first].push_back(entry.second);
2052 acc[std::move(present)].push_back(
2053 andGate(entry.second, inGate(fact.
event)));
2054 acc[entry.first].push_back(
2055 andGate(entry.second, notGate(fact.
event)));
2058 table = finalize(acc);
2070 std::size_t next_child = 0;
2072 bool has_table =
false;
2073 explicit Frame(
bag_t b) : bag(b) {
2079 std::vector<Frame> stack;
2080 stack.push_back(Frame(td.
getRoot()));
2081 while (!stack.empty()) {
2082 Frame &frame = stack.back();
2084 if (frame.next_child < children.size()) {
2085 bag_t c = children[frame.next_child++];
2086 stack.push_back(Frame(c));
2090 CHECK_FOR_INTERRUPTS();
2091 const std::size_t b = bag_index(frame.bag);
2092 Table table = frame.has_table ? std::move(frame.table) : trivialTable();
2093 table = applyFacts(std::move(table), b);
2095 if (stack.size() == 1) {
2096 root_table = std::move(table);
2101 Frame &parent = stack[stack.size() - 2];
2102 const std::size_t pb = bag_index(parent.bag);
2103 Table lifted = lift(table, domains[b], domains[pb]);
2104 if (!parent.has_table) {
2105 parent.table = std::move(lifted);
2106 parent.has_table =
true;
2108 parent.table = joinTables(parent.table, lifted);
2120 std::vector<gate_t> accepting;
2121 for (
const auto &[s, g] : root_table)
2123 accepting.push_back(g);
2126 if (accepting.empty()) {
2129 }
else if (accepting.size() == 1) {
2130 root = accepting[0];
2134 for (
gate_t g : accepting)
2145 unsigned max_treewidth,
2146 std::size_t max_states)
2148 return compileImpl(enc, ucq, max_treewidth, max_states,
2149 nullptr,
nullptr,
nullptr);
2155 const std::vector<unsigned> &head_vars,
2156 unsigned max_treewidth,
2157 std::size_t max_states)
2159 return compileAnswersOneDPImpl(enc, ucq, head_vars, max_treewidth,
constexpr unsigned DNNF_CERT_INFO
d-DNNF certificate value for the (gate-type-specific) per-gate info field.
@ NOT
Logical negation of a single child gate.
@ OR
Logical disjunction of child gates.
@ AND
Logical conjunction of child gates.
@ IN
Input (variable) gate representing a base tuple.
bool operator<(gate_t t, std::vector< gate_t >::size_type u)
Compare a gate_t against a std::vector size type.
gate_t
Strongly-typed gate identifier.
SliceGateType
Gate kind of a circuit-slice node (correlated regime).
@ GATE
Present iff its slice gate evaluates true (correlated regime).
@ CERTAIN
Always present: an untracked relation, constant-true token.
bag_t
Strongly-typed bag identifier for a tree decomposition.
static UCQJointCompiler::Result compileImpl(const JointEncoding &enc, const UCQ &ucq, unsigned max_treewidth, std::size_t max_states, const TreeDecomposition *shared_td, const std::unordered_map< unsigned long, bag_t > *shared_elim, const std::map< unsigned, unsigned long > *head_pin)
Data-graph (TID/BID) compile, with optional decomposition reuse and an optional in-DP head pin.
Phase C of the joint-width UCQ compiler: a UCQ-specialised homomorphism-type DP that runs directly ov...
gate_t setGate(BooleanGate type) override
Allocate a new gate with type type and no UUID.
void setInfo(gate_t g, unsigned info)
Store an integer annotation on gate g.
void addWire(gate_t f, gate_t t)
Add a directed wire from gate f (parent) to gate t (child).
std::vector< gate_t >::size_type getNbGates() const
Return the total number of gates in the circuit.
Mutable adjacency-list graph over unsigned-long node IDs.
Exception thrown when joint-width compilation cannot proceed.
The joint encoding of an instance: facts, world events, and the joint graph the screen and the DP run...
Graph buildGraph() const
Construct the joint graph for the screen and the DP.
std::vector< Event > events
Independent world variables (data-graph regime).
bool correlated
true when the slice is present (correlated regime).
unsigned circuit_treewidth_lb
Degeneracy lower bound of the slice-only graph (0 in the data-graph regime).
std::vector< Fact > facts
Deduplicated facts.
std::vector< SliceGate > slice
Circuit slice (correlated regime); gate vertex = n_elements + index.
unsigned data_treewidth_lb
Degeneracy lower bound of the data-only graph (diagnostics).
unsigned long n_elements
One past the largest domain element id (vertex ids [0,n_elements)).
Exception thrown when a tree decomposition cannot be constructed.
Tree decomposition of a Boolean circuit's primal graph.
std::size_t getNbBags() const
Return the number of bags in the decomposition.
static unsigned degeneracyLowerBound(const BooleanCircuit &bc, unsigned &max_degree)
Cheap degeneracy lower bound on the treewidth of bc's primal graph.
unsigned getTreewidth() const
Return the treewidth of this decomposition.
bag_t getRoot() const
Return the root bag of the decomposition.
Bag & getBag(bag_t b)
Mutable access to bag b.
std::vector< bag_t > & getChildren(bag_t b)
Mutable access to the children of bag b.
static AnswerCircuit compileAnswersOneDP(const JointEncoding &enc, const UCQ &ucq, const std::vector< unsigned > &head_vars, unsigned max_treewidth=TreeDecomposition::MAX_TREEWIDTH, std::size_t max_states=DEFAULT_MAX_STATES)
static Result compile(const JointEncoding &enc, const UCQ &ucq, unsigned max_treewidth=TreeDecomposition::MAX_TREEWIDTH, std::size_t max_states=DEFAULT_MAX_STATES)
Compile a Boolean UCQ over enc into a certified d-D.
A d-DNNF circuit supporting exact probabilistic and game-theoretic evaluation.
void setRoot(gate_t g)
Set the root gate.
constexpr bool isStrong(BooleanGate type, bool value)
Return true if assigning value to a gate of type type is a "strong" assignment.
bool operator==(const pg_uuid_t &u, const pg_uuid_t &v)
Test two pg_uuid_t values for equality.
One atom of a conjunctive query: a relation symbol applied to query variables.
std::vector< unsigned > vars
Query-variable indices, one per column.
unsigned relation_id
Dense id of the relation symbol.
One conjunctive query (a disjunct of the UCQ).
std::vector< Atom > atoms
The conjuncts.
unsigned n_vars
Number of query variables.
A deduplicated fact participating in the DP.
std::size_t event
Index into events (when INDEP).
std::size_t gate
Index into slice (when GATE).
std::vector< unsigned long > elements
Dense element ids (the fact's tuple).
FactGateKind kind
How the fact's presence is gated.
unsigned relation_id
Dense id of the relation symbol.
One node of the extracted circuit slice (correlated regime).
SliceGateType type
Node kind.
std::vector< unsigned > children
Child slice indices (≤ 2; empty for INPUT).
std::size_t max_states
Peak DP state count.
dDNNF dd
The shared certified d-D.
std::vector< AnswerRoot > answers
One root per discovered answer.
Per-answer evaluation by a single top-down DP (data-graph regime).
A compiled UCQ: the d-D and its statistics.
Stats stats
Compilation statistics.
dDNNF dd
d-D whose root computes "the UCQ holds"; input gates carry the event tokens and probabilities.
Structural statistics of a compilation, for diagnostics and tests.
std::size_t nb_variables
Number of world variables (events).
unsigned joint_treewidth
Treewidth of the min-fill decomposition of the joint graph.
std::vector< unsigned > n_enumerating
Per-disjunct static count of enumerating variables.
std::size_t dd_size
Number of gates of the emitted d-D.
std::size_t max_states
Maximum number of DP states at any node.
unsigned data_treewidth_lb
Degeneracy lower bound of the data-only graph.
unsigned circuit_treewidth_lb
Degeneracy lower bound of the slice-only graph.
std::size_t nb_bags
Number of bags of the decomposition.
A union of conjunctive queries.
std::vector< CQ > disjuncts
The disjuncts (at least one).
size_t size() const
Return the number of elements in the set.
Build-loop interrupt hook for the standalone tdkc binary.