Source code for gerrychain.constraints.bounds

from collections.abc import Callable, Iterable
from typing import Generic, ParamSpec

from .._deprecated import deprecated_parameters, deprecated_property
from ..partition import Partition

P = ParamSpec("P")


def _fn_name(value_fn: object) -> str:
    return getattr(value_fn, "__name__", type(value_fn).__name__)


[docs] class Bounds(Generic[P]): """ Wrapper for numeric-validators to enforce upper and lower limits. This class is meant to be called as a function after instantiation; its return is ``True`` if the numeric validator is within set limits, and ``False`` otherwise. """ @deprecated_parameters(renamed={"func": "value_fn"}) def __init__(self, value_fn: Callable[P, Iterable[float]], bounds: tuple[float, float]) -> None: """Initialize a Bounds instance. This initializer sets up `Bounds` with the provided arguments and validates required state. Args: value_fn (Callable): Numeric validator function. Should return an iterable of values. bounds (tuple[float, float]): Tuple of (lower, upper) numeric bounds. """ self.value_fn = value_fn self.bounds = bounds func = deprecated_property("Bounds.func", "value_fn", writable=True) def __call__(self, *args: P.args, **kwargs: P.kwargs) -> bool: lower, upper = self.bounds values = self.value_fn(*args, **kwargs) return lower <= min(values) and max(values) <= upper @property def __name__(self) -> str: return f"Bounds({_fn_name(self.value_fn)},{self.bounds})" def __repr__(self) -> str: return f"<{self.__name__}>"
[docs] class UpperBound(Generic[P]): """ Wrapper for numeric-validators to enforce upper limits. This class is meant to be called as a function after instantiation; its return is ``True`` if the numeric validator is within a set upper limit, and ``False`` otherwise. """ @deprecated_parameters(renamed={"func": "value_fn"}) def __init__(self, value_fn: Callable[P, float], bound: float) -> None: """Initialize a UpperBound instance. This initializer sets up `UpperBound` with the provided arguments and validates required state. Args: value_fn (Callable): Numeric validator function. Should return a comparable value. bound (float): Comparable upper bound. """ self.value_fn = value_fn self.bound = bound func = deprecated_property("UpperBound.func", "value_fn", writable=True) def __call__(self, *args: P.args, **kwargs: P.kwargs) -> bool: return self.value_fn(*args, **kwargs) <= self.bound @property def __name__(self) -> str: return f"UpperBound({_fn_name(self.value_fn)} >= {self.bound})" def __repr__(self) -> str: return f"<{self.__name__}>"
[docs] class LowerBound(Generic[P]): """ Wrapper for numeric-validators to enforce lower limits. This class is meant to be called as a function after instantiation; its return is ``True`` if the numeric validator is within a set lower limit, and ``False`` otherwise. """ @deprecated_parameters(renamed={"func": "value_fn"}) def __init__(self, value_fn: Callable[P, float], bound: float) -> None: """Initialize a LowerBound instance. This initializer sets up `LowerBound` with the provided arguments and validates required state. Args: value_fn (Callable): Numeric validator function. Should return a comparable value. bound (float): Comparable lower bound. """ self.value_fn = value_fn self.bound = bound func = deprecated_property("LowerBound.func", "value_fn", writable=True) def __call__(self, *args: P.args, **kwargs: P.kwargs) -> bool: return self.value_fn(*args, **kwargs) >= self.bound @property def __name__(self) -> str: return f"LowerBound({_fn_name(self.value_fn)} <= {self.bound})" def __repr__(self) -> str: return f"<{self.__name__}>"
[docs] class SelfConfiguringUpperBound: """ Wrapper for numeric-validators to enforce automatic upper limits. When instantiated, the initial upper bound is set as the initial value of the numeric-validator. This class is meant to be called as a function after instantiation; its return is ``True`` if the numeric validator is within a set upper limit, and ``False`` otherwise. """ @deprecated_parameters(renamed={"func": "value_fn"}) def __init__(self, value_fn: Callable[[Partition], float]) -> None: """Initialize a SelfConfiguringUpperBound instance. This initializer sets up `SelfConfiguringUpperBound` with the provided arguments and validates required state. Args: value_fn (Callable): Numeric validator function. """ self.value_fn = value_fn self.bound = None func = deprecated_property("SelfConfiguringUpperBound.func", "value_fn", writable=True) def __call__(self, partition: Partition) -> bool: if not self.bound: self.bound = self.value_fn(partition) return self.value_fn(partition) <= self.bound @property def __name__(self) -> str: return f"SelfConfiguringUpperBound({_fn_name(self.value_fn)})" def __repr__(self) -> str: return f"<{self.__name__}>"
[docs] class SelfConfiguringLowerBound: """ Wrapper for numeric-validators to enforce automatic lower limits. When instantiated, the initial lower bound is set as the initial value of the numeric-validator minus some configurable ε. This class is meant to be called as a function after instantiation; its return is ``True`` if the numeric validator is within a set lower limit, and ``False`` otherwise. """ @deprecated_parameters(renamed={"func": "value_fn"}) def __init__(self, value_fn: Callable[[Partition], float], epsilon: float = 0.05) -> None: """Initialize a SelfConfiguringLowerBound instance. This initializer sets up `SelfConfiguringLowerBound` with the provided arguments and validates required state. Args: value_fn (Callable): Numeric validator function. epsilon (float, optional): Initial population deviation allowable by the validator as a percentage of the ideal population. Defaults to 0.05. """ self.value_fn = value_fn self.bound = None self.epsilon = epsilon func = deprecated_property("SelfConfiguringLowerBound.func", "value_fn", writable=True) def __call__(self, partition: Partition) -> bool: if not self.bound: self.bound = self.value_fn(partition) - self.epsilon return self.value_fn(partition) >= self.bound @property def __name__(self) -> str: return f"SelfConfiguringLowerBound({_fn_name(self.value_fn)})" def __repr__(self) -> str: return f"<{self.__name__}>"
[docs] class WithinPercentRangeOfBounds: """ Wrapper for numeric-validators to enforce upper and lower limits determined by a percentage of the initial value. When instantiated, the initial upper and lower bounds are set as the initial value of the numeric-validator times (1 ± percent). This class is meant to be called as a function after instantiation; its return is ``True`` if the numeric validator is within the desired percentage range of the initial value, and ``False`` otherwise. """ @deprecated_parameters(renamed={"func": "value_fn"}) def __init__(self, value_fn: Callable[[Partition], float], percent: float) -> None: """Initialize a WithinPercentRangeOfBounds instance. This initializer sets up `WithinPercentRangeOfBounds` with the provided arguments and validates required state. Args: value_fn (Callable): Numeric validator function. percent (float): Percentage of the initial value to use as the bounds. Warning: The percentage is assumed to be in the range [0.0, 100.0]. """ self.value_fn = value_fn self.percent = float(percent) / 100.0 self.lbound = None self.ubound = None func = deprecated_property("WithinPercentRangeOfBounds.func", "value_fn", writable=True) def __call__(self, partition: Partition) -> bool: if not (self.lbound and self.ubound): self.lbound = self.value_fn(partition) * (1.0 - self.percent) self.ubound = self.value_fn(partition) * (1.0 + self.percent) return True else: return self.lbound <= self.value_fn(partition) <= self.ubound @property def __name__(self) -> str: return f"WithinPercentRangeOfBounds({_fn_name(self.value_fn)})" def __repr__(self) -> str: return f"<{self.__name__}>"