22#include <unordered_map>
33 std::stack<gate_t> to_process;
34 std::unordered_set<gate_t> processed;
35 to_process.push(
root);
37 std::unordered_set<gate_t> result;
38 while(!to_process.empty())
40 auto g = to_process.top();
43 if(processed.find(g)==processed.end()) {
67 std::unordered_set<gate_t> all;
68 std::vector<std::unordered_set<gate_t> > varss;
72 varss.push_back(
vars(x));
74 std::for_each(varss.begin(), varss.end(),
76 all.insert(s.begin(),s.end());
80 bool modified =
false;
81 for(
size_t i=0; i<varss.size(); ++i) {
82 if(varss[i].find(v)==varss[i].end()) {
96 addWire(dummy_or_gate, dummy_not_gate);
113 for(
size_t i=1; i<3; ++i) {
123 const auto k = w.size();
125 for(
unsigned i=0; i<k; ++i)
139 if (
gates.size() == 0)
145 using RecursionParams =
struct {
147 size_t children_processed;
148 double partial_value;
150 using RecursionResult = double;
151 std::stack<std::variant<RecursionParams,RecursionResult> > stack;
152 stack.emplace(RecursionParams{
root,0,0.});
155 double child_value{0.};
157 if(stack.top().index()==1) {
158 child_value=std::get<1>(stack.top());
162 auto [g, children_processed, partial_value]=std::get<0>(stack.top());
171 stack.emplace(it->second);
173 if(children_processed==0) {
192 partial_value *= child_value;
194 partial_value += child_value;
199 stack.emplace(RecursionParams{g,children_processed+1,partial_value});
200 stack.emplace(RecursionParams{
getWires(g)[children_processed],0,0.});
202 double result = partial_value;
208 stack.emplace(result);
218 std::unordered_map<gate_t, double> result;
219 std::unordered_map<gate_t, double> prod_one_plus_p;
224 std::stack<std::pair<gate_t, bool> > stack;
225 stack.emplace(std::make_pair(
root,
false));
227 while(!stack.empty())
229 auto [node, b] = stack.top();
232 if(result.find(node)!=result.end()) {
240 prod_one_plus_p[node] = 1+
getProb(node);
245 stack.push(std::make_pair(node,
true));
246 stack.push(std::make_pair(
getWires(node)[0],
false));
249 result[node] = prod_one_plus_p[child] - result[child];
250 prod_one_plus_p[node] = prod_one_plus_p[child];
258 prod_one_plus_p[node] = 1.;
260 stack.push(std::make_pair(node,
true));
262 stack.push(std::make_pair(c,
false));
267 [&](
auto r,
auto g) {
268 return r + result[g];
270 prod_one_plus_p[node] = prod_one_plus_p[
getWires(node)[0]];
278 prod_one_plus_p[node] = 1.;
280 stack.push(std::make_pair(node,
true));
282 stack.push(std::make_pair(c,
false));
287 [&](
auto r,
auto g) {
288 return r * result[g];
290 prod_one_plus_p[node] =
292 [&](
auto r,
auto g) {
293 return r * prod_one_plus_p[g];
310 std::unordered_map<gate_t, std::vector<double> > result;
318 std::stack<std::pair<gate_t, bool> > stack;
319 stack.emplace(std::make_pair(
root,
false));
321 while(!stack.empty())
323 auto [node, b] = stack.top();
326 if(result.find(node)!=result.end()) {
342 stack.push(std::make_pair(node,
true));
344 stack.push(std::make_pair(c,
false));
347 result[node] = result[
getWires(node)[0]];
357 stack.push(std::make_pair(node,
true));
359 stack.push(std::make_pair(c,
false));
363 result[node] = result[
getWires(node)[0]];
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) {
372 for(
size_t k1=std::max(0,
static_cast<int>(k-n2)); k1<=std::min(k,n1); ++k1) {
375 result[node].push_back(r);
400static long long comb(
unsigned n,
unsigned k)
408 else return n *
comb(n-1,k-1) / k;
412 std::unordered_map<gate_t, std::vector<double> > delta {
shapley_delta()};
413 std::unordered_map<gate_t, std::vector<std::vector<double> > > result;
418 std::stack<std::pair<gate_t, bool> > stack;
419 stack.emplace(std::make_pair(
root,
false));
421 while(!stack.empty())
423 auto [node, b] = stack.top();
426 if(result.find(node)!=result.end()) {
433 result[node] = {{0},{0,
getProb(node)}};
438 stack.push(std::make_pair(node,
true));
439 stack.push(std::make_pair(
getWires(node)[0],
false));
441 result[node] = result[
getWires(node)[0]];
443 for(
unsigned k=k0; k<result[node].size(); ++k)
444 for(
unsigned l=0; l<=k; ++l) {
445 result[node][k][l] *= -1;
454 result[node] = {{0.}};
456 stack.push(std::make_pair(node,
true));
458 stack.push(std::make_pair(c,
false));
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]];
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];
475 result[node] = {{1.}};
477 stack.push(std::make_pair(node,
true));
479 stack.push(std::make_pair(c,
false));
483 result[node] = result[
getWires(node)[0]];
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);
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];
519 auto alpha_pos=cond_pos.shapley_alpha();
520 auto alpha_neg=cond_neg.shapley_alpha();
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);
547 auto env_pos=cond_pos.banzhaf_internal();
548 auto env_neg=cond_neg.banzhaf_internal();
550 return getProb(var) * (env_pos-env_neg);
563 result.
uuid2id.erase(it->second);
572 std::vector<gate_t> result;
574 std::stack<gate_t> nodesToProcess;
575 std::vector<size_t> inDegree(
wires.size());
577 for(
size_t g=0; g<
wires.size(); ++g)
578 if(!(inDegree[g] =
wires[g].size()))
579 nodesToProcess.push(
gate_t{g});
581 while(!nodesToProcess.empty()) {
582 auto g = nodesToProcess.top();
583 nodesToProcess.pop();
585 for(
auto p: reversedWires[
static_cast<size_t>(g)])
586 if(!(--inDegree[
static_cast<size_t>(p)]))
587 nodesToProcess.push(p);
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});
600 auto &w =
wires[
static_cast<size_t>(node)];
610 else if(w.size()==1) {
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]);
619 for(
auto c=w.begin(); c!=w.end();) {
653 std::vector<bool> used(
gates.size());
654 std::stack<gate_t> to_process;
655 to_process.push(
root);
657 while(!to_process.empty()) {
658 auto g = to_process.top();
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)])
667 std::vector<gate_t> relabel(
gates.size());
668 for(
size_t i=0; i<
gates.size(); ++i)
719 for(
size_t i=0; i<w.size(); ++i)
720 w[i]=relabel[
static_cast<size_t>(w[i])];
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.
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.
std::vector< gate_t > & getWires(gate_t g)
Return a mutable reference to the child-wire list of gate g.
BooleanGate getGateType(gate_t g) const
Return the type of gate g.
std::unordered_map< gate_t, uuid > id2uuid
Gate index → UUID string.
void addWire(gate_t f, gate_t t)
Add a directed wire from gate f (parent) to gate t (child).
std::unordered_map< uuid, gate_t > uuid2id
UUID string → gate index.
void setGateType(gate_t g, gateType t)
Update the type of an existing gate.
std::vector< BooleanGate > gates
Gate type for each gate.
std::vector< std::vector< gate_t > > wires
Child wire lists for each gate.
A d-DNNF circuit supporting exact probabilistic and game-theoretic evaluation.
gate_t root
The root gate of the d-DNNF.
dDNNF condition(gate_t var, bool value) const
Condition on variable var having value value (no simplification).
double banzhaf_internal() const
Compute the unnormalised Banzhaf value for the whole circuit.
double probabilityEvaluation() const
Compute the exact probability of the d-DNNF being true.
void simplify()
Simplify the d-DNNF by removing redundant constants.
void makeSmooth()
Make the d-DNNF smooth.
void makeGatesBinary(BooleanGate type)
Rewrite all n-ary AND/OR gates into binary trees.
std::vector< std::vector< double > > shapley_alpha() const
Compute the α table used in the Shapley algorithm.
double shapley(gate_t var) const
Compute the Shapley value of input gate var.
double banzhaf(gate_t var) const
Compute the Banzhaf power index of input gate var.
std::vector< gate_t > topological_order(const std::vector< std::vector< gate_t > > &reversedWires) const
Compute a topological ordering of the circuit.
std::unordered_set< gate_t > vars(gate_t root) const
Return the set of all variable (IN) gates reachable from root.
std::unordered_map< gate_t, double, hash_gate_t > probability_cache
Memoisation cache mapping gates to their probability values.
gate_t getRoot() const
Return the root gate of this d-DNNF.
std::unordered_map< gate_t, std::vector< double > > shapley_delta() const
Compute the δ table used in the Shapley algorithm.
static long long comb(unsigned n, unsigned k)
Compute the binomial coefficient C(n, k).
Decomposable Deterministic Negation Normal Form circuit.