diff --git a/qutil/domains.py b/qutil/domains.py index f72f9eb438e6184d5779c229f10b94847c2fb6b6..1467714caf126465e05f9c5b28ce4790e7faff32 100644 --- a/qutil/domains.py +++ b/qutil/domains.py @@ -183,7 +183,31 @@ class Interval(Domain[_RealT], Bounded[_RealT], ABC): @dataclass class DiscreteInterval(Interval[_IntegralT], Bounded[_IntegralT]): - """A discrete interval that only allows integral values.""" + """A discrete interval that only allows integral values. + + Examples + -------- + >>> x = 7 + >>> domain = DiscreteInterval(lower=-4.3, upper={5, lambda: x}) + >>> domain + DiscreteInterval(lower=-4, upper=5) + >>> 4 in domain + True + >>> 4.0 in domain # only ints + False + >>> domain.next_closest(1.7) + 2 + >>> domain.next_smallest(1.7) + 1 + + Bounds can be dynamic and are lazily evaluated: + + >>> x = 2 + >>> 4 in domain + False + >>> domain + DiscreteInterval(lower=-4, upper=2) + """ lower: _BoundT[_IntegralT] = Bound[_IntegralT](default=-inf, integral=True) upper: _BoundT[_IntegralT] = Bound[_IntegralT](default=+inf, integral=True) @@ -217,7 +241,25 @@ class DiscreteInterval(Interval[_IntegralT], Bounded[_IntegralT]): class ContinuousInterval(Interval[_RealT]): - """A continuous interval that allows any value within its bounds.""" + """A continuous interval that allows any value within its bounds. + + Examples + -------- + >>> domain = ContinuousInterval(-3.1, 1.7) + >>> 0 in domain + True + >>> 1.7 in domain + True + >>> 10 in domain + False + >>> -10 < domain + True + >>> domain.next_smallest(0.91) + 0.91 + >>> domain.next_largest(6) + 1.7 + + """ def next_closest(self, value: _RealT) -> _RealT: if value < self: @@ -234,8 +276,27 @@ class ContinuousInterval(Interval[_RealT]): @dataclass - """A rational interval that only allows integral denominators.""" class ReciprocalDiscreteInterval(DiscreteInterval[_IntegralT], Reciprocal[_RealT]): + """A rational interval that only allows integral denominators. + + Examples + -------- + The interval [2.3/10, 2.3/9, ..., 2.3/1]: + >>> domain = ReciprocalDiscreteInterval(lower=1, upper=10, numerator=2.3) + >>> 2.3/8 in domain + True + >>> 2.4/8 in domain + False + >>> 2.3/11 < domain + True + >>> domain.next_smallest(0.3) # 2.3/8 + 0.2875 + >>> domain.next_largest(0.3) # 2.3/7 + 0.32857142857142857 + >>> domain.next_closest(0.3) # 2.3/8 + 0.2875 + + """ lower: _BoundT[_IntegralT] = ReciprocalBound[_IntegralT](default=1, integral=True) upper: _BoundT[_IntegralT] = ReciprocalBound[_IntegralT](default=inf, integral=True) numerator: _RealT = 1.0 @@ -296,7 +357,23 @@ class ReciprocalDiscreteInterval(DiscreteInterval[_IntegralT], Reciprocal[_RealT @dataclass class BoundedSet(frozenset, Domain[_RealT], Bounded[_RealT]): - """A finite set of numbers with lower and upper bounds.""" + """A finite set of numbers with lower and upper bounds. + + Examples + -------- + >>> domain = BoundedSet([1, 1.0, 3, -2.3, 7], lower=-4, upper=5) + >>> domain # behaves like frozenset + BoundedSet({1, 3, -2.3, 7}, lower=-4, upper=5) + >>> 3 in domain + True + >>> -1.7 in domain + False + >>> 7 in domain # out of bounds + False + >>> domain.next_largest(2.7) + 3 + + """ iterable: InitVar[Iterable[_RealT]] = ... lower: _BoundT[_RealT] = Bound[_RealT](default=-inf, integral=False) upper: _BoundT[_RealT] = Bound[_RealT](default=+inf, integral=False)