ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
nonzero.cpp
Go to the documentation of this file.
1/**
2 * @file nonzero.cpp
3 * @brief SQL function @c provsql.true_nonzero() – structural universal-zero
4 * test backing the default mode of @c provsql.nonzero().
5 *
6 * Decides whether a circuit is *provably* the semiring zero – zero in every
7 * m-semiring under every leaf valuation – by the sound structural rules:
8 * @c gate_zero; a ⊗ with a zero factor; a ⊕ (or δ, or a transparent
9 * single-child wrapper) all of whose children are zero; a ⊖ whose left
10 * operand is zero; a semimod whose provenance side is zero. Comparison
11 * gates are resolved through the Boolean-abstraction pipeline (the same
12 * @c provsql_having world enumeration every evaluator applies): a @c cmp
13 * whose satisfying-world set is empty is the empty disjunction, i.e. zero
14 * in every semiring, and surfaces in the Boolean circuit as a childless OR
15 * (the @c BoolExpr semiring's @c zero()). Everything else – input gates,
16 * undecided probabilistic comparisons, mixtures, conditioning – blocks the
17 * proof: the verdict is then @c true (not provably zero), never a dropped
18 * row.
19 *
20 * Exact zero-ness in the free m-semiring is the word problem for the
21 * m-semiring axioms, whose decidability is open; drop-only-on-proof is the
22 * specified contract, so a sound under-approximation is exactly right.
23 */
24extern "C"
25{
26#include "postgres.h"
27#include "fmgr.h"
28#include "utils/uuid.h"
29#include "provsql_utils.h"
30}
31
32#include <exception>
33#include <unordered_map>
34
35#include "BooleanCircuit.h"
36#include "CircuitFromMMap.h"
37#include "GenericCircuit.h"
38#include "provsql_utils_cpp.h"
39
40extern "C"
41{
42PG_FUNCTION_INFO_V1(true_nonzero);
43}
44
45/**
46 * @brief Structural proof of universal zero-ness of @p g, memoized over the
47 * DAG.
48 *
49 * @param gc The loaded circuit (post the load-time simplification
50 * passes, so RangeCheck-decided RV comparisons are already
51 * constants).
52 * @param g Gate to test.
53 * @param bc Boolean abstraction of @p gc, when it could be built;
54 * used only to read comparison gates' world-set emptiness.
55 * @param gc_to_bc Gate map into @p bc.
56 * @param memo Verdict cache (the circuit is a DAG).
57 */
58static bool provably_zero(
59 const GenericCircuit &gc, gate_t g,
60 const BooleanCircuit *bc,
61 const std::unordered_map<gate_t, gate_t> *gc_to_bc,
62 std::unordered_map<gate_t, bool> &memo)
63{
64 auto it = memo.find(g);
65 if(it != memo.end())
66 return it->second;
67 memo[g] = false; // provisional: blocks pathological cycles safely
68
69 const auto &w = gc.getWires(g);
70 bool z = false;
71
72 switch(gc.getGateType(g)) {
73 case gate_zero:
74 z = true;
75 break;
76
77 case gate_plus:
78 // The empty ⊕ is 0; any non-zero child blocks the proof.
79 z = true;
80 for(auto child : w)
81 if(!provably_zero(gc, child, bc, gc_to_bc, memo)) {
82 z = false;
83 break;
84 }
85 break;
86
87 case gate_times:
88 for(auto child : w)
89 if(provably_zero(gc, child, bc, gc_to_bc, memo)) {
90 z = true;
91 break;
92 }
93 break;
94
95 case gate_monus:
96 // 0 ⊖ x = 0 in every m-semiring; nothing else is provable here
97 // (x ⊖ x is folded to gate_zero by the load-time simplifier).
98 z = !w.empty() && provably_zero(gc, w[0], bc, gc_to_bc, memo);
99 break;
100
101 case gate_delta: // δ(0) = 0
102 case gate_project: // transparent single-child wrappers
103 case gate_annotation:
104 case gate_assumed:
105 z = !w.empty() && provably_zero(gc, w[0], bc, gc_to_bc, memo);
106 break;
107
108 case gate_semimod:
109 // value ⊛ K with K = 0 is the semimodule zero; wires are [K, value].
110 z = !w.empty() && provably_zero(gc, w[0], bc, gc_to_bc, memo);
111 break;
112
113 case gate_cmp:
114 // Resolved through the Boolean abstraction: the cmp's specified
115 // semantics is the disjunction over its satisfying worlds, and the
116 // empty disjunction (the BoolExpr semiring's zero(), a childless OR)
117 // is zero in every semiring. Each term of a non-empty disjunction is
118 // satisfiable by construction, so a non-childless expansion is never
119 // universally zero on its own.
120 if(bc && gc_to_bc) {
121 auto jt = gc_to_bc->find(g);
122 if(jt != gc_to_bc->end() &&
123 bc->getGateType(jt->second) == BooleanGate::OR &&
124 bc->getWires(jt->second).empty())
125 z = true;
126 }
127 break;
128
129 default:
130 // Inputs, values, mulinputs, undecided RV comparisons, mixtures,
131 // conditioning, arithmetic…: not provably zero.
132 break;
133 }
134
135 memo[g] = z;
136 return z;
137}
138
139/** @brief PostgreSQL-callable wrapper: the default mode of provsql.nonzero(). */
140Datum true_nonzero(PG_FUNCTION_ARGS)
141{
142 if(PG_ARGISNULL(0))
143 PG_RETURN_BOOL(true); // NULL token ≡ the neutral 1 ≠ 0
144
145 try {
146 pg_uuid_t token = *DatumGetUUIDP(PG_GETARG_DATUM(0));
148 gate_t root = gc.getGate(uuid2string(token));
149
150 // The Boolean abstraction is built only to decide comparison gates'
151 // world-set emptiness; on circuits it cannot express (an undecided
152 // probabilistic comparison, a mixture…) the comparison gates simply
153 // stay opaque and the proof works around them.
154 bool has_cmp = false;
155 for(gate_t i{0}; i < gc.getNbGates() && !has_cmp; ++i)
156 has_cmp = (gc.getGateType(i) == gate_cmp);
157
158 bool have_bc = false;
159 gate_t broot;
160 std::unordered_map<gate_t, gate_t> gc_to_bc;
162 if(has_cmp) {
163 try {
164 bc = getBooleanCircuit(gc, token, broot, gc_to_bc);
165 have_bc = true;
166 } catch(const std::exception &) {
167 // fall through with opaque cmp gates
168 }
169 }
170
171 std::unordered_map<gate_t, bool> memo;
172 bool zero = provably_zero(gc, root,
173 have_bc ? &bc : nullptr,
174 have_bc ? &gc_to_bc : nullptr,
175 memo);
176 PG_RETURN_BOOL(!zero);
177 } catch(const std::exception &e) {
178 provsql_error("nonzero: %s", e.what());
179 } catch(...) {
180 provsql_error("nonzero: Unknown exception");
181 }
182
183 PG_RETURN_NULL(); // unreachable: provsql_error does not return
184}
Boolean provenance circuit with support for knowledge compilation.
@ OR
Logical disjunction of child gates.
BooleanCircuit getBooleanCircuit(GenericCircuit &gc, pg_uuid_t token, gate_t &gate, std::unordered_map< gate_t, gate_t > &gc_to_bc)
Build a BooleanCircuit from an already-loaded GenericCircuit.
GenericCircuit getGenericCircuit(pg_uuid_t token)
Build a GenericCircuit from the mmap store rooted at token.
Build in-memory circuits from the mmap-backed persistent store.
gate_t
Strongly-typed gate identifier.
Definition Circuit.h:49
Semiring-agnostic in-memory provenance circuit.
Boolean circuit for provenance formula evaluation.
std::vector< gate_t > & getWires(gate_t g)
Return a mutable reference to the child-wire list of gate g.
Definition Circuit.h:140
gateType getGateType(gate_t g) const
Return the type of gate g.
Definition Circuit.h:130
gate_t getGate(const uuid &u)
Return (or create) the gate associated with UUID u.
Definition Circuit.hpp:33
std::vector< gate_t >::size_type getNbGates() const
Return the total number of gates in the circuit.
Definition Circuit.h:103
In-memory provenance circuit with semiring-generic evaluation.
static bool provably_zero(const GenericCircuit &gc, gate_t g, const BooleanCircuit *bc, const std::unordered_map< gate_t, gate_t > *gc_to_bc, std::unordered_map< gate_t, bool > &memo)
Structural proof of universal zero-ness of g, memoized over the DAG.
Definition nonzero.cpp:58
Datum true_nonzero(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper: the default mode of provsql.nonzero().
Definition nonzero.cpp:140
#define provsql_error(fmt,...)
Report a fatal ProvSQL error and abort the current transaction.
Core types, constants, and utilities shared across ProvSQL.
@ gate_annotation
Transparent single-child wrapper carrying a query-level annotation in extra (inversion-free certifica...
@ gate_assumed
Structural marker over a single child whose sub-circuit was computed under a Boolean-provenance assum...
string uuid2string(pg_uuid_t uuid)
Format a pg_uuid_t as a std::string.
C++ utility functions for UUID manipulation.
UUID structure.