3. Probabilities under changing assumptions

Rain or a sprinkler makes the ground wet: W = R ∨ S. Given wet ground, what is the probability of rain? We build the events once, then evaluate them under two sets of independent priors. A third variable, wind, is unconstrained by these events.

from fractions import Fraction
from tididi import Vtree, literal

vtree = Vtree.balanced(3)
rain = literal(vtree, 1)
sprinkler = literal(vtree, 2)
wet = rain.copy() | sprinkler
rain_and_wet = rain & wet.copy()

3.1. Exact fractions

Fraction comes from Python’s standard library. Each variable has a pair of weights: false first, true second. For independent probabilities these sum to one. In particular, the free wind variable contributes one.

In both scenarios below the evidence has positive probability, so P(R | W) = P(R ∧ W) / P(W) is defined.

for rain_probability in [Fraction(1, 5), Fraction(3, 5)]:
    priors = {1: rain_probability, 2: Fraction(1, 10), 3: Fraction(2, 5)}
    weights = {variable: (1 - p, p) for variable, p in priors.items()}
    wet_probability = wet.weighted_count(weights)
    conditional = rain_and_wet.weighted_count(weights) / wet_probability
    print(f"P(rain) = {rain_probability}, P(wet) = {wet_probability}")
    print("P(rain | wet) =", conditional)
P(rain) = 1/5, P(wet) = 7/25
P(rain | wet) = 5/7
P(rain) = 3/5, P(wet) = 16/25
P(rain | wet) = 15/16

Both evaluations borrowed their circuits. No rebuilding or copying was needed when the priors changed. More generally, weights need not sum to one: unit weights recover the model count.

unit_weights = {variable: (1, 1) for variable in vtree.variables}
print("Models of wet:", wet.weighted_count(unit_weights))
Models of wet: 6

3.2. Change observations

An evaluator caches values so changing an observation refreshes only affected branches. It consumes wet; finish() returns the circuit when we are done. The value with no rain is the joint probability P(W ∧ ¬R), without normalization.

evaluator = wet.evaluator(weights)
evaluator.observe([-1])
print("P(wet and no rain) =", evaluator.value())
evaluator.clear_observations()
print("P(wet) =", evaluator.value())
wet = evaluator.finish()
P(wet and no rain) = 1/25
P(wet) = 16/25