Graphs¶
Adjacency graphs represent the geographic units and neighborhood relationships used by a plan.
- class gerrychain.graph.graph.Graph[source]¶
This class closely mirrors the interface of a NetworkX Graph object, but with additions specific to GerryChain. It was created initially to represent geographical data (such as voting precincts) that would support the creation of voting district plans, but it can be used for general graph operations. For a more detailed description of the kinds of operations this class supports, please refer to the NetworkX documentation (https://networkx.org/documentation/stable/)
Note that this class encapsulates / wraps an underlying graph object which can either be a NetworkX graph or a RustworkX graph. The intent is that this class provides the same external interface as a NetworkX graph (for all of the uses that GerryChain cares about, at least) so that legacy GerryChain code that operated on NetworkX based Graph objects can mostly continue to work unchanged.
When a graph is added to a partition, however, the NX graph will be converted into an RX graph and the NX graph will become inaccessible to the user. The RX graph may also be “frozen” the way the NX graph was “frozen” in the legacy code, but we have not yet gotten that far in the implementation.
The reason for converting to an underlying RX graph is to gain the performance gains of the RustworkX library vis-a-vis NetworkX. The reason to continue to support NetworkX is because NetworkX has many user-friendly convenience functions that a user might want to use to create a graph.
So, the usage paradigm is to create a graph using NetworkX and to then convert to RustworkX for the compute intensive work done by MarkovChain().
Note that the conversion from NX to RX is done automatically when the user creates a Partition object.
- add_data(df: DataFrame, columns: Iterable[str] | None = None) None[source]¶
Add columns of a DataFrame to a graph as node attributes by matching the DataFrame’s.
- Parameters:
df (DataFrame) – Dataframe containing given columns.
columns (Iterable[str] | None, optional) – list of dataframe column names to add. Default is None.
- add_edge(node_id1: Hashable, node_id2: Hashable) None[source]¶
Add an edge to the graph from node_id1 to node_id2.
Note that both nodes need to already be members of the graph
- Parameters:
node_id1 (Hashable) – One node ID in the edge.
node_id2 (Hashable) – The other node ID in the edge.
- convert_from_nx_to_rx() Graph[source]¶
Convert an NX-based graph object to be an RX-based graph object.
The primary use case for this routine is support for users constructing a graph using NetworkX functionality and then converting that NetworkX graph to RustworkX when creating a Partition object.
- Returns:
An RX-based graph that is “the same” as the given NX-based graph
- Return type:
‘Graph’
- degree(node_id: Hashable) int[source]¶
Return the degree of the given node, that is, the number of other nodes directly.
This method returns the degree of the given node, that is, the number of other nodes directly. It returns number of nodes directly connected to the given node.
- Parameters:
node_id (Hashable) – A node ID.
- Returns:
Number of nodes directly connected to the given node
- Return type:
- edge_data(edge_id: Hashable) dict[str, Any][source]¶
Return the data dictionary that contains the data for the given edge.
Note that in NetworkX an edge_id can be almost anything, for instance, a string or even a tuple. However, in RustworkX, an edge_id is an integer. This method handles both kinds.
- property edge_indices: set[Hashable]¶
Return a
setof the edge ids in the graph.Unlike
nodes/node_indices(which carry the same content up to a permutation),edge_indicesandedgesare genuinely different:edge_indicesreturns edge ids (opaque integers under RustworkX), whileedgesreturns the edges themselves as(u, v)tuples of node_ids. Use an edge id withget_edge_from_edge_id()/get_edge_id_from_edge()to convert between the two.- Returns:
The edge IDs in the graph.
- Return type:
set[Hashable]
- property edges: set[tuple[Hashable, Hashable]]¶
Return a
setof all of the edges in the graph, where each edge is a(u, v)tuple of node_ids.This returns the edges themselves, which may not be the same as their ids (in fact, for RustworkX backed graphs, they are guaranteed to be different). For the edge ids (opaque integers under RustworkX) see
edge_indices.
- classmethod from_file(filename: str, adjacency: str = 'rook', cols_to_add: list[str] | None = None, reproject: bool = False, ignore_errors: bool = False) Graph[source]¶
Create a Graph from a shapefile, GeoPackage, GeoJSON, or similar source.
This method reads any format that geopandas can load and builds a graph from it.
See from_geodataframe for more details.
- Parameters:
filename (str) – Path to the shapefile / GeoPackage / GeoJSON / etc.
adjacency (str, optional) – The adjacency type to use (“rook” or “queen”). Default is “rook”
cols_to_add (list[str] | None, optional) – The names of the columns that you want to add to the graph as node attributes. Default is None.
reproject (bool, optional) – Whether to reproject to a UTM projection before creating the graph. Default is False.
ignore_errors (bool, optional) – Whether to ignore all invalid geometries and try to continue creating the graph. Default is False.
- Returns:
The Graph object of the geometries from filename.
- Return type:
Warning
This method requires the optional
geopandasdependency. Installgerrychainwith thegeoextra viapip install gerrychain[geo], or installgeopandasseparately.
- classmethod from_geodataframe(dataframe: GeoDataFrame, adjacency: str = 'rook', cols_to_add: list[str] | None = None, reproject: bool = False, ignore_errors: bool = False, crs_override: str | int | None = None) Graph[source]¶
Create the adjacency Graph of geometries described by dataframe.
The areas of the polygons are included as node attributes (with key area). The shared perimeter of neighboring polygons are included as edge attributes (with key shared_perim).
Nodes corresponding to polygons on the boundary of the union of all the geometries (e.g., the state, if your dataframe describes VTDs) have a boundary_node attribute (set to True) and a boundary_perim attribute with the length of this “exterior” boundary.
By default, areas and lengths are computed in a UTM projection suitable for the geometries. This prevents the bizarro area and perimeter values that show up when you accidentally do computations in Longitude-Latitude coordinates. If the user specifies reproject=False, then the areas and lengths will be computed in the GeoDataFrame’s current coordinate reference system. This option is for users who have a preferred CRS they would like to use.
- Parameters:
dataframe (GeoDataFrame) – The GeoDateFrame to convert
adjacency (str, optional) – The adjacency type to use (“rook” or “queen”). Default is “rook”.
cols_to_add (list[str] | None, optional) – The names of the columns that you want to add to the graph as node attributes. Default is None.
reproject (bool, optional) – Whether to reproject to a UTM projection before creating the graph. Default is
False.ignore_errors (bool, optional) – Whether to ignore all invalid geometries and attept to create the graph anyway. Default is
False.crs_override (str | int | None, optional) – Value to override the CRS of the GeoDataFrame. Default is None.
- Returns:
- The adjacency graph of the geometries from dataframe. Note that the returned
Graph object has an embedded NetworkX graph (not a RustworkX graph).
- Return type:
- classmethod from_json(json_file_name: str) Graph[source]¶
Create a
Graphfrom a JSON file in NetworkX “adjacency” format.This is the standard way to load a dual graph that has already been built and saved to disk; it is the inverse of
to_json(). To build a graph from geospatial data instead (a shapefile or a GeoDataFrame), usefrom_file()orfrom_geodataframe().- Expected file format:
The file must contain a single JSON object in the node-link “adjacency” format produced by
networkx.readwrite.json_graph.adjacency_data- which is exactly whatto_json()writes, so a file written byto_jsonround-trips back throughfrom_json. The top-level object has these keys:"directed"/"multigraph": booleans. GerryChain graphs are undirected and not multigraphs, so both are normallyfalse."graph": graph-level attributes as a list of[key, value]pairs (often empty,[]); e.g. a coordinate reference system might live here."nodes": a list of node objects. Each has an"id"plus any number of arbitrary attributes (population, district, county, geometry, …)."adjacency": a list parallel to"nodes".adjacency[i]is the list of edges incident to nodei; each edge object has the neighbor’s"id"plus any edge attributes (e.g."shared_perim").
A minimal two-node example:
{ 'directed': false, "multigraph": false, "graph": [], 'nodes': [{"pop": 5, "id": 0}, {"pop": 3, "id": 1}], 'adjacency': [[{"id": 1}], [{"id": 0}]] }
Node and edge attributes are preserved and become accessible as
graph.node_data(node_id)[attr]andgraph.edge_data(edge_id)[attr]. Anything you intend to use later (a population column forpop_col, aregion_surchargeattribute, an election column, …) must already be present as a node attribute here.- Backend:
The returned
Graphis NetworkX-backed, which is convenient for inspection and editing. When you later wrap it in aPartition, GerryChain converts it to the faster RustworkX backend automatically. Node ids are taken verbatim from the"id"fields; note that the RustworkX conversion reassigns node ids to a contiguous integer range, so do not rely on a node’s id carrying semantic meaning.- Side effect (data warnings):
After loading, this calls
issue_warnings(), which warns about “islands” - degree-0 nodes. An island usually indicates a problem with the dual graph (a unit with no recorded adjacencies) and will break contiguity-based proposals, so the warning is worth heeding.
- Parameters:
json_file_name (str) – Path to the JSON file to read. This is an ordinary filesystem path; the file is opened and parsed directly.
- Returns:
A NetworkX-backed GerryChain
Graphcontaining the nodes, edges, and attributes described by the file.- Return type:
- Raises:
FileNotFoundError – If
json_file_namedoes not exist.json.JSONDecodeError – If the file does not contain valid JSON.
KeyError – If the JSON is valid but is not in the expected adjacency format.
Example
>>> from gerrychain import Graph >>> graph = Graph.from_json("./my_state.json")
If you just want a graph to experiment with, GerryChain bundles a ready-made example, which needs no file at all:
from gerrychain.examples import gerrymandria graph = gerrymandria()
- classmethod from_networkx(nx_graph: networkx.Graph[_NodeT, _NodeDataT, _EdgeDataT]) Graph[source]¶
Create a Graph from a NetworkX.Graph object.
This supports the use case of users creating a graph using NetworkX which is convenient - both for users of the previous implementation of a GerryChain object which was a subclass of NetworkX.Graph and for users more generally who are familiar with NetworkX.
Note that most users will not ever call this function directly, because they can create a GerryChain Partition object directly from a NetworkX graph, and the Partition initialization code will use this function to convert the NetworkX graph to a GerryChain Graph object.
- Parameters:
nx_graph (networkx.Graph) – A NetworkX.Graph object with node and edge data to be converted into a GerryChain Graph object.
- Returns:
A Graph object embedding the given NetworkX Graph
- Return type:
- classmethod from_null_networkx() Graph[source]¶
Create a Graph that has an empty embedded NetworkX Graph.
This was originally implemented as a way to encapsulate NetworkX dependencies in GerryChain code to this module (graph.py).
It supports the use case of a user who wants to build a graph from scratch without reference to NetworkX.
- Returns:
A Graph object with no nodes
- Return type:
- classmethod from_rustworkx(rx_graph: PyGraph[_NodeDataT, Any] | PyDiGraph[_NodeDataT, Any]) Graph[source]¶
Create a Graph from a RustworkX.PyGraph object.
There are three primary use cases for this routine: 1) converting an NX-based Graph to be an RX-based Graph, 2) creating a subgraph of an RX-based Graph, and 3) creating a Graph whose node_ids do not need to be mapped to some previous graph’s node_ids.
In a little more detail:
1) A typical way to use GerryChain is to create a graph using NetworkX functionality and to then rely on the initialization code in the Partition class to create an RX-based Graph object. That initialization code constructs a RustworkX PyGraph and then uses this routine to create an RX-based Graph object, and it then creates maps from the node_ids of the resulting RX-based Graph back to the original NetworkX.Graph’s node_ids.
2) When creating a subgraph of a RustworkX PyGraph object, the node_ids of the subgraph are (in general) different from those of the parent graph. So we create a mapping from the subgraph’s node_ids to the node_ids of the parent. The subgraph() routine creates a RustworkX PyGraph subgraph, then uses this routine to create an RX-based Graph using that subgraph, and it then creates the mapping of subgraph node_ids to the parent (RX) graph’s node_ids.
3) In those cases where no node_id mapping is needed this routine provides a simple way to create an RX-based GerryChain graph object.
- Parameters:
rx_graph (rustworkx.PyGraph | rustworkx.PyDiGraph) – a RustworkX graph object. A directed
PyDiGraphis rejected withGraphValidationError.- Returns:
a GerryChain Graph object with an embedded RustworkX.PyGraph object
- Return type:
‘Graph’
- generic_bfs_predecessors(root_node_id: Hashable) dict[Hashable, Hashable][source]¶
Return A dict mapping each node_id to the node_id of its parent node.
Returns a dict mapping each node_id in the graph to its predecessor node_id where the parent/child relationship is created by doing a breadth-first traversal of the graph starting at the root_node_id.
Note that this works for both NX and RX based Graph objects.
- Parameters:
root_node_id (Hashable) – Root node of the breadth-first traversal.
- Returns:
Node IDs mapped to their parent node IDs.
- Return type:
dict[Hashable, Hashable]
- generic_bfs_successors(root_node_id: Hashable) dict[Hashable, list[Hashable]][source]¶
Return the BFS successors mapping for
root_node_id.The returned dictionary maps each parent node_id to a list of child node_ids.
Does a breadth-first traversal of the given graph, starting at the node specified by “root_node_id”, and returns a dict mapping parent node_ids to a list of the node_ids for that node’s children.
- generic_bfs_successors_generator(root_node_id: Hashable) Generator[tuple[Hashable, list[Hashable]], None, None][source]¶
Yield BFS
(parent, children)pairs starting fromroot_node_id.Each yielded tuple contains a parent node and its children in breadth-first traversal order.
Does a breadth-first traversal of the given graph, starting at the node specified by “root_node_id”, and yields (in breadth-first order) a tuple consisting of each of the nodes traversed along with the children of that node.
- get_edge_from_edge_id(edge_id: Hashable) tuple[Hashable, Hashable][source]¶
Return the edge (tuple of node_ids) corresponding to the given edge_id.
Note that in NX, an edge_id is the same as an edge - it is just a tuple of node_ids. However, in RX, an edge_id is an integer, so if you want to get the tuple of node_ids you need to use the edge_id to get that tuple…
- Parameters:
edge_id (Hashable) – The desired edge ID.
- Returns:
The edge’s node IDs.
- Return type:
tuple[Hashable, Hashable]
- get_edge_id_from_edge(edge: tuple[Hashable, Hashable]) Hashable[source]¶
Get the edge_id that corresponds to the given edge.
In RX an edge_id is an integer that designates an edge (an edge is a tuple of node_ids). In NX, an edge_id IS the tuple of node_ids. So, in general, to support both NX and RX, if you want to get access to the edge data for an edge (tuple of node_ids), you need to ask for the edge_id.
- Parameters:
edge (tuple[Hashable, Hashable]) – A tuple of node IDs.
- Returns:
The ID associated with the edge.
- Return type:
Hashable
- get_nx_graph() networkx.Graph[Hashable, _AttributeDict, _AttributeDict][source]¶
Return the embedded NX graph object.
- Return type:
- get_nx_to_rx_node_id_map() dict[Hashable, Hashable][source]¶
Return the dict that maps NX node_ids to RX node_ids.
The primary use case for this routine is to support automatically converting NX-based graph objects to be RX-based when creating a Partition object. The issue is that when you convert from NX to RX the node_ids change and so you need to update the Partition object’s Assignment to use the new RX node_ids. This routine is used to translate those NX node_ids to the new RX node_ids when initializing a Partition object.
- Returns:
NetworkX node IDs mapped to RustworkX node IDs.
- Return type:
dict[Hashable, Hashable]
- get_rx_graph() PyGraph[dict[str, Any], dict[str, Any]][source]¶
Return the embedded RX graph object.
- Return type:
- internal_node_id_for_original_nx_node_id(original_nx_node_id: Hashable) Hashable[source]¶
Return corresponding “internal” node_id.
Discover the “internal” node_id in the current GerryChain graph that corresponds to the “original” node_id in the top-level graph (presumably an NX-based graph object).
This was originally created to facilitate testing where it was convenient to express the test success criteria in terms of “original” node_ids, but the actual test needed to be made using the “internal” (RX) node_ids.
- Parameters:
original_nx_node_id (Hashable) – The original node ID.
- Returns:
The corresponding internal node ID.
- Return type:
Hashable
- is_a_tree() bool[source]¶
Return whether the current graph is a tree - meaning that it is connected and that it.
This method returns whether the current graph is a tree - meaning that it is connected and that it. It returns whether the current graph is a tree.
- Returns:
Whether the current graph is a tree
- Return type:
- is_connected() bool[source]¶
Return whether the (undirected) graph is connected.
Delegates to the backend’s native connectivity routine -
rustworkx.is_connectedfor an RX graph,networkx.is_connectedfor an NX graph - which are faster than hand-rolled Python traversal.A graph with 0 or 1 nodes is treated as trivially connected. This also guards the backend calls, both of which raise on an empty graph (rustworkx
NullGraph/ networkxNetworkXPointlessConcept).- Returns:
True if the graph is connected (or has at most one node).
- Return type:
- is_directed() bool[source]¶
Returns False, because GerryChain graphs are not directed.
This is used by low level routines that can operate on both directed and un-directed graphs, that is, it exists so that we can use off-the-shelf code that needs to know if the graph is directed or not.
- Returns:
False
- Return type:
- is_node_set_connected(nodes: Iterable[Hashable]) bool[source]¶
Return whether the given set of nodes induces a connected subgraph of this graph.
This is a fast path for connectivity checks. It hands the node set straight to the backend’s subgraph constructor and runs the native connectivity routine on the result, skipping the
Graphwrapper and the node-id translation maps thatsubgraph()builds - none of which are needed to answer a yes/no connectivity question.A set of 0 or 1 nodes is treated as trivially connected, mirroring
is_connected().- Parameters:
nodes (Iterable[Hashable]) – Node IDs to check.
- Returns:
- True if the nodes induce a connected subgraph (or there are at most one
of them).
- Return type:
- property islands: set[Hashable]¶
Return A set of all node_ids for nodes of degree 0.
Return a set of all node_ids that are not connected via an edge to any other node in the graph - that is, nodes with degree = 0
- Returns:
Node IDs for nodes of degree zero.
- Return type:
set[Hashable]
- join(dataframe: DataFrame, columns: list[str] | None = None, left_index: str | None = None, right_index: str | None = None) None[source]¶
Add data from a dataframe to the graph, matching nodes to rows when the node’s.
Add data from a dataframe to the graph, matching nodes to rows when the node’s left_index attribute equals the row’s right_index value.
- Parameters:
dataframe (DataFrame) – DataFrame.
columns (list[str] | None, optional) – The columns whose data you wish to add to the graph. If not provided, all columns are added. Default is None.
left_index (str | None, optional) – The node attribute used to match nodes to rows. If not provided, node IDs are used. Default is None.
right_index (str | None, optional) – The DataFrame column name to use to match rows to nodes. If not provided, the DataFrame’s index is used. Default is None.
- laplacian_matrix() csr_array[source]¶
Return a SciPy sparse array containing the Laplacian matrix for the given graph.
For more details on the Graph Laplacian matrix, please refer to - https://fanchung.ucsd.edu/research/cb/ch1.pdf - https://en.wikipedia.org/wiki/Laplacian_matrix
- Returns:
A SciPy sparse array containing the Laplacian matrix
- Return type:
scipy.sparse.csr_array
- minimum_spanning_tree_from_edge_weight(edge_weight_attribute_name: str) Graph[source]¶
Computes and returns the minimum spanning tree give the edge weights.
This method computes and returns the minimum spanning tree give the edge weights. It returns a Graph object containing the miniumum spanning tree given the edge weights.
- neighbors(node_id: Hashable) Sequence[Hashable][source]¶
Return a sequence of neighbor node_ids.
Return a sequence of the node_ids of the nodes that are neighbors of the given node - that is, all of the nodes that are directly connected to the given node by an edge.
The result supports iteration (repeatedly),
len(), and indexing, but it is not guaranteed to be alist. Callers that need list methods should wrap it inlist().The neighbors are returned in a deterministic order. RX collects neighbors into a randomly seeded HashSet, so the raw
rustworkx.NodeIndicesorder varies call to call; any seeded algorithm that maps RNG draws over neighbor order (a random walk, a BFS feeding a random choice) would otherwise be unreproducible.- Parameters:
node_id (Hashable) – A node ID.
- Returns:
The neighboring node IDs.
- Return type:
Sequence[Hashable]
- node_data(node_id: Hashable) dict[str, Any][source]¶
Return the data dictionary that contains the given node’s data.
As docmented elsewhere, in GerryChain code before the conversion to RustworkX, users could access node data using the syntax:
graph.nodes[node_id][attribute_name]
This was because a GerryChain Graph object in that codebase was a subclass of NetworkX.Graph, and NetworkX was clever and implemented dict-like behavior for the syntax graph.nodes[]…
This Python cleverness was not carried over to the RustworkX implementation, so in the current GerryChain Graph implementation users need to access node data using the syntax:
graph.node_data(node_id)[attribute_name]
- property node_indices: set[Hashable]¶
Return a
setof the node_ids in the graph.This is the canonical accessor for the graph’s node_ids. It returns a
set, so it is suited to membership tests (node in graph.node_indices) and de-duplication, but it is unordered. If you need an ordered, indexable sequence of the same node_ids, usenodes(which returns alist). Prefernode_indicesunless list semantics are specifically required.- Returns:
An unordered set of node IDs in the graph.
- Return type:
set[Hashable]
- property nodes: list[Hashable]¶
Return a
listof all of the node_ids in the graph.This returns the same node_ids as
node_indices, the difference being only the container type:nodesreturns an ordered, indexablelistmaintaining the backend’s node insertion order whilenode_indicesreturns an unorderedset. Prefernode_indicesunless you specifically need list semantics (ordering or indexing).Note the related distinction for edges:
edgesreturns the edges themselves (tuples of node_ids), whereasedge_indicesreturns edge ids (integers under RustworkX). That object-vs-id distinction is load-bearing for edges, but for nodes the node and its id coincide, which is whynodesandnode_indicescarry the same content.There is also a minor subtlety that users are unlikely to encounter unless accessing the graph attribute off of a Partition object, but it is worth noting: the node_ids in the graph attribute of a Partition object are not necessarily the same as the node_ids in the original graph that was used to create the Partition object. Before the move to RustworkX it was common to create nodes whose ids were either meaningful (e.g., the FIPS code of a VTD), a subset of node ids of another graph (when the graph was a subgraph), or some coordinate pairs (common with grid graphs). That is not true of RustworkX node_ids, so any code that relies on the semantics of a node’s id (treating it like a name) is suspect in the RustworkX world.
- Returns:
An ordered list of node IDs in the graph.
- Return type:
list[Hashable]
- normalized_laplacian_matrix() csr_array[source]¶
Return a SciPy sparse array containing the normalized Laplacian matrix for the given.
graph. For more details on the normalized Graph Laplacian matrix, please refer to - https://fanchung.ucsd.edu/research/cb/ch1.pdf - https://en.wikipedia.org/wiki/Laplacian_matrix#Laplacian_matrix_normalization_2
- Returns:
A SciPy sparse array containing the normalized Laplacian matrix
- Return type:
scipy.sparse.csr_array
- num_connected_components() int[source]¶
Return the number of connected components.
Note: A connected component is a maximal subgraph where every vertex is reachable from every other vertex in that same subgraph. In a graph that is not fully connected, connected components are the separate, distinct “islands” of connected nodes. Every node in a graph belongs to exactly one connected component.
- Returns:
The number of connected components
- Return type:
- original_nx_node_id_for_internal_node_id(internal_node_id: Hashable) Hashable[source]¶
Translate a node_id to its “original” node_id.
- Parameters:
internal_node_id (Hashable) – A node ID to translate.
- Returns:
The translated node ID.
- Return type:
Hashable
- original_nx_node_ids_for_list(list_of_node_ids: list[Hashable]) list[Hashable][source]¶
Translate a list of node_ids to their “original” node_ids.
- original_nx_node_ids_for_set(set_of_node_ids: set[Hashable]) set[Hashable][source]¶
Translate a set of node_ids to their “original” node_ids.
- predecessors(root_node_id: Hashable) dict[Hashable, Hashable][source]¶
Return A dict mapping each node_id to the node_id of its parent node.
Returns a dict mapping each node_id in the graph to its predecessor node_id where the parent/child relationship is created by doing a breadth-first traversal of the graph starting at the root_node_id.
Note that the description above is exactly the same description as the description for generic_bfs_predecessors().
The only difference between this routine and generic_bfs_predecessors() is that this routine delegates to a built-in NetworkX routine in the case when the embedded graph object is NX-based. The assumption is that the built-in NetworkX implementation is faster.
In the case of an RX-based graph, this code delegates to generic_bfs_predecessors().
- Parameters:
root_node_id (Hashable) – Root node of the breadth-first traversal.
- Returns:
Node IDs mapped to their parent node IDs.
- Return type:
dict[Hashable, Hashable]
- subgraph(nodes: Iterable[Hashable]) Graph[source]¶
Create a subgraph that contains the given nodes.
Note that creating a subgraph of an RustworkX (RX) graph renumbers the nodes, so that a node that had node_id: 4 in the parent graph might have node_id: 2 in the subgraph. This is a HUGE difference from the NX world where the node_ids in a subgraph do not change from those in the parent graph.
In order to make sense of the nodes in a subgraph in the RX world, we need to maintain mappings from the node_ids in the subgraph to the node_ids of the immediate parent graph and to the “original” top-level graph that contains all of the nodes. You will notice the creation of those maps in the code below.
- Parameters:
nodes (Iterable[Hashable]) – Nodes to include in the subgraph.
- Returns:
A subgraph containing the given nodes.
- Return type:
‘Graph’
- subgraphs_for_connected_components() list[Graph][source]¶
Create and return a list of subgraphs for each set of nodes in the given graph that are.
connected. Note that a connected graph is one in which there is a path from every node in the graph to every other node in the graph.
Note also that each of the subgraphs returned is a maximal subgraph of connected components, meaning that there is no other larger subgraph of connected components that includes it as a subset.
- Returns:
- A list of “maximal” subgraphs each of which contains nodes that are
connected.
- Return type:
list[‘Graph’]
- successors(root_node_id: Hashable) dict[Hashable, list[Hashable]][source]¶
Return a dictionary mapping each node to a list of its children.
Does a breadth-first traversal of the given graph, starting at the node specified by “root_node_id”, and returns a dict mapping parent node_ids to a list of the node_ids for that node’s children.
Note that the above is the exact same description as the description for generic_bfs_successors(). In fact, for NX-based graphs, this routine just delegates to NetworkX for the result.
The reason for the delegation to NetworkX is the presumption that the NX version would be faster - which may or may not actually be the case.
- to_json(json_file_name: str, include_geometries_as_geojson: bool = False) None[source]¶
Write this
Graphto disk as a JSON file in NetworkX “adjacency” format.This is the inverse of
from_json(): it serializes the graph - every node and edge, with all of their attributes - to the node-link “adjacency” format produced bynetworkx.readwrite.json_graph.adjacency_data, so a file written here can be read back withfrom_json(). Seefrom_json()for a description of the on-disk structure.- Backend requirement (NetworkX only):
to_jsoncurrently only works on a NetworkX-backed graph and raisesTypeErrorotherwise. This matters in practice because wrapping a graph in aPartitionconverts it to the RustworkX backend, so the graph reached viapartition.graphcannot be serialized directly - keep a reference to the original NetworkXGraph(e.g. the one returned byfrom_json()/from_file()) if you need to write it out. (Serializing an RX-backed graph is a planned enhancement.)- Geometries:
Graphs built from geospatial data (
from_file()/from_geodataframe()) carry ashapelygeometry object on each node, which is not JSON-serializable. Theinclude_geometries_as_geojsonflag decides what happens to those geometry attributes (it has no effect on graphs that have no geometry):False(default): geometry attributes are dropped from the output. The file is smaller, but the geometry is not saved, so a round-trip throughfrom_json()will not recover it.True: each geometry is converted to GeoJSON (its__geo_interface__) and written into the file, preserving it. Note that reading the file back returns those attributes as GeoJSON dicts, not asshapelyobjects.
- Non-JSON-native values:
Attribute values that the standard JSON encoder cannot handle are passed through
json_serialize(), which converts pandas / numpy integer values to plain Pythonint(a common result of building a graph from a GeoDataFrame).
- Parameters:
- Returns:
The graph is written to
json_file_name.- Return type:
None
- Raises:
Example
>>> from gerrychain import Graph >>> graph = Graph.from_json("./my_state.json") >>> graph.to_json("./my_state_copy.json")
- to_networkx_graph() networkx.Graph[Hashable, _AttributeDict, _AttributeDict][source]¶
Create a NetworkX.Graph object that has the same nodes, edges, node_data, and edge_data. as the GerryChain Graph object.
The intended purpose of this routine is to allow a user to run a MarkovChain - which uses an embedded RustworkX graph and then extract an equivalent version of that graph with all of its data as a NetworkX.Graph object - in order to use NetworkX routines to access and manipulate the graph.
In short, this routine allows users to use NetworkX functionality on a graph after running a MarkovChain.
If the GerryChain graph object is NX-based, then this routine merely returns the embedded NetworkX.Graph object.
- Returns:
- A NetworkX.Graph object that is equivalent to the GerryChain Graph
object (nodes, edges, node_data, edge_data)
- Return type:
- translate_subgraph_node_ids_for_flips(flips: dict[Hashable, Hashable]) dict[Hashable, Hashable][source]¶
Translate the given flips so that the subgraph node_ids in the flips correspond to the appropriate node_ids in the parent graph.
The flips parameter is a dict mapping node_ids to parts (districts).
This routine is used when a computation that creates flips is made on a subgraph, but those flips want to be translated into the context of the parent graph at the end of the computation.
For more details, refer to the larger comment on subgraphs…
- translate_subgraph_node_ids_for_set_of_nodes(set_of_nodes: Set[Hashable]) set[Hashable][source]¶
Translate the given set_of_nodes to have the appropriate node_ids for the parent graph.
This routine is used when a computation that creates a set of nodes is made on a subgraph, but those nodes want to be translated into the context of the parent graph at the end of the computation.
For more details, refer to the larger comment on subgraphs…
- Parameters:
set_of_nodes (AbstractSet[Hashable]) – Node IDs in a subgraph.
- Returns:
- Node IDs translated to have the IDs appropriate
for the parent graph
- Return type:
set[Hashable]
- verify_graph_is_valid(thorough: bool | None = None) bool[source]¶
Verify that the graph is internally consistent.
Two levels of checking are performed:
An always-on, O(1) structural invariant: a Graph must embed exactly one backing graph - either NetworkX or RustworkX, never both or neither. This runs automatically at every construction boundary and costs nothing measurable, so it is never gated.
An opt-in, O(nodes + edges) thorough audit (see
_verify_graph_thoroughly). This is expensive enough to matter inside a chain, so it is off by default and controlled by the global runtime-checks switch (gerrychain.set_runtime_checks/gerrychain.runtime_checks). The test suite enables it.
- Parameters:
thorough (bool | None) – Whether to run the expensive audit. If
None(default), follows the global runtime-checks setting. PassTrueto force it on orFalseto force it off (e.g. on hot construction paths that build many short-lived subgraphs).- Returns:
Trueif the graph is valid.- Return type:
- Raises:
GraphValidationError – If the graph fails a check.