Skip to content

Commit 1788e43

Browse files
committed
Edit doc strings and __repr__ methods for constraints module
1 parent dff63b5 commit 1788e43

File tree

5 files changed

+264
-92
lines changed

5 files changed

+264
-92
lines changed

gerrychain/constraints/__init__.py

-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
and should return whether or not the instance is valid according to their
3535
rules. Many top-level functions following this signature in this module are
3636
examples of this.
37-
3837
"""
3938

4039
from .bounds import (LowerBound, SelfConfiguringLowerBound,

gerrychain/constraints/bounds.py

+78-23
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
from typing import Callable, Tuple
2+
from ..partition import Partition
3+
4+
15
class Bounds:
26
"""
37
Wrapper for numeric-validators to enforce upper and lower limits.
@@ -8,23 +12,28 @@ class Bounds:
812
913
"""
1014

11-
def __init__(self, func, bounds):
15+
def __init__(self, func: Callable, bounds: Tuple[float, float]) -> None:
1216
"""
1317
:param func: Numeric validator function. Should return an iterable of values.
18+
:type func: Callable
1419
:param bounds: Tuple of (lower, upper) numeric bounds.
20+
:type bounds: Tuple[float, float]
1521
"""
1622
self.func = func
1723
self.bounds = bounds
1824

19-
def __call__(self, *args, **kwargs):
25+
def __call__(self, *args, **kwargs) -> bool:
2026
lower, upper = self.bounds
2127
values = self.func(*args, **kwargs)
2228
return lower <= min(values) and max(values) <= upper
2329

2430
@property
25-
def __name__(self):
31+
def __name__(self) -> str:
2632
return "Bounds({},{})".format(self.func.__name__, str(self.bounds))
2733

34+
def __repr__(self) -> str:
35+
return "<{}>".format(self.__name__)
36+
2837

2938
class UpperBound:
3039
"""
@@ -33,25 +42,26 @@ class UpperBound:
3342
This class is meant to be called as a function after instantiation; its
3443
return is ``True`` if the numeric validator is within a set upper limit,
3544
and ``False`` otherwise.
36-
3745
"""
3846

39-
def __init__(self, func, bound):
47+
def __init__(self, func: Callable, bound: float) -> None:
4048
"""
4149
:param func: Numeric validator function. Should return a comparable value.
50+
:type func: Callable
4251
:param bounds: Comparable upper bound.
52+
:type bounds: float
4353
"""
4454
self.func = func
4555
self.bound = bound
4656

47-
def __call__(self, *args, **kwargs):
57+
def __call__(self, *args, **kwargs) -> bool:
4858
return self.func(*args, **kwargs) <= self.bound
4959

5060
@property
51-
def __name__(self):
61+
def __name__(self) -> str:
5262
return "UpperBound({} >= {})".format(self.func.__name__, self.bound)
5363

54-
def __repr__(self):
64+
def __repr__(self) -> str:
5565
return "<{}>".format(self.__name__)
5666

5767

@@ -62,25 +72,26 @@ class LowerBound:
6272
This class is meant to be called as a function after instantiation; its
6373
return is ``True`` if the numeric validator is within a set lower limit,
6474
and ``False`` otherwise.
65-
6675
"""
6776

68-
def __init__(self, func, bound):
77+
def __init__(self, func: Callable, bound: float) -> None:
6978
"""
7079
:param func: Numeric validator function. Should return a comparable value.
80+
:type func: Callable
7181
:param bounds: Comparable lower bound.
82+
:type bounds: float
7283
"""
7384
self.func = func
7485
self.bound = bound
7586

76-
def __call__(self, *args, **kwargs):
87+
def __call__(self, *args, **kwargs) -> bool:
7788
return self.func(*args, **kwargs) >= self.bound
7889

7990
@property
80-
def __name__(self):
91+
def __name__(self) -> str:
8192
return "LowerBound({} <= {})".format(self.func.__name__, self.bound)
8293

83-
def __repr__(self):
94+
def __repr__(self) -> str:
8495
return "<{}>".format(self.__name__)
8596

8697

