ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
Semiring.h
Go to the documentation of this file.
1/**
2 * @file semiring/Semiring.h
3 * @brief Abstract semiring interface for provenance evaluation.
4 *
5 * ProvSQL evaluates provenance circuits over arbitrary (m-)semirings.
6 * This header defines the abstract base class @c semiring::Semiring<V>
7 * that every concrete semiring must implement.
8 *
9 * A **semiring** @f$(S, \oplus, \otimes, \mathbb{0}, \mathbb{1})@f$
10 * consists of:
11 * - A carrier set @f$S@f$ (the @c value_type).
12 * - An additive operation @f$\oplus@f$ with identity @f$\mathbb{0}@f$.
13 * - A multiplicative operation @f$\otimes@f$ with identity @f$\mathbb{1}@f$.
14 *
15 * An **m-semiring** additionally provides:
16 * - A monus operation @f$\ominus@f$ used for set-difference queries.
17 * - A @f$\delta@f$ operator.
18 *
19 * Optional operations -- comparison, semimodule scalar multiplication,
20 * aggregation, value literals, and the measure-carrier gates
21 * (random-variable leaves, scalar arithmetic, mixtures, guarded
22 * selection, observations, conditioning) -- are provided by subclasses
23 * that support them; the base class throws @c SemiringException for all
24 * of these. Most carry no algebraic meaning in a general semiring and
25 * are overridden only by @c Formula, whose carrier is a symbolic
26 * rendering of the circuit rather than a semantic value.
27 *
28 * Concrete implementations live in the same @c semiring/ directory:
29 * @c Boolean.h, @c Counting.h, @c Formula.h, @c Why.h, @c BoolExpr.h.
30 *
31 * @see https://provsql.org/lean-docs/Provenance/SemiringWithMonus.html
32 * Lean 4 formalization of the @c SemiringWithMonus typeclass and
33 * proofs of the key monus identities (@c monus_smallest,
34 * @c monus_self, @c zero_monus, @c monus_add, @c add_monus,
35 * @c idempotent_iff_add_monus).
36 */
37#ifndef SEMIRING_H
38#define SEMIRING_H
39
40#include <vector>
41#include <string>
42
43#include "../Aggregation.h"
44
45namespace semiring {
46
47/**
48 * @brief Exception thrown when a semiring operation is not supported.
49 *
50 * Raised by the default implementations of optional operations
51 * (@c cmp, @c semimod, @c agg, @c value) when a subclass does not
52 * override them.
53 */
54class SemiringException : public std::exception
55{
56std::string message; ///< Human-readable description of the error
57
58public:
59/**
60 * @brief Construct with a descriptive error message.
61 * @param m Error message.
62 */
63SemiringException(const std::string &m) : message(m) {
64}
65/**
66 * @brief Return the error message as a C-string.
67 * @return Null-terminated error description.
68 */
69virtual char const * what() const noexcept {
70 return message.c_str();
71}
72};
73
74/**
75 * @brief A gate kind this semiring does not interpret (a comparison, a
76 * semimodule multiplication, an aggregation, a value).
77 *
78 * Thrown by the defaults of the methods below. In the measure
79 * interpretation, which interprets them all, it means that the resolution
80 * passes left a comparison of aggregate results unresolved: a shape ProvSQL
81 * does not support, which @c probability_evaluate reports as such.
82 */
84{
85public:
86/** @brief Construct with a descriptive error message. */
87SemiringGateException(const std::string &m) : SemiringException(m) {
88}
89};
90
91/**
92 * @brief Abstract base class for (m-)semirings.
93 *
94 * @tparam V The carrier type (e.g. @c bool, @c unsigned, @c std::string).
95 *
96 * ### Required operations
97 * All pure-virtual methods must be implemented by concrete subclasses.
98 *
99 * ### Optional operations
100 * @c cmp(), @c semimod(), @c agg(), and @c value() have default
101 * implementations that throw @c SemiringException. Override them in
102 * subclasses that support these circuit gate types.
103 *
104 * ### Absorptive semirings
105 * A semiring is *absorptive* (i.e.,
106 * @f$\mathbb{1} \oplus a = \mathbb{1}@f$ for all @f$a@f$) iff
107 * @c absorptive() returns @c true. Absorptivity implies idempotency
108 * (@f$a \oplus a = a@f$), which lets the circuit evaluator and the
109 * HAVING-semantics machinery deduplicate operands and short-circuit
110 * over the multiplicative identity. An absorptive semiring gets its
111 * existential @c MIN / @c MAX HAVING comparisons (@c MIN below,
112 * @c MAX above a constant) in a single scan instead of a world
113 * enumeration; when its @f$\otimes@f$ also distributes over
114 * @f$\ominus@f$ (@c mul_sub_left_distributive()), so do the other
115 * @c MIN / @c MAX comparisons.
116 */
117template<typename V>
119{
120public:
121/** @brief The carrier type of this semiring. */
122typedef V value_type;
123
124/**
125 * @brief Return the additive identity @f$\mathbb{0}@f$.
126 * @return The zero element of the semiring.
127 */
128virtual value_type zero() const = 0;
129
130/**
131 * @brief Return the multiplicative identity @f$\mathbb{1}@f$.
132 * @return The one element of the semiring.
133 */
134virtual value_type one() const = 0;
135
136/**
137 * @brief Apply the additive operation to a list of values.
138 *
139 * @param v Ordered list of operands (empty list should return @c zero()).
140 * @return @f$v_0 \oplus v_1 \oplus \cdots@f$.
141 */
142virtual value_type plus(const std::vector<value_type> &v) const = 0;
143
144/**
145 * @brief Apply the multiplicative operation to a list of values.
146 *
147 * @param v Ordered list of operands (empty list should return @c one()).
148 * @return @f$v_0 \otimes v_1 \otimes \cdots@f$.
149 */
150virtual value_type times(const std::vector<value_type> &v) const = 0;
151
152/**
153 * @brief Apply the monus (m-semiring difference) operation.
154 *
155 * @param x Minuend.
156 * @param y Subtrahend.
157 * @return @f$x \ominus y@f$.
158 */
159virtual value_type monus(value_type x, value_type y) const = 0;
160
161/**
162 * @brief Apply the @f$\delta@f$ operator.
163 *
164 * @param x Input value.
165 * @return @f$\delta(x)@f$.
166 */
167virtual value_type delta(value_type x) const = 0;
168
169/**
170 * @brief Evaluate a comparison gate.
171 *
172 * @param s1 Left operand.
173 * @param op Comparison operator.
174 * @param s2 Right operand.
175 * @return Result of the comparison in this semiring.
176 * @throws SemiringException if not overridden.
177 */
179 throw SemiringGateException("This semiring does not support cmp gates.");
180}
181
182/**
183 * @brief Apply a semimodule scalar multiplication.
184 *
185 * @param x Provenance value.
186 * @param s Scalar value.
187 * @return @f$x * s@f$ in the semimodule.
188 * @throws SemiringException if not overridden.
189 */
191 throw SemiringGateException("This semiring does not support semimod gates.");
192}
193
194/**
195 * @brief Evaluate an aggregation gate.
196 *
197 * @param op The aggregation function (COUNT, SUM, MIN…).
198 * @param s List of child semiring values to aggregate.
199 * @return The aggregated value.
200 * @throws SemiringException if not overridden.
201 */
202virtual value_type agg(AggregationOperator op, const std::vector<value_type> &s) {
203 throw SemiringGateException("This semiring does not support agg gates.");
204}
205
206/**
207 * @brief Interpret a literal string as a semiring value.
208 *
209 * Used for @c gate_value gates whose payload is a string.
210 *
211 * @param s Literal string.
212 * @return The corresponding semiring value.
213 * @throws SemiringException if not overridden.
214 */
215virtual value_type value(const std::string &s) const {
216 throw SemiringGateException("This semiring does not support value gates.");
217}
218
219/**
220 * @brief Value of a variable leaf (@c gate_input / @c gate_mulinput)
221 * that the provenance mapping does not name.
222 *
223 * The absent-mapping convention: such a leaf contributes no provenance,
224 * i.e. the multiplicative identity, which is what every proper semiring
225 * wants and what this default returns. A semiring that *renders* the
226 * circuit rather than evaluating it overrides this to identify the leaf
227 * instead -- @f$\mathbb{1}@f$ would not merely be anonymous there, it
228 * would be absorbed by the enclosing @c times and take the structure
229 * with it. @c BooleanCircuit::toString does the same for the
230 * Boolean-expression rendering (its @c x@<id@> fallback).
231 *
232 * @param uuid The leaf gate's UUID, in canonical text form.
233 */
234virtual value_type unmapped_input(const std::string &uuid) const {
235 return one();
236}
237
238/**
239 * @brief Evaluate a continuous random-variable leaf (@c gate_rv).
240 *
241 * @param spec The gate's distribution encoding (@c "normal:2.5,0.5"),
242 * where a parameter written @c "$i" is wired rather than
243 * literal (a latent / compound leaf).
244 * @param params The values of the gate's wires, indexed as the @c "$i"
245 * references; empty for an all-literal leaf.
246 * @return The leaf's value in this semiring.
247 * @throws SemiringException if not overridden.
248 */
249virtual value_type rv(const std::string &spec,
250 const std::vector<value_type> &params) const {
251 throw SemiringException(
252 "This semiring does not support rv gates: a continuous "
253 "distribution is not a semiring value. Query a "
254 "random-variable token through the measure surface "
255 "(expected / variance / quantile / support / sample), or use "
256 "the formula pseudo-semiring for a symbolic rendering.");
257}
258
259/**
260 * @brief Evaluate an arithmetic gate over scalar children (@c gate_arith).
261 *
262 * @param op The arithmetic operation.
263 * @param children The values of the gate's wires (for @c PERCENTILE,
264 * interleaved @c [indicator, value] pairs).
265 * @param extra The gate's payload (the fraction for @c PERCENTILE,
266 * empty otherwise).
267 * @return The result of the operation in this semiring.
268 * @throws SemiringException if not overridden.
269 */
271 const std::vector<value_type> &children,
272 const std::string &extra) const {
273 throw SemiringException(
274 "This semiring does not support arith gates: arithmetic over "
275 "scalar (random-variable) children is not a semiring "
276 "operation. Query such a token through the measure surface "
277 "(expected / variance / quantile / support / sample), or use "
278 "the formula pseudo-semiring for a symbolic rendering.");
279}
280
281/**
282 * @brief Evaluate a Bernoulli mixture (@c gate_mixture, three wires).
283 *
284 * @param p The Bernoulli event's value.
285 * @param x The value taken when the event holds.
286 * @param y The value taken otherwise.
287 * @throws SemiringException if not overridden.
288 */
290 throw SemiringException(
291 "This semiring does not support mixture gates: a probabilistic "
292 "mixture is not a semiring operation. Query such a token "
293 "through the measure surface (expected / variance / quantile / "
294 "support / sample), or use the formula pseudo-semiring for a "
295 "symbolic rendering.");
296}
297
298/**
299 * @brief Evaluate a categorical mixture (@c gate_mixture over
300 * @c gate_mulinput outcomes).
301 *
302 * @param key The value of the mixture's key (its @c gate_input wire).
303 * @param probs Probability of each outcome.
304 * @param outcomes Textual payload of each outcome, parallel to @p probs.
305 * @throws SemiringException if not overridden.
306 */
308 const std::vector<double> &probs,
309 const std::vector<std::string> &outcomes) const {
310 throw SemiringException(
311 "This semiring does not support categorical mixture gates: a "
312 "distribution over outcomes is not a semiring value. Query "
313 "such a token through the measure surface (expected / "
314 "variance / quantile / support / sample), or use the formula "
315 "pseudo-semiring for a symbolic rendering.");
316}
317
318/**
319 * @brief Evaluate a guarded selection (@c gate_case).
320 *
321 * @param children The gate's wires, @c [guard_1, value_1, …,
322 * guard_k, value_k, default] (odd length), with
323 * first-match semantics.
324 * @throws SemiringException if not overridden.
325 */
326virtual value_type guarded_case(const std::vector<value_type> &children) const {
327 throw SemiringException(
328 "The requested semiring does not support a CASE / guarded "
329 "selection over random variables (gate_case): it is evaluable "
330 "only through the random-variable / measure surface "
331 "(expected / variance / support / probability / sample).");
332}
333
334/**
335 * @brief Evaluate a latent-variable observation (@c gate_observe).
336 *
337 * @param child The observed leaf's value.
338 * @param datum The observed value, as text.
339 * @throws SemiringException if not overridden.
340 */
341virtual value_type observe(value_type child, const std::string &datum) const {
342 throw SemiringException(
343 "This semiring does not support observe gates: the density "
344 "factor of a likelihood-weighting observation is not a "
345 "semiring value. Such a token is evaluated by the "
346 "importance-sampling surface, or rendered symbolically by the "
347 "formula pseudo-semiring.");
348}
349
350/**
351 * @brief Evaluate a conditioning marker (@c gate_conditioned).
352 *
353 * @param children The gate's wires: @c [target, evidence] for the
354 * value-level (random-variable) form, @c [target,
355 * evidence, joint] for the Boolean-event one, where
356 * @c joint is the materialised @c times(target, evidence).
357 * @throws SemiringException if not overridden.
358 */
359virtual value_type conditioned(const std::vector<value_type> &children) const {
360 throw SemiringException(
361 "The requested semiring does not support conditioning: "
362 "P(·|C) = P(·∧C)/P(C) needs a normalising division "
363 "no general semiring provides. A conditioned token is "
364 "evaluable only in the measure interpretation "
365 "(probability_evaluate, or the random-variable / agg_token "
366 "distribution evaluators).");
367}
368
369virtual ~Semiring() = default;
370
371/**
372 * @brief Return @c true if this semiring is absorptive
373 * (@f$\mathbb{1} \oplus a = \mathbb{1}@f$ for all @f$a@f$).
374 *
375 * When @c true, the circuit evaluator and HAVING-semantics machinery
376 * may exploit the resulting idempotency (@f$a \oplus a = a@f$, implied
377 * by absorptivity) to deduplicate children of @c plus gates and to
378 * short-circuit over the multiplicative identity.
379 *
380 * @return @c false by default; override to return @c true.
381 */
382virtual bool absorptive() const {
383 return false;
384}
385
386/**
387 * @brief Return @c true if this semiring is idempotent
388 * (@f$a \oplus a = a@f$ for all @f$a@f$).
389 *
390 * Absorptivity implies idempotence, so the default forwards to
391 * @c absorptive(); override to return @c true in a semiring that is
392 * idempotent without being absorptive (why- and which-provenance,
393 * where @f$\oplus@f$ is set union).
394 *
395 * The HAVING machinery (@c provsql_having in @c having_semantics.hpp)
396 * uses it for the *monotone* comparisons, those whose valid worlds are
397 * closed under supersets (@c MIN below, @c MAX above a constant,
398 * @c COUNT above a constant, @c SUM above a constant over non-negative
399 * values, @c bool_or @c = @c true, @c bool_and @c = @c false): over
400 * such a family the monus factors cancel from the possible-world sum,
401 * which is then the plain @f$\oplus@f$-sum of the products of the
402 * present annotations (Lean @c Having.witness_identity in
403 * provenance-lean/Provenance/Having.lean, which assumes idempotence
404 * only). The enumeration stays exhaustive; absorptivity is what
405 * further collapses it to the minimal worlds.
406 *
407 * @return @c absorptive() by default; override to return @c true only
408 * with a proof (the Lean @c *.idempotent lemmas).
409 */
410virtual bool idempotent() const {
411 return absorptive();
412}
413
414/**
415 * @brief Return @c true if @f$\otimes@f$ distributes over the monus
416 * on the left: @f$a \otimes (b \ominus c) = (a \otimes b) \ominus
417 * (a \otimes c)@f$ for all @f$a, b, c@f$.
418 *
419 * Together with absorptivity, this is the hypothesis under which the
420 * HAVING machinery (@c provsql_having in @c having_semantics.hpp) may
421 * replace the possible-world enumeration of a non-existential @c MIN /
422 * @c MAX comparison (@c MIN above, @c MAX below a constant, or either
423 * equal to one) by its single-scan closed form (Lean
424 * @c Having.minScan_correct / @c Having.maxScan_correct in
425 * provenance-lean/Provenance/HavingMinMax.lean); the existential
426 * comparisons need absorptivity alone (Lean @c Having.sum_ann_meet in
427 * provenance-lean/Provenance/Having.lean). Holds in the Boolean,
428 * counting, tropical, Viterbi, Łukasiewicz and interval-union
429 * semirings; fails in the security (min-max) semiring, which is
430 * absorptive but not distributive, and in Why / Which / How.
431 *
432 * @return @c false by default; override to return @c true only with a
433 * proof (the Lean @c *.mul_sub_left_distributive lemmas).
434 */
435virtual bool mul_sub_left_distributive() const {
436 return false;
437}
438
439/**
440 * @brief Return @c true if a semiring homomorphism @c BoolFunc(X)
441 * →+* @c S exists, so the safe-query (Boolean-rewrite)
442 * optimisation produces circuits that are semantically faithful
443 * when evaluated under this semiring.
444 *
445 * The compiled-semiring dispatcher consults this predicate before
446 * evaluating a circuit whose root gate carries
447 * @c PROVSQL_ROOT_TAG_BOOLEAN_REWRITE. Returning @c false on a tagged
448 * circuit raises @c CircuitException.
449 *
450 * Defaults to @c false: a new semiring whose author has not yet
451 * verified the homomorphism is fail-closed by construction.
452 * Subclasses with a verified homomorphism (currently @c Boolean,
453 * @c BoolExpr, @c Formula, and @c IntervalUnion) override to return
454 * @c true. The justification (Lean-proof reference) belongs in a
455 * comment next to each override; see the @c src/semiring/ headers.
456 */
457virtual bool compatibleWithBooleanRewrite() const {
458 return false;
459}
460
461/**
462 * @brief Whether this semiring builds *certified* exclusive
463 * enumerations (see the three hooks below).
464 *
465 * The HAVING possible-worlds machinery (@c provsql_having) enumerates,
466 * for a comparison over a group aggregate, the complete valuations of
467 * the group's contributors that satisfy the predicate. Those
468 * disjuncts partition the worlds -- the OR is *deterministic* and each
469 * world term an AND of literals over distinct contributors,
470 * *decomposable* -- i.e., the enumeration is a d-DNNF by construction.
471 * A circuit-building semiring can persist that knowledge as the d-DNNF
472 * certificate (the same mark the bounded-treewidth reachability route
473 * emits), letting the linear certificate-aware evaluators handle the
474 * result; scalar semirings have nothing to mark.
475 *
476 * @return @c false by default (the hooks below then go unused and the
477 * default constructions are used).
478 */
479virtual bool certifying() const {
480 return false;
481}
482
483/**
484 * @brief Whether @p v is an independent literal for certification
485 * purposes: a base Bernoulli variable (or a constant), so that
486 * distinct literals have disjoint supports and an AND over them
487 * is decomposable.
488 *
489 * Only consulted when @c certifying() is @c true.
490 *
491 * @return @c false by default.
492 */
493virtual bool independent_literal(const value_type &) const {
494 return false;
495}
496
497/**
498 * @brief Build one complete world term: the conjunction of the
499 * @p present literals and the *negations* of the @p missing
500 * literals, certified decomposable.
501 *
502 * Only called when @c certifying() is @c true; the default throws.
503 */
505 const std::vector<value_type> &,
506 const std::vector<value_type> &) const {
507 throw SemiringException("This semiring does not certify enumerations.");
508}
509
510/**
511 * @brief Build the disjunction of pairwise-exclusive @p disjuncts,
512 * certified deterministic.
513 *
514 * Only called when @c certifying() is @c true; the default throws.
515 */
517 const std::vector<value_type> &) const {
518 throw SemiringException("This semiring does not certify enumerations.");
519}
520};
521
522
523}
524
525#endif /* SEMIRING_H */
Typed aggregation value, operator, and aggregator abstractions.
AggregationOperator
SQL aggregation functions tracked by ProvSQL.
Definition Aggregation.h:51
ComparisonOperator
SQL comparison operators used in gate_cmp circuit gates.
Definition Aggregation.h:39
ArithmeticOperator
Arithmetic operations carried by gate_arith circuit gates.
Definition Aggregation.h:74
Exception thrown when a semiring operation is not supported.
Definition Semiring.h:55
std::string message
Human-readable description of the error.
Definition Semiring.h:56
virtual char const * what() const noexcept
Return the error message as a C-string.
Definition Semiring.h:69
SemiringException(const std::string &m)
Construct with a descriptive error message.
Definition Semiring.h:63
A gate kind this semiring does not interpret (a comparison, a semimodule multiplication,...
Definition Semiring.h:84
SemiringGateException(const std::string &m)
Construct with a descriptive error message.
Definition Semiring.h:87
Abstract base class for (m-)semirings.
Definition Semiring.h:119
V value_type
The carrier type of this semiring.
Definition Semiring.h:122
virtual value_type semimod(value_type x, value_type s) const
Apply a semimodule scalar multiplication.
Definition Semiring.h:190
virtual value_type plus(const std::vector< value_type > &v) const =0
Apply the additive operation to a list of values.
virtual value_type mixture(value_type p, value_type x, value_type y) const
Evaluate a Bernoulli mixture (gate_mixture, three wires).
Definition Semiring.h:289
virtual bool idempotent() const
Return true if this semiring is idempotent ( for all ).
Definition Semiring.h:410
virtual bool absorptive() const
Return true if this semiring is absorptive ( for all ).
Definition Semiring.h:382
virtual bool certifying() const
Whether this semiring builds certified exclusive enumerations (see the three hooks below).
Definition Semiring.h:479
virtual ~Semiring()=default
virtual bool compatibleWithBooleanRewrite() const
Return true if a semiring homomorphism BoolFunc(X) →+* S exists, so the safe-query (Boolean-rewrite) ...
Definition Semiring.h:457
virtual value_type zero() const =0
Return the additive identity .
virtual value_type guarded_case(const std::vector< value_type > &children) const
Evaluate a guarded selection (gate_case).
Definition Semiring.h:326
virtual value_type cmp(value_type s1, ComparisonOperator op, value_type s2) const
Evaluate a comparison gate.
Definition Semiring.h:178
virtual value_type agg(AggregationOperator op, const std::vector< value_type > &s)
Evaluate an aggregation gate.
Definition Semiring.h:202
virtual value_type conditioned(const std::vector< value_type > &children) const
Evaluate a conditioning marker (gate_conditioned).
Definition Semiring.h:359
virtual value_type arith(ArithmeticOperator op, const std::vector< value_type > &children, const std::string &extra) const
Evaluate an arithmetic gate over scalar children (gate_arith).
Definition Semiring.h:270
virtual value_type certified_world_term(const std::vector< value_type > &, const std::vector< value_type > &) const
Build one complete world term: the conjunction of the present literals and the negations of the missi...
Definition Semiring.h:504
virtual value_type unmapped_input(const std::string &uuid) const
Value of a variable leaf (gate_input / gate_mulinput) that the provenance mapping does not name.
Definition Semiring.h:234
virtual bool mul_sub_left_distributive() const
Return true if distributes over the monus on the left: for all .
Definition Semiring.h:435
virtual value_type one() const =0
Return the multiplicative identity .
virtual value_type monus(value_type x, value_type y) const =0
Apply the monus (m-semiring difference) operation.
virtual value_type rv(const std::string &spec, const std::vector< value_type > &params) const
Evaluate a continuous random-variable leaf (gate_rv).
Definition Semiring.h:249
virtual value_type certified_exclusive_plus(const std::vector< value_type > &) const
Build the disjunction of pairwise-exclusive disjuncts, certified deterministic.
Definition Semiring.h:516
virtual value_type delta(value_type x) const =0
Apply the operator.
virtual value_type times(const std::vector< value_type > &v) const =0
Apply the multiplicative operation to a list of values.
virtual value_type observe(value_type child, const std::string &datum) const
Evaluate a latent-variable observation (gate_observe).
Definition Semiring.h:341
virtual value_type categorical(value_type key, const std::vector< double > &probs, const std::vector< std::string > &outcomes) const
Evaluate a categorical mixture (gate_mixture over gate_mulinput outcomes).
Definition Semiring.h:307
virtual bool independent_literal(const value_type &) const
Whether v is an independent literal for certification purposes: a base Bernoulli variable (or a const...
Definition Semiring.h:493
virtual value_type value(const std::string &s) const
Interpret a literal string as a semiring value.
Definition Semiring.h:215