def updaters_MC(settings): my_updaters = { "cut_edges": cut_edges, "population": updaters.Tally("TOTPOP", alias = "population"), "avg_pop_dist": avg_pop_dist, "pop_dist_pct" : pop_dist_pct, "area_land": updaters.Tally("ALAND10", alias = "area_land"), "area_water": updaters.Tally("AWATER10", alias = "area_water"), "Perimeter": updaters.Tally("perimeter", alias = "Perimeter"), "Area": updaters.Tally("area", alias = "Area")} num_elections = settings['num_elections'] election_names = settings['election_names'] election_columns = settings['election_columns'] num_steps = settings['num_steps'] interval = settings['interval'] pop_tol = settings['pop_tol'] elections = [ Election( election_names[i], {"Democratic": election_columns[i][0], "Republican": election_columns[i][1]}, ) for i in range(num_elections)] election_updaters = {election.name: election for election in elections} my_updaters.update(election_updaters) return(my_updaters)
def test(): graph_path = "./Data/PA_VTDALL.json" graph = Graph.from_json(graph_path) k = 18 ep = 0.05 unit_id = "GEOID10" pop_col = "TOT_POP" plot_path = "./Data/VTD_FINAL" unit_df = gpd.read_file(plot_path) division_col = "COUNTYFP10" divisions = unit_df[[division_col, 'geometry']].dissolve(by=division_col, aggfunc='sum') updaters = {"population": updaters.Tally(pop_col, alias="population")} cddict = recursive_tree_part(graph, range(k), df[pop_col].sum() / k, pop_col, .01, node_repeats=1) initial_partition = Partition(graph, cddict, updaters) ideal_population = sum( initial_partition["population"].values()) / len(initial_partition) division_proposal = partial(recom, pop_col=pop_col, pop_target=ideal_population, epsilon=0.05, method=partial(division_bipartition_tree, division_col=division_col), node_repeats=2) chain = MarkovChain(proposal=division_proposal, constraints=[ constraints.within_percent_of_ideal_population( initial_partition, 0.05), ], accept=accept.always_accept, initial_state=initial_partition, total_steps=1000) t = 0 snapshot = 100 for part in chain: if t % snapshot == 0: draw_graph(graph, part.assignment, unit_df, divisions, './chain_' + str(t) + '.png', geo_id=unit_id) t += 1
def test_pa_freeze(): from gerrychain import ( GeographicPartition, Graph, MarkovChain, proposals, updaters, constraints, accept, ) import hashlib from gerrychain.proposals import recom from functools import partial graph = Graph.from_json("docs/user/PA_VTDs.json") my_updaters = {"population": updaters.Tally("TOTPOP", alias="population")} initial_partition = GeographicPartition(graph, assignment="CD_2011", updaters=my_updaters) ideal_population = sum( initial_partition["population"].values()) / len(initial_partition) # We use functools.partial to bind the extra parameters (pop_col, pop_target, epsilon, node_repeats) # of the recom proposal. proposal = partial(recom, pop_col="TOTPOP", pop_target=ideal_population, epsilon=0.02, node_repeats=2) pop_constraint = constraints.within_percent_of_ideal_population( initial_partition, 0.02) chain = MarkovChain( proposal=proposal, constraints=[pop_constraint], accept=accept.always_accept, initial_state=initial_partition, total_steps=100, ) result = "" for count, partition in enumerate(chain): result += str(list(sorted(partition.population.values()))) result += str(len(partition.cut_edges)) result += str(count) + "\n" assert hashlib.sha256(result.encode()).hexdigest( ) == "3bef9ac8c0bfa025fb75e32aea3847757a8fba56b2b2be6f9b3b952088ae3b3c"
def run_GerryChain_heuristic(G, population_deviation, k, iterations): my_updaters = {"population": updaters.Tally("TOTPOP", alias="population")} start = recursive_tree_part( G, range(k), sum(G.nodes[i]["TOTPOP"] for i in G.nodes()) / k, "TOTPOP", population_deviation / 2, 1) initial_partition = GeographicPartition(G, start, updaters=my_updaters) proposal = partial(recom, pop_col="TOTPOP", pop_target=sum(G.nodes[i]["TOTPOP"] for i in G.nodes()) / k, epsilon=population_deviation / 2, node_repeats=2) compactness_bound = constraints.UpperBound( lambda p: len(p["cut_edges"]), 1.5 * len(initial_partition["cut_edges"])) pop_constraint = constraints.within_percent_of_ideal_population( initial_partition, population_deviation / 2) my_chain = MarkovChain(proposal=proposal, constraints=[pop_constraint, compactness_bound], accept=accept.always_accept, initial_state=initial_partition, total_steps=iterations) min_cut_edges = sum(G[i][j]['edge_length'] for i, j in G.edges) print("In GerryChain heuristic, current # of cut edges: ", end='') print(min_cut_edges, ",", sep='', end=' ') for partition in my_chain: current_cut_edges = sum(G[i][j]['edge_length'] for i, j in partition["cut_edges"]) print(current_cut_edges, ",", sep='', end=' ') if current_cut_edges < min_cut_edges: best_partition = partition min_cut_edges = current_cut_edges print("Best heuristic solution has # cut edges =", min_cut_edges) return ([[i for i in G.nodes if best_partition.assignment[i] == j] for j in range(k)], min_cut_edges)
def run_chain(adj_graph, elections, total_steps=100): my_updaters = { "population": updaters.Tally("population"), "cut_edges": cut_edges, "elections": lambda x: [elec.alias for elec in elections], "expected_seat_share": expected_seat_share_updater } election_updaters = {election.name: election for election in elections} my_updaters.update(election_updaters) initial_partition = GeographicPartition(adj_graph, assignment="initial_plan", updaters=my_updaters) ideal_population = sum( initial_partition["population"].values()) / len(initial_partition) proposal = partial(recom, pop_col="population", pop_target=ideal_population, epsilon=0.01, node_repeats=2) pop_constraint = constraints.within_percent_of_ideal_population( initial_partition, 0.01) chain = MarkovChain(proposal=proposal, constraints=[ pop_constraint, ], accept=accept.always_accept, initial_state=initial_partition, total_steps=total_steps) return pd.DataFrame({ ix: { "cut_edges": len(partition["cut_edges"]), "expected_R_seats": partition["expected_seat_share"] } for ix, partition in enumerate(chain) }).T
def main(graph_json, shp, n_steps, output_dir, prefix, seed, pop_col, pop_tol, plan_col, reproject, election): os.makedirs(output_dir, exist_ok=True) has_geometry = False if not shp and not graph_json: print('Specify a shapefile or a NetworkX-format graph ' 'JSON file.', file=sys.stderr) sys.exit(1) elif shp and not graph_json: gdf = gpd.read_file(shp) if reproject: gdf = reprojected(gdf) graph = Graph.from_geodataframe(gdf) has_geometry = True elif graph_json and not shp: graph = Graph.from_json(graph_json) else: graph = Graph.from_json(graph_json) gdf = gpd.read_file(shp) if reproject: gdf = reprojected(gdf) print('Appending geometries from shapefile to graph...') graph.geometry = gdf.geometry # TODO: is this always valid? has_geometry = True my_updaters = {'population': updaters.Tally(pop_col, alias='population')} if election: election_up = Election('election', { 'Democratic': election[0], 'Republican': election[1] }) my_updaters['election'] = election_up initial_state = GeographicPartition(graph, assignment=plan_col, updaters=my_updaters) normal_chain = RecomChain(graph=graph, total_steps=n_steps, initial_state=initial_state, pop_col=pop_col, pop_tol=pop_tol, reversible=False, seed=seed) reversible_chain = RecomChain(graph=graph, total_steps=n_steps, initial_state=initial_state, pop_col=pop_col, pop_tol=pop_tol, reversible=True, seed=seed) normal_plans = [plan for plan in tqdm(normal_chain)] reversible_plans = [plan for plan in tqdm(reversible_chain)] cut_edges_fig(output_dir, prefix, normal_plans, reversible_plans) longest_boundary_fig(output_dir, prefix, normal_plans, reversible_plans) if has_geometry: demo_plans(output_dir, '_'.join([prefix, 'recom']), normal_plans, n_steps, n_steps // 25) demo_plans(output_dir, '_'.join([prefix, 'reversible_recom']), reversible_plans, n_steps, n_steps // 25) if election: election_hists(output_dir, 'dem_vote_share', 'election', 'Democratic', normal_plans, reversible_plans) acceptance_stats(output_dir, '_'.join([prefix, 'recom']), normal_plans) acceptance_stats(output_dir, '_'.join([prefix, 'reversible_recom']), reversible_plans)
def subset_list(percentage_list, lower_threshold, upper_threshold): percentage_intermediate = percentage_list[ percentage_list >= lower_threshold] return len( percentage_intermediate[percentage_intermediate < upper_threshold]) # ---- Load in graph ------\ graph = Graph.from_file(path_to_shapefile) #graph = Graph.from_json(path_to_shapefile) #graph = gpd.read_file(path_to_shapefile) # ---- Updaters & Elections ----- updaters = { "population": updaters.Tally(pop_col, alias="population"), "cut_edges": cut_edges, "BVAP": updaters.Tally(BVAP_col, alias="BVAP"), "HVAP": updaters.Tally(HVAP_col, alias="HVAP"), "AVAP": updaters.Tally(AVAP_col, alias="AVAP"), "NVAP": updaters.Tally(Native_col, alias="NVAP"), "PVAP": updaters.Tally(Pacific_col, alias="PVAP"), "VAP": updaters.Tally(VAP_col, alias="VAP"), "county_splits": county_splits("county_splits", county_assignment_col) } num_elections = len(election_names) elections = [ Election( election_names[i],
election_names = [ "PRES12", "PRES16", "SENW101216", ] election_columns = [ ["PRES12D", "PRES12R"], ["T16PRESD", "T16PRESR"], ["W101216D", "W101216R"], ] graph = Graph.from_json(graph_path) updaters = { "population": updaters.Tally("TOT_POP", alias="population"), "cut_edges": cut_edges, } elections = [ Election( election_names[i], { "Democratic": election_columns[i][0], "Republican": election_columns[i][1] }, ) for i in range(num_elections) ] election_updaters = {election.name: election for election in elections}
whole_start = time.time() # Initialization df = gpd.read_file("./AK_precincts_ns/AK_precincts_ns/AK_precincts_ns.shp") df["nAMIN"] = df["TOTPOP"] - df["AMIN"] elections = [ Election("GOV18x", {"Democratic": "GOV18D_x", "Republican": "GOV18R_x"}), Election("USH18x", {"Democratic": "USH18D_x", "Republican": "USH18R_x"}), Election("GOV18ns", {"Democratic": "GOV18D_NS", "Republican": "GOV18R_NS"}), Election("USH18ns", {"Democratic": "USH18D_NS", "Republican": "USH18R_NS"}), Election("Native_percent", {"Native": "AMIN", "nonNative": "nAMIN"}), ] my_updaters = { "population": updaters.Tally("POPULATION", alias="population"), "cut_edges": cut_edges, } election_updaters = {election.name: election for election in elections} my_updaters.update(election_updaters) # Construct Dual Graphs # Tightest Alaska print("Building Tight Graph") G_tight = Graph.from_file("./AK_precincts_ns/AK_precincts_ns/AK_precincts_ns.shp")
def plan_evaluation(partition): graph = partition.graph # Naming constants county_column = "COUNTY" municipality_column = "TOWN" # Contiguity split_districts = [] for district in partition.parts.keys(): if district != -1: part_contiguous = nx.is_connected(partition.subgraphs[district]) if not part_contiguous: split_districts.append(district) contiguity = (len(split_districts) == 0) # Calculate cut edges cut_edges = updaters.cut_edges(partition) # Polsby Popper try: polsbypopper = [ v for _k, v in metrics.polsby_popper(partition).items() ] polsbypopper_stats = { 'max': max(polsbypopper), 'min': min(polsbypopper), 'mean': statistics.mean(polsbypopper), 'median': statistics.median(polsbypopper) } if len(polsbypopper) > 1: polsbypopper_stats['variance'] = statistics.variance(polsbypopper) except: polsbypopper = [] polsbypopper_stats = "Polsby Popper unavailable for this geometry." # county splits try: county_splits = updaters.county_splits("county_splits", county_column)(partition) county_response = {} counties = set([graph.nodes[n][county_column] for n in graph.nodes]) county_response['num_counties'] = len(counties) try: county_partition = GeographicPartition( graph, county_column, {"population": updaters.Tally('TOTPOP', alias="population")}) county_pop_dict = { c: county_partition.population[c] for c in counties } county_response['population'] = county_pop_dict except KeyError: county_response['population'] = -1 split_list = {} splits = 0 num_split = 0 for c in counties: s = county_splits[c].contains # this is caused by a bug in some states in gerrychain I think if -1 in s: s.remove(-1) if len(s) > 1: num_split += 1 split_list[c] = list(s) splits += len(s) - 1 county_response['splits'] = splits county_response['split_list'] = split_list except: county_response = -1 # municipality splits try: municipality_splits = updaters.county_splits( "muni_splits", municipality_column)(partition) muni_response = {} munis = set([graph.nodes[n][municipality_column] for n in graph.nodes]) muni_response['num_counties'] = len(munis) try: muni_partition = GeographicPartition( graph, municipality_column, {"population": updaters.Tally('TOTPOP', alias="population")}) muni_pop_dict = {m: muni_partition.population[m] for m in munis} muni_response['population'] = muni_pop_dict muni_response['num_counties'] = len(munis) except KeyError: muni_response['population'] = -1 split_list = {} splits = 0 num_split = 0 for m in munis: s = municipality_splits[m].contains # this is caused by a bug in some states in gerrychain I think if -1 in s: s.remove(-1) if len(s) > 1: num_split += 1 split_list[m] = list(s) splits += len(s) - 1 muni_response['splits'] = splits muni_response['split_list'] = split_list except: muni_response = -1 # Build Response dictionary response = { 'cut_edges': len(cut_edges), 'contiguity': contiguity, 'split': split_districts, 'polsbypopper': polsbypopper_stats, 'pp_scores': polsbypopper, 'num_units': len(graph.nodes), 'counties': county_response, 'municipalities': muni_response } return response
partition.plot(geometries=va_precincts.geometry, cmap='tab20') plt.savefig(newdir + "plot" + str(count) + ".png") plt.close() #Use ReCom to build gerrychain and plot. elections = [ Election("PRES16", { "Dem": "G16DPRS", "Rep": "G16RPRS" }), Election("SEN12", { "Dem": "G18DSEN", "Rep": "G18RSEN" }) ] my_updaters = {"population": updaters.Tally("TOTPOP", alias="population")} election_updaters = {election.name: election for election in elections} my_updaters.update(election_updaters) initial_partition = GeographicPartition(graph, assignment="CD_16", updaters=my_updaters) # The ReCom proposal needs to know the ideal population for the districts so that # we can improve speed by bailing early on unbalanced partitions. ideal_population = sum( initial_partition["population"].values()) / len(initial_partition) # We use functools.partial to bind the extra parameters (pop_col, pop_target, epsilon, node_repeats) # of the recom proposal. proposal = partial(recom, pop_col="TOTPOP",
return parent["step_num"] + 1 def b_nodes_bi(partition): return {x[0] for x in partition["cut_edges"] }.union({x[1] for x in partition["cut_edges"]}) def cut_length(partition): return len(partition["cut_edges"]) updater = { "population": updaters.Tally("TOT_POP"), "cut_edges": cut_edges, "step_num": step_num, 'b_nodes': b_nodes_bi } initial_partition = Partition(g, "GOV", updater) compactness_bound = constraints.UpperBound(cut_length, cut_length(initial_partition)) cols = [ "GOV", "TS", "REMEDIAL_P", "538CPCT__1", "538DEM_PL", "538GOP_PL", "8THGRADE_1" ]
""" return { part: compute_reock(partition, part, partition["area"][part]) for part in partition.parts } # ---- Load in graph ------ #graph = Graph.from_file(path_to_shapefile) #graph = Graph.from_json(path_to_shapefile) #graph = gpd.read_file(path_to_shapefile) # ---- Updaters & Elections ----- updaters = { "population": updaters.Tally(pop_col, alias="population"), "cut_edges": cut_edges, "BVAP": updaters.Tally(BVAP_col, alias="BVAP"), "HVAP": updaters.Tally(HVAP_col, alias="HVAP"), "VAP": updaters.Tally(VAP_col, alias="VAP"), "num_vra_districts": num_vra_districts, "num_opp_districts": num_opp_districts, "county_splits": county_splits("county_splits", county_assignment_col) } num_elections = len(election_names) elections = [ Election( election_names[i], {
fid_to_node = {} f = open(filename, 'r') f.readline() for line in f: lineitems = line.rstrip("\n").split("\t") fid, geoid = int(lineitems[0]), lineitems[1] geoid_to_fid[geoid] = fid for n in graph.nodes: fid_to_node[geoid_to_fid[graph.nodes[n][geoidcol]]] = n return fid_to_node #set up geography and elections pop_col = "TOTPOP" my_updaters = { "population": updaters.Tally(pop_col, alias="population"), "cut_edges": cut_edges, } import os folder = "districtMaps" fid_to_geoid_file = "./preprocessedData/arcgis_FIDtovtdkeymap.txt" geoidcol = "VTD" files = [] pop_col = "TOTPOP" for filename in os.listdir(folder): #print(filename) files.append("./" + folder + "/" + filename)
total_population = sum([graph.nodes[n][pop_col] for n in graph.nodes()]) print("Shapefiles loaded and ready to run ReCom...") ''' Run the chain ''' for k in [num_districts]: HISPs = [] POPs = [] BPOPs = [] pop_target = total_population/k myproposal = partial(recom, pop_col=pop_col, pop_target=pop_target, epsilon=pop_tol/k, node_repeats=2) #updaters myupdaters = { "population": updaters.Tally(pop_col, alias="population"), "HISP": updaters.Tally("HISP", alias="HISP"), "BPOP": updaters.Tally("NH_BLACK", alias="BPOP") } #initial partition initial_ass = littlehelpers.factor_seed(graph, k, pop_tol, pop_col) initial_partition = Partition(graph, initial_ass, myupdaters) #chain compactness_bound = constraints.UpperBound( lambda p: len(p["cut_edges"]), 2*len(initial_partition["cut_edges"]) ) myconstraints = [ constraints.within_percent_of_ideal_population(initial_partition, pop_tol),
def run_GerryChain_heuristic(G, population_deviation, k, threshold, iterations): my_updaters = {"population": updaters.Tally("TOTPOP", alias="population")} start = recursive_tree_part( G, range(k), sum(G.nodes[i]["TOTPOP"] for i in G.nodes()) / k, "TOTPOP", population_deviation / 2, 1) initial_partition = GeographicPartition(G, start, updaters=my_updaters) proposal = partial(recom, pop_col="TOTPOP", pop_target=sum(G.nodes[i]["TOTPOP"] for i in G.nodes()) / k, epsilon=population_deviation / 2, node_repeats=2) compactness_bound = constraints.UpperBound( lambda p: len(p["cut_edges"]), 1.5 * len(initial_partition["cut_edges"])) pop_constraint = constraints.within_percent_of_ideal_population( initial_partition, population_deviation / 2) my_chain = MarkovChain(proposal=proposal, constraints=[pop_constraint, compactness_bound], accept=accept.always_accept, initial_state=initial_partition, total_steps=iterations) min_cut_edges = sum(G[i][j]['edge_length'] for i, j in G.edges) print( "In GerryChain heuristic, current # of cut edges and minority districts: ", end='') print(min_cut_edges, ",", sep='', end=' ') max_of_minority_districts = -1 all_maps = [] pareto_frontier = [] obj_vals = [] for partition in my_chain: current_cut_edges = sum(G[i][j]['edge_length'] for i, j in partition["cut_edges"]) number_minority_district = 0 for district in range(k): total_pop_district = 0 total_pop_minority = 0 for node in partition.graph: if partition.assignment[node] == district: total_pop_district += G.node[node]["VAP"] total_pop_minority += G.node[node]["BVAP"] #total_pop_minority += G.node[node]["HVAP"] #total_pop_minority += G.node[node]["AMINVAP"] #total_pop_minority += G.node[node]["ASIANVAP"] #total_pop_minority += G.node[node]["NHPIVAP"] if (total_pop_minority > threshold * total_pop_district): number_minority_district += 1 if number_minority_district > max_of_minority_districts: max_of_minority_districts = number_minority_district if current_cut_edges < min_cut_edges: min_cut_edges = current_cut_edges print((current_cut_edges, number_minority_district), ",", sep='', end=' ') obj_vals.append([current_cut_edges, number_minority_district]) all_maps.append( [partition, current_cut_edges, number_minority_district]) print("Best heuristic solution has # cut edges =", min_cut_edges) print("Best heuristic solution has # minority districts =", max_of_minority_districts) all_maps.sort(key=lambda x: x[1]) all_maps.sort(key=lambda x: x[2], reverse=True) pareto_frontier.append(all_maps[0]) least_number_of_cut_edges = all_maps[0][1] for i in range(1, len(all_maps)): if all_maps[i][1] < least_number_of_cut_edges: pareto_frontier.append(all_maps[i]) least_number_of_cut_edges = all_maps[i][1] print("Pareto Frontier: ", pareto_frontier) optimal_maps = [] optimal_cut_edges = [] optimal_minority_districts = [] for plan in pareto_frontier: optimal_maps.append( [[i for i in G.nodes if plan[0].assignment[i] == j] for j in range(k)]) optimal_cut_edges.append(plan[1]) optimal_minority_districts.append(plan[2]) return (optimal_maps, optimal_cut_edges, optimal_minority_districts, obj_vals)
centroids = {k: (CXs[k], CYs[k]) for k in list(partition.parts.keys())} return centroids def num_cut_edges(partition): return len(partition["cut_edges"]) def num_county_splits(partition, df=state_gdf): df["current"] = df.index.map(partition.assignment.to_dict()) return sum(df.groupby(county_split_id)['current'].nunique() > 1) #####construct updaters for Chain############################################### my_updaters = { "population": updaters.Tally(tot_pop, alias="population"), "CVAP": updaters.Tally(CVAP, alias="CVAP"), "WCVAP": updaters.Tally(WCVAP, alias="WCVAP"), "HCVAP": updaters.Tally(HCVAP, alias="HCVAP"), "BCVAP": updaters.Tally(BCVAP, alias="BCVAP"), "Sum_CX": updaters.Tally(C_X, alias="Sum_CX"), "Sum_CY": updaters.Tally(C_Y, alias="Sum_CY"), "cut_edges": cut_edges, "num_cut_edges": num_cut_edges, "num_county_splits": num_county_splits, "demo_percents": demo_percents, "final_elec_model": final_elec_model, "centroids": centroids } #add elections updaters
"BVAP", "LTGOV", "GOV", "AG"] election_columns = [ ["VAPBLACK", "nBVAP"], ["D_LTGOV", "R_LTGOV"], ["D_GOV", "R_GOV"], ["D_ATTGEN", "R_ATTGEN"], ["PRES12D", "PRES12R"], ] updater = { "population": updaters.Tally("TAPERSONS", alias="population"), "cut_edges": cut_edges, 'b_nodes':b_nodes, } elections = [ Election( election_names[i], {"First": election_columns[i][0], "Second": election_columns[i][1]}, ) for i in range(num_elections) ] election_updaters = {election.name: election for election in elections} updater.update(election_updaters)
def MC_sample(jgraph, settings, save_part = True): """ :param jgraph: gerrychain Graph object :param settings: settings dictionary (possibly loaded from a yaml file) with election info, MC parameters, and constraints params (see settings.yaml file for an example of the structure needed) :param save_part: True is you want to save the partition as json :returns: a list of partitions sapmpled every interval step """ my_updaters = { "cut_edges": cut_edges, "population": updaters.Tally("TOTPOP", alias = "population"), "avg_pop_dist": avg_pop_dist, "pop_dist_pct" : pop_dist_pct, "area_land": updaters.Tally("ALAND10", alias = "area_land"), "area_water": updaters.Tally("AWATER10", alias = "area_water"), "Perimeter": updaters.Tally("perimeter", alias = "Perimeter"), "Area": updaters.Tally("area", alias = "Area") } num_elections = settings['num_elections'] election_names = settings['election_names'] election_columns = settings['election_columns'] num_steps = settings['num_steps'] interval = settings['interval'] pop_tol = settings['pop_tol'] MC_type = settings['MC_type'] elections = [ Election( election_names[i], {"Democratic": election_columns[i][0], "Republican": election_columns[i][1]}, ) for i in range(num_elections) ] election_updaters = {election.name: election for election in elections} my_updaters.update(election_updaters) initial_partition = Partition(jgraph, "CD", my_updaters) # by typing in "CD," we are saying to put every county into the congressional district that they belong to print('computed initial partition') ideal_population = sum(initial_partition["population"].values()) / len( initial_partition ) pop_constraint = constraints.within_percent_of_ideal_population(initial_partition, pop_tol) compactness_bound = constraints.UpperBound( lambda p: len(p["cut_edges"]), 2 * len(initial_partition["cut_edges"]) ) proposal = partial( recom, pop_col=pop_col, pop_target=ideal_population, epsilon=pop_tol, node_repeats=1) constraints_=[pop_constraint, compactness_bound] if MC_type == "flip": proposal = propose_random_flip constraints_=[single_flip_contiguous, pop_constraint, compactness_bound] chain = MarkovChain( proposal=proposal, constraints=constraints_, accept=always_accept, initial_state=initial_partition, total_steps=num_steps ) partitions=[] # recording partitions at each step for index, part in enumerate(chain): if index % interval == 0: print('Markov chain step '+str(index)) partitions += [part] if save_part: sd.dump_run(settings['partitions_path'], partitions) print('saved partitions to '+ settings['partitions_path']) return(partitions)