Skip to content

Commit

Permalink
First completely working version of random events with the better arc…
Browse files Browse the repository at this point in the history
…hitecture and without the bug in make_disjoint.
  • Loading branch information
tomsch420 committed Jun 3, 2024
1 parent f65ea4a commit b8284ac
Show file tree
Hide file tree
Showing 17 changed files with 11,413 additions and 1,814 deletions.
8 changes: 3 additions & 5 deletions doc/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
-r ../requirements.txt
-r ../requirements-dev.txt
sphinx_rtd_theme
sphinx-autoapi
sphinxcontrib-bibtex
nbsphinx
sphinx-gallery
myst-nb
random-events
pillow
jupyterquiz==2.7.0a4
sphinx-gallery
random-events
98 changes: 49 additions & 49 deletions examples/example.ipynb

Large diffs are not rendered by default.

2,713 changes: 1,234 additions & 1,479 deletions examples/logo_generation.ipynb

Large diffs are not rendered by default.

10,079 changes: 9,925 additions & 154 deletions examples/product_spaces.ipynb

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions examples/self_assessment.ipynb

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-r requirements.txt

jupyter
pillow
jupyterquiz==2.7.0a4
6 changes: 3 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
numpy~=1.26.1
plotly~=5.20.0
numpy
plotly
typing_extensions
sortedcontainers~=2.4.0
sortedcontainers
2 changes: 1 addition & 1 deletion src/random_events/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '2.0.10'
__version__ = '3.0.1'
23 changes: 16 additions & 7 deletions src/random_events/interval.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from __future__ import annotations
import enum
from dataclasses import dataclass
from typing import Dict, Any
Expand All @@ -6,6 +7,7 @@
from typing_extensions import Self

from . import sigma_algebra
from .sigma_algebra import AbstractCompositeSet


class Bound(enum.Enum):
Expand Down Expand Up @@ -62,6 +64,9 @@ class SimpleInterval(sigma_algebra.AbstractSimpleSet):
The bound type of the upper bound.
"""

def as_composite_set(self) -> Interval:
return Interval(self)

def __lt__(self, other: Self):
if self.lower == other.lower:
return self.upper < other.upper
Expand Down Expand Up @@ -165,7 +170,7 @@ def simplify(self) -> Self:
return self

# initialize the result
result = Interval([self.simple_sets[0]])
result = self.simple_sets[0].as_composite_set()

# iterate over the simple sets
for current_simple_interval in self.simple_sets[1:]:
Expand Down Expand Up @@ -195,14 +200,18 @@ def complement_if_empty(self) -> Self:
return Interval([SimpleInterval(float('-inf'), float('inf'), Bound.OPEN, Bound.OPEN)])


# Type definitions



def open(left: float, right: float) -> Interval:
"""
Creates an open interval.
:param left: The left bound of the interval.
:param right: The right bound of the interval.
:return: The open interval.
"""
return Interval([SimpleInterval(left, right, Bound.OPEN, Bound.OPEN)])
return SimpleInterval(left, right, Bound.OPEN, Bound.OPEN).as_composite_set()


def closed(left: float, right: float) -> Interval:
Expand All @@ -212,7 +221,7 @@ def closed(left: float, right: float) -> Interval:
:param right: The right bound of the interval.
:return: The closed interval.
"""
return Interval([SimpleInterval(left, right, Bound.CLOSED, Bound.CLOSED)])
return SimpleInterval(left, right, Bound.CLOSED, Bound.CLOSED).as_composite_set()


def open_closed(left: float, right: float) -> Interval:
Expand All @@ -222,7 +231,7 @@ def open_closed(left: float, right: float) -> Interval:
:param right: The right bound of the interval.
:return: The open-closed interval.
"""
return Interval([SimpleInterval(left, right, Bound.OPEN, Bound.CLOSED)])
return SimpleInterval(left, right, Bound.OPEN, Bound.CLOSED).as_composite_set()


def closed_open(left: float, right: float) -> Interval:
Expand All @@ -232,7 +241,7 @@ def closed_open(left: float, right: float) -> Interval:
:param right: The right bound of the interval.
:return: The closed-open interval.
"""
return Interval([SimpleInterval(left, right, Bound.CLOSED, Bound.OPEN)])
return SimpleInterval(left, right, Bound.CLOSED, Bound.OPEN).as_composite_set()


