Skip to content

Commit

Permalink
Add event complement
Browse files Browse the repository at this point in the history
  • Loading branch information
samirelanduk committed Mar 16, 2018
1 parent 22f6790 commit 261abd8
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 24 deletions.
27 changes: 24 additions & 3 deletions inferi/probability.py
Expand Up @@ -44,6 +44,17 @@ def __and__(self, event):
return Event(*(self._simple_events & event._simple_events))


def complement(self):
"""Returns the complement of the event - the event that this event does
not happen.
:rtype: ``Event``"""

return Event(
*(self.sample_space().simple_events() - self._simple_events)
)


def simple_events(self):
"""The set of simple events in this event.
Expand All @@ -68,6 +79,15 @@ def probability(self):
return sum(event._probability for event in self._simple_events)


def sample_space(self):
"""The sample space that event is part of.
:rtype: ``SampleSpace``"""

for event in self._simple_events:
return event._sample_space


def mutually_exclusive_with(self, event):
"""Looks at some other event and checks if this event is mutually
exclusive with the other event. That is, whether it is impossible for
Expand Down Expand Up @@ -105,13 +125,14 @@ class SimpleEvent(Event):
:raises TypeError: if probability isn't numeric.
:raises ValueError: if probability is not between 0 and 1."""

def __init__(self, outcome, probability):
def __init__(self, outcome, probability, space):
self._outcome = self._name = outcome
if not isinstance(probability, (int, float)):
raise TypeError("probability {} is not numeric".format(probability))
if not 0 <= probability <= 1:
raise ValueError("probability {} is invalid".format(probability))
self._probability = probability
self._sample_space = space
self._simple_events = set((self,))


Expand Down Expand Up @@ -145,7 +166,7 @@ def __init__(self, *outcomes, p=None):
p[e] = p_per_event
if round(sum(p.values()), 8) != 1:
raise ValueError(f"Probabilities do not add up to 1: {p}")
self._simple_events = set([SimpleEvent(e, p[e]) for e in p])
self._simple_events = set([SimpleEvent(e, p[e], self) for e in p])


def __repr__(self):
Expand All @@ -170,7 +191,7 @@ def simple_events(self):
def outcomes(self, p=False):
"""The set of outcomes that the sample space's simple events can
produce.
:param bool p: if ``True``, the results will be returned as a dict with
probabilities associated.
Expand Down
3 changes: 3 additions & 0 deletions tests/integration/test_probability.py
Expand Up @@ -87,6 +87,9 @@ def test_events(self):
self.assertEqual(combined.outcomes(), {2, 4, 5, 6})
combined = event1 & event2
self.assertEqual(combined.outcomes(), {2})
odd = event2.complement()
self.assertEqual(odd.probability(), 0.5)
self.assertEqual(odd.outcomes(), {1, 3, 5})

# Unfair die
sample_space = inferi.SampleSpace(1, 2, 3, 4, 5, 6, p={4: 0.3})
Expand Down
22 changes: 22 additions & 0 deletions tests/unit/test_events.py
Expand Up @@ -11,6 +11,7 @@ def setUp(self):
event._simple_events = set([event])
event._probability = 5
event._outcome = i
event._sample_space = "SPACE"
for i, event in enumerate(self.events):
event._simple_events = set(self.simple_events[i * 3: (i + 1) * 3])

Expand Down Expand Up @@ -120,6 +121,19 @@ def test_and_needs_event(self):



class EventComplementTests(EventTest):

@patch("inferi.probability.Event.sample_space")
def test_can_get_event_complement(self, mock_sample_space):
space = Mock()
mock_sample_space.return_value = space
space.simple_events.return_value = set(self.simple_events)
event = Event(*self.simple_events[:3])
not_ = event.complement()
self.assertEqual(not_._simple_events, set(self.simple_events[3:]))



class EventSimpleEvents(EventTest):

def test_can_get_event_simple_events(self):
Expand All @@ -145,6 +159,14 @@ def test_can_get_event_probability(self):



class SimpleEventSampleSpaceTests(EventTest):

def test_event_sample_space(self):
event = Event(*self.simple_events)
self.assertEqual(event.sample_space(), "SPACE")



class EventOutcomestests(EventTest):

def test_can_get_outcomes(self):
Expand Down
22 changes: 11 additions & 11 deletions tests/unit/test_sample_space.py
Expand Up @@ -27,8 +27,8 @@ class SampleSpaceCreationTests(SampleSpaceTest):
def test_can_create_sample_space_from_outcomes(self):
space = SampleSpace("H", "T")
self.assertEqual(space._simple_events, set(self.simple_events[:2]))
self.mock_simple_event.assert_any_call("H", 0.5)
self.mock_simple_event.assert_any_call("T", 0.5)
self.mock_simple_event.assert_any_call("H", 0.5, space)
self.mock_simple_event.assert_any_call("T", 0.5, space)


