ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
shapley.cpp
Go to the documentation of this file.
1/**
2 * @file shapley.cpp
3 * @brief SQL functions for Shapley and Banzhaf power-index computation.
4 *
5 * Implements two SQL-callable functions:
6 * - @c provsql.shapley(token, variable, method, args): Shapley value of
7 * a given input gate (tuple) in the provenance circuit rooted at @p token.
8 * - @c provsql.shapley_all_vars(token, method, args): Shapley values for
9 * all input gates simultaneously (more efficient than calling @c shapley()
10 * once per variable).
11 *
12 * The @p method argument selects the d-DNNF construction:
13 * - empty / @c "default" / @c "auto": cost-select the cheapest route via the
14 * probability catalog's chooser.
15 * - @c "tree-decomposition": exact, polynomial if treewidth ≤ @c MAX_TREEWIDTH.
16 * - @c "interpret-as-dd": direct interpretation of the circuit as a d-DNNF.
17 * - @c "compilation": external knowledge compiler, named in @p args
18 * (@c "d4", @c "c2d", …); empty @p args picks the highest-preference one.
19 *
20 * Banzhaf power index computation is exposed via the same internal helper
21 * (@c shapley_internal with @c banzhaf=true), called by the
22 * @c provsql.banzhaf() SQL function defined in the SQL layer.
23 */
24extern "C" {
25#include "postgres.h"
26#include "fmgr.h"
27#include "catalog/pg_type.h"
28#include "utils/tuplestore.h"
29#include "utils/uuid.h"
30#include "executor/spi.h"
31#include "provsql_shmem.h"
32#include "provsql_utils.h"
33
34PG_FUNCTION_INFO_V1(shapley);
35PG_FUNCTION_INFO_V1(shapley_all_vars);
36}
37
38#include "c_cpp_compatibility.h"
39#include "BooleanCircuit.h"
40#include "ProbabilityMethod.h"
41#include "GenericCircuit.h"
42#include "Circuit.hpp"
43#include <unordered_map>
44#include "provsql_utils_cpp.h"
46#include "CircuitFromMMap.h"
47#include "tool_registry_sync.h"
48#include <fstream>
49
50using namespace std;
51
52/**
53 * @brief Core implementation for Shapley and Banzhaf index computation.
54 * @param token UUID of the root provenance gate.
55 * @param variable UUID of the input gate whose index is to be computed.
56 * @param method d-DNNF compilation method.
57 * @param args Additional arguments for the compilation method.
58 * @param banzhaf If @c true, compute the Banzhaf index instead of Shapley.
59 * @return The Shapley (or Banzhaf) value of @p variable.
60 */
61static double shapley_internal
62 (pg_uuid_t token, pg_uuid_t variable, const std::string &method, const std::string &args, bool banzhaf)
63{
64 /* A conditioned token (X | C) is refused: Shapley / Banzhaf are linear in
65 * their value function, whereas P(X|C) = P(X∧C)/P(C) is a non-linear ratio,
66 * so the conditional indices are not a combination of the unconditioned
67 * ones and have no implementation here. Detect the conditioned root and
68 * raise a Shapley-specific message rather than the generic semiring-refusal
69 * thrown deeper in the Boolean-circuit build. */
72 provsql_error("shapley/banzhaf: conditional Shapley / Banzhaf values are "
73 "not supported -- a conditioned token (X | C) cannot be "
74 "passed to shapley() / banzhaf(). Compute the index on the "
75 "unconditioned token, or use probability_evaluate for the "
76 "conditional probability P(X|C)");
77 gate_t root;
78 std::unordered_map<gate_t, gate_t> gc_to_bc;
79 BooleanCircuit c = getBooleanCircuit(gc, token, root, gc_to_bc);
80
82 provsql_error("Computing Shapley/Banzhaf values is ill-defined for circuits with multivalued (mulinput) gates");
83
84 if(c.getGateType(c.getGate(uuid2string(variable))) != BooleanGate::IN)
85 return 0.;
86
87 // Default / "auto": cost-select the d-D construction (interpret-as-dd /
88 // tree-decomposition / compilation) via the probability catalog's chooser;
89 // any other method (tree-decomposition / interpret-as-dd / compilation, the
90 // latter with a compiler name in `args`) is taken by name.
91 dDNNF dd = (method.empty() || method == "default" || method == "auto")
92 ? provsql::makeDDAuto(c, root)
93 : c.makeDD(root, method, args);
94
95 dd.makeSmooth();
96 if(!banzhaf)
98
99 auto var_gate=dd.getGate(uuid2string(variable));
100
101 double result;
102
103 if(!banzhaf)
104 result = dd.shapley(var_gate);
105 else
106 result = dd.banzhaf(var_gate);
107
108 return result;
109}
110
111/** @brief PostgreSQL-callable wrapper for shapley() and banzhaf(). */
112Datum shapley(PG_FUNCTION_ARGS)
113{
114 provsql_sync_tool_registry(); // honour persisted tool-registry overrides
115 try {
116 if(PG_ARGISNULL(0) || PG_ARGISNULL(1))
117 PG_RETURN_NULL();
118
119 Datum token = PG_GETARG_DATUM(0);
120 Datum variable = PG_GETARG_DATUM(1);
121
122 std::string method;
123 if(!PG_ARGISNULL(2)) {
124 text *t = PG_GETARG_TEXT_P(2);
125 method = string(VARDATA(t),VARSIZE(t)-VARHDRSZ);
126 }
127
128 std::string args;
129 if(!PG_ARGISNULL(3)) {
130 text *t = PG_GETARG_TEXT_P(3);
131 args = string(VARDATA(t),VARSIZE(t)-VARHDRSZ);
132 }
133
134 bool banzhaf = false;
135 if(!PG_ARGISNULL(4)) {
136 banzhaf = PG_GETARG_BOOL(4);
137 }
138
139 PG_RETURN_FLOAT8(shapley_internal(*DatumGetUUIDP(token), *DatumGetUUIDP(variable), method, args, banzhaf));
140 } catch(const std::exception &e) {
141 provsql_error("shapley: %s", e.what());
142 } catch(...) {
143 provsql_error("shapley: Unknown exception");
144 }
145
146 PG_RETURN_NULL();
147}
148
149/** @brief PostgreSQL-callable wrapper for shapley_all_vars() set-returning function. */
150Datum shapley_all_vars(PG_FUNCTION_ARGS)
151{
152 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
153
154 MemoryContext per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
155 MemoryContext oldcontext = MemoryContextSwitchTo(per_query_ctx);
156
157 TupleDesc tupdesc = rsinfo->expectedDesc;
158 Tuplestorestate *tupstore = tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, false, work_mem);
159
160 rsinfo->returnMode = SFRM_Materialize;
161 rsinfo->setResult = tupstore;
162
163 if(!PG_ARGISNULL(0)) {
164 pg_uuid_t token = *DatumGetUUIDP(PG_GETARG_DATUM(0));
165
166 std::string method;
167 if(!PG_ARGISNULL(1)) {
168 text *t = PG_GETARG_TEXT_P(1);
169 method = string(VARDATA(t),VARSIZE(t)-VARHDRSZ);
170 }
171
172 std::string args;
173 if(!PG_ARGISNULL(2)) {
174 text *t = PG_GETARG_TEXT_P(2);
175 args = string(VARDATA(t),VARSIZE(t)-VARHDRSZ);
176 }
177
178 bool banzhaf = false;
179 if(!PG_ARGISNULL(3)) {
180 banzhaf = PG_GETARG_BOOL(3);
181 }
182
183
185 if(gc.getGateType(gc.getGate(uuid2string(token))) == gate_conditioned)
186 provsql_error("shapley/banzhaf: conditional Shapley / Banzhaf values are "
187 "not supported -- a conditioned token (X | C) cannot be "
188 "passed to shapley() / banzhaf(). Compute the index on the "
189 "unconditioned token, or use probability_evaluate for the "
190 "conditional probability P(X|C)");
191 gate_t root;
192 std::unordered_map<gate_t, gate_t> gc_to_bc;
193 BooleanCircuit c = getBooleanCircuit(gc, token, root, gc_to_bc);
194
195 if(c.hasMultivaluedGates())
196 provsql_error("Computing Shapley/Banzhaf values is ill-defined for circuits with multivalued (mulinput) gates");
197
198 dDNNF dd = (method.empty() || method == "default" || method == "auto")
199 ? provsql::makeDDAuto(c, root)
200 : c.makeDD(root, method, args);
201 dd.makeSmooth();
202 if(!banzhaf)
204
205 for(auto &v_circuit_gate: c.getInputs()) {
206 auto var_uuid_string = c.getUUID(v_circuit_gate);
207 auto var_gate=dd.getGate(var_uuid_string);
208 pg_uuid_t *uuidp = reinterpret_cast<pg_uuid_t*>(palloc(UUID_LEN));
209 *uuidp = string2uuid(var_uuid_string);
210
211 double result;
212
213 if(!banzhaf)
214 result = dd.shapley(var_gate);
215 else
216 result = dd.banzhaf(var_gate);
217
218 Datum values[2] = {
219 UUIDPGetDatum(uuidp), Float8GetDatum(result)
220 };
221 bool nulls[sizeof(values)] = {0, 0};
222
223 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
224 }
225 }
226
227 MemoryContextSwitchTo(oldcontext);
228
229 PG_RETURN_NULL();
230}
Boolean provenance circuit with support for knowledge compilation.
@ AND
Logical conjunction of child gates.
@ IN
Input (variable) gate representing a base tuple.
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
Out-of-line template method implementations for Circuit<gateType>.
Semiring-agnostic in-memory provenance circuit.
Catalog of probability-evaluation methods (Strategy + registry).
Fix macro conflicts between PostgreSQL headers and the C++ STL/Boost.
Boolean circuit for provenance formula evaluation.
const std::set< gate_t > & getInputs() const
Return the set of input (IN) gate IDs.
dDNNF makeDD(gate_t g, const std::string &method, const std::string &args) const
Dispatch to the appropriate d-DNNF construction method.
bool hasMultivaluedGates() const
Return true if the circuit contains any MULIN gates.
gateType getGateType(gate_t g) const
Return the type of gate g.
Definition Circuit.h:130
uuid getUUID(gate_t g) const
Return the UUID string associated with gate g.
Definition Circuit.hpp:46
gate_t getGate(const uuid &u)
Return (or create) the gate associated with UUID u.
Definition Circuit.hpp:33
In-memory provenance circuit with semiring-generic evaluation.
A d-DNNF circuit supporting exact probabilistic and game-theoretic evaluation.
Definition dDNNF.h:71
void makeSmooth()
Make the d-DNNF smooth.
Definition dDNNF.cpp:61
void makeGatesBinary(BooleanGate type)
Rewrite all n-ary AND/OR gates into binary trees.
Definition dDNNF.cpp:108
double shapley(gate_t var) const
Compute the Shapley value of input gate var.
Definition dDNNF.cpp:526
double banzhaf(gate_t var) const
Compute the Banzhaf power index of input gate var.
Definition dDNNF.cpp:554
Constructs a d-DNNF from a Boolean circuit and its tree decomposition.
dDNNF makeDDAuto(BooleanCircuit &c, gate_t g)
Cost-select a d-DNNF construction route for gate g of Boolean circuit c and build it – the default ma...
#define provsql_error(fmt,...)
Report a fatal ProvSQL error and abort the current transaction.
Shared-memory segment and inter-process pipe management.
Core types, constants, and utilities shared across ProvSQL.
@ gate_conditioned
Conditioning marker with two children [target, evidence]: measure-only, probability_evaluate returns ...
#define UUID_LEN
Number of bytes in a UUID.
pg_uuid_t string2uuid(const string &source)
Parse a UUID string into a pg_uuid_t.
string uuid2string(pg_uuid_t uuid)
Format a pg_uuid_t as a std::string.
C++ utility functions for UUID manipulation.
static double shapley_internal(pg_uuid_t token, pg_uuid_t variable, const std::string &method, const std::string &args, bool banzhaf)
Core implementation for Shapley and Banzhaf index computation.
Definition shapley.cpp:62
Datum shapley(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for shapley() and banzhaf().
Definition shapley.cpp:112
Datum shapley_all_vars(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for shapley_all_vars() set-returning function.
Definition shapley.cpp:150
UUID structure.
void provsql_sync_tool_registry()
Rebuild the in-memory registry as "compiled seed overlaid with the provsql.tool_overrides rows"...
Reload the in-memory external-tool registry from its persistent overrides.