Skip to content

Commit

Permalink
Fix Chris comments
Browse files Browse the repository at this point in the history
  • Loading branch information
peterrrock2 committed Apr 29, 2024
1 parent 2a30296 commit 9fa629e
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 74 deletions.
2 changes: 1 addition & 1 deletion docs/repeated_subsections/reproducible_envs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ Reproducible Environments in VSCode and Jupyter Lab

In general, it is easier to use jupyter notebooks with bespoke virtual environments
through a text editor like `VSCode <https://code.visualstudio.com/download>`_, but
we will show how to do this using the standard Jupyter Lab interface as well.
we will also show how to do this using the standard Jupyter Lab interface as well.

Regardless of which method you prefer, you will need to make sure that you have
installed the ``ipykernel`` package into the virtual environment that you will be
Expand Down
4 changes: 2 additions & 2 deletions docs/topics/reporting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
Reporting Issues
================

Sometimes, you may encounter a problem with the GerryChain, and that is okay! We would
Sometimes, you may encounter a problem with GerryChain, and that is okay! We would
love to hear about it so that we can fix it. If you can provide a minimal example that
reproduces the issue, please feel free to report it on the `GitHub issue tracker
<https://github.com/mggg/GerryChain/issues>`_.

In the event that you would prefer not to post the issue publicly, you can also email
us at "code[at]mggg[dot]org". We will do our best to respond to your issue in a timely
manner. Thank you for your help in making the GerryChain better!
manner. Thank you for your help in making GerryChain better!
33 changes: 17 additions & 16 deletions docs/user/optimizers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,29 @@ Optimization Methods of GerryChain
==================================

In GerryChain, we provide a class known as the ``SingleMetricOptimizer`` as well as a
``Gingelator``` subclass that allow us to perform optimization runs.
``Gingelator`` subclass that allow us to perform optimization runs.


Currently, there are 3 different optimization methods available in GerryChain:

- **Short Bursts**: This method chains together a series of neutral explorers. The main
idea is to run the chain for a short period of time (short burst) and then continue
the chain from the partition that maximizes the objective function within the most
recent short burst. For more information, please refer to
`this paper <https://arxiv.org/abs/2011.02288>`_.
idea is to run the chain for a short period of time (short burst) and then continue
the chain from the partition that maximizes the objective function within the most
recent short burst. For more information, please refer to
`this paper <https://arxiv.org/abs/2011.02288>`_.

- **Simulated Annealing**: This method varies the probablity of accepting a worse plan
according to a temperature schedule which ranges from 0 to 1.
- **Tilted Runs**: This method accepts a worse plan with a fixed probability :math:`p`.
according to a temperature schedule which ranges from 0 to 1.

- **Tilted Runs**: This method accepts a worse plan with a fixed probability :math:`p`,
and always accepts better plans.


While sampling naively with GerryChain can give us an understanding of the neutral
baseline for a state, there are often cases where we want to find plans with
properties that are rare to encounter in a neutral run. Many states have
laws/guidelines that state that plans should be as compact as feasibly possible, maximize
preservation of political boundaries and/or communities of interest, some even look to
preservation of political boundaries and/or communities of interest; some even look to
minimize double bunking of incumbents or seek proportionality/competitiveness in
contests. Heuristic optimization methods can be used to find example plans with these
properties and to explore the trade-offs between them.
Expand Down Expand Up @@ -96,8 +99,8 @@ And now we load in our file and set up our initial chain.
Using ``SingleMetricOptimizer``
-------------------------------

Now the `SingleMetricOptimizer` is set up as a wrapper around our basic `MarkovChain`
class, so interacting with it should be familiar. To set up our optimizer, we, we simply
Now the ``SingleMetricOptimizer`` is set up as a wrapper around our basic ``MarkovChain``
class, so interacting with it should be familiar. To set up our optimizer, we simply
pass it a proposal function, some constraints, an initial state, and the objective function:

.. code:: python
Expand Down Expand Up @@ -164,13 +167,12 @@ This should give you something like:
Using ``Gingleator``
--------------------

Named for the Supreme Court case *Thornburg v. Gingles*, which created their precedent
as one of the litmus tests in bringing forth a VRA court case, **Gingles' Districts** are
Named for the Supreme Court case *Thornburg v. Gingles*, **Gingles' Districts** are
districts that are 50% + 1 of a minority population subgroup (more colloquially called
majority-minority districts). It is common to seek plans with greater/maximal numbers
of gingles districts to understand the landscape of the state space.
of Gingles' districts to understand the landscape of the state space.

The `Gingleator` class is a subclass of the `SingleMetricOptimizer` class, so much of
The ``Gingleator`` class is a subclass of the ``SingleMetricOptimizer`` class, so much of
the setup is the same:

.. code:: python
Expand Down Expand Up @@ -237,8 +239,7 @@ This should give you something like:
:align: center
:alt: Gingleator Optimization Method Comparison Image

