39#include <boost/archive/text_oarchive.hpp>
40#include <boost/archive/text_iarchive.hpp>
48enum levels {ERROR, NOTICE};
49#define elog(level, ...) {fprintf(stderr, __VA_ARGS__); if(level==ERROR) exit(EXIT_FAILURE);}
53#include "utils/elog.h"
144 else if(!result.empty())
149 return "("+result+
")";
154 std::stringstream ss;
156 std::unordered_set<gate_t> processed;
157 std::stack<gate_t> to_process;
158 to_process.push(root);
160 while(!to_process.empty()) {
161 auto g=to_process.top();
164 if(processed.find(g)!=processed.end())
201 if(processed.find(s)==processed.end())
213 bool disjunction=
false;
217 return sampled.find(g)!=sampled.end();
220 throw CircuitException(
"Monte-Carlo sampling not implemented on multivalued inputs");
237 if(!disjunction && !e)
251 for(
unsigned i=0; i<samples; ++i) {
252 std::unordered_set<gate_t> sampled;
254 if(rand() *1. / RAND_MAX <
getProb(in)) {
263 throw CircuitException(
"Interrupted after "+std::to_string(i+1)+
" samples");
266 return success*1./samples;
271 if(
inputs.size()>=8*
sizeof(
unsigned long long))
274 unsigned long long nb=(1<<
inputs.size());
277 for(
unsigned long long i=0; i < nb; ++i) {
278 std::unordered_set<gate_t> s;
303 std::vector<std::vector<int> > clauses;
310 int id{
static_cast<int>(i)+1};
311 std::vector<int> c = {
id};
313 clauses.push_back({-id,
static_cast<int>(s)+1});
314 c.push_back(-
static_cast<int>(s)-1);
316 clauses.push_back(c);
322 int id{
static_cast<int>(i)+1};
323 std::vector<int> c = {-
id};
325 clauses.push_back({id, -
static_cast<int>(s)-1});
326 c.push_back(
static_cast<int>(s)+1);
328 clauses.push_back(c);
334 int id=
static_cast<int>(i)+1;
336 clauses.push_back({-id,-
static_cast<int>(s)-1});
337 clauses.push_back({id,
static_cast<int>(s)+1});
342 throw CircuitException(
"Multivalued inputs should have been removed by then.");
349 clauses.push_back({(int)g+1});
352 char cfilename[] =
"/tmp/provsqlXXXXXX";
353 fd = mkstemp(cfilename);
356 std::string filename=cfilename;
357 std::ofstream ofs(filename.c_str());
359 ofs <<
"p cnf " <<
gates.size() <<
" " << clauses.size() <<
"\n";
361 for(
unsigned i=0; i<clauses.size(); ++i) {
362 for(
int x : clauses[i]) {
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";
381 std::string outfilename=filename+
".nnf";
388 std::string cmdline=compiler+
" ";
390 cmdline+=
"-dDNNF "+filename+
" -out="+outfilename;
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;
402 int retvalue=system(cmdline.c_str());
404 if(retvalue && compiler==
"d4") {
407 cmdline =
"d4 "+filename+
" -out="+outfilename;
408 retvalue=system(cmdline.c_str());
415 if(unlink(filename.c_str())) {
420 std::ifstream ifs(outfilename.c_str());
425 if(line.rfind(
"nnf", 0) != 0) {
428 if(compiler !=
"d4") {
434 unsigned nb_nodes, nb_edges, nb_variables;
436 std::stringstream ss(line);
437 ss >> nnf >> nb_nodes >> nb_edges >> nb_variables;
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()) +
")");
449 std::stringstream ss(line);
457 auto id=dnnf.
getGate(std::to_string(i));
461 auto id2=dnnf.
getGate(std::to_string(g));
467 auto id=dnnf.
getGate(std::to_string(i));
471 auto id2=dnnf.
getGate(std::to_string(g));
482 dnnf.
addWire(not_gate, leaf_gate);
483 dnnf.
addWire(and_gate, not_gate);
486 dnnf.
addWire(and_gate, leaf_gate);
491 }
else if(c==
"f" || c==
"o") {
497 }
else if(c==
"t" || c==
"a") {
507 auto id2=dnnf.
getGate(std::to_string(var));
509 std::vector<int> decisions;
511 while(ss >> decision) {
515 decisions.push_back(decision);
518 if(decisions.empty()) {
524 for(
auto leaf : decisions) {
528 dnnf.
addWire(not_gate, leaf_gate);
529 dnnf.
addWire(and_gate, not_gate);
532 dnnf.
addWire(and_gate, leaf_gate);
537 throw CircuitException(std::string(
"Unreadable d-DNNF (unknown node type: ")+c+
")");
540 }
while(getline(ifs, line));
545 if(unlink(outfilename.c_str())) {
560 std::stringstream ssopt(opt);
561 std::string delta_s, epsilon_s;
562 getline(ssopt, delta_s,
';');
563 getline(ssopt, epsilon_s,
';');
568 }
catch (std::invalid_argument &) {
573 epsilon=stod(epsilon_s);
574 }
catch (std::invalid_argument &) {
577 if(delta == 0) delta=0.2;
578 if(epsilon == 0) epsilon=0.8;
583 const double pivotAC=2*ceil(exp(3./2)*(1+1/epsilon)*(1+1/epsilon));
585 std::string cmdline=
"weightmc --startIteration=0 --gaussuntil=400 --verbosity=0 --pivotAC="+std::to_string(pivotAC)+
" "+filename+
" > "+filename+
".out";
587 int retvalue=system(cmdline.c_str());
593 std::ifstream ifs((filename+
".out").c_str());
594 std::string line, prev_line;
595 while(getline(ifs,line))
598 std::stringstream ss(prev_line);
600 ss >> result >> result >> result >> result >> result;
602 std::istringstream iss(result);
603 std::string val, exp;
604 getline(iss, val,
'x');
606 double value=stod(val);
608 double exponent=stod(exp);
609 double ret=value*(pow(2.0,exponent));
611 if(unlink(filename.c_str())) {
615 if(unlink((filename+
".out").c_str())) {
623 gate_t g, std::set<gate_t> &seen)
const
638 std::map<gate_t, double> groups;
639 std::set<gate_t> local_mulins;
640 std::set<std::pair<gate_t, unsigned> > mulin_seen;
646 if(local_mulins.find(group)==local_mulins.end()) {
647 if(seen.find(group)!=seen.end())
651 local_mulins.insert(group);
653 auto p = std::make_pair(group,
getInfo(c));
654 if(mulin_seen.find(p)==mulin_seen.end()) {
656 mulin_seen.insert(p);
662 for(
const auto [k, v]: groups)
673 if(seen.find(g)!=seen.end())
682 if(seen.find(child)!=seen.end())
699 std::set<gate_t> seen;
710 auto it =
info.find(g);
719 const std::vector<gate_t> &muls,
720 const std::vector<double> &cumulated_probs,
723 std::vector<gate_t> &prefix)
730 unsigned mid = (start+end)/2;
733 (cumulated_probs[mid+1] - cumulated_probs[start]) /
734 (cumulated_probs[end] - cumulated_probs[start]));
741 prefix.push_back(not_g);
755 constexpr double epsilon = std::numeric_limits<double>::epsilon() * 10;
757 return (diff < epsilon && diff > -epsilon);
762 std::map<gate_t,std::vector<gate_t> > var2mulinput;
764 var2mulinput[*
getWires(mul).begin()].push_back(mul);
768 for(
const auto &[var, muls]: var2mulinput)
770 const unsigned n = muls.size();
771 std::vector<double> cumulated_probs(n);
772 double cumulated_prob=0.;
774 for(
unsigned i=0; i<n; ++i) {
775 cumulated_prob +=
getProb(muls[i]);
776 cumulated_probs[i] = cumulated_prob;
781 std::vector<gate_t> prefix;
782 prefix.reserve(
static_cast<unsigned>(log(n)/log(2)+2));
827 if(seen.find(g)!=seen.end())
845 std::set<gate_t> seen;
854 if(method==
"compilation") {
856 }
else if(method==
"tree-decomposition") {
860 *
this, g, td}.build();
874 *
this, g, td}.build();
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.
std::string to_string(gate_t g)
Convert a gate_t to its decimal string representation.
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.
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.
virtual gate_t setGate(const uuid &u, gateType type)
Create or update the gate associated with UUID u.
void addWire(gate_t f, gate_t t)
Add a directed wire from gate f (parent) to gate t (child).
std::vector< BooleanGate > gates
Gate type for each gate.
uuid getUUID(gate_t g) const
Return the UUID string associated with gate g.
gate_t getGate(const uuid &u)
Return (or create) the gate associated with UUID u.
bool hasGate(const uuid &u) const
Test whether a gate with UUID u exists.
std::vector< gate_t >::size_type getNbGates() const
Return the total number of gates in the circuit.
virtual gate_t addGate()
Allocate a new gate with a default-initialised type.
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.
void setRoot(gate_t g)
Set the root gate.
Constructs a d-DNNF from a Boolean circuit and its tree decomposition.
int provsql_verbose
Verbosity level; controlled by the provsql.verbose_level GUC.
bool provsql_interrupted
Global variable that becomes true if this particular backend received an interrupt signal.
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.