Boolean circuits, read-once and d-D correctness #
This file formalises Boolean circuits over a set X of variables, together
with two recursive bottom-up probability evaluators and their correctness
theorems:
- The read-once correctness theorem
Circuit.readOnce_funcProb_eq_prob: for any read-once circuit, the inclusion-exclusion evaluatorCircuit.probagrees with the sum-over-valuations semanticsPr(c.toBoolFunc) = ∑_{v ⊨ c} Pr(v). - The d-D correctness theorem
Circuit.dD_funcProb_eq_probDD: for any decomposable + deterministic circuit, the evaluatorCircuit.probDD(which sums the OR children's probabilities directly rather than applying inclusion-exclusion) agrees with the same sum-over-valuations semantics.
These are the formal counterpart of Section V-D step 1 of Sen, Maniu &
Senellart, ProvSQL: A General System for Keeping Track of the Provenance
and Probability of Data: on a read-once Boolean circuit
each gate's probability is computed in O(1) from those of its two
children, and on a decomposable-deterministic circuit (d-D, the structural
property targeted by knowledge compilers) the same is true with simpler
OR formula.
Main definitions #
Circuit X– inductive Boolean circuit (constants, variables, NOT, AND, OR).Circuit.eval– evaluation under a valuationv : X → Bool.Circuit.toBoolFunc– view a circuit as aBoolFunc X.Circuit.vars– the variables used by a circuit (as aFinset).Circuit.ReadOnce– the gate-by-gate disjoint-supports predicate.Circuit.prob– the read-once probability evaluator (OR uses inclusion-exclusion).Circuit.Decomposable– every AND gate has children with disjoint variable supports.Circuit.Deterministic– every OR gate has mutually exclusive children (c₁.toBoolFunc * c₂.toBoolFunc = 0).Circuit.probDD– the d-D probability evaluator (OR sums directly).
Main results #
BoolFunc.DependsOn–fdepends only on a Finset of variables.Circuit.toBoolFunc_dependsOn_vars– a circuit's denotation depends only on its variables.ProbAssignment.funcProb_mul_disjoint– the independence lemma:Pr(f * g) = Pr(f) * Pr(g)wheneverf,gdepend on disjoint variable supports.Circuit.readOnce_funcProb_eq_prob– the read-once correctness theorem: for anyReadOncecircuitc,Pr(c.toBoolFunc) = c.prob P.Circuit.dD_funcProb_eq_probDD– the d-D correctness theorem: for anyDecomposable+Deterministiccircuitc,Pr(c.toBoolFunc) = c.probDD P.
References #
- Sen, Maniu & Senellart (Section V-D step 1)
Boolean circuit over a set X of variables.
- const
{X : Type}
: Bool → Circuit X
A Boolean constant.
- var
{X : Type}
: X → Circuit X
A variable leaf.
- not
{X : Type}
: Circuit X → Circuit X
Logical negation.
- and
{X : Type}
: Circuit X → Circuit X → Circuit X
Logical conjunction.
- or
{X : Type}
: Circuit X → Circuit X → Circuit X
Logical disjunction.
Instances For
Equations
- instReprCircuit = { reprPrec := instReprCircuit.repr }
Equations
- One or more equations did not get rendered due to their size.
Instances For
Evaluate a circuit under a Boolean valuation.
Equations
Instances For
A circuit is read-once when each gate's two children have disjoint variable supports. Constants and variables are read-once by definition; NOT is read-once if its argument is.
- const {X : Type} [DecidableEq X] (b : Bool) : (Circuit.const b).ReadOnce
- var {X : Type} [DecidableEq X] (x : X) : (Circuit.var x).ReadOnce
- not {X : Type} [DecidableEq X] {c : Circuit X} : c.ReadOnce → c.not.ReadOnce
- and {X : Type} [DecidableEq X] {c₁ c₂ : Circuit X} : c₁.ReadOnce → c₂.ReadOnce → Disjoint c₁.vars c₂.vars → (c₁.and c₂).ReadOnce
- or {X : Type} [DecidableEq X] {c₁ c₂ : Circuit X} : c₁.ReadOnce → c₂.ReadOnce → Disjoint c₁.vars c₂.vars → (c₁.or c₂).ReadOnce
Instances For
The recursive bottom-up probability evaluator. Each gate combines the
probabilities of its children in O(1), with the formula depending on the
gate type.
Equations
- Circuit.prob P (Circuit.const true) = 1
- Circuit.prob P (Circuit.const false) = 0
- Circuit.prob P (Circuit.var x_1) = P.prob x_1
- Circuit.prob P c.not = 1 - Circuit.prob P c
- Circuit.prob P (c₁.and c₂) = Circuit.prob P c₁ * Circuit.prob P c₂
- Circuit.prob P (c₁.or c₂) = Circuit.prob P c₁ + Circuit.prob P c₂ - Circuit.prob P c₁ * Circuit.prob P c₂
Instances For
A circuit is decomposable when every AND gate has children with disjoint variable supports. Constants, variables, NOT, and OR are decomposable as soon as their immediate children (if any) are.
- const {X : Type} [DecidableEq X] (b : Bool) : (Circuit.const b).Decomposable
- var {X : Type} [DecidableEq X] (x : X) : (Circuit.var x).Decomposable
- not {X : Type} [DecidableEq X] {c : Circuit X} : c.Decomposable → c.not.Decomposable
- and {X : Type} [DecidableEq X] {c₁ c₂ : Circuit X} : c₁.Decomposable → c₂.Decomposable → Disjoint c₁.vars c₂.vars → (c₁.and c₂).Decomposable
- or {X : Type} [DecidableEq X] {c₁ c₂ : Circuit X} : c₁.Decomposable → c₂.Decomposable → (c₁.or c₂).Decomposable
Instances For
A circuit is deterministic when every OR gate has mutually exclusive
children: the conjunction of their BoolFunc denotations is the constant
false. Constants, variables, NOT, and AND are deterministic as soon as
their immediate children (if any) are. NOTE: there is no NNF restriction;
not is treated semantically (Pr(¬c) = 1 - Pr(c) always).
- const {X : Type} (b : Bool) : (Circuit.const b).Deterministic
- var {X : Type} (x : X) : (Circuit.var x).Deterministic
- not {X : Type} {c : Circuit X} : c.Deterministic → c.not.Deterministic
- and {X : Type} {c₁ c₂ : Circuit X} : c₁.Deterministic → c₂.Deterministic → (c₁.and c₂).Deterministic
- or {X : Type} {c₁ c₂ : Circuit X} : c₁.Deterministic → c₂.Deterministic → c₁.toBoolFunc * c₂.toBoolFunc = 0 → (c₁.or c₂).Deterministic
Instances For
The d-D bottom-up probability evaluator. Differs from Circuit.prob
only at OR gates, which sum the two children's probabilities directly
(no inclusion-exclusion correction term). Sound on Decomposable + Deterministic circuits: see Circuit.dD_funcProb_eq_probDD.
Equations
- Circuit.probDD P (Circuit.const true) = 1
- Circuit.probDD P (Circuit.const false) = 0
- Circuit.probDD P (Circuit.var x_1) = P.prob x_1
- Circuit.probDD P c.not = 1 - Circuit.probDD P c
- Circuit.probDD P (c₁.and c₂) = Circuit.probDD P c₁ * Circuit.probDD P c₂
- Circuit.probDD P (c₁.or c₂) = Circuit.probDD P c₁ + Circuit.probDD P c₂
Instances For
BoolFunc.DependsOn: support of a Boolean function #
f depends only on the variables in S: any two valuations agreeing
on S produce the same value. This is the standard notion of "support".
Instances For
Auxiliary funcProb lemmas #
These bridge the recursive evaluator Circuit.prob to the sum-over-valuations
semantics ProbAssignment.funcProb. They are stated for arbitrary BoolFunc X,
not specifically for circuit denotations, so that Circuit.readOnce_funcProb_eq_prob
can be obtained by case analysis on the ReadOnce derivation.
Pr(var i) = Pr(i): the probability of the single-variable Boolean function
equals the variable's own probability. Proved by reorganising the sum
∑_v if v i then valProb v else 0 as a product ∏_y h_y(v y) and applying
Fintype.prod_sum (the same swap used in sum_valProb_eq_one).
Pr(¬f) = 1 - Pr(f): probability of a Boolean complement. In BoolFunc X,
1 - f is pointwise f v && !(f v)-style Boolean subtraction, which on the
constant-true 1 reduces to Bool.not ∘ f. The proof splits each summand by
f v and uses sum_valProb_eq_one.
Independence lemma #
The independence lemma funcProb_mul_disjoint is the technical heart of the
read-once correctness theorem: Pr(f * g) = Pr(f) * Pr(g) whenever f and g
depend on disjoint variable supports. The proof splits each valuation
v : X → Bool into its restrictions on S and Sᶜ via
Equiv.piEquivPiSubtypeProd, factors the valuation probability over the
partition, and uses the marginalisation ∑_b (P̃_x b) = 1 to discard the
unused half on each of the two factors.
Independence lemma. If f, g : BoolFunc X depend on disjoint
variable supports S, T, then Pr(f * g) = Pr(f) * Pr(g).
The proof splits each valuation v : X → Bool into (v|S, v|Sᶜ) via
Equiv.piEquivPiSubtypeProd, factors valProb v as the product of the two
restricted products, and uses the marginalisations
∑_{vS} (∏_{x ∈ S} P̃_x(vS x)) = 1 and
∑_{vR} (∏_{x ∉ S} P̃_x(vR x)) = 1
(both proved via Fintype.prod_sum and sum_factor_at) to collapse the
unused half on each side.
Pr(f + g) = Pr(f) + Pr(g) - Pr(f * g): the universal inclusion-exclusion
identity for the BoolFunc disjunction (+) and conjunction (*). No
disjointness hypothesis is needed; the formula holds pointwise on each summand
via the Bool identity (b₁ || b₂).toℚ = b₁.toℚ + b₂.toℚ - (b₁ && b₂).toℚ.
A circuit's denotation depends only on its variables.
Read-once correctness #
Read-once correctness theorem. For any read-once Boolean circuit c,
the recursive bottom-up probability evaluator c.prob P agrees with the
sum-over-valuations semantics Pr(c.toBoolFunc). Proved by induction on the
ReadOnce derivation, using:
ProbAssignment.funcProb_varfor the variable leaves;ProbAssignment.funcProb_sub_self_const_onefor NOT (Pr(¬f) = 1 - Pr(f));ProbAssignment.funcProb_mul_disjointfor AND (independence under disjoint supports);ProbAssignment.funcProb_add_eqtogether with the independence lemma for OR.
d-D correctness #
d-D correctness theorem. For any decomposable + deterministic
Boolean circuit c, the recursive bottom-up evaluator c.probDD P agrees
with the sum-over-valuations semantics Pr(c.toBoolFunc). Proved by
structural induction on c, using:
ProbAssignment.funcProb_varfor the variable leaves;ProbAssignment.funcProb_sub_self_const_onefor NOT (no structural hypothesis needed: negation always satisfiesPr(¬f) = 1 - Pr(f));ProbAssignment.funcProb_mul_disjointfor AND (decomposability supplies the disjoint supports);ProbAssignment.funcProb_add_eqfor OR, with the determinism hypothesisc₁.toBoolFunc * c₂.toBoolFunc = 0collapsing the inclusion-exclusion correction termPr(f * g)to zero.