ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
BooleanCircuit.cpp
Go to the documentation of this file.
1/**
2 * @file BooleanCircuit.cpp
3 * @brief Boolean circuit implementation and evaluation algorithms.
4 *
5 * Implements the methods declared in @c BooleanCircuit.h, including:
6 * - Gate management (@c addGate, @c setGate, @c setInfo, @c setProb).
7 * - Probability evaluation algorithms: possible worlds, Monte Carlo,
8 * WeightMC, independent evaluation.
9 * - Knowledge compilation: @c compilation() (external tools),
10 * @c interpretAsDD() (direct from circuit structure),
11 * @c makeDD() (dispatcher).
12 * - @c rewriteMultivaluedGates(): replaces MULVAR/MULIN clusters with
13 * standard AND/OR/NOT circuits.
14 * - @c Tseytin(): DIMACS/weighted CNF generation for model counters.
15 * - @c exportCircuit(): serialisation in the @c tdkc text format.
16 * - @c toString(): human-readable gate description.
17 *
18 * In the standalone @c tdkc build (when @c TDKC is defined) a lightweight
19 * @c elog() stub replaces the PostgreSQL error-reporting function.
20 */
21#include "BooleanCircuit.h"
22#include "Circuit.hpp"
23#include <type_traits>
24
25extern "C" {
26#include <unistd.h>
27#include <math.h>
28}
29
30#include <cassert>
31#include <string>
32#include <fstream>
33#include <sstream>
34#include <cstdlib>
35#include <iostream>
36#include <vector>
37#include <stack>
38
39#include <boost/archive/text_oarchive.hpp>
40#include <boost/archive/text_iarchive.hpp>
41
43
44// "provsql_utils.h"
45#ifdef TDKC
46constexpr bool provsql_interrupted = false;
47constexpr int provsql_verbose = 0;
48enum levels {ERROR, NOTICE};
49#define elog(level, ...) {fprintf(stderr, __VA_ARGS__); if(level==ERROR) exit(EXIT_FAILURE);}
50#else
51extern "C" {
52#include "provsql_utils.h"
53#include "utils/elog.h"
54}
55#endif
56#include "provsql_error.h"
57
59{
60 auto id = Circuit::setGate(type);
61 if(type == BooleanGate::IN) {
62 setProb(id,1.);
63 inputs.insert(id);
64 } else if(type == BooleanGate::MULIN) {
65 mulinputs.insert(id);
66 }
67 return id;
68}
69
71{
72 auto id = Circuit::setGate(u, type);
73 if(type == BooleanGate::IN) {
74 setProb(id,1.);
75 inputs.insert(id);
76 } else if(type == BooleanGate::MULIN) {
77 mulinputs.insert(id);
78 }
79 return id;
80}
81
82gate_t BooleanCircuit::setGate(const uuid &u, BooleanGate type, double p)
83{
84 auto id = setGate(u, type);
85 if(std::isnan(p))
86 p=1.;
87 setProb(id,p);
88 return id;
89}
90
92{
93 auto id = setGate(type);
94 if(std::isnan(p))
95 p=1.;
96 setProb(id,p);
97 return id;
98}
99
101{
102 auto id=Circuit::addGate();
103 prob.push_back(1);
104 return id;
105}
106
108{
109 std::string op;
110 std::string result;
111
112 switch(getGateType(g)) {
113 case BooleanGate::IN:
114 return "x"+to_string(g);
116 return "{" + to_string(*getWires(g).begin()) + "=" + std::to_string(getInfo(g)) + "}[" + std::to_string(getProb(g)) + "]";
117 case BooleanGate::NOT:
118 op="¬";
119 break;
121 op="?";
122 break;
123 case BooleanGate::AND:
124 op="∧";
125 break;
126 case BooleanGate::OR:
127 op="∨";
128 break;
130 ; // already dealt with in MULIN
131 }
132
133 if(getWires(g).empty()) {
135 return "⊤";
136 else if(getGateType(g)==BooleanGate::OR)
137 return "⊥";
138 else return op;
139 }
140
141 for(auto s: getWires(g)) {
143 result = op;
144 else if(!result.empty())
145 result+=" "+op+" ";
146 result+=toString(s);
147 }
148
149 return "("+result+")";
150}
151
153{
154 std::stringstream ss;
155
156 std::unordered_set<gate_t> processed;
157 std::stack<gate_t> to_process;
158 to_process.push(root);
159
160 while(!to_process.empty()) {
161 auto g=to_process.top();
162 to_process.pop();
163
164 if(processed.find(g)!=processed.end())
165 continue;
166
167 ss << g << " ";
168
169 switch(getGateType(g)) {
170 case BooleanGate::IN:
171 ss << "IN " << getProb(g);
172 break;
173
174 case BooleanGate::NOT:
175 ss << "NOT " << getWires(g)[0];
176 break;
177
178 case BooleanGate::AND:
179 ss << "AND";
180
181 for(auto s:getWires(g))
182 ss << " " << s;
183 break;
184
185 case BooleanGate::OR:
186 ss << "OR";
187
188 for(auto s:getWires(g))
189 ss << " " << s;
190 break;
191
195 assert(false); // not done
196 }
197
198 ss << "\n";
199
200 for(auto s: getWires(g)) {
201 if(processed.find(s)==processed.end())
202 to_process.push(s);
203 }
204
205 processed.insert(g);
206 }
207
208 return ss.str();
209}
210
211bool BooleanCircuit::evaluate(gate_t g, const std::unordered_set<gate_t> &sampled) const
212{
213 bool disjunction=false;
214
215 switch(getGateType(g)) {
216 case BooleanGate::IN:
217 return sampled.find(g)!=sampled.end();
220 throw CircuitException("Monte-Carlo sampling not implemented on multivalued inputs");
221 case BooleanGate::NOT:
222 return !evaluate(*(getWires(g).begin()), sampled);
223 case BooleanGate::AND:
224 disjunction = false;
225 break;
226 case BooleanGate::OR:
227 disjunction = true;
228 break;
230 throw CircuitException("Incorrect gate type");
231 }
232
233 for(auto s: getWires(g)) {
234 bool e = evaluate(s, sampled);
235 if(disjunction && e)
236 return true;
237 if(!disjunction && !e)
238 return false;
239 }
240
241 if(disjunction)
242 return false;
243 else
244 return true;
245}
246
247double BooleanCircuit::monteCarlo(gate_t g, unsigned samples) const
248{
249 auto success{0u};
250
251 for(unsigned i=0; i<samples; ++i) {
252 std::unordered_set<gate_t> sampled;
253 for(auto in: inputs) {
254 if(rand() *1. / RAND_MAX < getProb(in)) {
255 sampled.insert(in);
256 }
257 }
258
259 if(evaluate(g, sampled))
260 ++success;
261
263 throw CircuitException("Interrupted after "+std::to_string(i+1)+" samples");
264 }
265
266 return success*1./samples;
267}
268
270{
271 if(inputs.size()>=8*sizeof(unsigned long long))
272 throw CircuitException("Too many possible worlds to iterate over");
273
274 unsigned long long nb=(1<<inputs.size());
275 double totalp=0.;
276
277 for(unsigned long long i=0; i < nb; ++i) {
278 std::unordered_set<gate_t> s;
279 double p = 1;
280
281 unsigned j=0;
282 for(gate_t in : inputs) {
283 if(i & (1 << j)) {
284 s.insert(in);
285 p*=getProb(in);
286 } else {
287 p*=1-getProb(in);
288 }
289 ++j;
290 }
291
292 if(evaluate(g, s))
293 totalp+=p;
294
296 throw CircuitException("Interrupted");
297 }
298
299 return totalp;
300}
301
302std::string BooleanCircuit::Tseytin(gate_t g, bool display_prob=false) const {
303 std::vector<std::vector<int> > clauses;
304
305 // Tseytin transformation
306 for(gate_t i{0}; i<gates.size(); ++i) {
307 switch(getGateType(i)) {
308 case BooleanGate::AND:
309 {
310 int id{static_cast<int>(i)+1};
311 std::vector<int> c = {id};
312 for(auto s: getWires(i)) {
313 clauses.push_back({-id, static_cast<int>(s)+1});
314 c.push_back(-static_cast<int>(s)-1);
315 }
316 clauses.push_back(c);
317 break;
318 }
319
320 case BooleanGate::OR:
321 {
322 int id{static_cast<int>(i)+1};
323 std::vector<int> c = {-id};
324 for(auto s: getWires(i)) {
325 clauses.push_back({id, -static_cast<int>(s)-1});
326 c.push_back(static_cast<int>(s)+1);
327 }
328 clauses.push_back(c);
329 }
330 break;
331
332 case BooleanGate::NOT:
333 {
334 int id=static_cast<int>(i)+1;
335 auto s=*getWires(i).begin();
336 clauses.push_back({-id,-static_cast<int>(s)-1});
337 clauses.push_back({id,static_cast<int>(s)+1});
338 break;
339 }
340
342 throw CircuitException("Multivalued inputs should have been removed by then.");
344 case BooleanGate::IN:
346 ;
347 }
348 }
349 clauses.push_back({(int)g+1});
350
351 int fd;
352 char cfilename[] = "/tmp/provsqlXXXXXX";
353 fd = mkstemp(cfilename);
354 close(fd);
355
356 std::string filename=cfilename;
357 std::ofstream ofs(filename.c_str());
358
359 ofs << "p cnf " << gates.size() << " " << clauses.size() << "\n";
360
361 for(unsigned i=0; i<clauses.size(); ++i) {
362 for(int x : clauses[i]) {
363 ofs << x << " ";
364 }
365 ofs << "0\n";
366 }
367 if(display_prob) {
368 for(gate_t in: inputs) {
369 ofs << "w " << (static_cast<std::underlying_type<gate_t>::type>(in)+1) << " " << getProb(in) << "\n";
370 ofs << "w -" << (static_cast<std::underlying_type<gate_t>::type>(in)+1) << " " << (1. - getProb(in)) << "\n";
371 }
372 }
373
374 ofs.close();
375
376 return filename;
377}
378
379dDNNF BooleanCircuit::compilation(gate_t g, std::string compiler) const {
380 std::string filename=BooleanCircuit::Tseytin(g);
381 std::string outfilename=filename+".nnf";
382
383 if(provsql_verbose>=20) {
384 provsql_notice("Tseytin circuit in %s", filename.c_str());
385 }
386
387 bool new_d4 {false};
388 std::string cmdline=compiler+" ";
389 if(compiler=="d4") {
390 cmdline+="-dDNNF "+filename+" -out="+outfilename;
391 new_d4 = true;
392 } else if(compiler=="c2d") {
393 cmdline+="-in "+filename+" -silent";
394 } else if(compiler=="minic2d") {
395 cmdline+="-in "+filename;
396 } else if(compiler=="dsharp") {
397 cmdline+="-q -Fnnf "+outfilename+" "+filename;
398 } else {
399 throw CircuitException("Unknown compiler '"+compiler+"'");
400 }
401
402 int retvalue=system(cmdline.c_str());
403
404 if(retvalue && compiler=="d4") {
405 // Temporary support for older version of d4
406 new_d4 = false;
407 cmdline = "d4 "+filename+" -out="+outfilename;
408 retvalue=system(cmdline.c_str());
409 }
410
411 if(retvalue)
412 throw CircuitException("Error executing "+compiler);
413
414 if(provsql_verbose<20) {
415 if(unlink(filename.c_str())) {
416 throw CircuitException("Error removing "+filename);
417 }
418 }
419
420 std::ifstream ifs(outfilename.c_str());
421
422 std::string line;
423 getline(ifs,line);
424
425 if(line.rfind("nnf", 0) != 0) {
426 // New d4 does not include this magic line
427
428 if(compiler != "d4") {
429 // unsatisfiable formula
430 return dDNNF();
431 }
432 } else {
433 std::string nnf;
434 unsigned nb_nodes, nb_edges, nb_variables;
435
436 std::stringstream ss(line);
437 ss >> nnf >> nb_nodes >> nb_edges >> nb_variables;
438
439 if(nb_variables!=gates.size())
440 throw CircuitException("Unreadable d-DNNF (wrong number of variables: " + std::to_string(nb_variables) +" vs " + std::to_string(gates.size()) + ")");
441
442 getline(ifs,line);
443 }
444
445 dDNNF dnnf;
446
447 unsigned i=0;
448 do {
449 std::stringstream ss(line);
450
451 std::string c;
452 ss >> c;
453
454 if(c=="O") {
455 int var, args;
456 ss >> var >> args;
457 auto id=dnnf.getGate(std::to_string(i));
458 dnnf.setGate(std::to_string(i), BooleanGate::OR);
459 int g;
460 while(ss >> g) {
461 auto id2=dnnf.getGate(std::to_string(g));
462 dnnf.addWire(id,id2);
463 }
464 } else if(c=="A") {
465 int args;
466 ss >> args;
467 auto id=dnnf.getGate(std::to_string(i));
468 dnnf.setGate(std::to_string(i), BooleanGate::AND);
469 int g;
470 while(ss >> g) {
471 auto id2=dnnf.getGate(std::to_string(g));
472 dnnf.addWire(id,id2);
473 }
474 } else if(c=="L") {
475 int leaf;
476 ss >> leaf;
477 auto and_gate=dnnf.setGate(std::to_string(i), BooleanGate::AND);
478 if(gates[abs(leaf)-1]==BooleanGate::IN) {
479 if(leaf<0) {
480 auto leaf_gate = dnnf.setGate(getUUID(static_cast<gate_t>(-leaf-1)), BooleanGate::IN, prob[-leaf-1]);
481 auto not_gate = dnnf.setGate(BooleanGate::NOT);
482 dnnf.addWire(not_gate, leaf_gate);
483 dnnf.addWire(and_gate, not_gate);
484 } else {
485 auto leaf_gate = dnnf.setGate(getUUID(static_cast<gate_t>(leaf-1)), BooleanGate::IN, prob[leaf-1]);
486 dnnf.addWire(and_gate, leaf_gate);
487 }
488 } else {
489 ; // Do nothing, TRUE gate
490 }
491 } else if(c=="f" || c=="o") {
492 // d4 extended format
493 // A FALSE gate is an OR gate without wires
494 int var;
495 ss >> var;
496 dnnf.setGate(std::to_string(var), BooleanGate::OR);
497 } else if(c=="t" || c=="a") {
498 // d4 extended format
499 // A TRUE gate is an AND gate without wires
500 int var;
501 ss >> var;
502 dnnf.setGate(std::to_string(var), BooleanGate::AND);
503 } else if(dnnf.hasGate(c)) {
504 // d4 extended format
505 int var;
506 ss >> var;
507 auto id2=dnnf.getGate(std::to_string(var));
508
509 std::vector<int> decisions;
510 int decision;
511 while(ss >> decision) {
512 if(decision==0)
513 break;
514 if(gates[abs(decision)-1]==BooleanGate::IN)
515 decisions.push_back(decision);
516 }
517
518 if(decisions.empty()) {
519 dnnf.addWire(dnnf.getGate(c), id2);
520 } else {
521 auto and_gate = dnnf.setGate(BooleanGate::AND);
522 dnnf.addWire(dnnf.getGate(c), and_gate);
523 dnnf.addWire(and_gate, id2);
524 for(auto leaf : decisions) {
525 if(leaf<0) {
526 auto leaf_gate = dnnf.setGate(getUUID(static_cast<gate_t>(-leaf-1)), BooleanGate::IN, prob[-leaf-1]);
527 auto not_gate = dnnf.setGate(BooleanGate::NOT);
528 dnnf.addWire(not_gate, leaf_gate);
529 dnnf.addWire(and_gate, not_gate);
530 } else {
531 auto leaf_gate = dnnf.setGate(getUUID(static_cast<gate_t>(leaf-1)), BooleanGate::IN, prob[leaf-1]);
532 dnnf.addWire(and_gate, leaf_gate);
533 }
534 }
535 }
536 } else
537 throw CircuitException(std::string("Unreadable d-DNNF (unknown node type: ")+c+")");
538
539 ++i;
540 } while(getline(ifs, line));
541
542 ifs.close();
543
544 if(provsql_verbose<20) {
545 if(unlink(outfilename.c_str())) {
546 throw CircuitException("Error removing "+outfilename);
547 }
548 } else
549 provsql_notice("Compiled d-DNNF in %s", outfilename.c_str());
550
551 dnnf.setRoot(dnnf.getGate(new_d4?"1":std::to_string(i-1)));
552
553 return dnnf;
554}
555
556double BooleanCircuit::WeightMC(gate_t g, std::string opt) const {
557 std::string filename=BooleanCircuit::Tseytin(g, true);
558
559 //opt of the form 'delta;epsilon'
560 std::stringstream ssopt(opt);
561 std::string delta_s, epsilon_s;
562 getline(ssopt, delta_s, ';');
563 getline(ssopt, epsilon_s, ';');
564
565 double delta = 0;
566 try {
567 delta=stod(delta_s);
568 } catch (std::invalid_argument &) {
569 delta=0;
570 }
571 double epsilon = 0;
572 try {
573 epsilon=stod(epsilon_s);
574 } catch (std::invalid_argument &) {
575 epsilon=0;
576 }
577 if(delta == 0) delta=0.2;
578 if(epsilon == 0) epsilon=0.8;
579
580 //TODO calcul numIterations
581
582 //calcul pivotAC
583 const double pivotAC=2*ceil(exp(3./2)*(1+1/epsilon)*(1+1/epsilon));
584
585 std::string cmdline="weightmc --startIteration=0 --gaussuntil=400 --verbosity=0 --pivotAC="+std::to_string(pivotAC)+ " "+filename+" > "+filename+".out";
586
587 int retvalue=system(cmdline.c_str());
588 if(retvalue) {
589 throw CircuitException("Error executing weightmc");
590 }
591
592 //parsing
593 std::ifstream ifs((filename+".out").c_str());
594 std::string line, prev_line;
595 while(getline(ifs,line))
596 prev_line=line;
597
598 std::stringstream ss(prev_line);
599 std::string result;
600 ss >> result >> result >> result >> result >> result;
601
602 std::istringstream iss(result);
603 std::string val, exp;
604 getline(iss, val, 'x');
605 getline(iss, exp);
606 double value=stod(val);
607 exp=exp.substr(2);
608 double exponent=stod(exp);
609 double ret=value*(pow(2.0,exponent));
610
611 if(unlink(filename.c_str())) {
612 throw CircuitException("Error removing "+filename);
613 }
614
615 if(unlink((filename+".out").c_str())) {
616 throw CircuitException("Error removing "+filename+".out");
617 }
618
619 return ret;
620}
621
623 gate_t g, std::set<gate_t> &seen) const
624{
625 double result=1.;
626
627 switch(getGateType(g)) {
628 case BooleanGate::AND:
629 for(const auto &c: getWires(g)) {
630 result*=independentEvaluationInternal(c, seen);
631 }
632 break;
633
634 case BooleanGate::OR:
635 {
636 // We collect probability among each group of children, where we
637 // group MULIN gates with the same key var together
638 std::map<gate_t, double> groups;
639 std::set<gate_t> local_mulins;
640 std::set<std::pair<gate_t, unsigned> > mulin_seen;
641
642 for(const auto &c: getWires(g)) {
643 auto group = c;
645 group = *getWires(c).begin();
646 if(local_mulins.find(group)==local_mulins.end()) {
647 if(seen.find(group)!=seen.end())
648 throw CircuitException("Not an independent circuit");
649 else
650 seen.insert(group);
651 local_mulins.insert(group);
652 }
653 auto p = std::make_pair(group, getInfo(c));
654 if(mulin_seen.find(p)==mulin_seen.end()) {
655 groups[group] += getProb(c);
656 mulin_seen.insert(p);
657 }
658 } else
659 groups[group] = independentEvaluationInternal(c, seen);
660 }
661
662 for(const auto [k, v]: groups)
663 result *= 1-v;
664 result = 1-result;
665 }
666 break;
667
668 case BooleanGate::NOT:
669 result=1-independentEvaluationInternal(*getWires(g).begin(), seen);
670 break;
671
672 case BooleanGate::IN:
673 if(seen.find(g)!=seen.end())
674 throw CircuitException("Not an independent circuit");
675 seen.insert(g);
676 result=getProb(g);
677 break;
678
680 {
681 auto child = *getWires(g).begin();
682 if(seen.find(child)!=seen.end())
683 throw CircuitException("Not an independent circuit");
684 seen.insert(child);
685 result=getProb(g);
686 }
687 break;
688
691 throw CircuitException("Bad gate");
692 }
693
694 return result;
695}
696
698{
699 std::set<gate_t> seen;
700 return independentEvaluationInternal(g, seen);
701}
702
703void BooleanCircuit::setInfo(gate_t g, unsigned int i)
704{
705 info[g] = i;
706}
707
709{
710 auto it = info.find(g);
711
712 if(it==info.end())
713 return 0;
714 else
715 return it->second;
716}
717
719 const std::vector<gate_t> &muls,
720 const std::vector<double> &cumulated_probs,
721 unsigned start,
722 unsigned end,
723 std::vector<gate_t> &prefix)
724{
725 if(start==end) {
726 getWires(muls[start]) = prefix;
727 return;
728 }
729
730 unsigned mid = (start+end)/2;
731 auto g = setGate(
733 (cumulated_probs[mid+1] - cumulated_probs[start]) /
734 (cumulated_probs[end] - cumulated_probs[start]));
735 auto not_g = setGate(BooleanGate::NOT);
736 getWires(not_g).push_back(g);
737
738 prefix.push_back(g);
739 rewriteMultivaluedGatesRec(muls, cumulated_probs, start, mid, prefix);
740 prefix.pop_back();
741 prefix.push_back(not_g);
742 rewriteMultivaluedGatesRec(muls, cumulated_probs, mid+1, end, prefix);
743 prefix.pop_back();
744}
745
746/**
747 * @brief Check whether two double values are approximately equal.
748 * @param a First value.
749 * @param b Second value.
750 * @return @c true if @p a and @p b differ by less than 10× machine epsilon.
751 */
752static constexpr bool almost_equals(double a, double b)
753{
754 double diff = a - b;
755 constexpr double epsilon = std::numeric_limits<double>::epsilon() * 10;
756
757 return (diff < epsilon && diff > -epsilon);
758}
759
761{
762 std::map<gate_t,std::vector<gate_t> > var2mulinput;
763 for(auto mul: mulinputs) {
764 var2mulinput[*getWires(mul).begin()].push_back(mul);
765 }
766 mulinputs.clear();
767
768 for(const auto &[var, muls]: var2mulinput)
769 {
770 const unsigned n = muls.size();
771 std::vector<double> cumulated_probs(n);
772 double cumulated_prob=0.;
773
774 for(unsigned i=0; i<n; ++i) {
775 cumulated_prob += getProb(muls[i]);
776 cumulated_probs[i] = cumulated_prob;
777 gates[static_cast<std::underlying_type<gate_t>::type>(muls[i])] = BooleanGate::AND;
778 getWires(muls[i]).clear();
779 }
780
781 std::vector<gate_t> prefix;
782 prefix.reserve(static_cast<unsigned>(log(n)/log(2)+2));
783 if(!almost_equals(cumulated_probs[n-1],1.)) {
784 prefix.push_back(setGate(BooleanGate::IN, cumulated_probs[n-1]));
785 }
786 rewriteMultivaluedGatesRec(muls, cumulated_probs, 0, n-1, prefix);
787 }
788}
789
790gate_t BooleanCircuit::interpretAsDDInternal(gate_t g, std::set<gate_t> &seen, dDNNF &dd) const {
791 gate_t dg{0};
792
793 switch(getGateType(g)) {
794 case BooleanGate::AND:
795 {
796 dg = dd.setGate(BooleanGate::AND);
797 for(const auto &c: getWires(g)) {
798 auto dc = interpretAsDDInternal(c, seen, dd);
799 dd.addWire(dg, dc);
800 }
801 }
802 break;
803
804 case BooleanGate::OR:
805 {
806 dg = dd.setGate(BooleanGate::NOT);
807 auto dng = dd.setGate(BooleanGate::AND);
808 dd.addWire(dg, dng);
809 for(const auto &c: getWires(g)) {
810 auto dc = interpretAsDDInternal(c, seen, dd);
811 auto dnc = dd.setGate(BooleanGate::NOT);
812 dd.addWire(dnc, dc);
813 dd.addWire(dng, dnc);
814 }
815 }
816 break;
817
818 case BooleanGate::NOT:
819 {
820 dg = dd.setGate(BooleanGate::NOT);
821 auto dc = interpretAsDDInternal(getWires(g)[0], seen, dd);
822 dd.addWire(dg, dc);
823 }
824 break;
825
826 case BooleanGate::IN:
827 if(seen.find(g)!=seen.end())
828 throw CircuitException("Not an independent circuit");
829 seen.insert(g);
830 dg = dd.setGate(getUUID(g), BooleanGate::IN, getProb(g));
831 break;
832
836 throw CircuitException("Unsupported gate in interpretAsDD");
837 }
838
839 return dg;
840}
841
843{
844 dDNNF dd;
845 std::set<gate_t> seen;
846
847 dd.setRoot(interpretAsDDInternal(g, seen, dd));
848
849 return dd;
850}
851
852dDNNF BooleanCircuit::makeDD(gate_t g, const std::string &method, const std::string &args) const
853{
854 if(method=="compilation") {
855 return compilation(g, args);
856 } else if(method=="tree-decomposition") {
857 try {
858 TreeDecomposition td(*this);
860 *this, g, td}.build();
861 } catch(TreeDecompositionException &) {
862 provsql_error("Treewidth greater than %u", TreeDecomposition::MAX_TREEWIDTH);
863 }
864 } else {
865 dDNNF dd;
866 try {
867 dd = interpretAsDD(g);
868 if(provsql_verbose>=20)
869 provsql_notice("Circuit interpreted as dD, %ld gates", dd.getNbGates());
870 } catch(CircuitException &) {
871 try {
872 TreeDecomposition td(*this);
874 *this, g, td}.build();
875 if(provsql_verbose>=20)
876 provsql_notice("dD obtained by tree decomposition, %ld gates", dd.getNbGates());
877 } catch(TreeDecompositionException &) {
878 dd = compilation(g, "d4");
879 if(provsql_verbose>=20)
880 provsql_notice("dD obtained by compilation using d4, %ld gates", dd.getNbGates());
881 }
882 }
883
884 return dd;
885 }
886}
static constexpr bool almost_equals(double a, double b)
Check whether two double values are approximately equal.
Boolean provenance circuit with support for knowledge compilation.
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
std::string to_string(gate_t g)
Convert a gate_t to its decimal string representation.
Definition Circuit.h:249
Out-of-line template method implementations for Circuit<gateType>.
std::vector< double > prob
Per-gate probability (for IN gates)
double WeightMC(gate_t g, std::string opt) const
Compute the probability using the weightmc model counter.
bool evaluate(gate_t g, const std::unordered_set< gate_t > &sampled) const
Evaluate the sub-circuit at g on one sampled world.
dDNNF interpretAsDD(gate_t g) const
Build a dDNNF directly from the Boolean circuit's structure.
double possibleWorlds(gate_t g) const
Compute the probability by exact enumeration of all possible worlds.
void setProb(gate_t g, double p)
Set the probability for gate g and mark the circuit as probabilistic.
std::set< gate_t > inputs
Set of IN (input) gate IDs.
void rewriteMultivaluedGatesRec(const std::vector< gate_t > &muls, const std::vector< double > &cumulated_probs, unsigned start, unsigned end, std::vector< gate_t > &prefix)
Recursive helper for rewriteMultivaluedGates().
double independentEvaluationInternal(gate_t g, std::set< gate_t > &seen) const
Recursive helper for independentEvaluation().
std::string exportCircuit(gate_t g) const
Export the circuit in the textual format expected by external compilers.
std::string Tseytin(gate_t g, bool display_prob) const
Generate a Tseytin transformation of the sub-circuit at g.
dDNNF makeDD(gate_t g, const std::string &method, const std::string &args) const
Dispatch to the appropriate d-DNNF construction method.
gate_t setGate(BooleanGate type) override
Allocate a new gate with type type and no UUID.
unsigned getInfo(gate_t g) const
Return the integer annotation for gate g.
gate_t addGate() override
Allocate a new gate with a default-initialised type.
double monteCarlo(gate_t g, unsigned samples) const
Estimate the probability via Monte Carlo sampling.
void rewriteMultivaluedGates()
Rewrite all MULVAR/MULIN gate clusters into standard AND/OR/NOT circuits.
double getProb(gate_t g) const
Return the probability stored for gate g.
void setInfo(gate_t g, unsigned info)
Store an integer annotation on gate g.
virtual std::string toString(gate_t g) const override
Return a textual description of gate g for debugging.
std::map< gate_t, unsigned > info
Per-gate integer info (for MULIN gates)
dDNNF compilation(gate_t g, std::string compiler) const
Compile the sub-circuit rooted at g to a dDNNF via an external tool.
gate_t interpretAsDDInternal(gate_t g, std::set< gate_t > &seen, dDNNF &dd) const
Recursive helper for interpretAsDD().
double independentEvaluation(gate_t g) const
Compute the probability exactly when inputs are independent.
std::set< gate_t > mulinputs
Set of MULVAR gate IDs.
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
virtual gate_t setGate(const uuid &u, gateType type)
Create or update the gate associated with UUID u.
Definition Circuit.hpp:73
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::vector< BooleanGate > gates
Gate type for each gate.
Definition Circuit.h:70
uuid getUUID(gate_t g) const
Return the UUID string associated with gate g.
Definition Circuit.hpp:46
gate_t getGate(const uuid &u)
Return (or create) the gate associated with UUID u.
Definition Circuit.hpp:33
bool hasGate(const uuid &u) const
Test whether a gate with UUID u exists.
Definition Circuit.hpp:27
std::vector< gate_t >::size_type getNbGates() const
Return the total number of gates in the circuit.
Definition Circuit.h:102
virtual gate_t addGate()
Allocate a new gate with a default-initialised type.
Definition Circuit.hpp:56
Exception thrown when a tree decomposition cannot be constructed.
Tree decomposition of a Boolean circuit's primal graph.
static constexpr int MAX_TREEWIDTH
Maximum supported treewidth.
Builds a d-DNNF from a Boolean circuit using a tree decomposition.
A d-DNNF circuit supporting exact probabilistic and game-theoretic evaluation.
Definition dDNNF.h:69
void setRoot(gate_t g)
Set the root gate.
Definition dDNNF.h:125
Constructs a d-DNNF from a Boolean circuit and its tree decomposition.
int provsql_verbose
Verbosity level; controlled by the provsql.verbose_level GUC.
Definition provsql.c:66
bool provsql_interrupted
Global variable that becomes true if this particular backend received an interrupt signal.
Definition provsql.c:62
Uniform error-reporting macros for ProvSQL.
#define provsql_error(fmt,...)
Report a fatal ProvSQL error and abort the current transaction.
#define provsql_notice(fmt,...)
Emit a ProvSQL informational notice (execution continues).
Core types, constants, and utilities shared across ProvSQL.