@@ -94,21 +105,28 @@ class SelfConfiguringUpperBound:
94105
This class is meant to be called as a function after instantiation; its
95106
return is ``True`` if the numeric validator is within a set upper limit,
96107
and ``False`` otherwise.
97-
98108
"""
99109

100-
def __init__(self, func):
110+
def __init__(self, func: Callable) -> None:
101111
"""
102112
:param func: Numeric validator function.
113+
:type func: Callable
103114
"""
104115
self.func = func
105116
self.bound = None
106117

107-
def __call__(self, partition):
118+
def __call__(self, partition: Partition) -> bool:
108119
if not self.bound:
109120
self.bound = self.func(partition)
110121
return self.func(partition) <= self.bound
111122

123+
@property
124+
def __name__(self) -> str:
125+
return "SelfConfiguringUpperBound({})".format(self.func.__name__)
126+
127+
def __repr__(self) -> str:
128+
return "<{}>".format(self.__name__)
129+
112130

113131
class SelfConfiguringLowerBound:
114132
"""
@@ -120,36 +138,73 @@ class SelfConfiguringLowerBound:
120138
This class is meant to be called as a function after instantiation; its
121139
return is ``True`` if the numeric validator is within a set lower limit,
122140
and ``False`` otherwise.
123-
124141
"""
125142

126-
def __init__(self, func, epsilon=0.05):
143+
def __init__(self, func: Callable, epsilon: float = 0.05) -> None:
127144
"""
128145
:param func: Numeric validator function.
129-
:param epsilon: Initial "wiggle room" that the validator allows.
146+
:type func: Callable
147+
:param epsilon: Initial population deviation allowable by the validator.
148+
:type epsilon: float
130149
"""
131150
self.func = func
132151
self.bound = None
133152
self.epsilon = epsilon
134-
self.__name__ = func.__name__
135153

136-
def __call__(self, partition):
154+
def __call__(self, partition: Partition) -> bool:
137155
if not self.bound:
138156
self.bound = self.func(partition) - self.epsilon
139157
return self.func(partition) >= self.bound
140158

159+
@property
160+
def __name__(self) -> str:
161+
return "SelfConfiguringLowerBound({})".format(self.func.__name__)
162+
163+
def __repr__(self) -> str:
164+
return "<{}>".format(self.__name__)
165+
141166

142167
class WithinPercentRangeOfBounds:
143-
def __init__(self, func, percent):
168+
"""
169+
Wrapper for numeric-validators to enforce upper and lower limits
170+
determined by a percentage of the initial value.
171+
172+
When instantiated, the initial upper and lower bounds are set as the
173+
initial value of the numeric-validator times (1 ± percent).
174+
175+
This class is meant to be called as a function after instantiation; its
176+
return is ``True`` if the numeric validator is within the desired
177+
percentage range of the initial value, and ``False`` otherwise.
178+
"""
179+
180+
def __init__(self, func: Callable, percent: float) -> None:
181+
"""
182+
:param func: Numeric validator function.
183+
:type func: Callable
184+
:param percent: Percentage of the initial value to use as the bounds.
185+
:type percent: float
186+
187+
:return: None
188+
189+
.. Warning::
190+
The percentage is assumed to be in the range [0.0, 100.0].
191+
"""
144192
self.func = func
145193
self.percent = float(percent) / 100.0
146194
self.lbound = None
147195
self.ubound = None
148196

149-
def __call__(self, partition):
197+
def __call__(self, partition: Partition) -> bool:
150198
if not (self.lbound and self.ubound):
151199
self.lbound = self.func(partition) * (1.0 - self.percent)
152200
self.ubound = self.func(partition) * (1.0 + self.percent)
153201
return True
154202
else:
155203
return self.lbound <= self.func(partition) <= self.ubound
204+
205+
@property
206+
def __name__(self) -> str:
207+
return "WithinPercentRangeOfBounds({})".format(self.func.__name__)
208+
209+
def __repr__(self) -> str:
210+
return "<{}>".format(self.__name__)

gerrychain/constraints/compactness.py

+45-4
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,63 @@
1-
1+
from ..partition import Partition
22
import math
33

4+
45
from .bounds import SelfConfiguringLowerBound, SelfConfiguringUpperBound
56

67

7-
def L1_reciprocal_polsby_popper(partition):
8+
def L1_reciprocal_polsby_popper(partition: Partition) -> float:
9+
"""
10+
Returns the $L^1$ norm of the reciprocal Polsby-Popper scores
11+
for the given partition
12+
13+
:param partition: Partition representing a districting plan
14+
:type partition: Partition
15+
16+
:return: $L^1$ norm of the reciprocal Polsby-Popper scores
17+
:rtype: float
18+
"""
819
return sum(1 / value for value in partition["polsby_popper"].values())
920

1021

11-
def L1_polsby_popper(partition):
22+
def L1_polsby_popper(partition: Partition) -> float:
23+
"""
24+
Returns the $L^1$ norm of the Polsby-Popper scores
25+
for the given partition
26+
27+
:param partition: Partition representing a districting plan
28+
:type partition: Partition
29+
30+
:return: $L^1$ norm of the reciprocal Polsby-Popper scores
31+
:rtype: float
32+
"""
1233
return sum(value for value in partition["polsby_popper"].values())
1334

1435

15-
def L2_polsby_popper(partition):
36+
def L2_polsby_popper(partition: Partition) -> float:
37+
"""
38+
Returns the $L^2$ norm of the Polsby-Popper scores
39+
for the given partition.
40+
41+
:param partition: Partition representing a districting plan
42+
:type partition: Partition
43+
44+
:return: $L^2$ norm of the Polsby-Popper scores
45+
:rtype: float
46+
"""
1647
return math.sqrt(sum(value ** 2 for value in partition["polsby_popper"].values()))
1748

1849

1950
def L_minus_1_polsby_popper(partition):
51+
"""
52+
Returns the $L^{-1}$ norm of the Polsby-Popper scores
53+
for the given partition.
54+
55+
:param partition: Partition representing a districting plan
56+
:type partition: Partition
57+
58+
:return: $L^{-1}$ norm of the Polsby-Popper scores
59+
:rtype: float
60+
"""
2061
return len(partition.parts) / sum(
2162
1 / value for value in partition["polsby_popper"].values()
2263
)

0 commit comments

Comments
 (0)