Source code for gerrychain.updaters.compactness

import collections

from .flows import on_flow
from .cut_edges import on_edge_flow
from typing import Dict, Set


[docs]def boundary_nodes(partition, alias: str = "boundary_nodes") -> Set: """ :param partition: A partition of a Graph :type partition: :class:`~gerrychain.partition.Partition` :param alias: The name of the attribute that the boundary nodes are stored under. Default is 'boundary_nodes'. :type alias: str, optional :returns: The set of nodes in the partition that are on the boundary. :rtype: Set """ if partition.parent: return partition.parent[alias] return { node for node in partition.graph.nodes if partition.graph.nodes[node]["boundary_node"] }
[docs]def initialize_exterior_boundaries_as_a_set(partition) -> Dict[int, Set]: """ :param partition: A partition of a Graph :type partition: :class:`~gerrychain.partition.Partition` :returns: A dictionary mapping each part of a partition to the set of nodes in that part that are on the boundary. :rtype: Dict[int, Set] """ part_boundaries = collections.defaultdict(set) for node in partition["boundary_nodes"]: part_boundaries[partition.assignment.mapping[node]].add(node) return part_boundaries
[docs]@on_flow(initialize_exterior_boundaries_as_a_set, alias="exterior_boundaries_as_a_set") def exterior_boundaries_as_a_set( partition, previous: Set, inflow: Set, outflow: Set ) -> Set: """ Updater function that responds to the flow of nodes between different partitions. :param partition: A partition of a Graph :type partition: :class:`~gerrychain.partition.Partition` :param previous: The previous set of exterior boundary nodes for a fixed part of the given partition. :type previous: Set :param inflow: The set of nodes that have flowed into the given part of the partition. :type inflow: Set :param outflow: The set of nodes that have flowed out of the given part of the partition. :type outflow: Set :returns: The new set of exterior boundary nodes for the given part of the partition. :rtype: Set """ graph_boundary = partition["boundary_nodes"] return (previous | (inflow & graph_boundary)) - outflow
[docs]def initialize_exterior_boundaries(partition) -> Dict[int, float]: """ :param partition: A partition of a Graph :type partition: :class:`~gerrychain.partition.Partition` :returns: A dictionary mapping each part of a partition to the total perimeter of the boundary nodes in that part. :rtype: Dict[int, float] """ graph_boundary = partition["boundary_nodes"] boundaries = collections.defaultdict(lambda: 0) for node in graph_boundary: part = partition.assignment.mapping[node] boundaries[part] += partition.graph.nodes[node]["boundary_perim"] return boundaries
[docs]@on_flow(initialize_exterior_boundaries, alias="exterior_boundaries") def exterior_boundaries(partition, previous: Set, inflow: Set, outflow: Set) -> Dict: """ Updater function that responds to the flow of nodes between different partitions. :param partition: A partition of a Graph :type partition: :class:`~gerrychain.partition.Partition` :param previous: The previous set of exterior boundary nodes for a fixed part of the given partition. :type previous: Set :param inflow: The set of nodes that have flowed into the given part of the partition. :type inflow: Set :param outflow: The set of nodes that have flowed out of the given part of the partition. :type outflow: Set :returns: A dict mapping each part of the partition to the new exterior boundary of that part. :rtype: Dict """ graph_boundary = partition["boundary_nodes"] added_perimeter = sum( partition.graph.nodes[node]["boundary_perim"] for node in inflow & graph_boundary ) removed_perimeter = sum( partition.graph.nodes[node]["boundary_perim"] for node in outflow & graph_boundary ) return previous + added_perimeter - removed_perimeter
[docs]def initialize_interior_boundaries(partition): """ :param partition: A partition of a Graph :type partition: :class:`~gerrychain.partition.Partition` :returns: A dictionary mapping each part of a partition to the total perimeter the given part shares with other parts. :rtype: Dict[int, float] """ return { part: sum( partition.graph.edges[edge]["shared_perim"] for edge in partition["cut_edges_by_part"][part] ) for part in partition.parts }
[docs]@on_edge_flow(initialize_interior_boundaries, alias="interior_boundaries") def interior_boundaries( partition, previous: Set, new_edges: Set, old_edges: Set ) -> Dict: """ Updater function that responds to the flow of nodes between different partitions. :param partition: A partition of a Graph :type partition: :class:`~gerrychain.partition.Partition` :param previous: The previous set of exterior boundary nodes for a fixed part of the given partition. :type previous: Set :param new_edges: The set of edges that have flowed into the given part of the partition. :type new_edges: Set :param old_edges: The set of edges that have flowed out of the given part of the partition. :type old_edges: Set :returns: A dict mapping each part of the partition to the new interior boundary of that part. :rtype: Dict """ added_perimeter = sum( partition.graph.edges[edge]["shared_perim"] for edge in new_edges ) removed_perimeter = sum( partition.graph.edges[edge]["shared_perim"] for edge in old_edges ) return previous + added_perimeter - removed_perimeter
[docs]def flips(partition) -> Dict: """ :param partition: A partition of a Graph :type partition: :class:`~gerrychain.partition.Partition` :returns: The flips that were made to get from the parent partition to the given partition. :rtype: Dict """ return partition.flips
[docs]def perimeter_of_part(partition, part: int) -> float: """ Totals up the perimeter of the part in the partition. .. Warning:: Requires that 'boundary_perim' be a node attribute, 'shared_perim' be an edge attribute, 'cut_edges' be an updater, and 'exterior_boundaries' be an updater. :param partition: A partition of a Graph :type partition: :class:`~gerrychain.partition.Partition` :param part: The id of the part of the partition whose perimeter we want to compute. :type part: int :returns: The perimeter of the desired part. :rtype: float """ exterior_perimeter = partition["exterior_boundaries"][part] interior_perimeter = partition["interior_boundaries"][part] return exterior_perimeter + interior_perimeter
[docs]def perimeter(partition) -> Dict[int, float]: """ :param partition: A partition of a Graph :type partition: :class:`~gerrychain.partition.Partition` :returns: A dictionary mapping each part of a partition to its perimeter. :rtype: Dict[int, float] """ return {part: perimeter_of_part(partition, part) for part in partition.parts}