def test_event_containment_union(): assert (X << (Interval(0, 1) | Interval(2, 3))) \ == (((0 <= X) <= 1) | ((2 <= X) <= 3)) assert (X << (FiniteReal(0, 1) | Interval(2, 3))) \ == ((X << {0, 1}) | ((2 <= X) <= 3)) assert (X << FiniteNominal('a', b=True)) \ == EventFiniteNominal(X, FiniteNominal('a', b=True)) assert X << EmptySet == EventFiniteReal(X, EmptySet) # Ordering is not guaranteed. a = X << (Interval(0,1) | (FiniteReal(1.5) | FiniteNominal('a'))) assert len(a.subexprs) == 3 assert EventInterval(X, Interval(0,1)) in a.subexprs assert EventFiniteReal(X, FiniteReal(1.5)) in a.subexprs assert EventFiniteNominal(X, FiniteNominal('a')) in a.subexprs
def test_sum_normal_nominal(): X = Id('X') children = [ X >> norm(loc=0, scale=1), X >> choice({ 'low': Fraction(3, 10), 'high': Fraction(7, 10) }), ] weights = [log(Fraction(4, 7)), log(Fraction(3, 7))] spe = SumSPE(children, weights) assert allclose(spe.logprob(X < 0), log(Fraction(4, 7)) + log(Fraction(1, 2))) assert allclose(spe.logprob(X << {'low'}), log(Fraction(3, 7)) + log(Fraction(3, 10))) # The semantics of ~(X<<{'low'}) are (X << String and X != 'low') assert allclose(spe.logprob(~(X << {'low'})), spe.logprob((X << {'high'}))) assert allclose( spe.logprob((X << FiniteNominal(b=True)) & ~(X << {'low'})), spe.logprob((X << FiniteNominal(b=True)) & (X << {'high'}))) assert isinf_neg(spe.logprob((X < 0) & (X << {'low'}))) assert allclose(spe.logprob((X < 0) | (X << {'low'})), logsumexp([spe.logprob(X < 0), spe.logprob(X << {'low'})])) assert isinf_neg(spe.logprob(X << {'a'})) assert allclose(spe.logprob(~(X << {'a'})), spe.logprob(X << {'low', 'high'})) assert allclose(spe.logprob(X**2 < 9), log(Fraction(4, 7)) + spe.children[0].logprob(X**2 < 9)) spe_condition = spe.condition(X**2 < 9) assert isinstance(spe_condition, ContinuousLeaf) assert spe_condition.support == Interval.open(-3, 3) spe_condition = spe.condition((X**2 < 9) | X << {'low'}) assert isinstance(spe_condition, SumSPE) assert spe_condition.children[0].support == Interval.open(-3, 3) assert spe_condition.children[1].support == FiniteNominal('low', 'high') assert isinf_neg(spe_condition.children[1].logprob(X << {'high'})) assert spe_condition == spe.condition((X**2 < 9) | ~(X << {'high'})) assert allclose(spe.logprob((X < oo) | ~(X << {'1'})), 0)
def test_solver_finite_symbolic(): # Transform can never be symbolic. event = Y << {'a', 'b'} assert event.solve() == FiniteNominal('a', 'b') # Complement the Identity. event = ~(Y << {'a', 'b'}) assert event.solve() == FiniteNominal('a', 'b', b=True) # Transform can never be symbolic. event = Y**2 << {'a', 'b'} assert event.solve() is EmptySet # Complement the Identity. event = ~(Y**2 << {'a', 'b'}) assert event.solve() == FiniteNominal(b=True) # Solve Identity mixed. event = Y << {9, 'a', '7'} assert event.solve() == Union( FiniteReal(9), FiniteNominal('a', '7')) # Solve Transform mixed. event = Y**2 << {9, 'a', 'b'} assert event.solve() == FiniteReal(-3, 3) # Solve a disjunction. event = (Y << {'a', 'b'}) | (Y << {'c'}) assert event.solve() == FiniteNominal('a', 'b', 'c') # Solve a conjunction with intersection. event = (Y << {'a', 'b'}) & (Y << {'b', 'c'}) assert event.solve() == FiniteNominal('b') # Solve a conjunction with no intersection. event = (Y << {'a', 'b'}) & (Y << {'c'}) assert event.solve() is EmptySet # Solve a disjunction with complement. event = (Y << {'a', 'b'}) & ~(Y << {'c'}) assert event.solve() == FiniteNominal('a', 'b') # Solve a disjunction with complement. event = (Y << {'a', 'b'}) | ~(Y << {'c'}) assert event.solve() == FiniteNominal('c', b=True) # Union of interval and symbolic. event = (Y**2 <= 9) | (Y << {'a'}) assert event.solve() == Interval(-3, 3) | FiniteNominal('a') # Union of interval and not symbolic. event = (Y**2 <= 9) | ~(Y << {'a'}) assert event.solve() == Interval(-3, 3) | FiniteNominal('a', b=True) # Intersection of interval and symbolic. event = (Y**2 <= 9) & (Y << {'a'}) assert event.solve() is EmptySet # Intersection of interval and not symbolic. event = (Y**2 <= 9) & ~(Y << {'a'}) assert event.solve() == EmptySet
def test_simple_parse_nominal(): assert isinstance(.7 * choice({'a': .1, 'b': .9}), DistributionMix) a = .3 * bernoulli(p=.1) | .7 * choice({'a': .1, 'b': .9}) spe = a(X) assert isinstance(spe, SumSPE) assert allclose(spe.weights, [log(.3), log(.7)]) assert isinstance(spe.children[0], DiscreteLeaf) assert isinstance(spe.children[1], NominalLeaf) assert spe.children[0].support == Interval(0, 1) assert spe.children[1].support == FiniteNominal('a', 'b')
def test_nominal_distribution(): X = Id('X') spe = X >> choice({ 'a': Fraction(1, 5), 'b': Fraction(1, 5), 'c': Fraction(3, 5), }) assert allclose(spe.logprob(X << {'a'}), log(Fraction(1, 5))) assert allclose(spe.logprob(X << {'b'}), log(Fraction(1, 5))) assert allclose(spe.logprob(X << {'a', 'c'}), log(Fraction(4, 5))) assert allclose(spe.logprob((X << {'a'}) & ~(X << {'b'})), log(Fraction(1, 5))) assert allclose(spe.logprob((X << {'a', 'b'}) & ~(X << {'b'})), log(Fraction(1, 5))) assert spe.logprob((X << {'d'})) == -float('inf') assert spe.logprob((X << ())) == -float('inf') samples = spe.sample(100) assert all(s[X] in spe.support for s in samples) samples = spe.sample_subset([X], 100) assert all(len(s) == 1 and s[X] in spe.support for s in samples) with pytest.raises(Exception): spe.sample_subset(['f'], 100) predicate = lambda X: (X in {'a', 'b'}) or X in {'c'} samples = spe.sample_func(predicate, 100) assert all(samples) predicate = lambda X: (not (X in {'a', 'b'})) and (not (X in {'c'})) samples = spe.sample_func(predicate, 100) assert not any(samples) func = lambda X: 1 if X in {'a'} else None samples = spe.sample_func(func, 100, prng=numpy.random.RandomState(1)) assert sum(1 for s in samples if s == 1) > 12 assert sum(1 for s in samples if s is None) > 70 with pytest.raises(ValueError): spe.sample_func(lambda Y: Y, 100) spe_condition = spe.condition(X << {'a', 'b'}) assert spe_condition.support == FiniteNominal('a', 'b', 'c') assert allclose(spe_condition.logprob(X << {'a'}), -log(2)) assert allclose(spe_condition.logprob(X << {'b'}), -log(2)) assert spe_condition.logprob(X << {'c'}) == -float('inf') assert isinf_neg(spe_condition.logprob(X**2 << {1})) with pytest.raises(ValueError): spe.condition(X << {'python'}) assert spe.condition(~(X << {'python'})) == spe
def test_event_containment_mixed(): assert X << {1, 2, 'a'} \ == (X << {1,2}) | (X << {'a'}) \ == EventOr([ EventFiniteReal(X, {1, 2}), EventFiniteNominal(X, FiniteNominal('a')) ]) assert (~(X << {1, 2, 'a'})) == ~(X << {1,2}) & ~(X << {'a'}) # https://github.com/probcomp/sum-product-dsl/issues/22 # Taking the And of EventBasic does not perform simplifications. assert ~(~(X << {1, 2, 'a'})) == EventOr([ ((1 <= X) & ((X <= 1) | (2 <= X)) & (X <= 2)), X << {'a'} ])
def test_xor(): A = X << {'a'} B = ~(X << {'b'}) assert (A ^ B) \ == ((A & ~B) | (~A & B)) \ == EventFiniteNominal(X, FiniteNominal('a', 'b', b=True))
def test_event_complex_simplification(): # De Morgan's case in EventFinteNominal.__or__. assert ~(X << {'a'}) | ~(X << {'b'}) == EventFiniteNominal(X, FiniteNominal(b=True)) assert ~(X << {'a'}) | ~(X << {'a', 'b'}) == ~(X << {'a'}) assert (X << {'1'}) | ~(X << {'1', '2'}) == ~(X << {'2'})
def test_event_containment_nominal(): assert X << {'a'} == EventFiniteNominal(X, FiniteNominal('a')) assert ~(X << {'a'}) == EventFiniteNominal(X, FiniteNominal('a',b=True)) assert ~(~(X << {'a'})) == X << {'a'}