def singleton(value: float) -> Interval:
Expand All @@ -241,12 +250,12 @@ def singleton(value: float) -> Interval:
:param value: The value of the interval.
:return: The singleton interval.
"""
return Interval([SimpleInterval(value, value, Bound.CLOSED, Bound.CLOSED)])
return SimpleInterval(value, value, Bound.CLOSED, Bound.CLOSED).as_composite_set()


def reals() -> Interval:
"""
Creates the set of real numbers.
:return: The set of real numbers.
"""
return Interval([SimpleInterval(float('-inf'), float('inf'), Bound.OPEN, Bound.OPEN)])
return SimpleInterval(float('-inf'), float('inf'), Bound.OPEN, Bound.OPEN).as_composite_set()
73 changes: 56 additions & 17 deletions src/random_events/product_algebra.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
from __future__ import annotations
import numpy as np
from sortedcontainers import SortedDict, SortedKeysView, SortedValuesView
from typing_extensions import List
from typing_extensions import List, TYPE_CHECKING
import plotly.graph_objects as go

from .sigma_algebra import *
from .variable import *
from .variable import Variable


class VariableMap(SortedDict[Variable, Any]):
# Type definitions
if TYPE_CHECKING:
VariableMapSuperClassType = SortedDict[Variable, Any]
else:
VariableMapSuperClassType = SortedDict


class VariableMap(VariableMapSuperClassType):
"""
A map of variables to values.
Expand Down Expand Up @@ -48,15 +56,24 @@ def __copy__(self):
return self.__class__({variable: value for variable, value in self.items()})


class SimpleEvent(AbstractSimpleSet, VariableMap[Variable, AbstractCompositeSet]):
class SimpleEvent(AbstractSimpleSet, VariableMap):
"""
A simple event is a set of assignments of variables to values.
A simple event is logically equivalent to a conjunction of assignments.
"""

def __init__(self, *args, **kwargs):
VariableMap.__init__(self, *args, **kwargs)
for key, value in self.items():
self[key] = value


def as_composite_set(self) -> Event:
return Event(self)

@property
def assignments(self) -> SortedValuesView[AbstractCompositeSet]:
def assignments(self) -> SortedValuesView:
return self.values()

def intersection_with(self, other: Self) -> Self:
Expand All @@ -72,7 +89,7 @@ def intersection_with(self, other: Self) -> Self:

return result

def complement(self) -> SortedSet[Self]:
def complement(self) -> SimpleSetContainer:

# initialize result
result = SortedSet()
Expand Down Expand Up @@ -114,7 +131,7 @@ def complement(self) -> SortedSet[Self]:
return result

def is_empty(self) -> bool:
if len(self) == 0:
if len(self.keys()) == 0:
return True

for assignment in self.values():
Expand All @@ -132,11 +149,22 @@ def contains(self, item: Tuple) -> bool:
def __hash__(self):
return hash(tuple(self.items()))

def __setitem__(self, key: Variable, value: Union[AbstractSimpleSet, AbstractCompositeSet]):
if isinstance(value, AbstractSimpleSet):
super().__setitem__(key, value.as_composite_set())
elif isinstance(value, AbstractCompositeSet):
super().__setitem__(key, value)
else:
raise TypeError(f"Value must be a SimpleSet or CompositeSet, got {type(value)} instead.")

def __lt__(self, other: Self):
for variable, assignment in self.items():
if assignment < other[variable]:
return True
return False
if len(self.variables) < len(other.variables):
return True
for variable in self.variables:
if self[variable] == other[variable]:
continue
else:
return self[variable] < other[variable]

def non_empty_to_string(self) -> str:
return "{" + ", ".join(f"{variable.name} = {assignment}" for variable, assignment in self.items()) + "}"
Expand Down Expand Up @@ -239,7 +267,7 @@ def plotly_layout(self) -> Dict:

return result

def fill_missing_variables(self, variables: SortedSet[Variable]):
def fill_missing_variables(self, variables: VariableSet):
"""
Fill this with the variables that are not in self but in `variables`.
The variables are mapped to their domain.
Expand All @@ -260,14 +288,14 @@ class Event(AbstractCompositeSet):
"""

