Source code for evidencelib.proposition

"""Symbolic propositions used by DST and DSmT frames."""

from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING, FrozenSet

if TYPE_CHECKING:
    from evidencelib.frame import Frame


[docs] @dataclass(frozen=True) class Proposition: """A canonical proposition represented by possible Venn regions. Users normally create propositions through a :class:`evidencelib.Frame` and combine them with ``|`` for union and ``&`` for intersection. """ frame: "Frame" regions: FrozenSet[int] def __post_init__(self) -> None: normalized = self.frame._normalize_regions(self.regions) object.__setattr__(self, "regions", frozenset(normalized)) def __or__(self, other: "Proposition") -> "Proposition": self._check_same_frame(other) return Proposition(self.frame, self.regions | other.regions) def __and__(self, other: "Proposition") -> "Proposition": self._check_same_frame(other) return Proposition(self.frame, self.regions & other.regions) def __le__(self, other: "Proposition") -> bool: self._check_same_frame(other) return self.regions <= other.regions def __lt__(self, other: "Proposition") -> bool: return self <= other and self.regions != other.regions def __bool__(self) -> bool: return bool(self.regions) def __str__(self) -> str: return self.frame.format(self) def __format__(self, format_spec: str) -> str: return format(str(self), format_spec) def __repr__(self) -> str: return f"Proposition({str(self)!r})" @property def is_empty(self) -> bool: return not self.regions @property def cardinality(self) -> int: """DSm cardinality: number of non-empty model regions.""" return len(self.regions)
[docs] def intersects(self, other: "Proposition") -> bool: self._check_same_frame(other) return bool(self.regions & other.regions)
[docs] def union_atoms(self) -> "Proposition": """Return the disjunction of singleton hypotheses involved in this proposition.""" mask = 0 for region in self.regions: mask |= region if mask == 0: return self.frame.empty return Proposition(self.frame, frozenset(r for r in self.frame._universe if r & mask))
def _check_same_frame(self, other: "Proposition") -> None: if not isinstance(other, Proposition) or other.frame is not self.frame: raise ValueError("Propositions must belong to the same frame.")