Cycle Walk
Overview
CycleWalk.jl is a registered Julia package that implements the Metropolized Cycle Walk algorithm which is used to sample a user-specified distribution on the space of political redistricting plans. This MCMC algorithm is used to create an ensemble of redistricting plans that can be used to analyze the impact of different redistricting plans on electoral outcomes.
Metropolized Cycle Walk supports number of different score/energy functions which are used to define the distribution. The distribution encodes the legal and policy preferences.
Metropolized Cycle Walk outputs the sampled redistricting into an Atlas file. AtlasIO files can be loaded using Julia or Python using the AtlasIO.jl library.
The Metropolized Cycle Walk Algorithm
The basic Cycle Walk produced \(d\)-tree spanning forests where each of the \(d\)-spanning trees is approximately balanced in the sense that the total population of each tree is approximately balanced.
One step of the Cycle Walk proceeds by either proposing a 1-Tree Cycle Walk or a 2-Tree Cycle Walk. The 1-Tree Cycle Walk adds an edge to the tree and then removes an edge from the cycle this addition creates so that one again has a tree. The 2-Tree Cycle Walk adds two edges between two adjacent trees and then removes two edges from the cycle these additions create so that one again has two trees.
The Metropolized Cycle Walk algorithm uses these walks as proposals to a Metropolis-Hastings algorithm to sample from a specified target distribution.
More details on the algorithm can be found in (DeFord et al. 2025).
Installation
The latest released version of the CycleWalk.jl package can be installed from within Julia by
using Pkg
Pkg.add("CycleWalk")This can be done from the commandline in a terminal with
julia -e 'using Pkg; Pkg.add("CycleWalk")'If you have not installed Julia, it is recommended that you install the juliaup command and use it to install julia. Direction can be found here: juliaup .
The CycleWalk code can be found in CycleWalk GitHub Repo. However, it is easier and better to install CycleWalk using the Julia Package manager as discussed above.
Local Environments
It is recommended that you run your Julia session in a local environment to minimize unanticipated side effects from other (unneeded) packages. To add the CycleWalk package to a local environment in the current working directory
using Pkg
Pkg.activate(".") # replace this path to keep the local
# environment somewhere else or with a specified name
Pkg.add("CycleWalk")Examples
In each example, we will step through the commands task by task. At the end of the section we will collect all of the commands into a complete script you can copy and execute.
A first example: 4x4 rectangular grid
Our first example will be on a small 4x4 rectangular graph. We choose this example because it executes fast and produces relatively small files that can be examined by hand. The first step is to download the needed JSON adjacency file. For this example it is call grid_graph_4_by_4.json and was discussed more fully in this Section on JSON Adjacency Files. You can download the file to current directory using the following commandline at the terminal
curl https://jonmjonm.github.io/QGDocs/Geo/Adjacency/grid_graph_4_by_4.json -o grid_graph_4_by_4.jsonNow that we have the needed JSON adjacency file, open a Julia session in the directory with that datafile. We begin by activating out local environment where we have installed the CycleWalk Package and loading the CycleWalk and RandomNumbers packages. If you have not installed the RandomNumbers package you need to execute Pkg.add("RandomNumbers") in a julia window. Make sure you have already activated the environment you intend to use.
using Pkg
Pkg.activate(".") # activate environment
using RandomNumbers # load Random Number package
using CycleWalk # load Cycle Walk packageBuild the Graph
To load the graph we begin by setting the path to the adjacency file; called grid_graph_4_by_4.json in this case. Next we create a set holding all of the variable names we want to load from the adjacency file. Lastly, we build the graph by specifying the file name, the variable that holds th population and the nodes names, and all of the node data you want load. You need to specify which of the node data names hold the area, border length, and edge parameter data.
## build graph
pctGraphPath = joinpath(".","grid_graph_4_by_4.json")
nodeData = Set(["node_name", "county", "population", "area", "border_length"]);
graph = Graph(pctGraphPath, "population", "node_name"; inc_node_data=nodeData,
area_col="area", node_border_col="border_length",
edge_perimeter_col="length")The variable graph is a structure that contains all of the adjacency data as well as all of the data on the vertices and edges.
Initialize a random Forest Partitions with specified Constraints
We now define the constraints on the phasespace. We specify the number of connected districts in our forest partition. We will use the term Forest Partition with \(d\) elements to describe a spanning forest with \(d\) disjoint spanning trees. The vertices/nodes in each spanning tree will be the elements of the partition of vertices/nodes induced by the Forest Partition. Beyond the number of constraints, in this case we will also add an absolute constraint on the population deviation. We will require all partitions (also referred to as distractings) to have elements whose relative population deviation from the ideal population is less than allowed_pop_dev. The ideal population is calculated by \[\text{Ideal Population}=\frac{\text{Total Population}}{\text{Number of Districts}}\] then the relative population deviation of the \(i\)th district is given by \[ \text{$i$th relative population deviation}= \frac{\big|\text{$i$th District Population} - \text{Ideal Population}\big|}{\text{Ideal Population}}\]
num_of_districts=2 # Number of districts
allowed_pop_dev=0.05 # population deviation (fraction from ideal)
# initialize constraints
constraints = initialize_constraints()
# add population constraint
add_constraint!(constraints, PopulationConstraint(graph, num_of_districts,
allowed_pop_dev))
# initialize a random initial partition
rng = PCG.PCGStateOneseq(UInt64, 4541901234) # initialize a random number generator
partition = LinkCutPartition(graph, constraints, num_of_districts; rng=rng,
verbose=true);There are other choices of constraints one can introduce which we will discuss later. The LinkCutPartition function tries to generate though a random algorithm a partition with num_of_districts districts that satisfies the specified constraints. It tires up to on the order of 100 times and returns the first acceptable partition generated. If all of the attempts fail, it throws an error message.
Defining the Measure
Next we define the target measure we want to sample from. In this simple example we will only consider log spanning forest energy that we will denote by \(h(\xi)\) for a given partition \(\xi\). It is defined by \(h(\xi)=\log \text{Tree}(\xi)\) where \(\text{Tree}(\xi)\) is the number spanning forest which induce the given partition \(\xi\). When the parameter \(\gamma\) bellow is taken to be zero then the measure is uniform on spanning forests while \(\gamma=1\) corresponds to uniform on partitions. Normally, it is not wise to consider the uniform measure on partitions without an isoparmetric energy in the measure. However on a small graph like this, it does not matter.
measure = Measure() # build measure
gamma=0.0 # typically a number in [0,1]
push_energy!(measure, get_log_spanning_forests, gamma) # add spanning forests energyBuild the Proposal for Metropolis–Hastings MCMC
We now build the Markov Chain that will be used as a proposal in a Metropolis–Hastings Algorithm to construct a markov chains guaranteed to have the measure constructed above as a stationary measure.
cycle_walk_2_tree = build_two_tree_cycle_walk(constraints)
cycle_walk_1_tree = build_one_tree_cycle_walk(constraints)
twocycle_frac=0.1 # fraction of 2-tree cycle walks among total walks
proposal = [(twocycle_frac, cycle_walk_2_tree),
(1.0-twocycle_frac, cycle_walk_1_tree)]Choose the Output
We now establish the name of the output file as well as fix a few parameters we want to be written to the file in addition to the vertex/node assignment that makes the current partition.
atlasName = "cycleWalk"*"_grid4x4_" * "_gamma" * string(gamma)*"_kappa"*string(twocycle_frac) *".jsonl"
output_file_path = joinpath("output","grid", atlasName) Next we construct a map writer that will output the current districting as well as some selected statistics about the districting and the run so far.
ad_param = Dict{String, Any}("popdev" => allowed_pop_dev) # specific info to write
writer = Writer(measure, constraints, partition, output_file_path;
additional_parameters=ad_param)
push_writer!(writer, get_log_spanning_trees) # add spanning trees count to writer
push_writer!(writer, get_log_spanning_forests) # add spanning forests count to writer
push_writer!(writer, get_isoperimetric_scores) # add isoperimetric scores to writerRun the Markov Chain
We now run the metropolized Cycle Walk with the proposal we constructed above.
cycle_walk_2_tree_steps = 100
steps = Int(cycle_walk_2_tree_steps/twocycle_frac)
outfreq = Int(1/twocycle_frac)println("running mcmc; outputting here: "* output_file_path)
run_metropolis_hastings!(partition, proposal, measure, steps, rng,
writer=writer, output_freq=outfreq)
close_writer(writer) # close atlasPutting Everything Together
using Pkg
Pkg.activate(".") # activate environment
using RandomNumbers # load Random Number package
using CycleWalk # load Cycle Walk package
## build graph
pctGraphPath = joinpath(".","grid_graph_4_by_4.json")
nodeData = Set(["node_name", "county", "population", "area", "border_length"]);
graph = Graph(pctGraphPath, "population", "node_name"; inc_node_data=nodeData,
area_col="area", node_border_col="border_length",
edge_perimeter_col="length")
num_of_districts=2 # Number of districts
allowed_pop_dev=0.05 # population deviation (fraction from ideal)
# initialize constraints
constraints = initialize_constraints()
# add population constraint
add_constraint!(constraints, PopulationConstraint(graph, num_of_districts,
allowed_pop_dev))
# initialize a random initial partition
rng = PCG.PCGStateOneseq(UInt64, 4541901234) # initialize a random number generator
partition = LinkCutPartition(graph, constraints, num_of_districts; rng=rng,
verbose=true);
measure = Measure() # build measure
gamma=0.0 # typically a number in [0,1]
push_energy!(measure, get_log_spanning_forests, gamma) # add spanning forests energy
cycle_walk_2_tree = build_two_tree_cycle_walk(constraints)
cycle_walk_1_tree = build_one_tree_cycle_walk(constraints)
twocycle_frac=0.1 # fraction of 2-tree cycle walks among total walks
proposal = [(twocycle_frac, cycle_walk_2_tree),
(1.0-twocycle_frac, cycle_walk_1_tree)]
#set output file name
atlasName = "cycleWalk"*"_grid4x4_" * "_gamma" * string(gamma)*"_kappa"*string(twocycle_frac) *".jsonl"
output_file_path = joinpath("output","grid", atlasName)
# create writer and open output atlas
ad_param = Dict{String, Any}("popdev" => allowed_pop_dev) # specific info to write
writer = Writer(measure, constraints, partition, output_file_path;
additional_parameters=ad_param)
push_writer!(writer, get_log_spanning_trees) # add spanning trees count to writer
push_writer!(writer, get_log_spanning_forests) # add spanning forests count to writer
push_writer!(writer, get_isoperimetric_scores) # add isoperimetric scores to writer
# Run the MCMC
cycle_walk_2_tree_steps = 100
steps = Int(cycle_walk_2_tree_steps/twocycle_frac)
outfreq = Int(1/twocycle_frac)
println("running mcmc; outputting here: "* output_file_path)
run_metropolis_hastings!(partition, proposal, measure, steps, rng,
writer=writer, output_freq=outfreq)
close_writer(writer) # close atlasAfter you run the script, look at the file generate. It should be inside the directory output/grid/ and named something like cycleWalk_grid4x4__gamma0.0_kappa0.1.jsonl. It should look something like the following file. Of course the details will be different as your random seed might be different. Notice that the first three lines describe the Atlas file while the fourth line on are the individual maps that were saved.
"This is an Atlas for Redistricting Maps. See 'https://git.math.duke.edu/gitlab/jonm/atlasio.jl' for more information."
{"description":"","date":"2025-09-07T15:39:21.934","atlasParamType":"Dict{String, Any}","mapParamType":"Dict{String, Any}"}
{"energy weights":[],"population bounds":[8,8],"energies":[],"package.version":"CycleWalk v0.1.3","districts":2,"popdev":0.05}
{"name":"step1","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":1}]}
{"name":"step10","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":1}]}
{"name":"step20","weight":1,"data":{"get_log_spanning_forests":5.411646051855039,"get_log_spanning_trees":[4.025351690735149,1.3862943611198901],"get_isoperimetric_scores":[18.0,32.0]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step30","weight":1,"data":{"get_log_spanning_forests":5.411646051855039,"get_log_spanning_trees":[4.025351690735149,1.3862943611198901],"get_isoperimetric_scores":[18.0,32.0]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step40","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step50","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step60","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step70","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step80","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step90","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step100","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step110","weight":1,"data":{"get_log_spanning_forests":5.416100402204422,"get_log_spanning_trees":[2.708050201102211,2.708050201102211],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step120","weight":1,"data":{"get_log_spanning_forests":5.416100402204422,"get_log_spanning_trees":[2.708050201102211,2.708050201102211],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step130","weight":1,"data":{"get_log_spanning_forests":5.416100402204422,"get_log_spanning_trees":[2.708050201102211,2.708050201102211],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step140","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step150","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step160","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step170","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step180","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step190","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step200","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step210","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step220","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step230","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step240","weight":1,"data":{"get_log_spanning_forests":5.411646051855042,"get_log_spanning_trees":[1.3862943611198915,4.0253516907351505],"get_isoperimetric_scores":[32.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step250","weight":1,"data":{"get_log_spanning_forests":4.02535169073515,"get_log_spanning_trees":[4.440892098500626e-16,4.025351690735149],"get_isoperimetric_scores":[40.5,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":1}]}
{"name":"step260","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step270","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step280","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step290","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step300","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step310","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step320","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step330","weight":1,"data":{"get_log_spanning_forests":5.41610040220442,"get_log_spanning_trees":[2.7080502011022105,2.7080502011022096],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step340","weight":1,"data":{"get_log_spanning_forests":5.41610040220442,"get_log_spanning_trees":[2.7080502011022105,2.7080502011022096],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step350","weight":1,"data":{"get_log_spanning_forests":5.416100402204422,"get_log_spanning_trees":[2.7080502011022123,2.7080502011022096],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":2},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":1},{"[\"(1,3)\"]":1}]}
{"name":"step360","weight":1,"data":{"get_log_spanning_forests":5.416100402204422,"get_log_spanning_trees":[2.7080502011022123,2.7080502011022096],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":2},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":1},{"[\"(1,3)\"]":1}]}
{"name":"step370","weight":1,"data":{"get_log_spanning_forests":5.41610040220442,"get_log_spanning_trees":[2.7080502011022105,2.7080502011022096],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step380","weight":1,"data":{"get_log_spanning_forests":5.41610040220442,"get_log_spanning_trees":[2.7080502011022105,2.7080502011022096],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step390","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step400","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step410","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step420","weight":1,"data":{"get_log_spanning_forests":5.416100402204419,"get_log_spanning_trees":[2.7080502011022096,2.7080502011022096],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step430","weight":1,"data":{"get_log_spanning_forests":4.094344562222103,"get_log_spanning_trees":[2.7080502011022105,1.3862943611198921],"get_isoperimetric_scores":[24.5,32.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step440","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step450","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step460","weight":1,"data":{"get_log_spanning_forests":4.0943445622221,"get_log_spanning_trees":[1.3862943611198908,2.7080502011022096],"get_isoperimetric_scores":[32.0,24.5]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step470","weight":1,"data":{"get_log_spanning_forests":4.0943445622221,"get_log_spanning_trees":[1.3862943611198908,2.7080502011022096],"get_isoperimetric_scores":[32.0,24.5]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step480","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step490","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step500","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step510","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step520","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step530","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step540","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step550","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step560","weight":1,"data":{"get_log_spanning_forests":4.094344562222101,"get_log_spanning_trees":[2.7080502011022105,1.3862943611198906],"get_isoperimetric_scores":[24.5,32.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step570","weight":1,"data":{"get_log_spanning_forests":4.094344562222101,"get_log_spanning_trees":[2.7080502011022105,1.3862943611198906],"get_isoperimetric_scores":[24.5,32.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step580","weight":1,"data":{"get_log_spanning_forests":4.094344562222101,"get_log_spanning_trees":[2.7080502011022105,1.3862943611198906],"get_isoperimetric_scores":[24.5,32.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step590","weight":1,"data":{"get_log_spanning_forests":4.094344562222101,"get_log_spanning_trees":[2.7080502011022105,1.3862943611198906],"get_isoperimetric_scores":[24.5,32.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step600","weight":1,"data":{"get_log_spanning_forests":4.094344562222101,"get_log_spanning_trees":[2.7080502011022105,1.3862943611198906],"get_isoperimetric_scores":[24.5,32.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step610","weight":1,"data":{"get_log_spanning_forests":4.094344562222101,"get_log_spanning_trees":[2.7080502011022105,1.3862943611198906],"get_isoperimetric_scores":[24.5,32.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step620","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step630","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step640","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":1}]}
{"name":"step650","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":1}]}
{"name":"step660","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step670","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":1}]}
{"name":"step680","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":1}]}
{"name":"step690","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":1}]}
{"name":"step700","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":1},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":2},{"[\"(1,0)\"]":2},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":1},{"[\"(1,3)\"]":2}]}
{"name":"step710","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":1},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":2},{"[\"(1,0)\"]":2},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":1},{"[\"(1,3)\"]":2}]}
{"name":"step720","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":1},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":2},{"[\"(1,0)\"]":2},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":1},{"[\"(1,3)\"]":1}]}
{"name":"step730","weight":1,"data":{"get_log_spanning_forests":4.094344562222103,"get_log_spanning_trees":[2.7080502011022114,1.3862943611198912],"get_isoperimetric_scores":[24.5,32.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":1},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":2},{"[\"(1,0)\"]":2},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":1},{"[\"(1,3)\"]":2}]}
{"name":"step740","weight":1,"data":{"get_log_spanning_forests":4.094344562222103,"get_log_spanning_trees":[2.7080502011022114,1.3862943611198912],"get_isoperimetric_scores":[24.5,32.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":1},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":2},{"[\"(1,0)\"]":2},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":1},{"[\"(1,3)\"]":2}]}
{"name":"step750","weight":1,"data":{"get_log_spanning_forests":5.41610040220442,"get_log_spanning_trees":[2.7080502011022096,2.7080502011022105],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":1},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":2},{"[\"(1,0)\"]":2},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":1},{"[\"(1,3)\"]":1}]}
{"name":"step760","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":1},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":2},{"[\"(1,0)\"]":2},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":1},{"[\"(1,3)\"]":1}]}
{"name":"step770","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":1},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":2},{"[\"(1,0)\"]":2},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":1},{"[\"(1,3)\"]":1}]}
{"name":"step780","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":1},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":2},{"[\"(1,0)\"]":2},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":1},{"[\"(1,3)\"]":1}]}
{"name":"step790","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":1},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":2},{"[\"(1,0)\"]":2},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":1},{"[\"(1,3)\"]":1}]}
{"name":"step800","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":2},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":1},{"[\"(3,1)\"]":2},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":1},{"[\"(0,3)\"]":1},{"[\"(0,0)\"]":2},{"[\"(1,0)\"]":2},{"[\"(2,1)\"]":2},{"[\"(1,2)\"]":1},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":1},{"[\"(1,3)\"]":1}]}
{"name":"step810","weight":1,"data":{"get_log_spanning_forests":5.41610040220442,"get_log_spanning_trees":[2.7080502011022096,2.7080502011022105],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":1},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":2},{"[\"(1,0)\"]":2},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":1},{"[\"(1,3)\"]":1}]}
{"name":"step820","weight":1,"data":{"get_log_spanning_forests":5.41610040220442,"get_log_spanning_trees":[2.7080502011022096,2.7080502011022105],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":1},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":2},{"[\"(1,0)\"]":2},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":1},{"[\"(1,3)\"]":1}]}
{"name":"step830","weight":1,"data":{"get_log_spanning_forests":5.41610040220442,"get_log_spanning_trees":[2.7080502011022096,2.7080502011022105],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":1},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":2},{"[\"(1,0)\"]":2},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":1},{"[\"(1,3)\"]":1}]}
{"name":"step840","weight":1,"data":{"get_log_spanning_forests":4.094344562222101,"get_log_spanning_trees":[2.7080502011022096,1.3862943611198917],"get_isoperimetric_scores":[24.5,32.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":1},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":2},{"[\"(1,0)\"]":2},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":1},{"[\"(1,3)\"]":2}]}
{"name":"step850","weight":1,"data":{"get_log_spanning_forests":4.094344562222101,"get_log_spanning_trees":[2.7080502011022096,1.3862943611198917],"get_isoperimetric_scores":[24.5,32.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":1},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":2},{"[\"(1,0)\"]":2},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":1},{"[\"(1,3)\"]":2}]}
{"name":"step860","weight":1,"data":{"get_log_spanning_forests":4.094344562222101,"get_log_spanning_trees":[2.7080502011022096,1.3862943611198917],"get_isoperimetric_scores":[24.5,32.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":1},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":2},{"[\"(1,0)\"]":2},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":1},{"[\"(1,3)\"]":2}]}
{"name":"step870","weight":1,"data":{"get_log_spanning_forests":4.094344562222101,"get_log_spanning_trees":[2.7080502011022096,1.3862943611198917],"get_isoperimetric_scores":[24.5,32.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":2},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":1},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":2},{"[\"(1,0)\"]":2},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":1},{"[\"(1,3)\"]":2}]}
{"name":"step880","weight":1,"data":{"get_log_spanning_forests":5.41610040220442,"get_log_spanning_trees":[2.7080502011022105,2.7080502011022096],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step890","weight":1,"data":{"get_log_spanning_forests":5.41610040220442,"get_log_spanning_trees":[2.7080502011022105,2.7080502011022096],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step900","weight":1,"data":{"get_log_spanning_forests":5.41610040220442,"get_log_spanning_trees":[2.7080502011022105,2.7080502011022096],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step910","weight":1,"data":{"get_log_spanning_forests":5.416100402204419,"get_log_spanning_trees":[2.7080502011022096,2.7080502011022096],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step920","weight":1,"data":{"get_log_spanning_forests":5.416100402204419,"get_log_spanning_trees":[2.7080502011022096,2.7080502011022096],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step930","weight":1,"data":{"get_log_spanning_forests":5.416100402204419,"get_log_spanning_trees":[2.7080502011022096,2.7080502011022096],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step940","weight":1,"data":{"get_log_spanning_forests":5.41610040220442,"get_log_spanning_trees":[2.7080502011022105,2.7080502011022096],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step950","weight":1,"data":{"get_log_spanning_forests":5.41610040220442,"get_log_spanning_trees":[2.7080502011022105,2.7080502011022096],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":2},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":1},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step960","weight":1,"data":{"get_log_spanning_forests":5.416100402204419,"get_log_spanning_trees":[2.7080502011022096,2.7080502011022096],"get_isoperimetric_scores":[24.5,24.5]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":2},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":1},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step970","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step980","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step990","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}
{"name":"step1000","weight":1,"data":{"get_log_spanning_forests":8.0507033814703,"get_log_spanning_trees":[4.02535169073515,4.02535169073515],"get_isoperimetric_scores":[18.0,18.0]},"districting":[{"[\"(3,0)\"]":1},{"[\"(0,1)\"]":1},{"[\"(2,0)\"]":1},{"[\"(0,2)\"]":2},{"[\"(3,1)\"]":1},{"[\"(1,1)\"]":1},{"[\"(3,2)\"]":2},{"[\"(3,3)\"]":2},{"[\"(0,3)\"]":2},{"[\"(0,0)\"]":1},{"[\"(1,0)\"]":1},{"[\"(2,1)\"]":1},{"[\"(1,2)\"]":2},{"[\"(2,2)\"]":2},{"[\"(2,3)\"]":2},{"[\"(1,3)\"]":2}]}A second example: 10x10 hexagonal grid
We now consider a small variation on our previous example. We consider a planner region with a regular triangulation. The dual/adjacency graph is a hexagonal lattice. We begin by downloading the needed JSON adjacency file to our current directory.
curl https://jonmjonm.github.io/QGDocs/Geo/Adjacency/hex_graph_10_by_10.json -o hex_graph_10_by_10.jsonA Modified Script
We now give code to run on the 10x10 hexagonal lattice. It will only require some small modifications from the previous example given above. Beyond the obvious such as modifying the input and output files, we will also increase the number of districts to 4 and decrease the allowed population deviation. This last step is possible as the number of element in each district increases providing finer granularity. We will also introduce an isodiametric score in the target measure to keep the districts compact no matter what constant is placed in front of the log spanning forests term in the energy. Recall that as that constant goes to one, the log spanning forests energy causes the probability measure to converge to the uniform measure on partitions if no other weights are used. The uniform measure on partitions is full of space-filling partitions that are very, very non-compact.
using Pkg
Pkg.activate(".") # activate environment
using RandomNumbers # load Random Number package
using CycleWalk # load Cycle Walk package
## build graph
pctGraphPath = joinpath(".","hex_graph_10_by_10.json")
nodeData = Set(["node_name", "county", "population", "area", "border_length"]);
graph = Graph(pctGraphPath, "population", "node_name"; inc_node_data=nodeData,
area_col="area", node_border_col="border_length",
edge_perimeter_col="length")
num_of_districts=5 # Number of districts
allowed_pop_dev=0.02 # population deviation (fraction from ideal)
# initialize constraints
constraints = initialize_constraints()
# add population constraint
add_constraint!(constraints, PopulationConstraint(graph, num_of_districts,
allowed_pop_dev))
# initialize a random initial partition
rng = PCG.PCGStateOneseq(UInt64, 4541901234) # initialize a random number generator
partition = LinkCutPartition(graph, constraints, num_of_districts; rng=rng,
verbose=true);
measure = Measure() # build measure
gamma=0.0 # typically a number in [0,1]
iso_weight= 0.3 # weight on the sum of isoperimetric ratios; i.e. Polsby-Popper
push_energy!(measure, get_log_spanning_forests, gamma) # add spanning forests energy
push_energy!(measure, get_isoperimetric_score, iso_weight) # add isoperimetric score energy
cycle_walk_2_tree = build_two_tree_cycle_walk(constraints)
cycle_walk_1_tree = build_one_tree_cycle_walk(constraints)
twocycle_frac=0.1 # fraction of 2-tree cycle walks among total walks
proposal = [(twocycle_frac, cycle_walk_2_tree),
(1.0-twocycle_frac, cycle_walk_1_tree)]
#set output file name
atlasName = "cycleWalk"*"_hex10x10_" * "_gamma" * string(gamma)*"_kappa"
atlasName*= string(twocycle_frac)*"_iso"*string(iso_weight)*".jsonl"
output_file_path = joinpath("output","grid", atlasName)
# create writer and open output atlas
ad_param = Dict{String, Any}("popdev" => allowed_pop_dev) # specific info to write
writer = Writer(measure, constraints, partition, output_file_path;
additional_parameters=ad_param)
push_writer!(writer, get_log_spanning_trees) # add spanning trees count to writer
push_writer!(writer, get_log_spanning_forests) # add spanning forests count to writer
push_writer!(writer, get_isoperimetric_scores) # add isoperimetric scores to writer
# Run the MCMC
cycle_walk_2_tree_steps = 100
steps = Int(cycle_walk_2_tree_steps/twocycle_frac)
outfreq = Int(1/twocycle_frac)
println("running mcmc; outputting here: "* output_file_path)
run_metropolis_hastings!(partition, proposal, measure, steps, rng,
writer=writer, output_freq=outfreq)
close_writer(writer) # close atlasThe lines change are 7, 13, 14, 27, 29, 38 and 39. Of these the most important of the non-cosmetic changes is the introduction of the isoparmetric score in line 29.
A small realistic example: Connecticut Congressional Districts.
We now consider a smaller but realistic example of generating district maps for Connecticut with five congressional districts. As before, we begin by downloading the needed JSON adjacency file for Connecticut.
curl https://jonmjonm.github.io/QGDocs/Geo/Adjacency/CT_pct20.json -o CT_pct20.jsonimport Pkg
Pkg.activate(".")
using RandomNumbers
using CycleWalk
twocycle_frac = 0.1
gamma = 0.0 # 0 is spanning forest measure, 1 is partition
iso_weight = 0.3 # weight on the sum of isoperimetric ratios; i.e. Polsby-Popper
num_dists = 5
@assert 0 ≤ twocycle_frac ≤ 1
rng = PCG.PCGStateOneseq(UInt64, 4541901234)
pop_dev = 0.02 # population deviation (fraction from ideal)
cycle_walk_steps = 10^4
steps = Int(cycle_walk_steps/twocycle_frac)
outfreq = Int(400/twocycle_frac)
## build graph
pctGraphPath = joinpath(".","CT_pct20.json")
nodeData = Set(["COUNTY", "NAME", "POP20", "area", "border_length"]);
graph = Graph(pctGraphPath, "POP20", "NAME"; inc_node_data=nodeData,
area_col="area", node_border_col="border_length",
edge_perimeter_col="length")
## build partition
constraints = initialize_constraints()
add_constraint!(constraints, PopulationConstraint(graph, num_dists, pop_dev))
partition = LinkCutPartition(graph, constraints, num_dists; rng=rng,
verbose=true);
## build proposal
cycle_walk_2_tree = build_two_tree_cycle_walk(constraints)
cycle_walk_1_tree = build_one_tree_cycle_walk(constraints)
proposal = [(twocycle_frac, cycle_walk_2_tree),
(1.0-twocycle_frac, cycle_walk_1_tree)]
## build measure
measure = Measure()
push_energy!(measure, get_log_spanning_forests, gamma) # add spanning forests energy
push_energy!(measure, get_isoperimetric_score, iso_weight) # add isoperimetric score energy
## establish output name and path
atlasName = "cycleWalk_ct_kappa"*string(twocycle_frac)
atlasName *= "_gamma"*string(gamma)
atlasName *= "_iso"*string(iso_weight)
atlasName *= ".jsonl.gz" # or just ".jsonl" for an uncompressed output
output_file_path = joinpath("output","ct", atlasName) # add output directory to path
## establish writer to which the output will be written
ad_param = Dict{String, Any}("popdev" => pop_dev) # specific info to write
writer = Writer(measure, constraints, partition, output_file_path;
additional_parameters=ad_param)
push_writer!(writer, get_log_spanning_trees) # add spanning trees count to writer
push_writer!(writer, get_log_spanning_forests) # add spanning forests count to writer
push_writer!(writer, get_isoperimetric_scores) # add isoperimetric scores to writer
## run MCMC sampler
println("running mcmc; outputting here: "* output_file_path)
run_metropolis_hastings!(partition, proposal, measure, steps, rng,
writer=writer, output_freq=outfreq)
close_writer(writer) # close atlasA larger realistic example: North Carolina Congressional Districts.
We now consider a larger realistic example of generating district maps for North Carolina with 14 congressional districts. As before, we begin by downloading the needed JSON adjacency file for North Carolina.
curl https://raw.githubusercontent.com/jonmjonm/QGDocs/refs/heads/main/Geo/Adjacency/NC_pct21.json -o NC_pct21.jsonUnlike the previous example, there is a small complication with the JSON Adjacency file Geo/Adjacency/NC_pct21.json. There is no field that can be used for the nodes/vertices that is a string and unique. The id field is a unique identifier but it is an integer. The prec_id field is only unique with in each county. There are several ways to remedy this. We choose to combine the county with the prec_id to create a new field called county_and_prec_id.
Unlike the previous example, we can not load the JSON Adjacency in a single shot to produce an adjacency graph object. First we load the file in to sample base graph.
base_graph = BaseGraph(pctGraphPath, "pop2020cen", inc_node_data=nodeData,
area_col="area", node_border_col="border_length",
edge_perimeter_col="length", edge_weights="connections");Next we loop through the nodes and create the new new field called county_and_prec_id.
for ii = 1:length(base_graph.node_attributes)
county = base_graph.node_attributes[ii]["county"]
prec_id = base_graph.node_attributes[ii]["prec_id"]
name = county*"_"*prec_id
base_graph.node_attributes[ii]["county_and_prec_id"] = name
endLastly we load basic graph into an adjacency graph object.
graph = Graph(base_graph, "county_and_prec_id"); We now place this modification in a script that is a small modification of the script we used for Connecticut. What follows is the full processing script.
import Pkg
Pkg.activate(".")
using RandomNumbers
using CycleWalk
twocycle_frac = 0.1
gamma = 0.0 # 0 is spanning forest measure, 1 is partition
iso_weight = 0.45 # weight on the sum of isoperimetric ratios; i.e. Polsby-Popper
num_dists = 14
@assert 0 ≤ twocycle_frac ≤ 1
rng = PCG.PCGStateOneseq(UInt64, 4541901234)
pop_dev = 0.02 # population deviation (fraction from ideal)
cycle_walk_steps = 10^3
steps = Int(cycle_walk_steps/twocycle_frac)
outfreq = Int(400/twocycle_frac)
## build graph
pctGraphPath = joinpath(".","NC_pct21.json")
nodeData = Set(["county","prec_id", "pop2020cen", "area", "border_length"]);
# load in base graph
base_graph = BaseGraph(pctGraphPath, "pop2020cen", inc_node_data=nodeData,
area_col="area", node_border_col="border_length",
edge_perimeter_col="length", edge_weights="connections");
# combines county and precinct id
for ii = 1:length(base_graph.node_attributes)
county = base_graph.node_attributes[ii]["county"]
prec_id = base_graph.node_attributes[ii]["prec_id"]
name = county*"_"*prec_id
base_graph.node_attributes[ii]["county_and_prec_id"] = name
end
# now that the field "county_and_prec_id" is set, we can use it to create the
# graph object that we will sample on
graph = Graph(base_graph,"county_and_prec_id");
## build partition
constraints = initialize_constraints()
add_constraint!(constraints, PopulationConstraint(graph, num_dists, pop_dev))
partition = LinkCutPartition(graph, constraints, num_dists; rng=rng,
verbose=true);
## build proposal
cycle_walk_2_tree = build_two_tree_cycle_walk(constraints)
cycle_walk_1_tree = build_one_tree_cycle_walk(constraints)
proposal = [(twocycle_frac, cycle_walk_2_tree),
(1.0-twocycle_frac, cycle_walk_1_tree)]
## build measure
measure = Measure()
push_energy!(measure, get_log_spanning_forests, gamma) # add spanning forests energy
push_energy!(measure, get_isoperimetric_score, iso_weight) # add isoperimetric score energy
## establish output name and path
atlasName = "cycleWalk_nc_kappa"*string(twocycle_frac)
atlasName *= "_gamma"*string(gamma)
atlasName *= "_iso"*string(iso_weight)
atlasName *= ".jsonl.gz" # or just ".jsonl" for an uncompressed output
output_file_path = joinpath("output","nc", atlasName) # add output directory to path
## establish writer to which the output will be written
ad_param = Dict{String, Any}("popdev" => pop_dev,"kappa"=>twocycle_frac,"iso_weight"=>iso_weight,"gamma"=>gamma) # specific info to write
writer = Writer(measure, constraints, partition, output_file_path;
additional_parameters=ad_param)
push_writer!(writer, get_log_spanning_trees) # add spanning trees count to writer
push_writer!(writer, get_log_spanning_forests) # add spanning forests count to writer
push_writer!(writer, get_isoperimetric_scores) # add isoperimetric scores to writer
## run MCMC sampler
println("running mcmc; outputting here: "* output_file_path)
run_metropolis_hastings!(partition, proposal, measure, steps, rng,
writer=writer, output_freq=outfreq)
close_writer(writer) # close atlasRunning from a configuration file
Every example above writes the run out in Julia. A run can instead be described in a TOML configuration file and executed by examples/run_cyclewalk_toml.jl, which is how ensembles are usually produced on a cluster: one file describes the run, and the parts that vary between runs are given on the command line.
julia run_cyclewalk_toml.jl toml/param_ct.tomlThe configuration is embedded in the Atlas header the run writes, so a sampled ensemble carries the description of how it was produced.
What follows describes a configuration file as it is normally written. The complete reference — every key of every table with its type and default, the energies and observables a file may name, and what each error message means — is docs/run_cyclewalk_toml.md in the repository, alongside ready-to-run configurations in examples/toml.
The configuration file
[plans]
num_dists = 4
pop_dev = 0.0001
pop_col = "population"
geo_units = ["node_name"]
node_data = ["county", "node_name", "population", "area", "border_length"]
map_directory = ["data", "grid"]
map_file = "grid_graph_4_by_4.json"
area_col = "area" # optional; needed for isoperimetric scores
node_border_col = "border_length" # optional
edge_perimeter_col = "length" # optional
[mcmc]
cycle_walk_steps = 5e8
two_cycle_walk_frac = 0.3 # fraction of 2-tree proposals
[measure]
gamma = 1.0
iso_weight = 0.3
[[measure.energy]]
name = "get_log_spanning_forests"
weight = "gamma"
[[measure.energy]]
name = "get_isoperimetric_score"
weight = "iso_weight"
[run]
thread_id = 1 # seeds the RNG and names the file
atlasNameBase = "grid4x4_cycleWalk"
outputDirectory = ["output", "grid"]
cycle_walk_out_freq = 100
writer_stats = ["get_log_spanning_trees"] # observables written with each map
compress = "gz" # omit for an uncompressed .jsonl
run_diagnostics = true
# output_districting = false # optional; record only the statistics
# io_mode = "a" # optional; append rather than truncate
# description = "…" # optional; free text in the Atlas header
# rng_seed_base = 454190 # optional; seed = rng_seed_base + 15123*thread_idThe four tables answer four questions: which map and how many districts ([plans]), how long to run and with which proposals ([mcmc]), what measure to sample ([measure]), and where the output goes ([run]).
Both step counts are given in two-tree steps and converted using the proposal mix, so that changing two_cycle_walk_frac does not change how much sampling a configuration asks for: the chain runs cycle_walk_steps / two_cycle_walk_frac steps in total and records a map every cycle_walk_out_freq / two_cycle_walk_frac of them. This is the arithmetic written by hand at line steps = Int(cycle_walk_2_tree_steps/twocycle_frac) in the examples above.
The measure
Each [[measure.energy]] block adds one energy to the target measure, exactly as a push_energy! call does in the scripts above — including the rule that an energy whose weight is zero is left out of the measure. A file with gamma = 0.0 and iso_weight = 0.0 therefore samples the plain spanning-forest measure, and the Atlas header of such a run records an empty energy list.
| Key | Meaning |
|---|---|
name |
An energy function exported by CycleWalk, or a builder returning one. |
weight |
A number, or arithmetic over the parameters in [measure]. |
weight_start |
The weight at the start of an annealing schedule (see Section 5.5); absent means the energy does not anneal. |
args / kwargs |
Arguments for a builder. |
context |
Values the runner supplies rather than the file — "graph", "base_graph", "num_dists", "pop_col" — passed before args. |
desc |
The label recorded in the Atlas header. |
Any number in the [measure] table is a named parameter that a weight may be written in terms of. This is what lets several energies move together, and what makes a parameter sweepable from the command line:
[measure]
gamma = 1.0
vra_weight = 2.0
[[measure.energy]]
name = "get_log_spanning_forests"
weight = "gamma"
[[measure.energy]]
name = "get_log_district_trees"
weight = "2*gamma + 1" # moves with gamma
[[measure.energy]]
name = "build_get_partisan_seats"
args = ["G20PREDEM", "G20PREREP"]
weight = "vra_weight"
desc = "Dem seats (G20 pres)"A weight expression may use + - * / ^, numbers, and the names of parameters in [measure] — nothing else. Expressions are interpreted rather than evaluated as Julia, so a configuration file cannot run code, which matters because these files are read back out of Atlas headers written elsewhere.
desc is worth setting on a built energy such as build_get_partisan_seats: a builder returns a closure rather than a named function, so its automatic label in the Atlas header carries no useful description of what was built — and a builder may appear several times in one measure with different arguments, which only the labels tell apart. The vote columns a builder names, like any column an energy reads, must be listed in node_data.
An energy has to return a single number per districting. The scalar energies a configuration may name are get_log_spanning_forests, get_isoperimetric_score, get_log_district_trees, get_log_linking_edges, and the built build_get_partisan_seats. The per-district functions — get_log_spanning_trees, get_isoperimetric_scores, get_diameters, get_average_degrees and the rest — return a vector and belong in writer_stats, which records them with each map rather than weighting them into the target. writer_stats takes bare names only, so an observable that has to be built from arguments, such as build_get_partisan_margins, must be registered in a script with push_writer! as in the examples above.
Two parameter names are not arbitrary. gamma is the weight in front of get_log_spanning_forests and iso_weight the weight in front of get_isoperimetric_score, in the sense used throughout Section 4.1.3 — and the output file is named with them, so they are read back off the measure that was built rather than from the keys themselves.
Overriding on the command line
Values in the file can be replaced per run, which is how a sweep is driven without writing a configuration file per point:
# named flags for the most common parameters
julia run_cyclewalk_toml.jl toml/param_ct.toml --thread_id 7 --gamma 0.5
# --set reaches any key in any table, and is repeatable
julia run_cyclewalk_toml.jl toml/param_ct.toml \
--set measure.vra_weight=2.0 --set mcmc.cycle_walk_steps=1e6Values given to --set are read as TOML, so they get the types they would have had in the file. Setting the same value with both a flag and --set is an error rather than one silently winning, and the table must already exist, so a mistyped table name is caught rather than creating a new section.
The overrides are recorded in the Atlas header, since the embedded configuration file by itself would not show them.
Output file names
The output name is assembled from atlasNameBase, the thread id, the proposal mix, and the measure’s parameters — gamma, iso_weight, and every other parameter a weight expression reads:
grid4x4_cycleWalk_thread1_cyclewalkVS_2treeCycleWalk_0.3_gamma1.0_vra_weight2.0.jsonl.gz
A parameter that no expression reads cannot change the target, so it does not appear. This keeps the points of a sweep in separate files. As a backstop, a run that would overwrite an existing Atlas stops rather than truncating it; pass --overwrite to replace it deliberately, or set io_mode = "a" in [run] to append.
Annealed runs
The annealed importance sampling and annealed SMC runners (run_ais_toml.jl, run_asmc_toml.jl) read the same [plans], [measure] and [run] tables, with each energy’s weight as the target it anneals to and weight_start as the base it anneals from. A base of zero recovers the spanning-forest base measure.
[[measure.energy]]
name = "get_log_spanning_forests"
weight = "gamma"
weight_start = 0.0Each adds one table of its own — [ais] (the base-chain and annealing step counts) or [smc] (particle count, tempering schedule, rejuvenation) — for the settings that only that sampler has. See examples/toml/param_ais_ct.toml and examples/toml/param_annealed_smc_grid.toml.
An energy annealed down to zero is the one case where a zero weight does not drop the energy: with a nonzero weight_start it is kept, since a schedule cannot ramp an energy the measure does not contain.
Extending an existing ensemble
More samples can be added to an Atlas, restarting the chain from the last plan it recorded, with examples/run_cyclewalk_extend.jl:
julia run_cyclewalk_extend.jl output/grid/<atlas>.jsonl.gz --add_cycle_walk_steps 1e5The configuration is read back out of the Atlas header, so the extension samples the same measure on the same graph; --burn_in discards a given number of steps before recording again. An Atlas records a districting rather than a spanning forest, so the forest is redrawn on restart exactly as a fresh run’s initial partition is.
The earlier measure format (deprecated)
Configuration files written before the [[measure.energy]] blocks listed the energies by name and weighted two of them with gamma and iso_weight:
[measure]
measure_scores = ["get_log_spanning_forests", "get_isoperimetric_score"]
gamma = 1.0
iso_weight = 0.3
[measure.weights] # for any score gamma/iso_weight do not cover
get_log_district_trees = "2*gamma + 1"This form still works and existing files need not be changed. Every Atlas the sampler has written embeds its configuration, and the extension script reads those back to resume a chain, so files in this form have to keep running indefinitely. It is read as exactly the equivalent list of energies — "get_log_spanning_forests" becomes an energy weighted by the expression "gamma" — rather than being handled by a separate code path.
It is deprecated for new configurations because it cannot express what the newer form can: a weight for any energy beyond the two named ones without a second table, an energy built from arguments such as build_get_partisan_seats, a label for such an energy, or an annealing start. A file may use one form or the other, not both.