ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
dDNNF.cpp
Go to the documentation of this file.
1/**
2 * @file dDNNF.cpp
3 * @brief d-DNNF circuit operations and evaluation algorithms.
4 *
5 * Implements all methods of @c dDNNF declared in @c dDNNF.h:
6 * - @c vars(): set of reachable input (IN) gates.
7 * - @c makeSmooth(): smooth the circuit so every OR gate's children
8 * mention the same variable set.
9 * - @c makeGatesBinary(): binarise n-ary AND/OR gates.
10 * - @c simplify(): constant propagation.
11 * - @c condition() / @c conditionAndSimplify(): fix one variable.
12 * - @c probabilityEvaluation(): exact probability in linear time.
13 * - @c shapley() / @c banzhaf(): power index computation.
14 * - @c topological_order(): DFS topological sort.
15 *
16 * The private helpers @c shapley_delta() and @c shapley_alpha() implement
17 * the polynomial-time Shapley-value algorithm for d-DNNFs.
18 */
19#include "dDNNF.h"
20#include "Circuit.hpp"
21
22#include <unordered_map>
23#include <stack>
24#include <variant>
25#include <cassert>
26#include <algorithm>
27#include <numeric>
28
29std::unordered_set<gate_t> dDNNF::vars(gate_t root) const
30{
31 // No recursion, so using a stack to handle all children; order of
32 // calls is here irrelevant
33 std::stack<gate_t> to_process;
34 std::unordered_set<gate_t> processed;
35 to_process.push(root);
36
37 std::unordered_set<gate_t> result;
38 while(!to_process.empty())
39 {
40 auto g = to_process.top();
41 to_process.pop();
42
43 if(processed.find(g)==processed.end()) {
45 result.insert(g);
46 else {
47 for(auto c: getWires(g))
48 to_process.push(c);
49 }
50 processed.insert(g);
51 }
52 }
53
54 return result;
55}
56
58{
59 gate_t original_gates_size{gates.size()};
60 // gates.size() might change in the loop, but newly added gates do not
61 // need to be iterated upon
62
63 for(gate_t g = gate_t{0}; g<original_gates_size; ++g) {
64 if(getGateType(g)!=BooleanGate::OR || getWires(g).size()<=1)
65 continue;
66
67 std::unordered_set<gate_t> all;
68 std::vector<std::unordered_set<gate_t> > varss;
69
70 std::for_each(getWires(g).begin(), getWires(g).end(),
71 [&](gate_t x) {
72 varss.push_back(vars(x));
73 });
74 std::for_each(varss.begin(), varss.end(),
75 [&](const auto &s) {
76 all.insert(s.begin(),s.end());
77 });
78
79 for(auto v: all) {
80 bool modified = false;
81 for(size_t i=0; i<varss.size(); ++i) {
82 if(varss[i].find(v)==varss[i].end()) {
83 if(!modified) {
86 addWire(and_gate, getWires(g)[i]);
87 getWires(g)[i]=and_gate;
88 }
89 modified = true;
90 }
91
92 gate_t dummy_or_gate = setGate(BooleanGate::OR);
93 gate_t dummy_not_gate = setGate(BooleanGate::NOT);
94 addWire(dummy_or_gate, v);
95 addWire(dummy_not_gate, v);
96 addWire(dummy_or_gate, dummy_not_gate);
97 addWire(getWires(g)[i],dummy_or_gate);
98 }
99 }
100 }
101 }
102}
103
105{
106 for(gate_t g{0}; g<gates.size(); ++g) {
107 if(getGateType(g)!=type || getWires(g).size()<=2)
108 continue;
109
110 if(getWires(g).size()==3) {
111 const gate_t child = setGate(type);
112 auto &w = getWires(g);
113 for(size_t i=1; i<3; ++i) {
114 addWire(child, w[i]);
115 }
116 w.resize(1);
117 addWire(g, child);
118 } else {
119 const gate_t child1 = setGate(type);
120 const gate_t child2 = setGate(type);
121
122 auto &w = getWires(g);
123 const auto k = w.size();
124
125 for(unsigned i=0; i<k; ++i)
126 if(i<k/2)
127 addWire(child1, w[i]);
128 else
129 addWire(child2, w[i]);
130 w.clear();
131 addWire(g, child1);
132 addWire(g, child2);
133 }
134 }
135}
136
138{
139 if (gates.size() == 0)
140 return 0.;
141
142 // Unfortunately, dDNNFs can be quite deep so we need to simulate
143 // recursion with a heap-based stack, to avoid exhausting the actual
144 // memory stack
145 using RecursionParams = struct {
146 gate_t g;
147 size_t children_processed;
148 double partial_value;
149 };
150 using RecursionResult = double;
151 std::stack<std::variant<RecursionParams,RecursionResult> > stack;
152 stack.emplace(RecursionParams{root,0,0.});
153
154 while(1) {
155 double child_value{0.};
156
157 if(stack.top().index()==1) { // RecursionResult
158 child_value=std::get<1>(stack.top());
159 stack.pop();
160 }
161
162 auto [g, children_processed, partial_value]=std::get<0>(stack.top());
163 stack.pop();
164
165 auto it = probability_cache.find(g);
166
167 if(it!=probability_cache.end()) {
168 if(stack.empty())
169 return it->second;
170 else
171 stack.emplace(it->second);
172 } else {
173 if(children_processed==0) {
174 switch(getGateType(g)) {
175 case BooleanGate::IN:
176 partial_value = getProb(g);
177 break;
178 case BooleanGate::NOT:
179 partial_value = 1-getProb(*getWires(g).begin());
180 break;
181 case BooleanGate::AND:
182 partial_value = 1;
183 break;
184 case BooleanGate::OR:
185 partial_value = 0;
186 break;
187 default:
188 assert(false);
189 }
190 } else {
191 if(getGateType(g) == BooleanGate::AND) {
192 partial_value *= child_value;
193 } else { // BooleanGate::OR
194 partial_value += child_value;
195 }
196 }
197
198 if(getGateType(g)!=BooleanGate::NOT && children_processed<getWires(g).size()) {
199 stack.emplace(RecursionParams{g,children_processed+1,partial_value});
200 stack.emplace(RecursionParams{getWires(g)[children_processed],0,0.});
201 } else {
202 double result = partial_value;
203 probability_cache[g]=result;
204
205 if(stack.empty())
206 return result;
207 else
208 stack.emplace(result);
209 }
210 }
211 }
212
213 // We return from within the while loop, when the stack is empty
214 assert(false);
215}
216
218 std::unordered_map<gate_t, double> result;
219 std::unordered_map<gate_t, double> prod_one_plus_p;
220
221 // Stack to simulate recursion: contains a pair (node, b) where b
222 // indicates whether this is the beginning (false) or ending (true) of
223 // the processing of a node
224 std::stack<std::pair<gate_t, bool> > stack;
225 stack.emplace(std::make_pair(root, false));
226
227 while(!stack.empty())
228 {
229 auto [node, b] = stack.top();
230 stack.pop();
231
232 if(result.find(node)!=result.end()) {
233 // Already processed, skip
234 continue;
235 }
236
237 switch(getGateType(node)) {
238 case BooleanGate::IN:
239 result[node] = getProb(node);
240 prod_one_plus_p[node] = 1+getProb(node);
241 break;
242
243 case BooleanGate::NOT:
244 if(!b) {
245 stack.push(std::make_pair(node, true));
246 stack.push(std::make_pair(getWires(node)[0], false));
247 } else {
248 auto child = getWires(node)[0];
249 result[node] = prod_one_plus_p[child] - result[child];
250 prod_one_plus_p[node] = prod_one_plus_p[child];
251 }
252 break;
253
254 case BooleanGate::OR:
255 if(!b) {
256 if(getWires(node).size()==0) { // Has to be an OR False gate
257 result[node] = 0.;
258 prod_one_plus_p[node] = 1.;
259 } else {
260 stack.push(std::make_pair(node, true));
261 for(auto c: getWires(node))
262 stack.push(std::make_pair(c, false));
263 }
264 } else {
265 result[node] =
266 std::accumulate(getWires(node).begin(), getWires(node).end(), 0.,
267 [&](auto r, auto g) {
268 return r + result[g];
269 });
270 prod_one_plus_p[node] = prod_one_plus_p[getWires(node)[0]];
271 }
272 break;
273
274 case BooleanGate::AND:
275 if(!b) {
276 if(getWires(node).size()==0) { // Has to be an AND True gate
277 result[node] = 1.;
278 prod_one_plus_p[node] = 1.;
279 } else {
280 stack.push(std::make_pair(node, true));
281 for(auto c: getWires(node))
282 stack.push(std::make_pair(c, false));
283 }
284 } else {
285 result[node] =
286 std::accumulate(getWires(node).begin(), getWires(node).end(), 1.,
287 [&](auto r, auto g) {
288 return r * result[g];
289 });
290 prod_one_plus_p[node] =
291 std::accumulate(getWires(node).begin(), getWires(node).end(), 1.,
292 [&](auto r, auto g) {
293 return r * prod_one_plus_p[g];
294 });
295 }
296 break;
297
301 throw CircuitException("Incorrect gate type");
302 break;
303 }
304 }
305
306 return result[root];
307}
308
309std::unordered_map<gate_t, std::vector<double> > dDNNF::shapley_delta() const {
310 std::unordered_map<gate_t, std::vector<double> > result;
311
312 if(!isProbabilistic())
313 return result;
314
315 // Stack to simulate recursion: contains a pair (node, b) where b
316 // indicates whether this is the beginning (false) or ending (true) of
317 // the processing of a node
318 std::stack<std::pair<gate_t, bool> > stack;
319 stack.emplace(std::make_pair(root, false));
320
321 while(!stack.empty())
322 {
323 auto [node, b] = stack.top();
324 stack.pop();
325
326 if(result.find(node)!=result.end()) {
327 // Already processed, skip
328 continue;
329 }
330
331 switch(getGateType(node)) {
332 case BooleanGate::IN:
333 result[node] = {1-getProb(node), getProb(node)};
334 break;
335
336 case BooleanGate::NOT:
337 case BooleanGate::OR:
338 if(!b) {
339 if(getWires(node).size()==0) // Has to be an OR False gate
340 result[node] = {1};
341 else {
342 stack.push(std::make_pair(node, true));
343 for(auto c: getWires(node))
344 stack.push(std::make_pair(c, false));
345 }
346 } else {
347 result[node] = result[getWires(node)[0]];
348 }
349 break;
350
351 case BooleanGate::AND:
352 {
353 if(!b) {
354 if(getWires(node).size()==0) // Has to be an AND True gate
355 result[node] = {1};
356 else {
357 stack.push(std::make_pair(node, true));
358 for(auto c: getWires(node))
359 stack.push(std::make_pair(c, false));
360 }
361 } else {
362 if(getWires(node).size()==1)
363 result[node] = result[getWires(node)[0]];
364 else {
365 assert(getWires(node).size()==2); // AND has been made binary
366 const auto &r1 = result[getWires(node)[0]];
367 const auto &r2 = result[getWires(node)[1]];
368 const auto n1=r1.size()-1;
369 const auto n2=r2.size()-1;
370 for(size_t k=0; k<=n1+n2; ++k) {
371 double r = 0.;
372 for(size_t k1=std::max(0,static_cast<int>(k-n2)); k1<=std::min(k,n1); ++k1) {
373 r+=r1[k1]*r2[k-k1];
374 }
375 result[node].push_back(r);
376 }
377 }
378 }
379
380 break;
381 }
382
386 throw CircuitException("Incorrect gate type");
387 break;
388 }
389 }
390
391 return result;
392}
393
394/**
395 * @brief Compute the binomial coefficient C(n, k).
396 * @param n Total number of elements.
397 * @param k Number of elements to choose; must satisfy k ≤ n.
398 * @return The binomial coefficient n-choose-k.
399 */
400static long long comb(unsigned n, unsigned k)
401{
402 assert(k<=n);
403
404 if(k == 0)
405 return 1;
406 else if(k > n/2)
407 return comb(n,n-k);
408 else return n * comb(n-1,k-1) / k;
409}
410
411std::vector<std::vector<double> > dDNNF::shapley_alpha() const {
412 std::unordered_map<gate_t, std::vector<double> > delta {shapley_delta()};
413 std::unordered_map<gate_t, std::vector<std::vector<double> > > result;
414
415 // Stack to simulate recursion: contains a pair (node, b) where b
416 // indicates whether this is the beginning (false) or ending (true) of
417 // the processing of a node
418 std::stack<std::pair<gate_t, bool> > stack;
419 stack.emplace(std::make_pair(root, false));
420
421 while(!stack.empty())
422 {
423 auto [node, b] = stack.top();
424 stack.pop();
425
426 if(result.find(node)!=result.end()) {
427 // Already processed, skip
428 continue;
429 }
430
431 switch(getGateType(node)) {
432 case BooleanGate::IN:
433 result[node] = {{0},{0,getProb(node)}};
434 break;
435
436 case BooleanGate::NOT:
437 if(!b) {
438 stack.push(std::make_pair(node, true));
439 stack.push(std::make_pair(getWires(node)[0], false));
440 } else {
441 result[node] = result[getWires(node)[0]];
442 auto k0=isProbabilistic()?0:result[node].size()-1;
443 for(unsigned k=k0; k<result[node].size(); ++k)
444 for(unsigned l=0; l<=k; ++l) {
445 result[node][k][l] *= -1;
446 result[node][k][l] += comb(k,l)*(isProbabilistic()?delta[node][k]:1);
447 }
448 }
449 break;
450
451 case BooleanGate::OR:
452 if(!b) {
453 if(getWires(node).size()==0) // Has to be an OR False gate
454 result[node] = {{0.}};
455 else {
456 stack.push(std::make_pair(node, true));
457 for(auto c: getWires(node))
458 stack.push(std::make_pair(c, false));
459 }
460 } else {
461 result[node] = result[getWires(node)[0]];
462 for(size_t i=1; i<getWires(node).size(); ++i) {
463 const auto &r = result[getWires(node)[i]];
464 auto k0=isProbabilistic()?0:r.size()-1;
465 for(unsigned k=k0; k<r.size(); ++k)
466 for(unsigned l=0; l<r[k].size(); ++l)
467 result[node][k][l]+=r[k][l];
468 }
469 }
470 break;
471
472 case BooleanGate::AND:
473 if(!b) {
474 if(getWires(node).size()==0) // Has to be an AND True gate
475 result[node] = {{1.}};
476 else {
477 stack.push(std::make_pair(node, true));
478 for(auto c: getWires(node))
479 stack.push(std::make_pair(c, false));
480 }
481 } else {
482 if(getWires(node).size()==1)
483 result[node] = result[getWires(node)[0]];
484 else {
485 assert(getWires(node).size()==2); // AND has been made binary
486 const auto &r1 = result[getWires(node)[0]];
487 const auto &r2 = result[getWires(node)[1]];
488 const auto n1=r1.size()-1;
489 const auto n2=r2.size()-1;
490 result[node].resize(n1+n2+1);
491 auto k0=isProbabilistic()?0:n1+n2;
492 for(size_t k=k0; k<=n1+n2; ++k) {
493 result[node][k].resize(k+1);
494 for(size_t l=0; l<=k; ++l) {
495 for(size_t k1=std::max(0,static_cast<int>(k-n2)); k1<=std::min(k,n1); ++k1)
496 for(size_t l1=std::max(0,static_cast<int>(l-k+k1)); l1<=std::min(k1,l); ++l1)
497 result[node][k][l] += r1[k1][l1] * r2[k-k1][l-l1];
498 }
499 }
500 }
501 }
502 break;
503
507 throw CircuitException("Incorrect gate type");
508 break;
509 }
510 }
511
512 return result[root];
513}
514
515double dDNNF::shapley(gate_t var) const {
516 auto cond_pos = condition(var, true);
517 auto cond_neg = condition(var, false);
518
519 auto alpha_pos=cond_pos.shapley_alpha();
520 auto alpha_neg=cond_neg.shapley_alpha();
521
522 double result=0.;
523
524 double k0=isProbabilistic()?0:alpha_pos.size()-1;
525 for(size_t k=k0; k<alpha_pos.size(); ++k)
526 for(size_t l=0; l<=k; ++l) {
527 double pos = alpha_pos[k][l];
528 double neg = alpha_neg[k][l];
529 result += (pos-neg)/comb(k,l)/(k+1);
530 }
531
532 result *= getProb(var);
533
534 // Avoid rounding errors that make expected Shapley value outside of [-1,1]
535 if(result>1.)
536 result=1.;
537 else if(result<-1.)
538 result=-1.;
539
540 return result;
541}
542
543double dDNNF::banzhaf(gate_t var) const {
544 auto cond_pos = condition(var, true);
545 auto cond_neg = condition(var, false);
546
547 auto env_pos=cond_pos.banzhaf_internal();
548 auto env_neg=cond_neg.banzhaf_internal();
549
550 return getProb(var) * (env_pos-env_neg);
551}
552
553dDNNF dDNNF::condition(gate_t var, bool value) const {
554 assert(getGateType(var)==BooleanGate::IN);
555
556 dDNNF result=*this;
557
558 result.setGateType(var, value ? BooleanGate::AND : BooleanGate::OR);
559 result.probability_cache[var] = value?1.:0.;
560 result.inputs.erase(var);
561 auto it = id2uuid.find(var);
562 if(it!=id2uuid.end()) {
563 result.uuid2id.erase(it->second);
564 result.id2uuid.erase(var);
565 }
566
567 return result;
568}
569
570std::vector<gate_t> dDNNF::topological_order(const std::vector<std::vector<gate_t> > &reversedWires) const
571{
572 std::vector<gate_t> result;
573
574 std::stack<gate_t> nodesToProcess;
575 std::vector<size_t> inDegree(wires.size());
576
577 for(size_t g=0; g<wires.size(); ++g)
578 if(!(inDegree[g] = wires[g].size()))
579 nodesToProcess.push(gate_t{g});
580
581 while(!nodesToProcess.empty()) {
582 auto g = nodesToProcess.top();
583 nodesToProcess.pop();
584 result.push_back(g);
585 for(auto p: reversedWires[static_cast<size_t>(g)])
586 if(!(--inDegree[static_cast<size_t>(p)]))
587 nodesToProcess.push(p);
588 }
589
590 return result;
591}
592
594 std::vector<std::vector<gate_t> > reversedWires(gates.size());
595 for(size_t i=0; i<wires.size(); ++i)
596 for(auto g: wires[i])
597 reversedWires[static_cast<size_t>(g)].push_back(gate_t{i});
598
599 for(auto node: topological_order(reversedWires)) {
600 auto &w = wires[static_cast<size_t>(node)];
601
602 switch(getGateType(node)) {
603 case BooleanGate::IN:
604 break;
605
606 case BooleanGate::AND:
607 case BooleanGate::OR:
608 if(w.size()==0)
610 else if(w.size()==1) {
611 if(node==getRoot()) {
612 root=w[0];
613 } else {
614 for(auto p: reversedWires[static_cast<size_t>(node)])
615 std::replace(wires[static_cast<size_t>(p)].begin(), wires[static_cast<size_t>(p)].end(), node, w[0]);
616 }
617 w.clear();
618 } else {
619 for(auto c=w.begin(); c!=w.end();) {
620 if(getGateType(*c)==getGateType(node) && getWires(*c).size()==0)
621 c = w.erase(c);
622 else if(getGateType(*c)==(getGateType(node)==BooleanGate::AND?BooleanGate::OR:BooleanGate::AND) && getWires(*c).size()==0) {
623 setGateType(node, getGateType(*c));
625 w.clear();
626 break;
627 } else
628 ++c;
629 }
630 }
631 break;
632
633 case BooleanGate::NOT:
634 if(getGateType(w[0])==BooleanGate::AND && getWires(w[0]).size()==0) {
636 probability_cache[node]=0.;
637 w.clear();
638 } else if(getGateType(w[0])==BooleanGate::OR && getWires(w[0]).size()==0) {
640 probability_cache[node]=1.;
641 w.clear();
642 }
643 break;
644
648 throw CircuitException("Incorrect gate type");
649 break;
650 }
651 }
652
653 std::vector<bool> used(gates.size());
654 std::stack<gate_t> to_process;
655 to_process.push(root);
656
657 while(!to_process.empty()) {
658 auto g = to_process.top();
659 to_process.pop();
660 used[static_cast<size_t>(g)]=true;
661 for(auto c: wires[static_cast<size_t>(g)])
662 if(!used[static_cast<size_t>(c)])
663 to_process.push(c);
664 }
665
666 size_t newi = 0;
667 std::vector<gate_t> relabel(gates.size());
668 for(size_t i=0; i<gates.size(); ++i)
669 {
670 if(!used[i]) {
671 inputs.erase(gate_t{i});
672 probability_cache.erase(gate_t{i});
673 auto it = id2uuid.find(gate_t{i});
674 if(it!=id2uuid.end()) {
675 uuid2id.erase(it->second);
676 id2uuid.erase(it);
677 }
678 continue;
679 }
680
681 relabel[i]=gate_t{newi};
682
683 if(i!=newi) {
684 gates[newi] = gates[i];
685 wires[newi] = wires[i];
686 prob[newi]=prob[i];
687
688 auto it1 = probability_cache.find(gate_t{i});
689 if(it1!=probability_cache.end()) {
690 probability_cache[gate_t{newi}] = it1->second;
691 probability_cache.erase(it1);
692 }
693
694 auto it2 = id2uuid.find(gate_t{i});
695 if(it2!=id2uuid.end()) {
696 id2uuid[gate_t{newi}] = it2->second;
697 uuid2id[it2->second] = gate_t{newi};
698 id2uuid.erase(it2);
699 }
700
701 if(root==gate_t{i})
702 root=gate_t{newi};
703
704 auto it3 = inputs.find(gate_t{i});
705 if(it3!=inputs.end()) {
706 inputs.insert(gate_t{newi});
707 inputs.erase(it3);
708 }
709 }
710
711 ++newi;
712 }
713
714 gates.resize(newi);
715 wires.resize(newi);
716 prob.resize(newi);
717
718 for(auto &w: wires)
719 for(size_t i=0; i<w.size(); ++i)
720 w[i]=relabel[static_cast<size_t>(w[i])];
721}
BooleanGate
Gate types for a Boolean provenance circuit.
@ MULVAR
Auxiliary gate grouping all MULIN siblings.
@ 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.
@ UNDETERMINED
Placeholder gate whose type has not been set yet.
@ MULIN
Multivalued-input gate (one of several options)
gate_t
Strongly-typed gate identifier.
Definition Circuit.h:48
Out-of-line template method implementations for Circuit<gateType>.
std::vector< double > prob
Per-gate probability (for IN gates)
std::set< gate_t > inputs
Set of IN (input) gate IDs.
gate_t setGate(BooleanGate type) override
Allocate a new gate with type type and no UUID.
double getProb(gate_t g) const
Return the probability stored for gate g.
bool isProbabilistic() const
Return true if any gate has a non-trivial (< 1) probability.
Exception type thrown by circuit operations on invalid input.
Definition Circuit.h:205
std::vector< gate_t > & getWires(gate_t g)
Return a mutable reference to the child-wire list of gate g.
Definition Circuit.h:139
BooleanGate getGateType(gate_t g) const
Return the type of gate g.
Definition Circuit.h:129
std::unordered_map< gate_t, uuid > id2uuid
Gate index → UUID string.
Definition Circuit.h:68
void addWire(gate_t f, gate_t t)
Add a directed wire from gate f (parent) to gate t (child).
Definition Circuit.hpp:81
std::unordered_map< uuid, gate_t > uuid2id
UUID string → gate index.
Definition Circuit.h:67
void setGateType(gate_t g, gateType t)
Update the type of an existing gate.
Definition Circuit.h:78
std::vector< BooleanGate > gates
Gate type for each gate.
Definition Circuit.h:70
std::vector< std::vector< gate_t > > wires
Child wire lists for each gate.
Definition Circuit.h:71
A d-DNNF circuit supporting exact probabilistic and game-theoretic evaluation.
Definition dDNNF.h:69
gate_t root
The root gate of the d-DNNF.
Definition dDNNF.h:111
dDNNF condition(gate_t var, bool value) const
Condition on variable var having value value (no simplification).
Definition dDNNF.cpp:553
double banzhaf_internal() const
Compute the unnormalised Banzhaf value for the whole circuit.
Definition dDNNF.cpp:217
double probabilityEvaluation() const
Compute the exact probability of the d-DNNF being true.
Definition dDNNF.cpp:137
void simplify()
Simplify the d-DNNF by removing redundant constants.
Definition dDNNF.cpp:593
void makeSmooth()
Make the d-DNNF smooth.
Definition dDNNF.cpp:57
void makeGatesBinary(BooleanGate type)
Rewrite all n-ary AND/OR gates into binary trees.
Definition dDNNF.cpp:104
std::vector< std::vector< double > > shapley_alpha() const
Compute the α table used in the Shapley algorithm.
Definition dDNNF.cpp:411
double shapley(gate_t var) const
Compute the Shapley value of input gate var.
Definition dDNNF.cpp:515
double banzhaf(gate_t var) const
Compute the Banzhaf power index of input gate var.
Definition dDNNF.cpp:543
std::vector< gate_t > topological_order(const std::vector< std::vector< gate_t > > &reversedWires) const
Compute a topological ordering of the circuit.
Definition dDNNF.cpp:570
std::unordered_set< gate_t > vars(gate_t root) const
Return the set of all variable (IN) gates reachable from root.
Definition dDNNF.cpp:29
std::unordered_map< gate_t, double, hash_gate_t > probability_cache
Memoisation cache mapping gates to their probability values.
Definition dDNNF.h:72
gate_t getRoot() const
Return the root gate of this d-DNNF.
Definition dDNNF.h:118
std::unordered_map< gate_t, std::vector< double > > shapley_delta() const
Compute the δ table used in the Shapley algorithm.
Definition dDNNF.cpp:309
static long long comb(unsigned n, unsigned k)
Compute the binomial coefficient C(n, k).
Definition dDNNF.cpp:400
Decomposable Deterministic Negation Normal Form circuit.