def test_sample_space_needs_outcomes(self):
Expand All @@ -39,30 +39,30 @@ def test_sample_space_needs_outcomes(self):
def test_can_create_sample_space_with_probabilities(self):
space = SampleSpace("H", "T", p={"H": 0.2, "T": 0.8})
self.assertEqual(space._simple_events, set(self.simple_events[:2]))
self.mock_simple_event.assert_any_call("H", 0.2)
self.mock_simple_event.assert_any_call("T", 0.8)
self.mock_simple_event.assert_any_call("H", 0.2, space)
self.mock_simple_event.assert_any_call("T", 0.8, space)


def test_can_create_sample_space_with_missing_probabilities(self):
space = SampleSpace("H", "T", p={"H": 0.2})
self.assertEqual(space._simple_events, set(self.simple_events[:2]))
self.mock_simple_event.assert_any_call("H", 0.2)
self.mock_simple_event.assert_any_call("T", 0.8)
self.mock_simple_event.assert_any_call("H", 0.2, space)
self.mock_simple_event.assert_any_call("T", 0.8, space)


def test_can_create_sample_space_with_extra_probabilities(self):
space = SampleSpace("H", "T", p={"H": 0.2, "S": 0.45})
self.assertEqual(space._simple_events, set(self.simple_events))
self.mock_simple_event.assert_any_call("H", 0.2)
self.mock_simple_event.assert_any_call("T", 0.35)
self.mock_simple_event.assert_any_call("S", 0.45)
self.mock_simple_event.assert_any_call("H", 0.2, space)
self.mock_simple_event.assert_any_call("T", 0.35, space)
self.mock_simple_event.assert_any_call("S", 0.45, space)


def test_can_create_sample_space_with_only_probabilities(self):
space = SampleSpace(p={"H": 0.2, "S": 0.8})
self.assertEqual(space._simple_events, set(self.simple_events[:2]))
self.mock_simple_event.assert_any_call("H", 0.2)
self.mock_simple_event.assert_any_call("S", 0.8)
self.mock_simple_event.assert_any_call("H", 0.2, space)
self.mock_simple_event.assert_any_call("S", 0.8, space)


def test_probabilities_cant_add_up_to_more_than_1(self):
Expand Down
28 changes: 18 additions & 10 deletions tests/unit/test_simple_events.py
@@ -1,41 +1,49 @@
from unittest import TestCase
from unittest.mock import Mock, patch
from inferi.probability import SimpleEvent, Event
from inferi.probability import SimpleEvent, Event, SampleSpace

class SimpleEventCreationTests(TestCase):
class SimpleEventTest(TestCase):

def setUp(self):
self.space = Mock(SampleSpace)



class SimpleEventCreationTests(SimpleEventTest):

def test_can_create_simple_event(self):
e = SimpleEvent("H", 0.5)
e = SimpleEvent("H", 0.5, self.space)
self.assertIsInstance(e, Event)
self.assertEqual(e._outcome, "H")
self.assertEqual(e._name, "H")
self.assertEqual(e._probability, 0.5)
self.assertEqual(e._sample_space, self.space)
self.assertEqual(e._simple_events, set([e]))


def test_probability_must_be_numeric(self):
with self.assertRaises(TypeError):
SimpleEvent("H", "jj")
SimpleEvent("H", "jj", self.space)


def test_probability_must_be_valid(self):
with self.assertRaises(ValueError):
SimpleEvent("H", -0.1)
SimpleEvent("H", -0.1, self.space)
with self.assertRaises(ValueError):
SimpleEvent("H", 1.1)
SimpleEvent("H", 1.1, self.space)



class SimpleEventReprTests(TestCase):
class SimpleEventReprTests(SimpleEventTest):

def test_simple_event_repr(self):
e = SimpleEvent("H", 0.5)
e = SimpleEvent("H", 0.5, self.space)
self.assertEqual(str(e), "<SimpleEvent: H>")



class SimpleEventOutcomeTests(TestCase):
class SimpleEventOutcomeTests(SimpleEventTest):

def test_simple_event_outcome(self):
e = SimpleEvent("H", 0.5)
e = SimpleEvent("H", 0.5, self.space)
self.assertEqual(e.outcome(), e._outcome)

0 comments on commit 261abd8

Please sign in to comment.