42#include <boost/archive/text_oarchive.hpp>
43#include <boost/archive/text_iarchive.hpp>
53enum levels {ERROR, NOTICE};
54#define elog(level, ...) {fprintf(stderr, __VA_ARGS__); if(level==ERROR) exit(EXIT_FAILURE);}
55#define CHECK_FOR_INTERRUPTS() ((void)0)
59#include "utils/elog.h"
121 const std::unordered_map<gate_t, std::string> &labels)
const
129 const std::unordered_map<gate_t, std::string> *labels)
const
138 auto it = labels->find(g);
139 if(it != labels->end())
145 auto it = labels->find(g);
146 if(it != labels->end())
147 return it->second +
"[" + std::to_string(
getProb(g)) +
"]";
177 else if(!result.empty())
192 return "("+result+
")";
197 std::stringstream ss;
199 std::unordered_set<gate_t> processed;
200 std::stack<gate_t> to_process;
201 to_process.push(root);
203 while(!to_process.empty()) {
204 auto g=to_process.top();
207 if(processed.find(g)!=processed.end())
244 if(processed.find(s)==processed.end())
256 bool disjunction=
false;
260 return sampled.find(g)!=sampled.end();
263 throw CircuitException(
"Monte-Carlo sampling not implemented on multivalued inputs");
280 if(!disjunction && !e)
300 std::random_device rd;
301 rng.seed((
static_cast<uint64_t
>(rd()) << 32) | rd());
303 std::uniform_real_distribution<double> uniform01(0.0, 1.0);
307 for(
unsigned i=0; i<samples; ++i) {
308 std::unordered_set<gate_t> sampled;
310 if(uniform01(rng) <
getProb(in)) {
319 throw CircuitException(
"Interrupted after "+std::to_string(i+1)+
" samples");
322 return success*1./samples;
327 if(
inputs.size()>=8*
sizeof(
unsigned long long))
330 unsigned long long nb=(1<<
inputs.size());
333 for(
unsigned long long i=0; i < nb; ++i) {
334 std::unordered_set<gate_t> s;
359 std::vector<std::vector<int> > clauses;
366 int id{
static_cast<int>(i)+1};
367 std::vector<int> c = {
id};
369 clauses.push_back({-id,
static_cast<int>(s)+1});
370 c.push_back(-
static_cast<int>(s)-1);
372 clauses.push_back(c);
378 int id{
static_cast<int>(i)+1};
379 std::vector<int> c = {-
id};
381 clauses.push_back({id, -
static_cast<int>(s)-1});
382 c.push_back(
static_cast<int>(s)+1);
384 clauses.push_back(c);
390 int id=
static_cast<int>(i)+1;
392 clauses.push_back({-id,-
static_cast<int>(s)-1});
393 clauses.push_back({id,
static_cast<int>(s)+1});
398 throw CircuitException(
"Multivalued inputs should have been removed by then.");
405 clauses.push_back({(int)g+1});
411 char cdir[] =
"/tmp/provsqlXXXXXX";
412 if(mkdtemp(cdir) == NULL) {
415 std::string filename=std::string(cdir)+
"/input";
416 std::ofstream ofs(filename.c_str());
418 ofs <<
"p cnf " <<
gates.size() <<
" " << clauses.size() <<
"\n";
420 for(
unsigned i=0; i<clauses.size(); ++i) {
421 for(
int x : clauses[i]) {
428 ofs <<
"w " << (
static_cast<std::underlying_type<gate_t>::type
>(in)+1) <<
" " <<
getProb(in) <<
"\n";
429 ofs <<
"w -" << (
static_cast<std::underlying_type<gate_t>::type
>(in)+1) <<
" " << (1. -
getProb(in)) <<
"\n";
440 std::string outfilename=filename+
".nnf";
447 std::string cmdline=compiler+
" ";
449 cmdline+=
"-dDNNF "+filename+
" -out="+outfilename;
451 }
else if(compiler==
"c2d") {
452 cmdline+=
"-in "+filename+
" -silent";
453 }
else if(compiler==
"minic2d") {
454 cmdline+=
"-in "+filename;
455 }
else if(compiler==
"dsharp") {
456 cmdline+=
"-q -Fnnf "+outfilename+
" "+filename;
463 compiler +
" not found on PATH; install it or add its "
464 "directory to provsql.tool_search_path");
480 if(WIFSIGNALED(retvalue) && WTERMSIG(retvalue) == SIGINT) {
481 InterruptPending =
true;
482 QueryCancelPending =
true;
486 CHECK_FOR_INTERRUPTS();
500 if(WIFSIGNALED(retvalue) && WTERMSIG(retvalue) == SIGINT) {
501 InterruptPending =
true;
502 QueryCancelPending =
true;
506 CHECK_FOR_INTERRUPTS();
508 if(retvalue && compiler==
"d4") {
511 cmdline =
"d4 "+filename+
" -out="+outfilename;
514 if(WIFSIGNALED(retvalue) && WTERMSIG(retvalue) == SIGINT) {
515 InterruptPending =
true;
516 QueryCancelPending =
true;
521 CHECK_FOR_INTERRUPTS();
527 if(unlink(filename.c_str())) {
532 std::ifstream ifs(outfilename.c_str());
537 if(line.rfind(
"nnf", 0) != 0) {
540 if(compiler !=
"d4") {
546 unsigned nb_nodes, nb_edges, nb_variables;
548 std::stringstream ss(line);
549 ss >> nnf >> nb_nodes >> nb_edges >> nb_variables;
551 if(nb_variables!=
gates.size())
552 throw CircuitException(
"Unreadable d-DNNF (wrong number of variables: " + std::to_string(nb_variables) +
" vs " + std::to_string(
gates.size()) +
")");
561 std::stringstream ss(line);
569 auto id=dnnf.
getGate(std::to_string(i));
573 auto id2=dnnf.
getGate(std::to_string(g));
579 auto id=dnnf.
getGate(std::to_string(i));
583 auto id2=dnnf.
getGate(std::to_string(g));
594 dnnf.
addWire(not_gate, leaf_gate);
595 dnnf.
addWire(and_gate, not_gate);
598 dnnf.
addWire(and_gate, leaf_gate);
603 }
else if(c==
"f" || c==
"o") {
609 }
else if(c==
"t" || c==
"a") {
619 auto id2=dnnf.
getGate(std::to_string(var));
621 std::vector<int> decisions;
623 while(ss >> decision) {
627 decisions.push_back(decision);
630 if(decisions.empty()) {
636 for(
auto leaf : decisions) {
640 dnnf.
addWire(not_gate, leaf_gate);
641 dnnf.
addWire(and_gate, not_gate);
644 dnnf.
addWire(and_gate, leaf_gate);
649 throw CircuitException(std::string(
"Unreadable d-DNNF (unknown node type: ")+c+
")");
652 }
while(getline(ifs, line));
657 if(unlink(outfilename.c_str())) {
660 std::string dirname=filename.substr(0, filename.rfind(
'/'));
661 if(rmdir(dirname.c_str())) {
676 std::stringstream ssopt(opt);
677 std::string delta_s, epsilon_s;
678 getline(ssopt, delta_s,
';');
679 getline(ssopt, epsilon_s,
';');
684 }
catch (std::invalid_argument &) {
689 epsilon=stod(epsilon_s);
690 }
catch (std::invalid_argument &) {
693 if(delta == 0) delta=0.2;
694 if(epsilon == 0) epsilon=0.8;
699 const double pivotAC=2*ceil(exp(3./2)*(1+1/epsilon)*(1+1/epsilon));
703 "weightmc not found on PATH; install it or add its "
704 "directory to provsql.tool_search_path");
706 std::string cmdline=
"weightmc --startIteration=0 --gaussuntil=400 --verbosity=0 --pivotAC="+std::to_string(pivotAC)+
" "+filename+
" > "+filename+
".out";
714 std::ifstream ifs((filename+
".out").c_str());
715 std::string line, prev_line;
716 while(getline(ifs,line))
719 std::stringstream ss(prev_line);
721 ss >> result >> result >> result >> result >> result;
723 std::istringstream iss(result);
724 std::string val, exp;
725 getline(iss, val,
'x');
727 double value=stod(val);
729 double exponent=stod(exp);
730 double ret=value*(pow(2.0,exponent));
732 if(unlink(filename.c_str())) {
736 if(unlink((filename+
".out").c_str())) {
740 std::string dirname=filename.substr(0, filename.rfind(
'/'));
741 if(rmdir(dirname.c_str())) {
749 gate_t g, std::set<gate_t> &seen)
const
764 std::map<gate_t, double> groups;
765 std::set<gate_t> local_mulins;
766 std::set<std::pair<gate_t, unsigned> > mulin_seen;
772 if(local_mulins.find(group)==local_mulins.end()) {
773 if(seen.find(group)!=seen.end())
777 local_mulins.insert(group);
779 auto p = std::make_pair(group,
getInfo(c));
780 if(mulin_seen.find(p)==mulin_seen.end()) {
782 mulin_seen.insert(p);
788 for(
const auto [k, v]: groups)
810 if (p == 0.0 || p == 1.0) {
814 if(seen.find(g)!=seen.end())
824 if(seen.find(child)!=seen.end())
841 std::set<gate_t> seen;
852 auto it =
info.find(g);
861 const std::vector<gate_t> &muls,
862 const std::vector<double> &cumulated_probs,
865 std::vector<gate_t> &prefix)
872 unsigned mid = (start+end)/2;
878 double prev_start = (start == 0) ? 0. : cumulated_probs[start - 1];
881 (cumulated_probs[mid] - prev_start) /
882 (cumulated_probs[end] - prev_start));
889 prefix.push_back(not_g);
903 constexpr double epsilon = std::numeric_limits<double>::epsilon() * 10;
905 return (diff < epsilon && diff > -epsilon);
910 std::map<gate_t,std::vector<gate_t> > var2mulinput;
912 var2mulinput[*
getWires(mul).begin()].push_back(mul);
916 for(
const auto &[var, muls]: var2mulinput)
918 const unsigned n = muls.size();
919 std::vector<double> cumulated_probs(n);
920 double cumulated_prob=0.;
922 for(
unsigned i=0; i<n; ++i) {
923 cumulated_prob +=
getProb(muls[i]);
924 cumulated_probs[i] = cumulated_prob;
929 std::vector<gate_t> prefix;
930 prefix.reserve(
static_cast<unsigned>(log(n)/log(2)+2));
975 if(seen.find(g)!=seen.end())
996 std::set<gate_t> seen;
1005 if(method==
"compilation") {
1007 }
else if(method==
"tree-decomposition") {
1011 *
this, g, td}.build();
1025 *
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.
std::string toStringHelper(gate_t g, BooleanGate parent, const std::unordered_map< gate_t, std::string > *labels) const
Internal recursive helper for the two toString() variants.
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.
friend class dDNNFTreeDecompositionBuilder
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)
BooleanGate getGateType(gate_t g) const
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
uuid getUUID(gate_t g) const
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.
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.
int provsql_monte_carlo_seed
Seed for the Monte Carlo sampler; -1 means non-deterministic (std::random_device); controlled by the ...
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.