And we can see a little better how each method performs over the course of the run
by looking at all of the scores relative to the previous graph:
And we can see a little better how each method performs over the course of the run:

.. image:: ./images/gingleator_all.png
:align: center
Expand Down
61 changes: 32 additions & 29 deletions gerrychain/optimization/gingleator.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,29 @@ def __init__(
:type constraints: Union[Iterable[Callable], Validator, Iterable[Bounds], Callable]
:param initial_state: Initial :class:`gerrychain.partition.Partition` class.
:type initial_state: Partition
:param minority_perc_col: Which updater is a mapping of district ids to the fraction of
minority population within that district. Defaults to none.
:type minority_perc_col: str
:param threshold: Beyond which fraction to consider something a "Gingles"
:param minority_perc_col: The name of the updater mapping of district ids to the
fraction of minority population within that district. If no updater is
specified, one is made according to the ``min_perc_column_name`` parameter.
Defaults to None.
:type minority_perc_col: Optional[str]
:param threshold: Fraction beyond which to consider something a "Gingles"
(or opportunity) district. Defaults to 0.5.
:type threshold: float
:param score_function: The function to using doing optimization. Should have the
:type threshold: float, optional
:param score_function: The function to use during optimization. Should have the
signature ``Partition * str (minority_perc_col) * float (threshold) ->
'a where 'a is Comparable``. This class implements a few potential choices as class
methods. Defaults to None.
:type score_function: Callable
:param minority_pop_col: If minority_perc_col is defined, the minority population column
with which to compute percentage. Defaults to None.
:type minority_pop_col: str
:type score_function: Optional[Callable]
:param minority_pop_col: If minority_perc_col is not defined, the minority population
column with which to compute percentage. Defaults to None.
:type minority_pop_col: Optional[str]
:param total_pop_col: If minority_perc_col is defined, the total population column with
which to compute percentage. Defaults to "TOTPOP".
:type total_pop_col: str
:param min_perc_column_name: If minority_perc_col is defined, the name to give the created
percentage updater. Defaults to "_gingleator_auxiliary_helper_updater_min_perc_col".
:type min_perc_column_name: str
:type total_pop_col: str, optional
:param min_perc_column_name: If minority_perc_col is not defined, the name to give the
created percentage updater. Defaults to
"_gingleator_auxiliary_helper_updater_min_perc_col".
:type min_perc_column_name: str, optional
"""
if minority_perc_col is None and minority_pop_col is None:
raise ValueError(
Expand Down Expand Up @@ -103,10 +106,10 @@ def num_opportunity_dists(
:param part: Partition to score.
:type part: Partition
:param minority_perc_col: Which updater is a mapping of district ids to the fraction of
minority population within that district.
:param minority_perc_col: The name of the updater mapping of district ids to the
fraction of minority population within that district.
:type minority_perc_col: str
:param threshold: Beyond which fraction to consider something a "Gingles"
:param threshold: Fraction beyond which to consider something a "Gingles"
(or opportunity) district.
:type threshold: float
Expand All @@ -126,10 +129,10 @@ def reward_partial_dist(
:param part: Partition to score.
:type part: Partition
:param minority_perc_col: Which updater is a mapping of district ids to the fraction of
minority population within that district.
:param minority_perc_col: The name of the updater mapping of district ids to the
fraction of minority population within that district.
:type minority_perc_col: str
:param threshold: Beyond which fraction to consider something a "Gingles"
:param threshold: Fraction beyond which to consider something a "Gingles"
(or opportunity) district.
:type threshold: float
Expand All @@ -152,10 +155,10 @@ def reward_next_highest_close(
:param part: Partition to score.
:type part: Partition
:param minority_perc_col: Which updater is a mapping of district ids to the fraction of
minority population within that district.
:param minority_perc_col: The name of the updater mapping of district ids to the
fraction of minority population within that district.
:type minority_perc_col: str
:param threshold: Beyond which fraction to consider something a "Gingles"
:param threshold: Fraction beyond which to consider something a "Gingles"
(or opportunity) district.
:type threshold: float
Expand All @@ -182,10 +185,10 @@ def penalize_maximum_over(
:param part: Partition to score.
:type part: Partition
:param minority_perc_col: Which updater is a mapping of district ids to the fraction of
minority population within that district.
:param minority_perc_col: The name of the updater mapping of district ids to the
fraction of minority population within that district.
:type minority_perc_col: str
:param threshold: Beyond which fraction to consider something a "Gingles"
:param threshold: Fraction beyond which to consider something a "Gingles"
(or opportunity) district.
:type threshold: float
Expand All @@ -210,10 +213,10 @@ def penalize_avg_over(
:param part: Partition to score.
:type part: Partition
:param minority_perc_col: Which updater is a mapping of district ids to the fraction of
minority population within that district.
:param minority_perc_col: The name of the updater mapping of district ids to the
fraction of minority population within that district.
:type minority_perc_col: str
:param threshold: Beyond which fraction to consider something a "Gingles"
:param threshold: Fraction beyond which to consider something a "Gingles"
(or opportunity) district.
:type threshold: float
Expand Down
29 changes: 24 additions & 5 deletions gerrychain/optimization/optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class SingleMetricOptimizer:
SingleMetricOptimizer represents the class of algorithms / chains that optimize plans with
respect to a single metric. An instance of this class encapsulates the following state
information:
* the dualgraph and updaters via the initial partition,
* the dual graph and updaters via the initial partition,
* the constraints new proposals are subject to,
* the metric over which to optimize,
* and whether or not to seek maximal or minimal values of the metric.
Expand Down Expand Up @@ -50,7 +50,7 @@ def __init__(
:param initial_state: Initial state of the optimizer.
:type initial_state: Partition
:param optimization_metric: The score function with which to optimize over. This should have
the signature: ``Partition -> 'a`` where 'a is Comparable
the signature: ``Partition -> 'a`` where 'a is comparable.
:type optimization_metric: Callable[[Partition], Any]
:param maximize: Boolean indicating whether to maximize or minimize the function. Defaults to True for maximize.
:type maximize: bool, optional
Expand Down Expand Up @@ -135,7 +135,7 @@ def _tilted_acceptance_function(self, p: float) -> Callable[[Partition], bool]:
:param p: The probability of accepting a worse score.
:type p: float
:return: A acceptance function for tilted chains.
:return: An acceptance function for tilted chains.
:rtype: Callable[[Partition], bool]
"""

Expand Down Expand Up @@ -284,7 +284,7 @@ def logitcycle_beta_function(
) -> Callable[[int], float]:
"""
Class method that binds and returns a logit hot-cool cycle beta temperature function, where
the chain runs hot for some given duration, down according to the logit function
the chain runs hot for some given duration, cools down according to the logit function
:math:`f(x) = (log(x/(1-x)) + 5)/10`
Expand Down Expand Up @@ -334,7 +334,23 @@ def beta_function(step: int):
def logit_jumpcycle_beta_function(
cls, duration_hot: int, duration_cooldown: int, duration_cold: int
) -> Callable[[int], float]:
"""
Class method that binds and returns a logit hot-cool cycle beta temperature function, where
the chain runs hot for some given duration, cools down according to the logit function
:math:`f(x) = (log(x/(1-x)) + 5)/10`
for some duration, and then runs cold for some duration before jumping back to hot and
repeating.
:param duration_hot: Number of steps to run chain hot.
:type duration_hot: int
:param duration_cooldown: Number of steps needed to transition from hot to cold or
vice-versa.
:type duration_cooldown: int
:param duration_cold: Number of steps to run chain cold.
:type duration_cold: int
"""
cycle_length = duration_hot + duration_cooldown + duration_cold

# this will scale from 0 to 1 approximately
Expand Down Expand Up @@ -375,6 +391,8 @@ def short_bursts(
:param accept: Function accepting or rejecting the proposed state. Defaults to
:func:`~gerrychain.accept.always_accept`.
:type accept: Callable[[Partition], bool], optional
:param with_progress_bar: Whether or not to draw tqdm progress bar. Defaults to False.
:type with_progress_bar: bool, optional
:return: Partition generator.
:rtype: Generator[Partition]
Expand Down Expand Up @@ -423,7 +441,8 @@ def simulated_annealing(
the magnitude of change in score.
:type beta_function: Callable[[int], float]
:param beta_magnitude: Scaling parameter for how much to weight changes in score.
:type beta_magnitude: float
Defaults to 1.
:type beta_magnitude: float, optional
:param with_progress_bar: Whether or not to draw tqdm progress bar. Defaults to False.
:type with_progress_bar: bool, optional
Expand Down
12 changes: 6 additions & 6 deletions gerrychain/partition/partition.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,20 @@ def from_random_assignment(
"""
Create a Partition with a random assignment of nodes to districts.
:param graph: The graph to create the Partition from
:param graph: The graph to create the Partition from.
:type graph: :class:`~gerrychain.Graph`
:param n_parts: The number of districts to divide the nodes into
:param n_parts: The number of districts to divide the nodes into.
:type n_parts: int
:param epsilon: The maximum relative population deviation from the ideal
population. Should be in [0,1]
:type epsilon: float
:param pop_col: The column of the graph's node data that holds the population data
population. Should be in [0,1].
:param pop_col: The column of the graph's node data that holds the population data.
:type pop_col: str
:param updaters: dictionary of updaters
:param updaters: Dictionary of updaters
:type updaters: Optional[Dict[str, Callable]], optional
:param use_default_updaters: If `False`, do not include default updaters.
:type use_default_updaters: bool, optional
:param flips: dictionary assigning nodes of the graph to their new districts
:param flips: Dictionary assigning nodes of the graph to their new districts.
:type flips: Optional[Dict], optional
:param method: The function to use to partition the graph into ``n_parts``. Defaults to
:func:`~gerrychain.tree.recursive_tree_part`.
Expand Down

0 comments on commit 9fa629e

Please sign in to comment.