44#include <boost/archive/text_oarchive.hpp>
45#include <boost/archive/text_iarchive.hpp>
65enum levels {
ERROR, NOTICE};
66#define elog(level, ...) {fprintf(stderr, __VA_ARGS__); if(level==ERROR) exit(EXIT_FAILURE);}
67#define CHECK_FOR_INTERRUPTS() ((void)0)
70#define check_stack_depth() ((void)0)
74#include "utils/elog.h"
103double parse_wmc_value(
const std::string &line,
const char *tool) {
104 std::vector<std::string> tokens;
105 std::stringstream ss(line);
107 while(ss >> tok) tokens.push_back(tok);
109 for(
auto it = tokens.rbegin(); it != tokens.rend(); ++it) {
110 const std::string &t = *it;
112 auto slash = t.find(
'/');
113 if(slash != std::string::npos) {
114 size_t pn = 0, pd = 0;
115 double num = std::stod(t.substr(0, slash), &pn);
116 double den = std::stod(t.substr(slash + 1), &pd);
117 if(pn != slash || pd != t.size() - slash - 1)
continue;
118 return (den == 0.0) ? 0.0 : num / den;
121 double v = std::stod(t, &p);
122 if(p == t.size())
return v;
123 }
catch(
const std::exception &) {
127 throw CircuitException(std::string(tool) +
": could not parse '" + line +
"'");
189 const std::unordered_map<gate_t, std::string> &labels)
const
197 const std::unordered_map<gate_t, std::string> *labels)
const
206 auto it = labels->find(g);
207 if(it != labels->end())
213 auto it = labels->find(g);
214 if(it != labels->end())
215 return it->second +
"[" + std::to_string(
getProb(g)) +
"]";
245 else if(!result.empty())
260 return "("+result+
")";
265 std::stringstream ss;
267 std::unordered_set<gate_t> processed;
268 std::stack<gate_t> to_process;
269 to_process.push(root);
271 while(!to_process.empty()) {
272 auto g=to_process.top();
275 if(processed.find(g)!=processed.end())
312 if(processed.find(s)==processed.end())
325 bool disjunction=
false;
329 return sampled.find(g)!=sampled.end();
332 throw CircuitException(
"Monte-Carlo sampling not implemented on multivalued inputs");
349 if(!disjunction && !e)
369 std::random_device rd;
370 rng.seed((
static_cast<uint64_t
>(rd()) << 32) | rd());
372 std::uniform_real_distribution<double> uniform01(0.0, 1.0);
376 for(
unsigned i=0; i<samples; ++i) {
377 std::unordered_set<gate_t> sampled;
379 if(uniform01(rng) <
getProb(in)) {
388 throw CircuitException(
"Interrupted after "+std::to_string(i+1)+
" samples");
391 return success*1./samples;
396 std::vector<gate_t> &clauses,
397 std::vector<std::set<gate_t> > &supports)
const
405 std::vector<gate_t> clause_roots;
408 clause_roots.push_back(c);
410 clause_roots.push_back(g);
413 for(
auto root: clause_roots) {
418 std::set<gate_t> support;
419 std::unordered_set<gate_t> seen;
420 std::stack<gate_t> st;
425 if(!seen.insert(cur).second)
439 clauses.push_back(root);
440 supports.push_back(std::move(support));
449 std::vector<gate_t> clause_roots;
452 clause_roots.push_back(c);
454 clause_roots.push_back(g);
455 num_clauses = clause_roots.size();
460 std::unordered_set<gate_t> seen;
461 std::stack<gate_t> st;
462 for(
auto r: clause_roots)
467 if(!seen.insert(cur).second)
492struct KarpLubyState {
493 std::vector<double> p;
494 std::vector<double> cumulative;
496 std::vector<gate_t> relevant;
499KarpLubyState karpLubyState(
501 const std::vector<std::set<gate_t> > &supports)
504 const size_t m = supports.size();
506 st.cumulative.resize(m);
507 std::set<gate_t> rel;
508 for(
size_t i=0; i<m; ++i) {
510 for(
gate_t leaf: supports[i]) {
516 st.cumulative[i] = st.S;
518 st.relevant.assign(rel.begin(), rel.end());
524std::mt19937_64 karpLubySeededRNG()
530 std::random_device rd;
531 rng.seed((
static_cast<uint64_t
>(rd()) << 32) | rd());
537size_t karpLubyDrawClause(
const KarpLubyState &st,
538 std::mt19937_64 &rng,
539 std::uniform_real_distribution<double> &u01)
541 double u = u01(rng) * st.S;
542 size_t i =
static_cast<size_t>(
543 std::upper_bound(st.cumulative.begin(), st.cumulative.end(), u)
544 - st.cumulative.begin());
545 if(i >= st.cumulative.size())
546 i = st.cumulative.size() - 1;
560 const std::vector<std::set<gate_t> > &supports,
561 const KarpLubyState &st,
size_t i,
562 std::mt19937_64 &rng,
563 std::uniform_real_distribution<double> &u01,
564 std::unordered_set<gate_t> &trueLeaves)
567 for(
gate_t leaf: st.relevant) {
568 if(supports[i].count(leaf) || u01(rng) < c.
getProb(leaf))
569 trueLeaves.insert(leaf);
571 const size_t m = supports.size();
572 for(
size_t j=0; j<m; ++j) {
574 for(
gate_t leaf: supports[j]) {
575 if(trueLeaves.find(leaf)==trueLeaves.end()) { sat =
false;
break; }
586 const std::vector<gate_t> &clauses,
587 const std::vector<std::set<gate_t> > &supports,
588 unsigned long samples)
const
590 const size_t m = clauses.size();
591 if(m==0 || samples==0)
594 KarpLubyState st = karpLubyState(*
this, supports);
598 std::mt19937_64 rng = karpLubySeededRNG();
599 std::uniform_real_distribution<double> u01(0.0, 1.0);
600 std::unordered_set<gate_t> trueLeaves;
607 unsigned long accepts = 0;
608 for(
unsigned long s=0; s<samples; ++s) {
609 size_t i = karpLubyDrawClause(st, rng, u01);
610 if(karpLubyCovers(*
this, supports, st, i, rng, u01, trueLeaves))
613 throw CircuitException(
"Interrupted after "+std::to_string(s+1)+
" samples");
615 return st.S * accepts /
static_cast<double>(samples);
625 std::vector<unsigned long> n(m, 1);
626 const unsigned long rest = samples - m;
627 std::vector<double> frac(m);
628 unsigned long base_sum = 0;
629 for(
size_t i=0; i<m; ++i) {
630 double want =
static_cast<double>(rest) * st.p[i] / st.S;
631 unsigned long fl =
static_cast<unsigned long>(want);
634 frac[i] = want -
static_cast<double>(fl);
636 unsigned long leftover = rest - base_sum;
638 std::vector<size_t> idx(m);
639 for(
size_t i=0; i<m; ++i) idx[i] = i;
640 std::partial_sort(idx.begin(), idx.begin()+leftover, idx.end(),
641 [&](
size_t a,
size_t b){ return frac[a] > frac[b]; });
642 for(
unsigned long k=0; k<leftover; ++k)
647 for(
size_t i=0; i<m; ++i) {
648 unsigned long accepts = 0;
649 for(
unsigned long k=0; k<n[i]; ++k) {
650 if(karpLubyCovers(*
this, supports, st, i, rng, u01, trueLeaves))
656 est += st.p[i] *
static_cast<double>(accepts) /
static_cast<double>(n[i]);
662 const std::vector<gate_t> &clauses,
663 const std::vector<std::set<gate_t> > &supports,
664 double eps,
double delta,
665 unsigned long max_samples,
666 unsigned long &samples_used,
667 bool &reached_target)
const
670 reached_target =
false;
671 const size_t m = clauses.size();
672 if(m==0 || max_samples==0)
675 KarpLubyState st = karpLubyState(*
this, supports);
685 const double e = exp(1.0);
686 const double Y = 4.0 * (e - 2.0) * log(2.0/delta) / (eps*eps);
687 const double Y1 = 1.0 + (1.0 + eps) * Y;
689 std::mt19937_64 rng = karpLubySeededRNG();
690 std::uniform_real_distribution<double> u01(0.0, 1.0);
691 std::unordered_set<gate_t> trueLeaves;
693 unsigned long accepts = 0;
694 for(
unsigned long s=0; s<max_samples; ++s) {
695 size_t i = karpLubyDrawClause(st, rng, u01);
696 if(karpLubyCovers(*
this, supports, st, i, rng, u01, trueLeaves)) {
698 if(
static_cast<double>(accepts) >= Y1) {
699 samples_used = s + 1;
700 reached_target =
true;
701 return st.S * Y1 /
static_cast<double>(samples_used);
705 throw CircuitException(
"Interrupted after "+std::to_string(s+1)+
" samples");
711 samples_used = max_samples;
712 return st.S *
static_cast<double>(accepts) /
static_cast<double>(max_samples);
719 const std::vector<gate_t> &clauses,
720 const std::vector<std::set<gate_t> > &supports)
const
722 const size_t m = clauses.size();
727 "sieve: too many clauses (" + std::to_string(m) +
" > "
729 +
"); inclusion-exclusion is 2^m -- use another method");
733 std::unordered_set<gate_t> u;
734 for(
unsigned long long s = 1; s < (1ULL << m); ++s) {
737 for(
size_t i = 0; i < m; ++i)
738 if(s & (1ULL << i)) {
740 for(
gate_t leaf : supports[i])
746 if(bits & 1) total += p;
else total -= p;
755 const std::vector<std::set<gate_t> > &clauses,
756 double &lower,
double &upper)
const
758 const size_t m = clauses.size();
766 std::vector<double> clause_prob(m);
767 for(
size_t i = 0; i < m; ++i) {
769 for(
gate_t leaf : clauses[i])
776 std::vector<size_t> order(m);
777 for(
size_t i = 0; i < m; ++i)
779 std::sort(order.begin(), order.end(),
780 [&](
size_t a,
size_t b) {
781 return clause_prob[a] > clause_prob[b];
788 std::vector<std::set<gate_t> > bucket_support;
789 std::vector<double> bucket_prob;
790 for(
size_t idx : order) {
791 const std::set<gate_t> &sup = clauses[idx];
792 size_t target = bucket_support.size();
793 for(
size_t b = 0; b < bucket_support.size(); ++b) {
794 bool disjoint =
true;
796 if(bucket_support[b].count(leaf)) {
805 if(target == bucket_support.size()) {
806 bucket_support.emplace_back();
807 bucket_prob.push_back(0.);
809 bucket_prob[target] =
810 1. - (1. - bucket_prob[target]) * (1. - clause_prob[idx]);
811 bucket_support[target].insert(sup.begin(), sup.end());
819 double L = 0., U = 0.;
820 for(
double bp : bucket_prob) {
826 upper = (U > 1.) ? 1. : U;
831 if(
inputs.size()>=8*
sizeof(
unsigned long long))
834 unsigned long long nb=(1ULL<<
inputs.size());
837 for(
unsigned long long i=0; i < nb; ++i) {
838 std::unordered_set<gate_t> s;
843 if(i & (1ULL << j)) {
863 std::vector<std::vector<int> > clauses;
870 int id{
static_cast<int>(i)+1};
871 std::vector<int> c = {
id};
873 clauses.push_back({-id,
static_cast<int>(s)+1});
874 c.push_back(-
static_cast<int>(s)-1);
876 clauses.push_back(c);
882 int id{
static_cast<int>(i)+1};
883 std::vector<int> c = {-
id};
885 clauses.push_back({id, -
static_cast<int>(s)-1});
886 c.push_back(
static_cast<int>(s)+1);
888 clauses.push_back(c);
894 int id=
static_cast<int>(i)+1;
896 clauses.push_back({-id,-
static_cast<int>(s)-1});
897 clauses.push_back({id,
static_cast<int>(s)+1});
902 throw CircuitException(
"Multivalued inputs should have been removed by then.");
909 clauses.push_back({(int)g+1});
911 std::ostringstream oss;
918 oss <<
"c input " << m.variable <<
" "
919 << (m.uuid.empty() ?
"?" : m.uuid) <<
" "
920 << m.probability <<
"\n";
923 oss <<
"p cnf " <<
gates.size() <<
" " << clauses.size() <<
"\n";
924 for(
unsigned i=0; i<clauses.size(); ++i) {
925 for(
int x : clauses[i]) {
932 oss <<
"w " << (
static_cast<std::underlying_type<gate_t>::type
>(in)+1) <<
" " <<
getProb(in) <<
"\n";
933 oss <<
"w -" << (
static_cast<std::underlying_type<gate_t>::type
>(in)+1) <<
" " << (1. -
getProb(in)) <<
"\n";
939std::vector<BooleanCircuit::CNFInputMapping>
941 std::vector<CNFInputMapping> mapping;
945 auto id =
static_cast<std::underlying_type<gate_t>::type
>(in);
950 mapping.push_back({
static_cast<int>(id) + 1, u,
getProb(in)});
957 auto idOf = [](
gate_t x) {
958 return static_cast<std::underlying_type<gate_t>::type
>(x);
961 std::set<gate_t> seenInputs;
962 std::set<gate_t> internalGates;
965 std::function<std::string(
gate_t)> lit = [&](
gate_t w) -> std::string {
968 return "in" + std::to_string(idOf(w));
971 return "g" + std::to_string(idOf(w));
973 std::string inner = lit(*
getWires(w).begin());
974 return inner[0]==
'-' ? inner.substr(1) :
"-"+inner;
987 if(seenInputs.insert(w).second)
988 inputOrder.push_back(w);
995 if(internalGates.insert(w).second)
1005 std::ostringstream oss;
1006 oss <<
"c BC-S1.2\n";
1008 for(
gate_t in : inputOrder)
1009 oss <<
"I in" << idOf(in) <<
"\n";
1010 for(
gate_t w : internalGates) {
1014 oss <<
"G g" << idOf(w) <<
" := ";
1021 oss <<
" " << lit(c);
1024 oss <<
"T " << lit(g) <<
"\n";
1048 std::ifstream ifs(outfilename.c_str());
1056 bool found_data =
false;
1057 while (std::getline(ifs, line)) {
1058 if (line.rfind(
"0:", 0) == 0) { found_data =
true;
break; }
1066 std::vector<gate_t> id_to_gate;
1069 if (line.empty())
continue;
1070 auto colon_pos = line.find(
':');
1071 if (colon_pos == std::string::npos)
continue;
1075 int panini_id = std::stoi(line.substr(0, colon_pos));
1076 if (
static_cast<size_t>(panini_id) != id_to_gate.size())
1078 "Panini output: out-of-order node id "
1079 + std::to_string(panini_id));
1081 std::stringstream ss(line.substr(colon_pos + 1));
1089 }
else if (first ==
"T") {
1092 }
else if (first ==
"C" || first ==
"D") {
1102 while (ss >> child) {
1103 if (child == 0)
break;
1104 if (child < 0 ||
static_cast<size_t>(child) >= id_to_gate.size())
1106 "Panini output: forward / invalid child reference "
1107 + std::to_string(child));
1108 dnnf.
addWire(this_gate, id_to_gate[child]);
1110 }
else if (first ==
"K") {
1112 "Panini output: unexpected K (kernelize) node; ProvSQL "
1113 "does not support Panini target languages that emit K "
1114 "nodes (R2-D2, CCDD).");
1120 int var = std::stoi(first);
1121 int f_child, t_child;
1122 if (!(ss >> f_child >> t_child))
1124 "Panini output: malformed decision line at id "
1125 + std::to_string(panini_id));
1126 if (t_child < 0 || f_child < 0
1127 ||
static_cast<size_t>(t_child) >= id_to_gate.size()
1128 ||
static_cast<size_t>(f_child) >= id_to_gate.size())
1130 "Panini output: forward / invalid decision child at id "
1131 + std::to_string(panini_id));
1132 gate_t t_gate = id_to_gate[t_child];
1133 gate_t f_gate = id_to_gate[f_child];
1148 size_t var_idx =
static_cast<size_t>(var) - 1;
1154 dnnf.
addWire(neg_lit, pos_lit);
1162 dnnf.
addWire(this_gate, and_t);
1163 dnnf.
addWire(this_gate, and_f);
1166 dnnf.
addWire(this_gate, t_gate);
1167 dnnf.
addWire(this_gate, f_gate);
1170 id_to_gate.push_back(this_gate);
1171 }
while (std::getline(ifs, line));
1175 if (id_to_gate.empty())
1179 dnnf.
setRoot(id_to_gate.back());
1194 const std::string &preferred =
"") {
1195 if(!preferred.empty()) {
1218 std::string chosen =
selectTool(
"compile", fb);
1219 return chosen.empty() ? std::string(fb) : chosen;
1223 std::string *resolved)
const {
1227 if(compiler.empty()) {
1229 if(compiler.empty())
1231 "no knowledge compiler is available; install one (d4, d4v2, "
1232 "c2d, minic2d, dsharp) or add its directory to "
1233 "provsql.tool_search_path");
1248 "Compiler '"+compiler+
"' is disabled in the tool registry");
1251 "Compiler '"+compiler+
"' uses output parser '"+rec->
parser
1252 +
"', which compilation() does not implement");
1253 const std::string compiler_binary = rec->
binary;
1255 *resolved = compiler;
1262 if(rec->
kind ==
"kcmcp") {
1263 std::vector<gate_t> inputOrder;
1264 std::string content;
1265 uint8_t input_format = 0;
1268 content =
BCS12(g, inputOrder);
1280 std::string endpoint = rec->
endpoint;
1281 if(endpoint ==
"managed")
1283 if(endpoint.empty())
1285 "KCMCP tool '"+compiler+
"' has no endpoint (managed server not "
1286 "running, or provsql.kcmcp_server unset)");
1289 std::istringstream iss(nnf);
1293 }
catch(
const std::exception &e) {
1295 +
"' failed: "+e.what());
1305 bool circuit_input = rec->
acceptsInput(
"circuit-bcs12")
1309 compiler_binary +
" not found on PATH; install it or add its "
1310 "directory to provsql.tool_search_path");
1313 std::string filename = tmp.
file(
"input");
1314 std::string outfilename = tmp.
file(
"input.nnf");
1317 std::vector<gate_t> inputOrder;
1318 std::string content;
1321 content =
BCS12(g, inputOrder);
1324 circuit_input =
false;
1331 std::ofstream ofs(filename);
1345 std::string cmdline;
1348 compiler_binary, filename, outfilename);
1350 cmdline = rec->
buildCommand(filename, outfilename, compiler_binary);
1362 CHECK_FOR_INTERRUPTS();
1369 if(rec->
parser ==
"panini-dd")
1372 std::ifstream ifs(outfilename.c_str());
1387 const std::vector<gate_t> &inputOrder)
const {
1388 const bool circuit_input = !inputOrder.empty();
1401 if(line.rfind(
"nnf", 0) != 0) {
1410 unsigned nb_nodes, nb_edges, nb_variables;
1412 std::stringstream ss(line);
1413 ss >> nnf >> nb_nodes >> nb_edges >> nb_variables;
1415 if(nb_variables!=
gates.size())
1416 throw CircuitException(
"Unreadable d-DNNF (wrong number of variables: " + std::to_string(nb_variables) +
" vs " + std::to_string(
gates.size()) +
")");
1428 size_t k = inputOrder.size();
1429 auto resolveVar = [&](
int v) -> std::pair<bool, gate_t> {
1430 unsigned idx =
static_cast<unsigned>(abs(v));
1432 if(idx>=1 && idx<=k)
1433 return {
true, inputOrder[idx-1]};
1434 return {
false,
gate_t{}};
1437 return {
true,
static_cast<gate_t>(idx-1)};
1438 return {
false,
gate_t{}};
1443 std::stringstream ss(line);
1451 auto id=dnnf.
getGate(std::to_string(i));
1455 auto id2=dnnf.
getGate(std::to_string(g));
1461 auto id=dnnf.
getGate(std::to_string(i));
1465 auto id2=dnnf.
getGate(std::to_string(g));
1472 auto [is_in, in_gate] = resolveVar(leaf);
1474 auto pid =
static_cast<std::underlying_type<gate_t>::type
>(in_gate);
1478 dnnf.
addWire(not_gate, leaf_gate);
1479 dnnf.
addWire(and_gate, not_gate);
1481 dnnf.
addWire(and_gate, leaf_gate);
1486 }
else if(c==
"f" || c==
"o") {
1492 }
else if(c==
"t" || c==
"a") {
1502 auto id2=dnnf.
getGate(std::to_string(var));
1504 std::vector<int> decisions;
1506 while(ss >> decision) {
1513 if(resolveVar(decision).first)
1514 decisions.push_back(decision);
1517 if(decisions.empty()) {
1523 for(
auto leaf : decisions) {
1524 auto in_gate = resolveVar(leaf).second;
1525 auto pid =
static_cast<std::underlying_type<gate_t>::type
>(in_gate);
1529 dnnf.
addWire(not_gate, leaf_gate);
1530 dnnf.
addWire(and_gate, not_gate);
1532 dnnf.
addWire(and_gate, leaf_gate);
1537 throw CircuitException(std::string(
"Unreadable d-DNNF (unknown node type: ")+c+
")");
1540 }
while(getline(in, line));
1567 const std::string &opt)
const {
1570 std::string tool = requested;
1575 "no weighted model counter is available; install one (ganak, "
1576 "sharpsat-td, dpmc, weightmc) or add its directory to "
1577 "provsql.tool_search_path");
1584 throw CircuitException(
"Tool '" + tool +
"' is disabled in the tool registry");
1592 rec->
binary +
" not found on PATH; install it or add its "
1593 "directory to provsql.tool_search_path");
1597 tool +
" needs '" + dep +
"' on PATH; install it or add its "
1598 "directory to provsql.tool_search_path");
1600 const bool weightmc_io = (rec->
parser ==
"weightmc");
1603 const std::string &dirname = tmp.
path();
1604 std::string filename = tmp.
file(
"input");
1605 std::string outfilename = tmp.
file(
"input.out");
1607 std::ofstream ofs(filename);
1616 int id =
static_cast<int>(in) + 1;
1617 ofs <<
"c p weight " <<
id <<
' ' <<
getProb(in) <<
" 0\n";
1618 ofs <<
"c p weight -" <<
id <<
' ' << (1.0 -
getProb(in)) <<
" 0\n";
1626 double epsilon = 0.8;
1628 std::stringstream ssopt(opt);
1629 std::string delta_s, epsilon_s;
1630 getline(ssopt, delta_s,
';');
1631 getline(ssopt, epsilon_s,
';');
1632 try {
double e = stod(epsilon_s);
if(e != 0) epsilon = e; }
1633 catch(
const std::exception &) {}
1635 const double pivotAC = 2*ceil(exp(3./2)*(1+1/epsilon)*(1+1/epsilon));
1638 filename, outfilename, rec->
binary,
1639 {{
"tmpdir", dirname}, {
"pivotAC", std::to_string(pivotAC)}});
1642 CHECK_FOR_INTERRUPTS();
1646 std::ifstream ifs(outfilename.c_str());
1650 std::string line, prev_line;
1651 while(getline(ifs, line)) prev_line = line;
1652 std::stringstream ss(prev_line);
1654 ss >> result >> result >> result >> result >> result;
1655 std::istringstream iss(result);
1656 std::string val, exp;
1657 getline(iss, val,
'x');
1661 double value = stod(val);
1662 double exponent = stod(exp.substr(2));
1663 ret = value * pow(2.0, exponent);
1667 std::string line, matched;
1668 while(getline(ifs, line))
1669 if(line.rfind(
"c s exact", 0) == 0 || line.rfind(
"s wmc", 0) == 0)
1673 ret = parse_wmc_value(matched, tool.c_str());
1684 gate_t g, std::set<gate_t> &seen,
1685 std::unordered_map<gate_t, double> &memo)
const
1687 check_stack_depth();
1693 auto it = memo.find(g);
1694 if(it != memo.end())
1707 const std::size_t seen_before = seen.size();
1722 std::map<gate_t, double> groups;
1723 std::set<gate_t> local_mulins;
1724 std::set<std::pair<gate_t, unsigned> > mulin_seen;
1730 if(local_mulins.find(group)==local_mulins.end()) {
1731 if(seen.find(group)!=seen.end())
1735 local_mulins.insert(group);
1737 auto p = std::make_pair(group,
getInfo(c));
1738 if(mulin_seen.find(p)==mulin_seen.end()) {
1740 mulin_seen.insert(p);
1746 for(
const auto [k, v]: groups)
1768 if (p == 0.0 || p == 1.0) {
1772 if(seen.find(g)!=seen.end())
1782 if(seen.find(child)!=seen.end())
1795 if(seen.size() == seen_before)
1801 gate_t root, std::set<gate_t> &seen,
1802 std::unordered_map<gate_t, double> &memo)
const
1804 const std::size_t seen_before = seen.size();
1808 std::unordered_map<gate_t, double> val;
1813 std::unordered_set<gate_t> island_mulvars;
1814 std::vector<gate_t> stack{root};
1816 while(!stack.empty()) {
1817 const gate_t g = stack.back();
1818 if(val.find(g) != val.end()) {
1829 if(p != 0.0 && p != 1.0) {
1830 if(seen.find(g) != seen.end())
1840 if(island_mulvars.insert(child).second) {
1841 if(seen.find(child) != seen.end())
1859 if(val.find(c) == val.end()) {
1886 if(seen.size() == seen_before)
1887 memo[root] = val[root];
1893 std::set<gate_t> seen;
1894 std::unordered_map<gate_t, double> memo;
1905 auto it =
info.find(g);
1914 const std::vector<gate_t> &muls,
1915 const std::vector<double> &cumulated_probs,
1918 std::vector<gate_t> &prefix)
1925 unsigned mid = (start+end)/2;
1931 double prev_start = (start == 0) ? 0. : cumulated_probs[start - 1];
1934 (cumulated_probs[mid] - prev_start) /
1935 (cumulated_probs[end] - prev_start));
1939 prefix.push_back(g);
1942 prefix.push_back(not_g);
1955 double diff = a - b;
1956 constexpr double epsilon = std::numeric_limits<double>::epsilon() * 10;
1958 return (diff < epsilon && diff > -epsilon);
1963 std::map<gate_t,std::vector<gate_t> > var2mulinput;
1965 var2mulinput[*
getWires(mul).begin()].push_back(mul);
1969 for(
const auto &[var, muls]: var2mulinput)
1971 const unsigned n = muls.size();
1972 std::vector<double> cumulated_probs(n);
1973 double cumulated_prob=0.;
1975 for(
unsigned i=0; i<n; ++i) {
1976 cumulated_prob +=
getProb(muls[i]);
1977 cumulated_probs[i] = cumulated_prob;
1982 std::vector<gate_t> prefix;
1983 prefix.reserve(
static_cast<unsigned>(log(n)/log(2)+2));
1992 check_stack_depth();
2036 if(seen.find(g)!=seen.end())
2055 std::set<gate_t> &seen,
2062 std::unordered_map<gate_t, gate_t> val;
2063 std::vector<gate_t> stack{root};
2065 while(!stack.empty()) {
2066 const gate_t g = stack.back();
2067 if(val.find(g) != val.end()) {
2075 if(seen.find(g) != seen.end())
2094 if(val.find(c) == val.end()) {
2121 std::set<gate_t> seen;
2139 if(method==
"compilation") {
2141 }
else if(method==
"tree-decomposition") {
2145 *
this, g, td}.build();
2149 }
else if(method==
"interpret-as-dd") {
2161 *
this, g, td}.build();
2187 if(name==
"default" || name==
"tree-decomposition" || name==
"interpret-as-dd")
2188 return makeDD(g, name==
"default" ? std::string() : name,
"");
static std::string selectTool(const std::string &operation, const std::string &preferred="")
static const size_t kSieveMaxClauses
Largest clause count for which the 2^m sieve enumeration is admitted.
static std::string chooseCompiler()
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.
constexpr unsigned DNNF_CERT_INFO
d-DNNF certificate value for the (gate-type-specific) per-gate info field.
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>.
Boolean circuit for provenance formula evaluation.
std::vector< double > prob
Per-gate probability (for IN gates).
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 independentEvaluationInternal(gate_t g, std::set< gate_t > &seen, std::unordered_map< gate_t, double > &memo) const
Recursive helper for independentEvaluation().
double sieve(const std::vector< gate_t > &clauses, const std::vector< std::set< gate_t > > &supports) const
Exact probability of a monotone DNF by inclusion-exclusion (sieve).
double possibleWorlds(gate_t g) const
Compute the probability by exact enumeration of all possible worlds.
std::vector< CNFInputMapping > tseytinVariableMapping() const
Map each input gate to its DIMACS variable, UUID, probability.
double karpLubyStopping(const std::vector< gate_t > &clauses, const std::vector< std::set< gate_t > > &supports, double eps, double delta, unsigned long max_samples, unsigned long &samples_used, bool &reached_target) const
Karp-Luby FPRAS with the self-adjusting stopping rule (adaptive sample count for a relative (eps,...
void setProb(gate_t g, double p)
Set the probability for gate g and mark the circuit as probabilistic.
dDNNF parseDDNNF(std::istream &in, const std::vector< gate_t > &inputOrder) const
Parse a c2d/d4 NNF stream into a dDNNF over this circuit's input gates.
std::set< gate_t > inputs
Set of IN (input) gate IDs.
bool isDNNFCertified(gate_t g) const
Is gate g certified by the d-DNNF per-gate marking?
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 evaluateCertifiedIsland(gate_t root, std::set< gate_t > &seen, std::unordered_map< gate_t, double > &memo) const
Iteratively evaluate a certified d-DNNF island.
std::string exportCircuit(gate_t g) const
Export the circuit in the textual format expected by external compilers.
std::string BCS12(gate_t g, std::vector< gate_t > &inputOrder) const
Serialise the sub-circuit at g in d4's BC-S1.2 circuit format.
std::string TseytinCNF(gate_t g, bool display_prob, bool mapping=false) const
Return the Tseytin transformation of the sub-circuit at g as a DIMACS string.
dDNNF parsePaniniDD(const std::string &outfilename) const
Parse a Panini (KCBox) DD output file into a ProvSQL d-DNNF.
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.
double karpLuby(const std::vector< gate_t > &clauses, const std::vector< std::set< gate_t > > &supports, unsigned long samples) const
Karp-Luby FPRAS estimate of a DNF-shaped circuit's probability (fixed sample budget,...
dDNNF compilation(gate_t g, std::string compiler, std::string *resolved=nullptr) const
Compile the sub-circuit rooted at g to a dDNNF via an external tool.
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
double wmcCount(gate_t g, const std::string &tool, const std::string &opt) const
Weighted model counting through a registered external counter.
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.
bool dnfShapeInfo(gate_t g, std::size_t &num_clauses) const
Cheap shape test: is the circuit DNF-shaped, and how many clauses?
void setInfo(gate_t g, unsigned info)
Store an integer annotation on gate g.
void dnfBounds(const std::vector< std::set< gate_t > > &clauses, double &lower, double &upper) const
Cheap certified probability interval [lower,upper] of a monotone DNF, without compiling it (Olteanu-H...
virtual std::string toString(gate_t g) const override
Return a textual description of gate g for debugging.
bool dnfShape(gate_t g, std::vector< gate_t > &clauses, std::vector< std::set< gate_t > > &supports) const
Detect the DNF shape the Karp-Luby FPRAS requires.
std::map< gate_t, unsigned > info
Per-gate integer info (for MULIN gates).
gate_t interpretCertifiedIsland(gate_t root, std::set< gate_t > &seen, dDNNF &dd) const
Iteratively copy a certified island into dd.
dDNNF makeDDByName(gate_t g, const std::string &name) const
Build a dDNNF from a single compiler/route name.
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
std::unordered_map< gate_t, uuid > id2uuid
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.
void simplify()
Simplify the d-DNNF by removing redundant constants.
RAII guard around a freshly mkdtemp'd /tmp directory.
std::string file(const std::string &basename)
Build a path under the temp dir and register it for cleanup.
const std::string & path() const
void keep()
Leave the directory on disk; cleanup is skipped at scope exit.
Constructs a d-DNNF from a Boolean circuit and its tree decomposition.
In-extension KCMCP client: compile a Boolean problem on a warm, socket-attached knowledge compiler in...
const char * provsql_kcmcp_managed_endpoint(void)
Read the live endpoint of the managed KCMCP server from shared memory (e.g.
std::string kcmcp_compile(const std::string &endpoint, uint8_t input_format, const std::string &problem)
Compile problem on a KCMCP server and return its d-DNNF NNF text.
ToolRegistry & tool_registry()
Shorthand for ToolRegistry::instance().
std::string expandCommandTemplate(const std::string &tpl, const std::string &binary, const std::string &in, const std::string &out, const std::vector< std::pair< std::string, std::string > > &extra={})
Expand a command template into a runnable shell command line.
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.
char * provsql_fallback_compiler
Compiler used by BooleanCircuit::makeDD as the final fallback after interpretAsDD and tree-decomposit...
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.