ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
RandomVariable.h
Go to the documentation of this file.
1/**
2 * @file RandomVariable.h
3 * @brief Continuous random-variable helpers (distribution parsing, moments).
4 *
5 * Helpers for the @c gate_rv leaf introduced for continuous probabilistic
6 * c-tables. A @c gate_rv stores its distribution name and parameters in
7 * the gate's @c extra byte string using a small text encoding (e.g.
8 * <tt>"normal:2.5,0.5"</tt>); these helpers parse and format that encoding,
9 * and provide closed-form moments where they exist. Arithmetic over RV
10 * expressions is built on the generic @c gate_arith gate (see
11 * @c provsql_utils.h), which is shared with non-RV scalar arithmetic.
12 *
13 * Sampling itself lives in @c BooleanCircuit::monteCarlo and
14 * @c MonteCarloSampler; this header only exposes what is needed for
15 * parsing and analytical moment computation.
16 */
17#ifndef PROVSQL_RANDOM_VARIABLE_H
18#define PROVSQL_RANDOM_VARIABLE_H
19
20#include <optional>
21#include <string>
22
23namespace provsql {
24
25struct DistributionFamily; // src/distributions/Distribution.h
26
27/**
28 * @brief Parsed distribution spec (family + up to two parameters).
29 *
30 * Stored in the @c extra byte string of a @c gate_rv as
31 * <tt>"normal:μ,σ"</tt>, <tt>"uniform:a,b"</tt>, <tt>"exponential:λ"</tt>,
32 * <tt>"erlang:k,λ"</tt>, <tt>"gamma:k,λ"</tt>, ... @c family points at
33 * the interned registry descriptor of the parsed name token (one
34 * instance per family, so plain pointer comparison is family
35 * identity); there is deliberately no family enum -- adding a family
36 * registers a new descriptor without touching any shared header.
37 */
40 double p1; ///< First parameter (μ, a, k, or λ)
41 double p2; ///< Second parameter (σ, b, or λ; unused for 1-parameter families)
42};
43
44/**
45 * @brief One parameter slot of a @c gate_rv, either a literal or a wire.
46 *
47 * A latent-variable @c gate_rv (see the "Latent variables" surface)
48 * lets a distribution parameter be a scalar provenance token rather
49 * than a concrete double: the parameter is then a random variable
50 * itself, making the leaf a compound (hierarchical) distribution. In
51 * the on-disk @c extra text a wired slot is written @c "$i" (0-based
52 * index into the gate's wire vector); a literal slot keeps its decimal
53 * text as before. When @c wire_slot @c < @c 0 the slot is the literal
54 * @c literal; otherwise it is resolved from @c wires[wire_slot] at
55 * evaluation time.
56 */
58 double literal; ///< Value when @c wire_slot < 0.
59 int wire_slot; ///< >= 0: resolve from the gate's @c wires[wire_slot].
60};
61
62/**
63 * @brief A @c gate_rv distribution spec that may carry wired (token)
64 * parameters -- the parse-time counterpart of @c DistributionSpec.
65 *
66 * @c DistributionSpec stays the fully-resolved @c {family, double, double}
67 * form every analytic call site consumes; this parallel template keeps the
68 * per-slot literal-or-wire distinction so the Monte Carlo sampler can
69 * resolve wired parameters per iteration. @c parametric() is true when any
70 * slot is wired -- the signal every analytic path uses to fall through to
71 * Monte Carlo (a compound leaf has no constant-parameter closed form).
72 */
76 /// True iff any parameter is a wire reference (a compound / latent leaf).
77 bool parametric() const { return p1.wire_slot >= 0 || p2.wire_slot >= 0; }
78};
79
80/**
81 * @brief Parse the on-disk text encoding of a @c gate_rv distribution,
82 * keeping wired (token) parameters as wire references.
83 *
84 * Accepts the same family tokens as @c parse_distribution_spec, but a
85 * parameter may be either a decimal literal or a wire reference @c "$i"
86 * (0-based index into the gate's wire vector). The literal form is
87 * byte-identical to the pre-latent encoding, so an all-literal spec
88 * round-trips unchanged.
89 *
90 * @param s The byte string read from @c MMappedCircuit::getExtra.
91 * @return The parsed template, or @c std::nullopt on malformed input.
92 */
93std::optional<DistributionTemplate>
94parse_distribution_template(const std::string &s);
95
96/**
97 * @brief Parse the on-disk text encoding of a @c gate_rv distribution.
98 *
99 * Accepts <tt>"normal:μ,σ"</tt>, <tt>"uniform:a,b"</tt>,
100 * <tt>"exponential:λ"</tt>, <tt>"erlang:k,λ"</tt>, and
101 * <tt>"gamma:k,λ"</tt>, with parameters parseable as @c double.
102 * Whitespace around the kind name and parameters is tolerated.
103 *
104 * @param s The byte string read from @c MMappedCircuit::getExtra.
105 * @return The parsed spec, or @c std::nullopt on malformed input @b or
106 * when any parameter is a wire reference (a compound / latent
107 * leaf, which has no constant-parameter closed form -- use
108 * @c parse_distribution_template for that case).
109 */
110std::optional<DistributionSpec> parse_distribution_spec(const std::string &s);
111
112/**
113 * @brief Strictly parse @p s as a @c double.
114 *
115 * Used by every consumer that has to interpret the @c extra byte
116 * string of a @c gate_value: the sampler when sampling a constant
117 * leaf, the interval-arith pass when bounding a constant leaf, and
118 * any future scalar-evaluation pass. Lives here (rather than next
119 * to one specific consumer) so the parsing convention is shared.
120 *
121 * @throws CircuitException on empty input, non-numeric input, or
122 * trailing characters past the parsed double.
123 */
124double parseDoubleStrict(const std::string &s);
125
126/**
127 * @brief Format a double back into the canonical text form used by
128 * @c gate_value extras and @c gate_rv distribution parameters
129 * (the serialisation counterpart of @c parseDoubleStrict).
130 *
131 * @c std::to_chars produces the shortest decimal representation that
132 * round-trips through @c std::from_chars / @c std::stod, so round
133 * cases like @c 0.2 = 0.4/2 print as @c "0.2" rather than
134 * @c "0.20000000000000001" while irrational values fall back to
135 * whatever length is needed for exact recovery. The legacy
136 * @c std::ostringstream @c << @c setprecision(17) path is kept as a
137 * defensive fallback in case @c to_chars fails (range / buffer).
138 */
139std::string double_to_text(double v);
140
141/**
142 * @brief Closed-form expectation E[X] for a basic distribution.
143 *
144 * - Normal(μ, σ): μ
145 * - Uniform(a, b): (a + b) / 2
146 * - Exponential(λ): 1 / λ
147 * - Erlang / Gamma(k, λ): k / λ
148 */
149double analytical_mean(const DistributionSpec &d);
150
151/**
152 * @brief Closed-form variance Var(X) for a basic distribution.
153 *
154 * - Normal(μ, σ): σ²
155 * - Uniform(a, b): (b − a)² / 12
156 * - Exponential(λ): 1 / λ²
157 * - Erlang / Gamma(k, λ): k / λ²
158 */
159double analytical_variance(const DistributionSpec &d);
160
161/**
162 * @brief Closed-form raw moment @f$E[X^k]@f$ for a basic distribution.
163 *
164 * - Normal(μ, σ):
165 * @f$\sum_{j=0,2,\ldots}^{k} \binom{k}{j} \mu^{k-j} \sigma^j (j-1)!!@f$
166 * (odd-@f$j@f$ terms vanish since central moments of @f$N(0, \sigma)@f$
167 * are zero for odd @f$j@f$).
168 * - Uniform(a, b): @f$(b^{k+1} - a^{k+1}) / ((k+1)(b-a))@f$.
169 * - Exponential(λ): @f$k! / \lambda^k@f$.
170 * - Erlang / Gamma(s, λ):
171 * @f$\Gamma(s+k) / (\Gamma(s) \lambda^k) = s(s+1)\cdots(s+k-1) / \lambda^k@f$
172 * (the rising factorial is valid for any real shape @f$s > 0@f$).
173 *
174 * Returns 1 for @f$k = 0@f$ and @c analytical_mean for @f$k = 1@f$.
175 */
176double analytical_raw_moment(const DistributionSpec &d, unsigned k);
177
178} // namespace provsql
179
180#endif // PROVSQL_RANDOM_VARIABLE_H
double analytical_variance(const DistributionSpec &d)
Closed-form variance Var(X) for a basic distribution.
double parseDoubleStrict(const std::string &s)
Strictly parse s as a double.
std::optional< DistributionSpec > parse_distribution_spec(const std::string &s)
Parse the on-disk text encoding of a gate_rv distribution.
std::optional< DistributionTemplate > parse_distribution_template(const std::string &s)
Parse the on-disk text encoding of a gate_rv distribution, keeping wired (token) parameters as wire r...
double analytical_mean(const DistributionSpec &d)
Closed-form expectation E[X] for a basic distribution.
double analytical_raw_moment(const DistributionSpec &d, unsigned k)
Closed-form raw moment for a basic distribution.
std::string double_to_text(double v)
Format a double back into the canonical text form used by gate_value extras and gate_rv distribution ...
A registered family's descriptor: its complete identity.
One parameter slot of a gate_rv, either a literal or a wire.
double literal
Value when wire_slot < 0.
int wire_slot
>= 0: resolve from the gate's wires[wire_slot].
Parsed distribution spec (family + up to two parameters).
const DistributionFamily * family
double p2
Second parameter (σ, b, or λ; unused for 1-parameter families).
double p1
First parameter (μ, a, k, or λ).
A gate_rv distribution spec that may carry wired (token) parameters – the parse-time counterpart of D...
const DistributionFamily * family
bool parametric() const
True iff any parameter is a wire reference (a compound / latent leaf).