diff --git a/gerrychain/constraints/validity.py b/gerrychain/constraints/validity.py index 2344b338..716c6c3a 100644 --- a/gerrychain/constraints/validity.py +++ b/gerrychain/constraints/validity.py @@ -75,6 +75,7 @@ def population(partition): return Bounds(population, bounds=bounds) + def within_percent_of_ideal_population_per_representative( initial_partition, percent=0.01, pop_key="population" ): @@ -83,7 +84,7 @@ def within_percent_of_ideal_population_per_representative( Ideal population is defined as "total population / number of representatives." - :param initial_partition: Starting MultiMemberPartition from which to compute district + :param initial_partition: Starting MultiMemberPartition from which to compute district information. :param percent: (optional) Allowed percentage deviation. Default is 1%. :param pop_key: (optional) The name of the population @@ -102,6 +103,7 @@ def population_per_rep(partition): return Bounds(population_per_rep, bounds=bounds) + def deviation_from_ideal(partition, attribute="population"): """Computes the deviation of the given ``attribute`` from exact equality among parts of the partition. Usually ``attribute`` is the population, and diff --git a/gerrychain/partition/multi_member.py b/gerrychain/partition/multi_member.py index 361b92cb..c10cf189 100644 --- a/gerrychain/partition/multi_member.py +++ b/gerrychain/partition/multi_member.py @@ -2,7 +2,8 @@ class MultiMemberPartition(Partition): - """A :class:`Partition` with district magnitude information included. + """ + A :class:`Partition` with district magnitude information included. These additional data allows for districts of different scales (number of representatives) to be properly balanced. """ @@ -15,9 +16,11 @@ def __init__(self, graph=None, assignment=None, updaters=None, magnitudes=None, :param updaters: Dictionary of functions to track data about the partition. The keys are stored as attributes on the partition class, which the functions compute. - :param magnitudes (dict): Dictionary assigning districts to number of representatives + :param magnitudes (dict): Dictionary assigning districts to number of + representatives """ - super().__init__(graph=graph, assignment=assignment, updaters=updaters, parent=parent, flips=flips) + super().__init__(graph=graph, assignment=assignment, updaters=updaters, parent=parent, + flips=flips) if parent is None: self._init_magnitudes(magnitudes) else: @@ -31,13 +34,12 @@ def _init_magnitudes(self, magnitudes): """ dist_ids = self.assignment.parts.keys() self.magnitudes = {dist_id: 1 for dist_id in dist_ids} - if magnitudes != None: + if magnitudes is not None: self.magnitudes = {**self.magnitudes, **magnitudes} - def _update_magnitudes_from_parent(self, magnitudes): parent = self.parent self.magnitudes = {**parent.magnitudes, **magnitudes} def flip(self, flips, magnitudes): - return self.__class__(parent=self, flips=flips, magnitudes=magnitudes) \ No newline at end of file + return self.__class__(parent=self, flips=flips, magnitudes=magnitudes) diff --git a/gerrychain/proposals/tree_proposals.py b/gerrychain/proposals/tree_proposals.py index 9e7a5d1d..3e44ec7a 100644 --- a/gerrychain/proposals/tree_proposals.py +++ b/gerrychain/proposals/tree_proposals.py @@ -9,7 +9,7 @@ def recom( - partition, pop_col, pop_target, epsilon, node_repeats=1, method=bipartition_tree, + partition, pop_col, pop_target, epsilon, node_repeats=1, method=bipartition_tree, multimember=False, ): """ReCom proposal. @@ -139,7 +139,7 @@ def __init__(self, pop_col, ideal_pop, epsilon, method=bipartition_tree_random, def __call__(self, partition): return recom( - partition, self.pop_col, self.ideal_pop, self.epsilon, method=self.method, + partition, self.pop_col, self.ideal_pop, self.epsilon, method=self.method, multimember=self.multimember ) diff --git a/gerrychain/tree.py b/gerrychain/tree.py index ce77c74c..9f070ff4 100644 --- a/gerrychain/tree.py +++ b/gerrychain/tree.py @@ -274,7 +274,8 @@ def bipartition_tree_random( def recursive_tree_part( - graph, parts, pop_target, pop_col, epsilon, node_repeats=1, method=bipartition_tree, magnitudes=None + graph, parts, pop_target, pop_col, epsilon, node_repeats=1, method=bipartition_tree, + magnitudes=None ): """Uses :func:`~gerrychain.tree.bipartition_tree` recursively to partition a tree into ``len(parts)`` parts of population ``pop_target`` (within ``epsilon``). Can be used to @@ -304,9 +305,11 @@ def recursive_tree_part( debt = 0 for part in parts[:-1]: - part_mag = 1 if magnitudes == None else magnitudes[part] - min_pop = max(pop_target * part_mag * (1 - epsilon), pop_target * part_mag * (1 - epsilon) - debt) - max_pop = min(pop_target * part_mag * (1 + epsilon), pop_target * part_mag * (1 + epsilon) - debt) + part_mag = 1 if magnitudes is None else magnitudes[part] + min_pop = max(pop_target * part_mag * (1 - epsilon), + pop_target * part_mag * (1 - epsilon) - debt) + max_pop = min(pop_target * part_mag * (1 + epsilon), + pop_target * part_mag * (1 + epsilon) - debt) nodes = method( graph.subgraph(remaining_nodes), pop_col=pop_col, @@ -325,17 +328,15 @@ def recursive_tree_part( debt += part_pop - pop_target * part_mag remaining_nodes -= nodes - if magnitudes != None: + if magnitudes is not None: new_magnitudes[part] = part_mag # All of the remaining nodes go in the last part for node in remaining_nodes: flips[node] = parts[-1] - if magnitudes != None: - new_magnitudes[parts[-1]] = magnitudes[parts[-1]] - - if magnitudes != None: + if magnitudes is not None: + new_magnitudes[parts[-1]] = magnitudes[parts[-1]] return flips, new_magnitudes else: return flips diff --git a/setup.cfg b/setup.cfg index 67d40082..0d43f7e8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,10 @@ [flake8] max-line-length = 100 ignore = E122,E123,E126,E127,E128,E731,E722,W503,W504 -exclude = build,gerrychain/_version.py,tests,conda.recipe,.git,versioneer.py,benchmarks,.asv,__init__.py,gerrychain/vendor/* +exclude = build,gerrychain/_version.py,tests,conda.recipe,.git,versioneer.py,benchmarks,.asv,__init__.py,gerrychain/vendor/*,gerrychain/local_tests/*,gerrychain/optimization/* + +env = + FORMAT: "${cyan}%(path)s${reset}:${yellow_bold}%(row)d${reset}:${green_bold}%(col)d${reset}: ${red_bold}%(code)s${reset} %(text)s" [tool:pytest] norecursedirs= .* *.egg* build dist conda.recipe