def test_sum_of_sums(): w \ = 0.3*(0.4*(X >> norm()) | 0.6*(X >> norm())) \ | 0.7*(0.1*(X >> norm()) | 0.9*(X >> norm())) assert isinstance(w, SumSPE) assert len(w.children) == 4 assert allclose(float(w.weights[0]), log(0.3) + log(0.4)) assert allclose(float(w.weights[1]), log(0.3) + log(0.6)) assert allclose(float(w.weights[2]), log(0.7) + log(0.1)) assert allclose(float(w.weights[3]), log(0.7) + log(0.9)) w \ = 0.3*(0.4*(X >> norm()) | 0.6*(X >> norm())) \ | 0.2*(0.1*(X >> norm()) | 0.9*(X >> norm())) assert isinstance(w, PartialSumSPE) assert allclose(float(w.weights[0]), 0.3) assert allclose(float(w.weights[1]), 0.2) a = w | 0.5*(X >> gamma(a=1)) assert isinstance(a, SumSPE) assert len(a.children) == 5 assert allclose(float(a.weights[0]), log(0.3) + log(0.4)) assert allclose(float(a.weights[1]), log(0.3) + log(0.6)) assert allclose(float(a.weights[2]), log(0.2) + log(0.1)) assert allclose(float(a.weights[3]), log(0.2) + log(0.9)) assert allclose(float(a.weights[4]), log(0.5)) # Wrong symbol. with pytest.raises(ValueError): z = w | 0.4*(Y >> gamma(a=1))
def test_or_and(): with pytest.raises(ValueError): (0.3*(X >> norm()) | 0.7*(Y >> gamma(a=1))) & (Z >> norm()) a = (0.3*(X >> norm()) | 0.7*(X >> gamma(a=1))) & (Z >> norm()) assert isinstance(a, ProductSPE) assert isinstance(a.children[0], SumSPE) assert isinstance(a.children[1], ContinuousLeaf)
def test_product_leaf(): with pytest.raises(TypeError): 0.3*(X >> gamma(a=1)) & (X >> norm()) with pytest.raises(TypeError): (X >> norm()) & 0.3*(X >> gamma(a=1)) with pytest.raises(ValueError): (X >> norm()) & (X >> gamma(a=1)) y = (X >> norm()) & (Y >> gamma(a=1)) & (Z >> norm()) assert isinstance(y, ProductSPE) assert len(y.children) == 3 assert y.get_symbols() == frozenset([X, Y, Z])
def test_sum_leaf(): # Cannot sum leaves without weights. with pytest.raises(TypeError): (X >> norm()) | (X >> gamma(a=1)) # Cannot sum a leaf with a partial sum. with pytest.raises(TypeError): 0.3*(X >> norm()) | (X >> gamma(a=1)) # Cannot sum a leaf with a partial sum. with pytest.raises(TypeError): (X >> norm()) | 0.3*(X >> gamma(a=1)) # Wrong symbol. with pytest.raises(ValueError): 0.4*(X >> norm()) | 0.6*(Y >> gamma(a=1)) # Sum exceeds one. with pytest.raises(ValueError): 0.4*(X >> norm()) | 0.7*(Y >> gamma(a=1)) y = 0.4*(X >> norm()) | 0.3*(X >> gamma(a=1)) assert isinstance(y, PartialSumSPE) assert len(y.weights) == 2 assert allclose(float(y.weights[0]), 0.4) assert allclose(float(y.weights[1]), 0.3) y = 0.4*(X >> norm()) | 0.6*(X >> gamma(a=1)) assert isinstance(y, SumSPE) assert len(y.weights) == 2 assert allclose(float(y.weights[0]), log(0.4)) assert allclose(float(y.weights[1]), log(0.6)) # Sum exceeds one. with pytest.raises(TypeError): y | 0.7 * (X >> norm()) y = 0.4*(X >> norm()) | 0.3*(X >> gamma(a=1)) | 0.1*(X >> norm()) assert isinstance(y, PartialSumSPE) assert len(y.weights) == 3 assert allclose(float(y.weights[0]), 0.4) assert allclose(float(y.weights[1]), 0.3) assert allclose(float(y.weights[2]), 0.1) y = 0.4*(X >> norm()) | 0.3*(X >> gamma(a=1)) | 0.3*(X >> norm()) assert isinstance(y, SumSPE) assert len(y.weights) == 3 assert allclose(float(y.weights[0]), log(0.4)) assert allclose(float(y.weights[1]), log(0.3)) assert allclose(float(y.weights[2]), log(0.3)) with pytest.raises(TypeError): (0.3)*(0.3*(X >> norm())) with pytest.raises(TypeError): (0.3*(X >> norm())) * (0.3) with pytest.raises(TypeError): 0.3*(0.3*(X >> norm()) | 0.5*(X >> norm())) w = 0.3*(0.4*(X >> norm()) | 0.6*(X >> norm())) assert isinstance(w, PartialSumSPE)
def test_sum_normal_gamma(): X = Id('X') weights = [log(Fraction(2, 3)), log(Fraction(1, 3))] spe = SumSPE([ X >> norm(loc=0, scale=1), X >> gamma(loc=0, a=1), ], weights) assert spe.logprob(X > 0) == logsumexp([ spe.weights[0] + spe.children[0].logprob(X > 0), spe.weights[1] + spe.children[1].logprob(X > 0), ]) assert spe.logprob(X < 0) == log(Fraction(2, 3)) + log(Fraction(1, 2)) samples = spe.sample(100, prng=numpy.random.RandomState(1)) assert all(s[X] for s in samples) spe.sample_func(lambda X: abs(X**3), 100) with pytest.raises(ValueError): spe.sample_func(lambda Y: abs(X**3), 100) spe_condition = spe.condition(X < 0) assert isinstance(spe_condition, ContinuousLeaf) assert spe_condition.conditioned assert spe_condition.logprob(X < 0) == 0 samples = spe_condition.sample(100) assert all(s[X] < 0 for s in samples) assert spe.logprob(X < 0) == logsumexp([ spe.weights[0] + spe.children[0].logprob(X < 0), spe.weights[1] + spe.children[1].logprob(X < 0), ])
def test_logpdf_mixture_real_continuous_continuous(): spe = X >> (.3*norm() | .7*gamma(a=1)) assert allclose( spe.logpdf({X: .5}), logsumexp([ log(.3) + spe.children[0].logpdf({X: 0.5}), log(.7) + spe.children[1].logpdf({X: 0.5}), ]))
def test_product_condition_or_probabilithy_zero(): X = Id('X') Y = Id('Y') spe = ProductSPE([X >> norm(loc=0, scale=1), Y >> gamma(a=1)]) # Condition on event which has probability zero. event = (X > 2) & (X < 2) with pytest.raises(ValueError): spe.condition(event) assert spe.logprob(event) == -float('inf') # Condition on event which has probability zero. event = (Y < 0) | (Y < -1) with pytest.raises(ValueError): spe.condition(event) assert spe.logprob(event) == -float('inf') # Condition on an event where one clause has probability # zero, yielding a single product. spe_condition = spe.condition((Y < 0) | ((Log(X) >= 0) & (1 <= Y))) assert isinstance(spe_condition, ProductSPE) assert spe_condition.children[0].symbol == X assert spe_condition.children[0].conditioned assert spe_condition.children[0].support == Interval(1, oo) assert spe_condition.children[1].symbol == Y assert spe_condition.children[1].conditioned assert spe_condition.children[0].support == Interval(1, oo) # We have (X < 2) & ~(1 < exp(|3X**2|) is empty. # Thus Y remains unconditioned, # and X is partitioned into (-oo, 0) U (0, oo) with equal weight. event = (Exp(abs(3 * X**2)) > 1) | ((Log(Y) < 0.5) & (X < 2)) spe_condition = spe.condition(event) # # The most concise representation of spe_condition is: # (Product (Sum [.5 .5] X|X<0 X|X>0) Y) assert isinstance(spe_condition, ProductSPE) assert isinstance(spe_condition.children[0], SumSPE) assert spe_condition.children[0].weights == (-log(2), -log(2)) assert spe_condition.children[0].children[0].conditioned assert spe_condition.children[0].children[1].conditioned assert spe_condition.children[0].children[0].support \ in [Interval.Ropen(-oo, 0), Interval.Lopen(0, oo)] assert spe_condition.children[0].children[1].support \ in [Interval.Ropen(-oo, 0), Interval.Lopen(0, oo)] assert spe_condition.children[0].children[0].support \ != spe_condition.children[0].children[1].support assert spe_condition.children[1].symbol == Y assert not spe_condition.children[1].conditioned
def test_sum_simplify_nested_sum_1(): X = Id('X') children = [ SumSPE( [X >> norm(loc=0, scale=1), X >> norm(loc=0, scale=2)], [log(0.4), log(0.6)]), X >> gamma(loc=0, a=1), ] spe = SumSPE(children, [log(0.7), log(0.3)]) assert spe.size() == 4 assert spe.children == ( children[0].children[0], children[0].children[1], children[1] ) assert allclose(spe.weights[0], log(0.7) + log(0.4)) assert allclose(spe.weights[1], log(0.7) + log(0.6)) assert allclose(spe.weights[2], log(0.3))
def test_product_distribution_normal_gamma_basic(): X1 = Id('X1') X2 = Id('X2') X3 = Id('X3') X4 = Id('X4') children = [ ProductSPE([ X1 >> norm(loc=0, scale=1), X4 >> norm(loc=10, scale=1), ]), X2 >> gamma(loc=0, a=1), X3 >> norm(loc=2, scale=3) ] spe = ProductSPE(children) assert spe.children == ( children[0].children[0], children[0].children[1], children[1], children[2], ) assert spe.get_symbols() == frozenset([X1, X2, X3, X4]) assert spe.size() == 5 samples = spe.sample(2) assert len(samples) == 2 for sample in samples: assert len(sample) == 4 assert all([X in sample for X in (X1, X2, X3, X4)]) samples = spe.sample_subset((X1, X2), 10) assert len(samples) == 10 for sample in samples: assert len(sample) == 2 assert X1 in sample assert X2 in sample samples = spe.sample_func(lambda X1, X2, X3: (X1, (X2**2, X3)), 1) assert len(samples) == 1 assert len(samples[0]) == 2 assert len(samples[0][1]) == 2 with pytest.raises(ValueError): spe.sample_func(lambda X1, X5: X1 + X4, 1)
def test_sum_normal_gamma_exposed(): X = Id('X') W = Id('W') weights = W >> choice({ '0': Fraction(2, 3), '1': Fraction(1, 3), }) children = { '0': X >> norm(loc=0, scale=1), '1': X >> gamma(loc=0, a=1), } spe = ExposedSumSPE(children, weights) assert spe.logprob(W << {'0'}) == log(Fraction(2, 3)) assert spe.logprob(W << {'1'}) == log(Fraction(1, 3)) assert allclose(spe.logprob((W << {'0'}) | (W << {'1'})), 0) assert spe.logprob((W << {'0'}) & (W << {'1'})) == -float('inf') assert allclose(spe.logprob((W << {'0', '1'}) & (X < 1)), spe.logprob(X < 1)) assert allclose(spe.logprob((W << {'0'}) & (X < 1)), spe.weights[0] + spe.children[0].logprob(X < 1)) spe_condition = spe.condition((W << {'1'}) | (W << {'0'})) assert isinstance(spe_condition, SumSPE) assert len(spe_condition.weights) == 2 assert \ allclose(spe_condition.weights[0], log(Fraction(2,3))) \ and allclose(spe_condition.weights[0], log(Fraction(2,3))) \ or \ allclose(spe_condition.weights[1], log(Fraction(2,3))) \ and allclose(spe_condition.weights[0], log(Fraction(2,3)) ) spe_condition = spe.condition((W << {'1'})) assert isinstance(spe_condition, ProductSPE) assert isinstance(spe_condition.children[0], NominalLeaf) assert isinstance(spe_condition.children[1], ContinuousLeaf) assert spe_condition.logprob(X < 5) == spe.children[1].logprob(X < 5)
def test_product_inclusion_exclusion_basic(): X = Id('X') Y = Id('Y') spe = ProductSPE([X >> norm(loc=0, scale=1), Y >> gamma(a=1)]) a = spe.logprob(X > 0.1) b = spe.logprob(Y < 0.5) c = spe.logprob((X > 0.1) & (Y < 0.5)) d = spe.logprob((X > 0.1) | (Y < 0.5)) e = spe.logprob((X > 0.1) | ((Y < 0.5) & ~(X > 0.1))) f = spe.logprob(~(X > 0.1)) g = spe.logprob((Y < 0.5) & ~(X > 0.1)) assert allclose(a, spe.children[0].logprob(X > 0.1)) assert allclose(b, spe.children[1].logprob(Y < 0.5)) # Pr[A and B] = Pr[A] * Pr[B] assert allclose(c, a + b) # Pr[A or B] = Pr[A] + Pr[B] - Pr[AB] assert allclose(d, logdiffexp(logsumexp([a, b]), c)) # Pr[A or B] = Pr[A] + Pr[B & ~A] assert allclose(e, d) # Pr[A and B] = Pr[A] * Pr[B] assert allclose(g, b + f) # Pr[A or (B & ~A)] = Pr[A] + Pr[B & ~A] assert allclose(e, logsumexp([a, b + f])) # (A => B) => Pr[A or B] = Pr[B] # i.e.,k (X > 1) => (X > 0). assert allclose(spe.logprob((X > 0) | (X > 1)), spe.logprob(X > 0)) # Positive probability event. # Pr[A] = 1 - Pr[~A] event = ((0 < X) < 0.5) | ((Y < 0) & (1 < X)) assert allclose(spe.logprob(event), logdiffexp(0, spe.logprob(~event))) # Probability zero event. event = ((0 < X) < 0.5) & ((Y < 0) | (1 < X)) assert isinf_neg(spe.logprob(event)) assert allclose(spe.logprob(~event), 0)
def test_product_condition_basic(): X = Id('X') Y = Id('Y') spe = ProductSPE([X >> norm(loc=0, scale=1), Y >> gamma(a=1)]) # Condition on (X > 0) and ((X > 0) | (Y < 0)) # where the second clause reduces to first as Y < 0 # has probability zero. for event in [(X > 0), (X > 0) | (Y < 0)]: dX = spe.condition(event) assert isinstance(dX, ProductSPE) assert dX.children[0].symbol == Id('X') assert dX.children[0].conditioned assert dX.children[0].support == Interval.open(0, oo) assert dX.children[1].symbol == Id('Y') assert not dX.children[1].conditioned assert dX.children[1].Fl == 0 assert dX.children[1].Fu == 1 # Condition on (Y < 0.5) dY = spe.condition(Y < 0.5) assert isinstance(dY, ProductSPE) assert dY.children[0].symbol == Id('X') assert not dY.children[0].conditioned assert dY.children[1].symbol == Id('Y') assert dY.children[1].conditioned assert dY.children[1].support == Interval.Ropen(0, 0.5) # Condition on (X > 0) & (Y < 0.5) dXY_and = spe.condition((X > 0) & (Y < 0.5)) assert isinstance(dXY_and, ProductSPE) assert dXY_and.children[0].symbol == Id('X') assert dXY_and.children[0].conditioned assert dXY_and.children[0].support == Interval.open(0, oo) assert dXY_and.children[1].symbol == Id('Y') assert dXY_and.children[1].conditioned assert dXY_and.children[1].support == Interval.Ropen(0, 0.5) # Condition on (X > 0) | (Y < 0.5) event = (X > 0) | (Y < 0.5) dXY_or = spe.condition((X > 0) | (Y < 0.5)) assert isinstance(dXY_or, SumSPE) assert all(isinstance(d, ProductSPE) for d in dXY_or.children) assert allclose(dXY_or.logprob(X > 0), dXY_or.weights[0]) samples = dXY_or.sample(100, prng=numpy.random.RandomState(1)) assert all(event.evaluate(sample) for sample in samples) # Condition on a disjoint union with one term in second clause. dXY_disjoint_one = spe.condition((X > 0) & (Y < 0.5) | (X <= 0)) assert isinstance(dXY_disjoint_one, SumSPE) component_0 = dXY_disjoint_one.children[0] assert component_0.children[0].symbol == Id('X') assert component_0.children[0].conditioned assert component_0.children[0].support == Interval.open(0, oo) assert component_0.children[1].symbol == Id('Y') assert component_0.children[1].conditioned assert component_0.children[1].support == Interval.Ropen(0, 0.5) component_1 = dXY_disjoint_one.children[1] assert component_1.children[0].symbol == Id('X') assert component_1.children[0].conditioned assert component_1.children[0].support == Interval(-oo, 0) assert component_1.children[1].symbol == Id('Y') assert not component_1.children[1].conditioned # Condition on a disjoint union with two terms in each clause dXY_disjoint_two = spe.condition((X > 0) & (Y < 0.5) | ((X <= 0) & ~(Y < 3))) assert isinstance(dXY_disjoint_two, SumSPE) component_0 = dXY_disjoint_two.children[0] assert component_0.children[0].symbol == Id('X') assert component_0.children[0].conditioned assert component_0.children[0].support == Interval.open(0, oo) assert component_0.children[1].symbol == Id('Y') assert component_0.children[1].conditioned assert component_0.children[1].support == Interval.Ropen(0, 0.5) component_1 = dXY_disjoint_two.children[1] assert component_1.children[0].symbol == Id('X') assert component_1.children[0].conditioned assert component_1.children[0].support == Interval(-oo, 0) assert component_1.children[1].symbol == Id('Y') assert component_1.children[1].conditioned assert component_1.children[1].support == Interval(3, oo) # Some various conditioning. spe.condition((X > 0) & (Y < 0.5) | ((X <= 1) | ~(Y < 3))) spe.condition((X > 0) & (Y < 0.5) | ((X <= 1) & (Y < 3)))
from sppl.sets import EmptySet from sppl.transforms import EventFiniteNominal from sppl.transforms import Exp from sppl.transforms import Exponential from sppl.transforms import Id from sppl.transforms import Log from sppl.transforms import Logarithm X = Id('X') Y = Id('Y') spes = [ X >> norm(loc=0, scale=1), X >> poisson(mu=7), Y >> choice({'a': 0.5, 'b': 0.5}), (X >> norm(loc=0, scale=1)) & (Y >> gamma(a=1)), 0.2*(X >> norm(loc=0, scale=1)) | 0.8*(X >> gamma(a=1)), ((X >> norm(loc=0, scale=1)) & (Y >> gamma(a=1))).constrain({Y:1}), ] @pytest.mark.parametrize('spe', spes) def test_serialize_equal(spe): metadata = spe_to_dict(spe) spe_json_encoded = json.dumps(metadata) spe_json_decoded = json.loads(spe_json_encoded) spe2 = spe_from_dict(spe_json_decoded) assert spe2 == spe transforms = [ X, X**(1,3), Exponential(X, base=3),