Source code for gerrychain.constraints.bounds

from typing import Callable, Tuple
from ..partition import Partition


[docs]class Bounds: """ 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. """ def __init__(self, func: Callable, bounds: Tuple[float, float]) -> None: """ :param func: Numeric validator function. Should return an iterable of values. :type func: Callable :param bounds: Tuple of (lower, upper) numeric bounds. :type bounds: Tuple[float, float] """ self.func = func self.bounds = bounds def __call__(self, *args, **kwargs) -> bool: lower, upper = self.bounds values = self.func(*args, **kwargs) return lower <= min(values) and max(values) <= upper @property def __name__(self) -> str: return "Bounds({},{})".format(self.func.__name__, str(self.bounds)) def __repr__(self) -> str: return "<{}>".format(self.__name__)
[docs]class UpperBound: """ 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. """ def __init__(self, func: Callable, bound: float) -> None: """ :param func: Numeric validator function. Should return a comparable value. :type func: Callable :param bounds: Comparable upper bound. :type bounds: float """ self.func = func self.bound = bound def __call__(self, *args, **kwargs) -> bool: return self.func(*args, **kwargs) <= self.bound @property def __name__(self) -> str: return "UpperBound({} >= {})".format(self.func.__name__, self.bound) def __repr__(self) -> str: return "<{}>".format(self.__name__)
[docs]class LowerBound: """ 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. """ def __init__(self, func: Callable, bound: float) -> None: """ :param func: Numeric validator function. Should return a comparable value. :type func: Callable :param bounds: Comparable lower bound. :type bounds: float """ self.func = func self.bound = bound def __call__(self, *args, **kwargs) -> bool: return self.func(*args, **kwargs) >= self.bound @property def __name__(self) -> str: return "LowerBound({} <= {})".format(self.func.__name__, self.bound) def __repr__(self) -> str: return "<{}>".format(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. """ def __init__(self, func: Callable) -> None: """ :param func: Numeric validator function. :type func: Callable """ self.func = func self.bound = None def __call__(self, partition: Partition) -> bool: if not self.bound: self.bound = self.func(partition) return self.func(partition) <= self.bound @property def __name__(self) -> str: return "SelfConfiguringUpperBound({})".format(self.func.__name__) def __repr__(self) -> str: return "<{}>".format(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. """ def __init__(self, func: Callable, epsilon: float = 0.05) -> None: """ :param func: Numeric validator function. :type func: Callable :param epsilon: Initial population deviation allowable by the validator as a percentage of the ideal population. Defaults to 0.05. :type epsilon: float, optional """ self.func = func self.bound = None self.epsilon = epsilon def __call__(self, partition: Partition) -> bool: if not self.bound: self.bound = self.func(partition) - self.epsilon return self.func(partition) >= self.bound @property def __name__(self) -> str: return "SelfConfiguringLowerBound({})".format(self.func.__name__) def __repr__(self) -> str: return "<{}>".format(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. """ def __init__(self, func: Callable, percent: float) -> None: """ :param func: Numeric validator function. :type func: Callable :param percent: Percentage of the initial value to use as the bounds. :type percent: float :returns: None .. Warning:: The percentage is assumed to be in the range [0.0, 100.0]. """ self.func = func self.percent = float(percent) / 100.0 self.lbound = None self.ubound = None def __call__(self, partition: Partition) -> bool: if not (self.lbound and self.ubound): self.lbound = self.func(partition) * (1.0 - self.percent) self.ubound = self.func(partition) * (1.0 + self.percent) return True else: return self.lbound <= self.func(partition) <= self.ubound @property def __name__(self) -> str: return "WithinPercentRangeOfBounds({})".format(self.func.__name__) def __repr__(self) -> str: return "<{}>".format(self.__name__)