def test_exterior_boundaries(three_by_three_grid): graph = three_by_three_grid for i in [0, 1, 2, 3, 5, 6, 7, 8]: graph.nodes[i]["boundary_node"] = True graph.nodes[i]["boundary_perim"] = 2 graph.nodes[4]["boundary_node"] = False assignment = {0: 1, 1: 1, 2: 2, 3: 1, 4: 1, 5: 2, 6: 2, 7: 2, 8: 2} updaters = { "exterior_boundaries": exterior_boundaries, "boundary_nodes": boundary_nodes, } partition = Partition(graph, assignment, updaters) result = partition["exterior_boundaries"] assert result[1] == 6 and result[2] == 10 # 112 111 # 112 -> 121 # 222 222 flips = {4: 2, 2: 1, 5: 1} new_partition = Partition(parent=partition, flips=flips) result = new_partition["exterior_boundaries"] assert result[1] == 10 and result[2] == 6
def test_exterior_boundaries_as_a_set(three_by_three_grid): graph = three_by_three_grid for i in [0, 1, 2, 3, 5, 6, 7, 8]: graph.nodes[i]['boundary_node'] = True graph.nodes[4]['boundary_node'] = False assignment = {0: 1, 1: 1, 2: 2, 3: 1, 4: 1, 5: 2, 6: 2, 7: 2, 8: 2} updaters = { 'exterior_boundaries_as_a_set': exterior_boundaries_as_a_set, 'boundary_nodes': boundary_nodes } partition = Partition(graph, assignment, updaters) result = partition['exterior_boundaries_as_a_set'] assert result[1] == {0, 1, 3} and result[2] == {2, 5, 6, 7, 8} # 112 111 # 112 -> 121 # 222 222 flips = {4: 2, 2: 1, 5: 1} new_partition = Partition(parent=partition, flips=flips) result = new_partition['exterior_boundaries_as_a_set'] assert result[1] == {0, 1, 2, 3, 5} and result[2] == {6, 7, 8}
def merge_parts_chen(partition, k, dist_func, draw_map = False): #until there are k parts, merge adjacent parts with smallest sum assert (len(partition.parts) >= k) while len(partition.parts) > k: part_rand = random.choice(range(len(partition.parts))) # print(partition.centroids[part_rand]) closest_part = (part_rand + 1) % len(partition.parts) dist_min = math.inf for e in partition["cut_edges"]: if partition.assignment[e[0]] == part_rand: i = partition.assignment[e[1]] dist_i = dist_func(partition.centroids[part_rand], partition.centroids[i]) if dist_i < dist_min: closest_part = i dist_min = dist_i if partition.assignment[e[1]] == part_rand: i = partition.assignment[e[0]] dist_i = dist_func(partition.centroids[part_rand], partition.centroids[i]) if dist_i < dist_min: closest_part = i dist_min = dist_i if draw_map: gdf_print_map(partition, './figures/iter_merge_'+str(len(partition.parts))+'.png', gdf, unit_key) merge_dict = {v:part_rand if partition.assignment[v] == closest_part else partition.assignment[v] for v in partition.graph.nodes()} partition = Partition(partition.graph, merge_dict, partition.updaters) keyshift = dict(zip(list(partition.parts.keys()), range(len(partition.parts.keys())))) keydict = {v:keyshift[partition.assignment[v]] for v in partition.graph.nodes()} partition = Partition(partition.graph, keydict, partition.updaters) return partition
def merge_parts_smallest_sum_alt(partition, k): # print('start merge-parts') #until there are k parts, merge adjacent parts with smallest sum assert (len(partition.parts) >= k) neighbor_districts = {} for e in partition["cut_edges"]: pair = (min(partition.assignment[e[0]], partition.assignment[e[1]]),max(partition.assignment[e[0]], partition.assignment[e[1]])) neighbor_districts.update({pair:partition["population"][partition.assignment[e[0]]] + partition["population"][partition.assignment[e[1]]]}) while len(partition.parts) > k: min_sum = min(neighbor_districts.values()) min_key = [key for key in neighbor_districts.keys() if neighbor_districts[key] == min_sum][0] merge_dict = {v:min_key[0] if partition.assignment[v] == min_key[1] else partition.assignment[v] for v in partition.graph.nodes()} partition = Partition(partition.graph, merge_dict, partition.updaters) #update neighbor_districts new_dict = {} for key in neighbor_districts.keys(): if key == min_key: continue elif key[0] == min_key[1]: new_key = (min(min_key[0], key[1]),max(min_key[0], key[1])) elif key[1] == min_key[1]: new_key = (min(min_key[0], key[0]),max(min_key[0], key[0])) elif min_key[0] in [key[0],key[1]]: new_key = key else: new_key = key new_val = partition["population"][new_key[0]] + partition["population"][new_key[1]] new_dict[new_key] = new_val neighbor_districts = new_dict keyshift = dict(zip(list(partition.parts.keys()), range(len(partition.parts.keys())))) keydict = {v:keyshift[partition.assignment[v]] for v in partition.graph.nodes()} return Partition(partition.graph, keydict, partition.updaters)
def discontiguous_partition(): graph = nx.Graph() graph.add_nodes_from(range(4)) graph.add_edges_from([(0, 1), (1, 2), (2, 3)]) partition = Partition(graph, {0: 0, 1: 1, 2: 1, 3: 0}) # This flip will maintain discontiguity. partition.test_flips = {1: 0} return partition
def test_data_tally_gives_expected_value(three_by_three_grid): assignment = {node: 1 for node in three_by_three_grid.nodes} data = {node: 1 for node in three_by_three_grid} updaters = {'tally': DataTally(data, alias='tally')} partition = Partition(three_by_three_grid, assignment, updaters) flip = {list(three_by_three_grid.nodes)[0]: 0} new_partition = partition.merge(flip) assert new_partition['tally'][1] == partition['tally'][1] - 1
def test_data_tally_works_as_an_updater(three_by_three_grid): assignment = random_assignment(three_by_three_grid, 4) data = {node: random.randint(1, 100) for node in three_by_three_grid.nodes} updaters = {'tally': DataTally(data, alias='tally')} partition = Partition(three_by_three_grid, assignment, updaters) flip = { random.choice(list(partition.graph.nodes)): random.choice(range(4)) } new_partition = partition.merge(flip) assert new_partition['tally']
def test_implementation_of_cut_edges_matches_naive_method(three_by_three_grid): graph = three_by_three_grid assignment = {0: 1, 1: 1, 2: 2, 3: 1, 4: 1, 5: 2, 6: 2, 7: 2, 8: 2} partition = Partition(graph, assignment) flip = {4: 2} new_partition = Partition(parent=partition, flips=flip) result = cut_edges(new_partition) naive_cut_edges = { edge for edge in graph.edges if new_partition.crosses_parts(edge) } assert edge_set_equal(result, naive_cut_edges)
def seam_split_merge(partition1, partition2, k, ep, gdf, pop_col = 'TOTPOP'): #merged assignment (put 2 seed maps together along the random split line) subgraph_connect = False while not subgraph_connect: split_part = half_split(partition1, gdf) merge_assign = {n: partition1.assignment[n] if split_part.assignment[n] == 0 else partition2.assignment[n] for n in split_part.assignment.keys() } merge_part = Partition(split_part.graph, merge_assign, split_part.updaters) num_extra_parts = len(merge_part) - len(partition1) #districts cut/changed in map merging (i.e. districts eligible for merging/rebalancing) dists1 = [k for k in partition1.parts.keys() if k in merge_part.parts.keys()] dists2 = [k for k in partition2.parts.keys() if k in merge_part.parts.keys()] changed_dists1 = [k for k in dists1 if merge_part.parts[k] != partition1.parts[k]] changed_dists2 = [k for k in dists2 if merge_part.parts[k] != partition2.parts[k]] changed_dists = changed_dists1 + changed_dists2 num_changed_dists = len(changed_dists) dists_to_make = num_changed_dists - num_extra_parts #lump together all nodes from districts that have been cut changed_nodes = [] for l in changed_dists: changed_nodes = changed_nodes + list(merge_part.parts[l]) change_part_assign = {n:1 if n in changed_nodes else 2 for n in split_part.assignment.keys()} seam_part = Partition(partition1.graph, change_part_assign, partition1.updaters) #check if "seam" subgraph is connected, if not, resplit the state and start again subgraph = split_part.graph.subgraph(changed_nodes) subgraph_connect = nx.is_connected(subgraph) #want graph of just nodes in changed/cut districts subgraph_pop = sum([subgraph.nodes[l][pop_col] for l in changed_nodes]) ideal_subgraph_pop = subgraph_pop/dists_to_make #gets stuck here new_dists_assign0 = recursive_tree_part(subgraph, range(dists_to_make), ideal_subgraph_pop, pop_col, ep, node_repeats = 5) #prob not here, if bad seam, num node repeats wont help new_dists_assign = {n: (new_dists_assign0[n] + 2*k) for n in new_dists_assign0.keys()} final_assign = {d: merge_assign[d] if d not in changed_nodes else new_dists_assign[d] for d in split_part.assignment.keys()} return Partition(partition1.graph, final_assign, partition1.updaters), split_part, merge_part, seam_part
def half_half(partitionA, partitionB, half_pop, pop_col="TOTPOP"): # print('start half-half') half_assign = recursive_tree_part(partitionA.graph, range(2), half_pop, pop_col, .02, 3) half_part = Partition(partitionA.graph, half_assign, partitionA.updaters) V0 = [v for v in partitionA.graph.nodes() if half_assign[v] == 0] V1 = [v for v in partitionA.graph.nodes() if half_assign[v] == 1] A_split = common_refinement(partitionA, half_part) B_split = common_refinement(partitionB, half_part) assignmentA0 = {v: A_split.assignment[v] for v in V0} assignmentA1 = {v: A_split.assignment[v] for v in V1} assignmentB0 = {v: B_split.assignment[v] for v in V0} assignmentB1 = {v: B_split.assignment[v] for v in V1} mapA0 = { i[0]: i[1] for i in zip(list(set(assignmentA0.values())), range(len( assignmentA0))) } mapB1 = { i[0]: i[1] + len(mapA0) for i in zip(list(set(assignmentB1.values())), range(len( assignmentB1))) } mapA1 = { i[0]: i[1] for i in zip(list(set(assignmentA1.values())), range(len( assignmentA1))) } mapB0 = { i[0]: i[1] + len(mapA1) for i in zip(list(set(assignmentB0.values())), range(len( assignmentB0))) } assignmentA0B1 = {} assignmentA1B0 = {} for v in V0: assignmentA0B1[v] = mapA0[assignmentA0[v]] assignmentA1B0[v] = mapB0[assignmentB0[v]] for v in V1: assignmentA0B1[v] = mapB1[assignmentB1[v]] assignmentA1B0[v] = mapA1[assignmentA1[v]] return Partition(partitionA.graph, assignmentA0B1, partitionA.updaters), Partition(partitionA.graph, assignmentA1B0, partitionA.updaters)
def test_perimeter(three_by_three_grid): graph = three_by_three_grid for i in [0, 1, 2, 3, 5, 6, 7, 8]: graph.nodes[i]["boundary_node"] = True graph.nodes[i]["boundary_perim"] = 1 graph.nodes[4]["boundary_node"] = False for edge in graph.edges: graph.edges[edge]["shared_perim"] = 1 assignment = {0: 1, 1: 1, 2: 2, 3: 1, 4: 1, 5: 2, 6: 2, 7: 2, 8: 2} updaters = { "exterior_boundaries": exterior_boundaries, "interior_boundaries": interior_boundaries, "cut_edges_by_part": cut_edges_by_part, "boundary_nodes": boundary_nodes, "perimeter": perimeter, } partition = Partition(graph, assignment, updaters) # 112 # 112 # 222 result = partition["perimeter"] assert result[1] == 3 + 4 # 3 nodes + 4 edges assert result[2] == 5 + 4 # 5 nodes + 4 edges
def test_Partition_can_update_stats(): graph = networkx.complete_graph(3) assignment = {0: 1, 1: 1, 2: 2} graph.nodes[0]["stat"] = 1 graph.nodes[1]["stat"] = 2 graph.nodes[2]["stat"] = 3 updaters = {"total_stat": Tally("stat", alias="total_stat")} partition = Partition(graph, assignment, updaters) assert partition["total_stat"][2] == 3 flip = {1: 2} new_partition = partition.flip(flip) assert new_partition["total_stat"][2] == 5
def test_Partition_can_update_stats(): graph = networkx.complete_graph(3) assignment = {0: 1, 1: 1, 2: 2} graph.nodes[0]['stat'] = 1 graph.nodes[1]['stat'] = 2 graph.nodes[2]['stat'] = 3 updaters = {'total_stat': Tally('stat', alias='total_stat')} partition = Partition(graph, assignment, updaters) assert partition['total_stat'][2] == 3 flip = {1: 2} new_partition = partition.merge(flip) assert new_partition['total_stat'][2] == 5
def initial_partition(disc): updaters = { 'population': Tally('population'), 'base': new_base, 'b_nodes': b_nodes_bi, 'cut_edges': cut_edges, "boundary": bnodes_p, 'step_num': step_num, 'geom': geom_wait, } assignment = {} for x in disc.nodes(): if x[0] > 0: assignment[x] = 1 else: assignment[x] = -1 #b_nodes = {(x[0], partition.assignment[x[1]]) for x in partition["cut_edges"]}.union({(x[1], partition.assignment[x[0]]) for x in partition["cut_edges"]}) partition = Partition(disc, assignment, updaters=updaters) return partition
def merge_parts_smallest_sum(partition, k): #until there are k parts, merge adjacent parts with smallest sum assert (len(partition.parts) >= k) while len(partition.parts) > k: min_pair = (partition.assignment[list(partition["cut_edges"])[0][0]],partition.assignment[list(partition["cut_edges"])[0][1]]) min_sum = math.inf for e in partition["cut_edges"]: edge_sum = partition["population"][partition.assignment[e[0]]] + partition["population"][partition.assignment[e[1]]] if edge_sum < min_sum: min_sum = edge_sum min_pair = (partition.assignment[e[0]], partition.assignment[e[1]]) merge_dict = {v:min_pair[0] if partition.assignment[v] == min_pair[1] else partition.assignment[v] for v in partition.graph.nodes()} partition = Partition(partition.graph, merge_dict, partition.updaters) keyshift = dict(zip(list(partition.parts.keys()), range(len(partition.parts.keys())))) keydict = {v:keyshift[partition.assignment[v]] for v in partition.graph.nodes()} return Partition(partition.graph, keydict, partition.updaters)
def test_perimeter(three_by_three_grid): graph = three_by_three_grid for i in [0, 1, 2, 3, 5, 6, 7, 8]: graph.nodes[i]['boundary_node'] = True graph.nodes[i]['boundary_perim'] = 1 graph.nodes[4]['boundary_node'] = False for edge in graph.edges: graph.edges[edge]['shared_perim'] = 1 assignment = {0: 1, 1: 1, 2: 2, 3: 1, 4: 1, 5: 2, 6: 2, 7: 2, 8: 2} updaters = { 'exterior_boundaries': exterior_boundaries, 'interior_boundaries': interior_boundaries, 'cut_edges_by_part': cut_edges_by_part, 'boundary_nodes': boundary_nodes, 'perimeter': perimeter } partition = Partition(graph, assignment, updaters) # 112 # 112 # 222 result = partition['perimeter'] assert result[1] == 3 + 4 # 3 nodes + 4 edges assert result[2] == 5 + 4 # 5 nodes + 4 edges
def test_cut_edges_doesnt_duplicate_edges_with_different_order_of_nodes( three_by_three_grid): graph = three_by_three_grid assignment = {0: 1, 1: 1, 2: 2, 3: 1, 4: 1, 5: 2, 6: 2, 7: 2, 8: 2} partition = Partition(graph, assignment) # 112 111 # 112 -> 121 # 222 222 flip = {4: 2, 2: 1, 5: 1} new_partition = Partition(parent=partition, flips=flip) result = new_partition['cut_edges'] for edge in result: assert (edge[1], edge[0]) not in result
def metamander_around_partition(graph, dual, target_partition, tag): updaters = {'population': Tally('population'), 'cut_edges': cut_edges, 'step_num': step_num, } assignment = {} for x in graph.nodes(): color = 0 for block in target_partition.keys(): if x in target_partition[block]: assignment[x] = color color += 1 target_partition = Partition(graph, assignment, updaters = updaters) plt.figure() viz(graph, set([]), target_partition.parts) plt.savefig("./plots/target_map" + tag + ".png", format = 'png') plt.close() print("made partition") crosses = compute_cross_edge(graph, target_partition) k = len(target_partition.parts) dual_crosses = [] for edge in dual.edges: if dual.edges[edge]["original_name"] in crosses: dual_crosses.append(edge) print("making dual distances") dual = distance_from_partition(dual, dual_crosses) print('finished making dual distances') special_faces = assign_special_faces(dual,2) print('finished assigning special faces') g_sierpinsky = face_sierpinski_mesh(graph, special_faces) print("made metamander") # change from RVAP and UVAP to approprate election data columns for node in g_sierpinsky: g_sierpinsky.nodes[node]['C_X'] = g_sierpinsky.nodes[node]['pos'][0] g_sierpinsky.nodes[node]['C_Y'] = g_sierpinsky.nodes[node]['pos'][1] if 'population' not in g_sierpinsky.nodes[node]: g_sierpinsky.nodes[node]['population'] = 0 if 'EL16G_PR_D' not in g_sierpinsky.nodes[node]: g_sierpinsky.nodes[node]['EL16G_PR_D'] = 0 if 'EL16G_PR_R' not in g_sierpinsky.nodes[node]: g_sierpinsky.nodes[node]['EL16G_PR_R'] = 0 ##Need to add the voting data total_pop = sum( [ g_sierpinsky.nodes[node]['population'] for node in g_sierpinsky]) #sierp_partition = build_trivial_partition(g_sierpinsky) plt.figure() nx.draw(g_sierpinsky, pos=nx.get_node_attributes(g_sierpinsky, 'pos'), node_size = 1, width = 1, cmap=plt.get_cmap('jet')) plt.savefig("./plots/sierpinsky_mesh.png", format='png') plt.close() left_mander, right_mander = produce_sample(g_sierpinsky, k , tag)
def discontiguous_partition_with_flips(): graph = nx.Graph() graph.add_nodes_from(range(4)) graph.add_edges_from([(0, 1), (1, 2), (2, 3)]) partition = Partition(Graph.from_networkx(graph), {0: 0, 1: 1, 2: 1, 3: 0}) # This flip will maintain discontiguity. return partition, {1: 0}
def shift_part_keys(partition): #get final partition to have parts in 0 indexed range of # total parts final_values = list(set(dict(partition.assignment).values())) final_assign = {} for key, value in dict(partition.assignment).items(): final_assign[key] = final_values.index(partition.assignment[key]) return Partition(partition.graph, final_assign, partition.updaters)
def contiguous_partition_with_flips(): graph = nx.Graph() graph.add_nodes_from(range(4)) graph.add_edges_from([(0, 1), (1, 2), (2, 3), (3, 0)]) partition = Partition(graph, {0: 0, 1: 1, 2: 1, 3: 0}) # This flip will maintain contiguity. return partition, {0: 1}
def build_balanced_k_partition(graph, k, pop_col, pop_target, epsilon): assignment = recursive_tree_part(graph, k, pop_target, pop_col, epsilon) updaters = {'population': Tally('population'), 'cut_edges': cut_edges, 'step_num': step_num, } partition = Partition(graph, assignment=assignment, updaters=updaters) return partition
def tiled_recom(partition1, partition2, verbose=False): remaining_nodes = set(partition1.graph.nodes) tile_assign = {} highest_key = len(partition1) - 1 while len(remaining_nodes) > 0: random_node = random.sample(remaining_nodes, 1)[0] part_choice = random.choice([partition1, partition2]) part_choice_name = '1' if part_choice == partition1 else '2' assigned_dist = part_choice.assignment[random_node] other_nodes_in_dist = set([ n for n in remaining_nodes if part_choice.assignment[n] == assigned_dist ]) if verbose: print("node", random_node, "part", part_choice_name, "assigned dist", assigned_dist) print("len other nodes", len(other_nodes_in_dist), "len part's dist", len(part_choice.parts[assigned_dist])) if len(other_nodes_in_dist) == len(part_choice.parts[assigned_dist]): highest_key += 1 for node in other_nodes_in_dist: tile_assign[node] = highest_key remaining_nodes = remaining_nodes.difference(other_nodes_in_dist) else: part_choice = partition2 if part_choice == partition1 else partition1 assigned_dist = part_choice.assignment[random_node] part_choice_name = '1' if part_choice == partition1 else '2' other_nodes_in_dist = set([ n for n in remaining_nodes if part_choice.assignment[n] == assigned_dist ]) if verbose: print("node", random_node, "ALT part", part_choice_name, "ALT assigned dist", assigned_dist) print("len other nodes", len(other_nodes_in_dist), "len part's dist", len(part_choice.parts[assigned_dist])) if len(other_nodes_in_dist) == len( part_choice.parts[assigned_dist]): highest_key += 1 for node in other_nodes_in_dist: tile_assign[node] = highest_key remaining_nodes = remaining_nodes.difference( other_nodes_in_dist) else: if verbose: print("lone node!") tile_assign[random_node] = 1000 remaining_nodes = remaining_nodes.difference({random_node}) return Partition(partition1.graph, tile_assign, partition1.updaters)
def test_cut_edges_can_handle_multiple_flips(three_by_three_grid): graph = three_by_three_grid assignment = {0: 1, 1: 1, 2: 2, 3: 1, 4: 1, 5: 2, 6: 2, 7: 2, 8: 2} partition = Partition(graph, assignment) # 112 111 # 112 -> 121 # 222 222 flip = {4: 2, 2: 1, 5: 1} new_partition = Partition(parent=partition, flips=flip) result = new_partition['cut_edges'] naive_cut_edges = { tuple(sorted(edge)) for edge in graph.edges if new_partition.crosses_parts(edge) } assert result == naive_cut_edges
def test_cut_edges_by_part_doesnt_duplicate_edges_with_opposite_order_of_nodes( three_by_three_grid): graph = three_by_three_grid assignment = {0: 1, 1: 1, 2: 2, 3: 1, 4: 1, 5: 2, 6: 2, 7: 2, 8: 2} updaters = {'cut_edges_by_part': cut_edges_by_part} partition = Partition(graph, assignment, updaters) # 112 111 # 112 -> 121 # 222 222 flip = {4: 2, 2: 1, 5: 1} new_partition = Partition(parent=partition, flips=flip) result = new_partition['cut_edges_by_part'] for part in result: for edge in result[part]: assert (edge[1], edge[0]) not in result
def main(config_data, id): try: timeBeg = time.time() print('Experiment', id, 'has begun') # Save configuration into global variable global config config = config_data graph = Graph.from_json(config['INPUT_GRAPH_FILENAME']) # List of districts in original graph parts = list( set([ graph.nodes[node][config['ASSIGN_COL']] for node in graph.nodes() ])) # Ideal population of districts ideal_pop = sum( [graph.nodes[node][config['POP_COL']] for node in graph.nodes()]) / len(parts) election = Election(config['ELECTION_NAME'], { 'PartyA': config['PARTY_A_COL'], 'PartyB': config['PARTY_B_COL'] }) updaters = { 'population': Tally(config['POP_COL']), 'cut_edges': cut_edges, config['ELECTION_NAME']: election } partDict = recursive_tree_part(graph, parts, ideal_pop, config['POP_COL'], config['EPSILON'], config['NODE_REPEATS']) for node in graph.nodes(): graph.nodes[node][config['ASSIGN_COL']] = partDict[node] part = Partition(graph=graph, assignment=config['ASSIGN_COL'], updaters=updaters) for len_ in config['RUN_LENGTHS']: for num in range(config['RUNS_PER_LEN']): run_chain(part, config['CHAIN_TYPE'], len_, ideal_pop, '{}_{}_{}_{}'.format(config['TAG'], id, len_, num)) print('Experiment {} completed in {} seconds'.format( id, time.time() - timeBeg)) except Exception as e: # Print notification if any experiment fails to complete track = traceback.format_exc() print(track) print('Experiment {} failed to complete after {:.2f} seconds'.format( id, time.time() - timeBeg))
def test_tally_multiple_columns(graph_with_d_and_r_cols): graph = graph_with_d_and_r_cols updaters = {'total': Tally(['D', 'R'], alias='total')} assignment = {i: 1 if i in range(4) else 2 for i in range(9)} partition = Partition(graph, assignment, updaters) expected_total_in_district_one = sum( graph.nodes[i]['D'] + graph.nodes[i]['R'] for i in range(4)) assert partition['total'][1] == expected_total_in_district_one
def merge_small_neighbor(partition,k): #finds smallest district in map and merges with its smallest neighbor (by population) #repeats until k dists reached merged_assign = dict(partition.assignment) cut_edge_list = list(partition["cut_edges"]) while len(partition) > k: smallest_pop_dist = min(partition["population"], key= partition["population"].get) dist_edges = [e for e in cut_edge_list for z in [0,1] if partition.assignment[e[z]] == smallest_pop_dist] neighbor_dists= list(np.unique([partition.assignment[e[z]] for z in [0,1] for e in dist_edges] )) neighbor_dists.remove(smallest_pop_dist) neighbor_pops = {q: partition["population"][q] for q in neighbor_dists} neighbor_smallest_pop = min(neighbor_pops, key=neighbor_pops.get) for n in partition.parts[smallest_pop_dist]: merged_assign[n] = neighbor_smallest_pop partition = Partition(partition.graph, merged_assign, partition.updaters) return Partition(partition.graph, merged_assign, partition.updaters)
def half_split(partition, gdf, pop_col="TOTPOP"): tot_pop = sum( [partition.graph.nodes[v][pop_col] for v in partition.graph.nodes()]) rand_split_assign = recursive_tree_part(partition.graph, range(2), tot_pop / 2, pop_col, .01, 3) return Partition(partition.graph, rand_split_assign, partition.updaters) ################################ testing ########################################
def test_tally_multiple_columns(graph_with_d_and_r_cols): graph = graph_with_d_and_r_cols updaters = {"total": Tally(["D", "R"], alias="total")} assignment = {i: 1 if i in range(4) else 2 for i in range(9)} partition = Partition(graph, assignment, updaters) expected_total_in_district_one = sum( graph.nodes[i]["D"] + graph.nodes[i]["R"] for i in range(4)) assert partition["total"][1] == expected_total_in_district_one