ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
GenericCircuit.hpp
Go to the documentation of this file.
1/**
2 * @file GenericCircuit.hpp
3 * @brief Template implementation of @c GenericCircuit::evaluate().
4 *
5 * Provides the out-of-line definition of the @c evaluate() template method
6 * declared in @c GenericCircuit.h. This file must be included (directly
7 * or transitively) by any translation unit that instantiates
8 * @c GenericCircuit::evaluate<S>() for a specific semiring type @c S.
9 *
10 * The @c evaluate() method performs a post-order traversal of the sub-circuit
11 * rooted at gate @p g, looking up input-gate values from @p provenance_mapping
12 * and combining them using the semiring operations:
13 *
14 * | Gate type | Semiring operation |
15 * |-------------|-------------------------------|
16 * | gate_input | lookup in @p provenance_mapping|
17 * | gate_plus | @c semiring.plus(children) |
18 * | gate_times | @c semiring.times(children) |
19 * | gate_monus | @c semiring.monus(left, right) |
20 * | gate_delta | @c semiring.delta(child) |
21 * | gate_cmp | @c semiring.cmp(left, op, right)|
22 * | gate_semimod| @c semiring.semimod(x, s) |
23 * | gate_agg | @c semiring.agg(op, children) |
24 * | gate_value | @c semiring.value(string) |
25 * | gate_one | @c semiring.one() |
26 * | gate_zero | @c semiring.zero() |
27 */
28#include "GenericCircuit.h"
29
30extern "C" {
31#include "utils/lsyscache.h"
32}
33
34template<typename S, std::enable_if_t<std::is_base_of_v<semiring::Semiring<typename S::value_type>, S>, int> >
35typename S::value_type GenericCircuit::evaluate(gate_t g, std::unordered_map<gate_t, typename S::value_type> &provenance_mapping, S semiring) const
36{
37 const auto it = provenance_mapping.find(g);
38 if(it != provenance_mapping.end())
39 return it->second;
40
41 /* In-memory Boolean-assumption marker (set by
42 * @c foldBooleanIdentities on gates whose wires were rewritten
43 * under a Boolean-only rule). Mirrors the @c gate_assumed_boolean
44 * structural-marker check below but applies to gates that keep
45 * their original type (the rule mutated their wires in place ;
46 * the persistent mmap was not touched). Same compatibility
47 * predicate, same failure mode. */
48 if(isBooleanAssumed(g) && !semiring.compatibleWithBooleanRewrite())
49 throw CircuitException(
50 "The requested semiring does not admit a homomorphism "
51 "from Boolean functions; this gate's wires were rewritten "
52 "under a Boolean-only rule (typically idempotence or "
53 "plus-with-one absorber by foldBooleanIdentities, gated "
54 "on provsql.boolean_provenance = on) and the evaluation "
55 "is unsound under this semiring. Re-run with "
56 "provsql.boolean_provenance = off, or pick a "
57 "Boolean-compatible semiring (boolean, boolexpr, "
58 "formula, ...).");
59
60 auto t = getGateType(g);
61
62 switch(t) {
63 case gate_one:
64 case gate_input:
65 case gate_update:
66 case gate_mulinput:
67 // If not in provenance mapping, return no provenance (one of the semiring)
68 return semiring.one();
69
70 case gate_zero:
71 return semiring.zero();
72
73 case gate_plus:
74 case gate_times:
75 case gate_monus: {
76 auto children = getWires(g);
77 std::vector<typename S::value_type> childrenResult;
78 std::transform(children.begin(), children.end(), std::back_inserter(childrenResult), [&](auto u) {
79 return evaluate<S>(u, provenance_mapping, semiring);
80 });
81 if(t==gate_plus) {
82 childrenResult.erase(std::remove(std::begin(childrenResult), std::end(childrenResult), semiring.zero()),
83 childrenResult.end());
84 return semiring.plus(childrenResult);
85 } else if(t==gate_times) {
86 for(const auto &c: childrenResult) {
87 if(c==semiring.zero())
88 return semiring.zero();
89 }
90 childrenResult.erase(std::remove(std::begin(childrenResult), std::end(childrenResult), semiring.one()),
91 childrenResult.end());
92 return semiring.times(childrenResult);
93 } else {
94 if(childrenResult[0]==semiring.zero() || childrenResult[0]==childrenResult[1])
95 return semiring.zero();
96 else
97 return semiring.monus(childrenResult[0], childrenResult[1]);
98 }
99 }
100
101 case gate_delta:
102 return semiring.delta(evaluate<S>(getWires(g)[0], provenance_mapping, semiring));
103
104 case gate_project:
105 case gate_eq:
106 // Where-provenance gates, ignored
107 return evaluate<S>(getWires(g)[0], provenance_mapping, semiring);
108
110 /* Structural marker: the wrapped sub-circuit was computed under a
111 * Boolean-provenance assumption (e.g. the safe-query rewrite
112 * collapses derivation multiplicities into a single Boolean
113 * witness). Identity for semirings whose evaluation factors
114 * through a homomorphism from Boolean functions; fatal for the
115 * rest, since otherwise we would silently return a value the
116 * semiring's semantics does not justify. */
117 if(!semiring.compatibleWithBooleanRewrite())
118 throw CircuitException(
119 "The requested semiring does not admit a homomorphism "
120 "from Boolean functions; the wrapped sub-circuit was "
121 "computed under a Boolean-provenance assumption "
122 "(typically by the safe-query rewrite, "
123 "provsql.boolean_provenance = on) and the evaluation is "
124 "unsound under this semiring. Re-run the query with "
125 "provsql.boolean_provenance = off, or pick a "
126 "Boolean-compatible semiring (boolean, boolexpr, "
127 "formula, ...).");
128 return evaluate<S>(getWires(g)[0], provenance_mapping, semiring);
129
130 case gate_cmp:
131 {
132 bool ok;
133 ComparisonOperator op = cmpOpFromOid(getInfos(g).first, ok);
134 if(!ok)
135 throw CircuitException(
136 "Comparison operator OID " +
137 std::to_string(getInfos(g).first) +
138 " not supported");
139
140 return semiring.cmp(evaluate<S>(getWires(g)[0], provenance_mapping, semiring), op, evaluate<S>(getWires(g)[1], provenance_mapping, semiring));
141 }
142
143 case gate_semimod:
144 return semiring.semimod(evaluate<S>(getWires(g)[0], provenance_mapping, semiring), evaluate<S>(getWires(g)[1], provenance_mapping, semiring));
145
146 case gate_agg:
147 {
148 auto infos = getInfos(g);
149
151
152 std::vector<typename S::value_type> vec;
153 for(auto it = getWires(g).begin(); it!=getWires(g).end(); ++it)
154 vec.push_back(evaluate<S>(*it, provenance_mapping, semiring));
155 return semiring.agg(op, vec);
156 break;
157 }
158
159 case gate_value:
160 return semiring.value(getExtra(g));
161
162 default:
163 throw CircuitException("Invalid gate type for semiring evaluation");
164 }
165}
ComparisonOperator cmpOpFromOid(Oid op_oid, bool &ok)
Map a PostgreSQL comparison-operator OID to a ComparisonOperator.
AggregationOperator getAggregationOperator(Oid oid)
Map a PostgreSQL aggregate function OID to an AggregationOperator.
AggregationOperator
SQL aggregation functions tracked by ProvSQL.
Definition Aggregation.h:50
ComparisonOperator
SQL comparison operators used in gate_cmp circuit gates.
Definition Aggregation.h:38
gate_t
Strongly-typed gate identifier.
Definition Circuit.h:49
Semiring-agnostic in-memory provenance circuit.
Exception type thrown by circuit operations on invalid input.
Definition Circuit.h:206
std::vector< gate_t > & getWires(gate_t g)
Definition Circuit.h:140
gate_type getGateType(gate_t g) const
Definition Circuit.h:130
std::map< gate_t, std::pair< unsigned, unsigned > > infos
Per-gate (info1, info2) annotations.
S::value_type evaluate(gate_t g, std::unordered_map< gate_t, typename S::value_type > &provenance_mapping, S semiring) const
Evaluate the sub-circuit rooted at gate g over semiring semiring.
std::string getExtra(gate_t g) const
Return the string extra for gate g.
bool isBooleanAssumed(gate_t g) const
Report whether g carries the Boolean-assumption flag.
std::pair< unsigned, unsigned > getInfos(gate_t g) const
Return the integer annotation pair for gate g.
@ gate_assumed_boolean
Structural marker over a single child whose sub-circuit was computed under a Boolean-provenance assum...