29constexpr std::size_t DNF_TERM_LIMIT = 8'000'000;
30constexpr std::size_t DECOMP_VAR_LIMIT = 1024;
36constexpr std::size_t DECOMP_TERM_LIMIT = 512;
38inline bool contains(
const std::vector<int> &term,
int v)
40 return std::binary_search(term.begin(), term.end(), v);
51 return fs == o.
fs &&
d == o.
d;
57 std::size_t h = 1469598103934665603ull;
58 auto mix = [&](std::size_t x){ h ^= x + 0x9e3779b97f4a7c15ull + (h<<6) + (h>>2); };
59 for (
const auto &t : k.
d) {
60 for (
int v : t) mix((std::size_t) (
unsigned) v);
63 mix((std::size_t) k.
fs);
72 std::sort(t.begin(), t.end());
73 t.erase(std::unique(t.begin(), t.end()), t.end());
76 for (
const auto &t : d)
80 std::sort(d.begin(), d.end());
81 d.erase(std::unique(d.begin(), d.end()), d.end());
89 if (d.size() > DECOMP_TERM_LIMIT)
91 std::vector<char> dead(d.size(), 0);
92 for (std::size_t i = 0; i < d.size(); ++i) {
93 if (dead[i])
continue;
94 for (std::size_t j = 0; j < d.size(); ++j) {
95 if (i == j || dead[j] || d[i].
size() > d[j].
size())
97 bool subset = std::includes(d[j].begin(), d[j].end(),
98 d[i].begin(), d[i].end());
104 for (std::size_t i = 0; i < d.size(); ++i)
106 out.push_back(std::move(d[i]));
114 out.reserve(d.size());
115 for (
const auto &t : d) {
116 bool has = contains(t, v);
118 if (!has) { out.push_back(t);
continue; }
120 nt.reserve(t.size() - 1);
121 for (
int x : t)
if (x != v) nt.push_back(x);
122 out.push_back(std::move(nt));
124 if (!has) out.push_back(t);
130std::vector<StructuredDNNFBuilder::DNF>
135 for (
const auto &t : d)
for (
int v : t) vset.insert(v);
136 std::vector<int> vars(vset.begin(), vset.end());
137 const std::size_t V = vars.size(), T = d.size();
138 if (V <= 1 || V > DECOMP_VAR_LIMIT || T > DECOMP_TERM_LIMIT)
141 std::map<int, int> idx;
142 for (std::size_t i = 0; i < V; ++i) idx[vars[i]] = (
int) i;
145 std::vector<int> cnt(V, 0);
146 std::map<std::pair<int,int>,
int> co;
147 for (
const auto &t : d) {
148 for (
int v : t) cnt[(std::size_t) idx[v]]++;
149 for (std::size_t a = 0; a < t.size(); ++a)
150 for (std::size_t b = a + 1; b < t.size(); ++b) {
151 int u = idx[t[a]], w = idx[t[b]];
152 if (u > w) std::swap(u, w);
160 std::vector<int> uf(V);
161 for (std::size_t i = 0; i < V; ++i) uf[i] = (
int) i;
162 std::function<int(
int)> find = [&](
int x){
while (uf[x]!=x){ uf[x]=uf[uf[x]]; x=uf[x]; }
return x; };
163 auto unite = [&](
int a,
int b){ a=find(a); b=find(b);
if(a!=b) uf[a]=b; };
165 auto projcard = [&](
int u){
return 1 + (cnt[(std::size_t)u] < (int) T ? 1 : 0); };
166 for (std::size_t a = 0; a < V; ++a)
167 for (std::size_t b = a + 1; b < V; ++b) {
168 int u = (int) a, w = (
int) b;
170 auto it = co.find({u, w});
171 if (it != co.end()) n11 = it->second;
172 int n10 = cnt[a] - n11, n01 = cnt[b] - n11;
173 int n00 = (int) T - cnt[a] - cnt[b] + n11;
174 int card = (n11>0) + (n10>0) + (n01>0) + (n00>0);
175 if (card != projcard(u) * projcard(w))
180 std::map<int, std::vector<int>> blocks;
181 for (std::size_t i = 0; i < V; ++i)
182 blocks[find((
int) i)].push_back(vars[i]);
183 if (blocks.size() <= 1)
189 std::vector<std::set<int>> blockset;
190 std::vector<std::vector<int>> blockvars;
191 for (
auto &kv : blocks) {
192 blockset.emplace_back(kv.second.begin(), kv.second.end());
193 blockvars.push_back(kv.second);
195 std::vector<std::set<Term>> proj(blockset.size());
196 for (
const auto &t : d)
197 for (std::size_t bi = 0; bi < blockset.size(); ++bi) {
199 for (
int v : t)
if (blockset[bi].count(v)) p.push_back(v);
200 proj[bi].insert(std::move(p));
202 std::size_t prod = 1;
203 for (
const auto &s : proj) prod *= s.size();
207 std::vector<DNF> parts;
208 for (
auto &s : proj) {
209 DNF part(s.begin(), s.end());
210 parts.push_back(
canonical(std::move(part)));
217 const std::map<gate_t, int> &rank,
218 std::map<gate_t, DNF> &memo)
const
220 auto mit = memo.find(g);
221 if (mit != memo.end())
return mit->second;
226 auto rit = rank.find(g);
227 if (rit == rank.end())
229 "in the supplied variable order");
230 res =
DNF{
Term{ rit->second } };
238 if (res.size() * cd.size() > DNF_TERM_LIMIT)
241 prod.reserve(res.size() * cd.size());
242 for (
const auto &ta : res)
243 for (
const auto &tb : cd) {
245 t.insert(t.end(), tb.begin(), tb.end());
246 prod.push_back(std::move(t));
255 for (
auto &t : cd) res.push_back(std::move(t));
256 if (res.size() > DNF_TERM_LIMIT)
265 "(NOT, multivalued input or non-Boolean gate)");
267 memo.emplace(g, res);
281 if (
in_gate_[(std::size_t) v] == NO_GATE)
290 if (
not_gate_[(std::size_t) v] == NO_GATE) {
300 std::vector<gate_t> real;
301 for (
gate_t c : children) {
307 if (real.size() == 1)
return real[0];
313std::vector<StructuredDNNFBuilder::DNF>
318 for (
const auto &t : d)
for (
int v : t) vset.insert(v);
319 if (vset.size() <= 1)
return { d };
320 std::vector<int> vars(vset.begin(), vset.end());
321 std::map<int,int> idx;
322 for (std::size_t i = 0; i < vars.size(); ++i) idx[vars[i]] = (
int) i;
324 std::vector<int> uf(vars.size());
325 for (std::size_t i = 0; i < uf.size(); ++i) uf[i] = (
int) i;
326 std::function<int(
int)> find = [&](
int x){
while (uf[x]!=x){ uf[x]=uf[uf[x]]; x=uf[x]; }
return x; };
328 for (
const auto &t : d)
329 for (std::size_t a = 1; a < t.size(); ++a) {
330 int r0 = find(idx[t[0]]), ra = find(idx[t[a]]);
331 if (r0 != ra) uf[r0] = ra;
335 std::map<int, DNF> comp;
336 for (
const auto &t : d) comp[find(idx[t[0]])].push_back(t);
337 if (comp.size() <= 1)
return { d };
340 std::vector<std::pair<int,DNF>> ordered;
341 for (
auto &kv : comp) {
343 for (
const auto &t : kv.second) mn = std::min(mn, t.front());
344 ordered.push_back({ mn, std::move(kv.second) });
346 std::sort(ordered.begin(), ordered.end(),
347 [](
const auto &a,
const auto &b){ return a.first < b.first; });
348 std::vector<DNF> out;
349 out.reserve(ordered.size());
350 for (
auto &pr : ordered) out.push_back(std::move(pr.second));
357 if (d.empty())
return false_sink;
358 if (d.size() == 1 && d[0].empty())
return true_gate_;
361 auto cit =
cache_.find(key);
362 if (cit !=
cache_.end())
return cit->second;
371 if (comps.size() > 1) {
373 for (
auto it = comps.rbegin(); it != comps.rend(); ++it)
374 tail =
build(*it, tail);
382 std::vector<DNF> parts = (false_sink ==
false_gate_)
384 if (parts.size() > 1) {
385 std::vector<gate_t> ch;
386 ch.reserve(parts.size());
392 for (
const auto &t : d)
if (t[0] < v) v = t[0];
414 const std::map<gate_t, int> &input_rank,
415 std::size_t max_nodes)
419 throw CircuitException(
"StructuredDNNFBuilder: multivalued inputs (BID) are "
420 "out of scope for the inversion-free path");
422 std::size_t ninputs = input_rank.size();
423 prob_.assign(ninputs, 0.0);
424 uuid_.assign(ninputs, std::string());
427 for (
const auto &kv : input_rank) {
428 if (kv.second < 0 || (std::size_t) kv.second >= ninputs)
440 std::map<gate_t, DNF> memo;
441 DNF top =
extract(bc, root, input_rank, memo);
451 std::set<gate_t> seen;
452 std::vector<gate_t> stack{
dd_.root };
453 while (!stack.empty()) {
454 gate_t g = stack.back(); stack.pop_back();
455 if (!seen.insert(g).second)
continue;
456 for (
gate_t c :
dd_.getWires(g)) stack.push_back(c);
BooleanGate
Gate types for a Boolean provenance circuit.
@ 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.
gate_t
Strongly-typed gate identifier.
In-process structured-d-DNNF construction over a query-derived variable order, for the inversion-free...
Boolean circuit for provenance formula evaluation.
bool hasMultivaluedGates() const
Return true if the circuit contains any MULIN gates.
double getProb(gate_t g) const
Return the probability stored for gate g.
Exception type thrown by circuit operations on invalid input.
std::vector< gate_t > & getWires(gate_t g)
Return a mutable reference to the child-wire list of gate g.
gateType getGateType(gate_t g) const
Return the type of gate g.
uuid getUUID(gate_t g) const
Return the UUID string associated with gate g.
gate_t mkNeg(Var v)
shared negative literal NOT(IN)
std::vector< DNF > andDecompose(const DNF &d) const
static DNF canonical(DNF d)
sort, dedup, drop supersets
StructuredDNNFBuilder(const BooleanCircuit &bc, gate_t root, const std::map< gate_t, int > &input_rank, std::size_t max_nodes=0)
DNF extract(const BooleanCircuit &bc, gate_t g, const std::map< gate_t, int > &rank, std::map< gate_t, DNF > &memo) const
std::size_t size() const
d-DNNF gates reachable from the root.
std::vector< std::string > uuid_
rank -> source input UUID (for labels)
std::vector< gate_t > in_gate_
rank -> shared dDNNF IN gate
std::unordered_map< CacheKey, gate_t, CacheKeyHash > cache_
std::vector< gate_t > not_gate_
rank -> shared dDNNF NOT(IN) gate (lazy)
gate_t mkAnd(const std::vector< gate_t > &children)
static DNF condition(const DNF &d, Var v, bool value)
std::vector< double > prob_
prob_[rank] = P(variable)
std::vector< DNF > orDecompose(const DNF &d) const
gate_t false_gate_
empty AND / empty OR terminals
gate_t mkLit(Var v)
shared positive literal IN
gate_t build(const DNF &d, gate_t false_sink)
std::vector< Term > DNF
A sum of products: canonicalised.
std::vector< Var > Term
A product: sorted, duplicate-free vars.
gate_t newGate(BooleanGate type)
setGate + size guard
int Var
Variable rank in [0,ninputs).
std::size_t operator()(const CacheKey &k) const
bool operator==(const CacheKey &o) const