|
1 | 1 | """Bound a real number to be between two values.""" |
| 2 | +from math import isfinite |
| 3 | +from typing import Union |
| 4 | + |
2 | 5 | from gemd.entity.bounds.base_bounds import BaseBounds |
3 | 6 | import gemd.units as units |
4 | 7 |
|
5 | | -from typing import Union |
6 | | - |
7 | 8 |
|
8 | 9 | class RealBounds(BaseBounds, typ="real_bounds"): |
9 | | - """ |
10 | | - Bounded subset of the real numbers, parameterized by a lower and upper bound. |
11 | | -
|
12 | | - Parameters |
13 | | - ---------- |
14 | | - lower_bound: float |
15 | | - Lower endpoint. |
16 | | - upper_bound: float |
17 | | - Upper endpoint. |
18 | | - default_units: str |
19 | | - A string describing the units. Units must be present and parseable by Pint. |
20 | | - An empty string can be used for the units of a dimensionless quantity. |
| 10 | + """Bounded subset of the real numbers, parameterized by a lower and upper bound.""" |
21 | 11 |
|
22 | | - """ |
| 12 | + def __init__(self, lower_bound: float, upper_bound: float, default_units: str): |
| 13 | + self._default_units = None |
| 14 | + self._lower_bound = None |
| 15 | + self._upper_bound = None |
23 | 16 |
|
24 | | - def __init__(self, lower_bound=None, upper_bound=None, default_units=None): |
| 17 | + self.default_units = default_units |
25 | 18 | self.lower_bound = lower_bound |
26 | 19 | self.upper_bound = upper_bound |
27 | 20 |
|
28 | | - self._default_units = None |
29 | | - self.default_units = default_units |
30 | | - |
31 | | - if self.lower_bound is None or abs(self.lower_bound) >= float("inf"): |
32 | | - raise ValueError("Lower bound must be given and finite: {}".format(self.lower_bound)) |
33 | | - |
34 | | - if self.upper_bound is None or abs(self.upper_bound) >= float("inf"): |
35 | | - raise ValueError("Upper bound must be given and finite") |
| 21 | + @property |
| 22 | + def lower_bound(self) -> float: |
| 23 | + """The lower endpoint of the permitted range.""" |
| 24 | + return self._lower_bound |
| 25 | + |
| 26 | + @lower_bound.setter |
| 27 | + def lower_bound(self, value: float): |
| 28 | + """Set the lower endpoint of the permitted range.""" |
| 29 | + if value is None or not isfinite(value): |
| 30 | + raise ValueError(f"Lower bound must be given and finite: {value}") |
| 31 | + if self.upper_bound is not None and value > self.upper_bound: |
| 32 | + raise ValueError(f"Upper bound ({self.upper_bound}) must be " |
| 33 | + f"greater than or equal to lower bound ({value})") |
| 34 | + self._lower_bound = float(value) |
36 | 35 |
|
37 | | - if self.upper_bound < self.lower_bound: |
38 | | - raise ValueError("Upper bound must be greater than or equal to lower bound") |
| 36 | + @property |
| 37 | + def upper_bound(self) -> float: |
| 38 | + """The upper endpoint of the permitted range.""" |
| 39 | + return self._upper_bound |
| 40 | + |
| 41 | + @upper_bound.setter |
| 42 | + def upper_bound(self, value: float): |
| 43 | + """Set the upper endpoint of the permitted range.""" |
| 44 | + if value is None or not isfinite(value): |
| 45 | + raise ValueError(f"Upper bound must be given and finite: {value}") |
| 46 | + if self.lower_bound is not None and value < self.lower_bound: |
| 47 | + raise ValueError(f"Upper bound ({value}) must be " |
| 48 | + f"greater than or equal to lower bound ({self.lower_bound})") |
| 49 | + self._upper_bound = float(value) |
39 | 50 |
|
40 | 51 | @property |
41 | | - def default_units(self): |
42 | | - """Get default units.""" |
| 52 | + def default_units(self) -> str: |
| 53 | + """ |
| 54 | + A string describing the units. |
| 55 | +
|
| 56 | + Units must be present and parseable by Pint. |
| 57 | + An empty string can be used for the units of a dimensionless quantity. |
| 58 | + """ |
43 | 59 | return self._default_units |
44 | 60 |
|
45 | 61 | @default_units.setter |
46 | | - def default_units(self, default_units): |
| 62 | + def default_units(self, default_units: str): |
| 63 | + """Set the string describing the units.""" |
47 | 64 | if default_units is None: |
48 | 65 | raise ValueError("Real bounds must have units. " |
49 | 66 | "Use an empty string for a dimensionless quantity.") |
50 | | - self._default_units = units.parse_units(default_units) |
| 67 | + self._default_units = units.parse_units(default_units, return_unit=False) |
51 | 68 |
|
52 | 69 | def contains(self, bounds: Union[BaseBounds, "BaseValue"]) -> bool: # noqa: F821 |
53 | 70 | """ |
|
0 commit comments