simple_sets: SortedSet[SimpleEvent]
simple_sets: SimpleEventContainer

def __init__(self, simple_sets: Iterable[SimpleEvent]):
super().__init__(simple_sets)
def __init__(self, *simple_sets):
super().__init__(*simple_sets)
self.fill_missing_variables()

@property
def all_variables(self) -> SortedSet[Variable]:
def all_variables(self) -> VariableSet:
result = SortedSet()
return result.union(*[SortedSet(simple_set.variables) for simple_set in self.simple_sets])

Expand Down Expand Up @@ -329,14 +357,14 @@ def simplify_once(self) -> Tuple[Self, bool]:

# create a new event with the simplified event and all other events
result = Event(
[simplified_event] + [event for event in self.simple_sets if event != event_a and event != event_b])
*([simplified_event] + [event for event in self.simple_sets if event != event_a and event != event_b]))
return result, True

# if nothing happened, return the original event and False
return self, False

def new_empty_set(self) -> Self:
return Event([])
return Event()

def complement_if_empty(self) -> Self:
raise NotImplementedError("Complement of an empty Event is not yet supported.")
Expand Down Expand Up @@ -375,3 +403,14 @@ def add_simple_set(self, simple_set: AbstractSimpleSet):
"""
super().add_simple_set(simple_set)
self.fill_missing_variables()


# Type definitions
if TYPE_CHECKING:
SimpleEventContainer = SortedSet[SimpleEvent]
EventContainer = SortedSet[Event]
VariableSet = SortedSet[Variable]
else:
SimpleEventContainer = SortedSet
EventContainer = SortedSet
VariableSet = SortedSet
33 changes: 22 additions & 11 deletions src/random_events/set.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
from __future__ import annotations
import enum
from abc import abstractmethod
from typing import Dict, Any

from sortedcontainers import SortedSet
from typing_extensions import Self
from typing_extensions import Self, TYPE_CHECKING, Dict, Any

from . import sigma_algebra
from .sigma_algebra import *


class SetElement(sigma_algebra.AbstractSimpleSet, enum.Enum):
class SetElement(AbstractSimpleSet, enum.Enum):
"""
Base class for enums that are used as elements in a set.
Expand All @@ -18,7 +17,7 @@ class SetElement(sigma_algebra.AbstractSimpleSet, enum.Enum):
@property
@abstractmethod
def EMPTY_SET(self):
raise NotImplementedError
raise NotImplementedError("The EMPTY_SET attribute has to be defined.")

@property
def all_elements(self):
Expand All @@ -30,15 +29,15 @@ def intersection_with(self, other: Self) -> Self:
else:
return self.all_elements.EMPTY_SET

def complement(self) -> SortedSet[Self]:
def complement(self) -> SimpleSetContainer:
result = SortedSet()
for element in self.all_elements:
if element != self and element != self.all_elements.EMPTY_SET:
result.add(element)
return result

def is_empty(self) -> bool:
return self is self.all_elements.EMPTY_SET
return self == self.EMPTY_SET

def contains(self, item: Self) -> bool:
return self == item
Expand All @@ -59,10 +58,12 @@ def to_json(self) -> Dict[str, Any]:
def _from_json(cls, data: Dict[str, Any]) -> Self:
return cls(data["value"])

def as_composite_set(self) -> AbstractCompositeSet:
return Set(self)

class Set(sigma_algebra.AbstractCompositeSet):

simple_sets: SortedSet[SetElement]
class Set(AbstractCompositeSet):
simple_sets: SetElementContainer

def complement_if_empty(self) -> Self:
raise NotImplementedError("I don't know how to do this yet.")
Expand All @@ -71,4 +72,14 @@ def simplify(self) -> Self:
return self

def new_empty_set(self) -> Self:
return Set([])
return Set()

def make_disjoint(self) -> Self:
return self


# Type definitions
if TYPE_CHECKING:
SetElementContainer = SortedSet[SetElement]
else:
SetElementContainer = SortedSet
Loading

0 comments on commit b8284ac

Please sign in to comment.