Beispiel #1
0
def build_markov_chain(steps, chaintype, ideal_population, grid_partition):
    if chaintype == "tree":
        tree_proposal = partial(recom, pop_col="population", pop_target=ideal_population, epsilon=0.05,
                            node_repeats=1, method=my_mst_bipartition_tree_random)
        # tree_proposal = partial(recom, pop_col="population", pop_target=ideal_population, epsilon=0.1,
        #                         node_repeats=1, method=recursive_tree_part)

    exp_chain = MarkovChain(tree_proposal,
                            Validator([#popbound  # ,boundary_condition
                                       ]), accept=cut_accept, initial_state=grid_partition,
                            total_steps=steps)

    if chaintype == "uniform_tree":
    #tree_proposal = partial(uniform_tree_propose)
        tree_proposal = partial(recom, pop_col="population", pop_target=ideal_population, epsilon=0.05,
                            node_repeats=1, method=my_uu_bipartition_tree_random)


    #This tree proposal is returning partitions that are balanced to within epsilon
    #But its not good enough for this validator.
    exp_chain = MarkovChain(tree_proposal,
                            Validator([#popbound  # ,boundary_condition
                                       ]), accept=cut_accept, initial_state=grid_partition,
                            total_steps=steps)
    return exp_chain
def debug():

    num_steps = 100000
    disc = integral_disc(10)
    disc_partition = initial_partition(disc)
    exp_chain = MarkovChain(
        slow_reversible_propose_bi,
        Validator([
            single_flip_contiguous  #,boundary_condition
        ]),
        accept=cut_accept,
        initial_state=disc_partition,
        total_steps=num_steps)

    t = 0
    for observation in exp_chain:
        t += 1

    bd = convert_partition_to_boundary(disc, observation)

    Facefinder.draw_with_location(disc)
    Facefinder.draw_with_location(bd, "red")

    path = convert_path_graph_to_sequence_of_nodes(bd)

    aligned_boundary = conformally_align(path)

    return aligned_boundary
Beispiel #3
0
def chain_with_election(partition_with_election):
    return MarkovChain(
        propose_random_flip,
        Validator([no_vanishing_districts]),
        lambda x: True,
        partition_with_election,
        total_steps=10,
    )
Beispiel #4
0
 def __init__(self, partition, constraints, total_steps):
     if len(constraints) > 0:
         validator = Validator(constraints)
     else:
         validator = lambda x: True
     super().__init__(
         propose_random_flip, validator, always_accept, partition, total_steps
     )
def run_simple2(graph):
    election = Election("2014 Senate", {
        "Democratic": "sen_blue",
        "Republican": "sen_red"
    },
                        alias="2014_Senate")

    initial_partition = Partition(graph,
                                  assignment="con_distri",
                                  updaters={
                                      "2014_Senate":
                                      election,
                                      "population":
                                      Tally("population", alias="population"),
                                      "exterior_boundaries":
                                      exterior_boundaries,
                                      "interior_boundaries":
                                      interior_boundaries,
                                      "perimeter":
                                      perimeter,
                                      "boundary_nodes":
                                      boundary_nodes,
                                      "cut_edges":
                                      cut_edges,
                                      "area":
                                      Tally("area", alias="area"),
                                      "cut_edges_by_part":
                                      cut_edges_by_part,
                                      "county_split":
                                      county_splits('county_split',
                                                    "COUNTY_ID"),
                                      "black_population":
                                      Tally("black_pop",
                                            alias="black_population"),
                                  })
    districts_within_tolerance_2 = lambda part: districts_within_tolerance(
        part, 'population', 0.3)
    is_valid = Validator(
        [single_flip_contiguous, districts_within_tolerance_2])

    chain = MarkovChain(
        proposal=propose_random_flip,
        is_valid=is_valid,
        accept=always_accept,
        #     accept=accept, #THe acceptance criteria is what needs to be defined ourselves - to match the paper
        initial_state=initial_partition,
        total_steps=30,
    )
    efficiency_gaps = []
    wins = []

    for partition in chain:
        if (hasattr(partition, 'accepted') and partition.accepted):
            efficiency_gaps.append(
                gerrychain.scores.efficiency_gap(partition["2014_Senate"]))
            wins.append(partition["2014_Senate"].wins("Democratic"))

    return (efficiency_gaps, wins, partition)
Beispiel #6
0
def test_validator_accepts_numpy_booleans():
    mock_partition = MagicMock()

    mock_constraint = MagicMock()
    mock_constraint.return_value = numpy.bool_(True)
    mock_constraint.__name__ = "mock_constraint"

    is_valid = Validator([mock_constraint])
    assert is_valid(mock_partition)
Beispiel #7
0
def test_vote_proportion_updater_returns_percentage_or_nan_on_later_steps(
        partition_with_election):
    chain = MarkovChain(propose_random_flip,
                        Validator([no_vanishing_districts]),
                        lambda x: True,
                        partition_with_election,
                        total_steps=10)

    for partition in chain:
        election_view = partition['Mock Election']
        assert all(
            is_percentage_or_nan(value)
            for party_percents in election_view.percents_for_party.values()
            for value in party_percents.values())
Beispiel #8
0
def test_validator_raises_TypeError_if_constraint_returns_non_boolean():
    def function():
        pass

    mock_partition = MagicMock()

    mock_constraint = MagicMock()
    mock_constraint.return_value = function
    mock_constraint.__name__ = "mock_constraint"

    validator = Validator([mock_constraint])

    with pytest.raises(TypeError):
        validator(mock_partition)
Beispiel #9
0
def run_chain(disc, disc_partition, num_steps):



    exp_chain = MarkovChain(slow_reversible_propose_bi ,Validator([single_flip_contiguous#,boundary_condition
            ]), accept = cut_accept, initial_state=disc_partition,
            total_steps =  num_steps)

    t = 0
    for observation in exp_chain:
        t += 1



    return observation
Beispiel #10
0
def test_elections_match_the_naive_computation(partition_with_election):
    chain = MarkovChain(
        propose_random_flip,
        Validator([no_vanishing_districts, reject_half_of_all_flips]),
        lambda x: True,
        partition_with_election,
        total_steps=100,
    )

    for partition in chain:
        election_view = partition["Mock Election"]
        expected_party_totals = {
            "D": expected_tally(partition, "D"),
            "R": expected_tally(partition, "R"),
        }
        assert expected_party_totals == election_view.totals_for_party
    ideal_population = sum(
        init_partition["population"].values()) / len(init_partition)

    proposal = partial(
        recom,
        pop_col="TOTPOP",
        pop_target=ideal_population,
        epsilon=0.02,
        node_repeats=1,
    )

    popbound = within_percent_of_ideal_population(init_partition, 0.05)

    gchain = MarkovChain(
        proposal,
        Validator([popbound]),
        accept=always_accept,
        initial_state=init_partition,
        total_steps=100,
    )

    t = 0
    for part in gchain:
        t += 1

    init_partition = part
    print("FINISHED TREE WALK")

gdf_print_map(init_partition, './opt_plots/starting_plan.png', gdf, unit_name)

record_partition(init_partition, 0, 'starting', graph_name, exp_num)
        return (partition["P1 vs P2"].wins("P2")/k_partitions)*100

def equal_check(partition, party):
    for number in range(1, k_partitions+1):
        if partition["P1 vs P2"].wins("P1")+partition["P1 vs P2"].wins("P2") == k_partitions + number:
            return ((partition["P1 vs P2"].wins(party)/k_partitions)*100)-((100/k_partitions)/2)*number
    raise error
    
ideal_population = sum(init_part["population"].values()) / len(init_part)
popbound = within_percent_of_ideal_population(init_part, 0.1)
cutedgebound = UpperBound(lambda part: len(part["cut_edges"]), 400)

flip_steps = 250000
flip_chain = MarkovChain(
propose_random_flip,
Validator([single_flip_contiguous, popbound, cutedgebound]), 
    accept = always_accept, 
    initial_state = init_part, 
    total_steps = flip_steps, )

flip_p1_seats = []
for part in flip_chain:
    flip_p1_seats.append(part["P1 vs P2"].wins("P1"))
plt.figure()
nx.draw(
graph,
pos = {x: x for x in graph.nodes()},
node_color = [dict(part.assignment)[x]
for x in graph.nodes()],
node_size = node_size,
node_shape = "p",
    # # tree_proposal = partial(recom,
    #                         pop_col="population",
    #                         pop_target=ideal_population,
    #                         epsilon=1,
    #                         node_repeats=1
    #                         )

    #######BUILD MARKOV CHAINS
    pop_col = "population"
    epsilon = 0.05
    if chaintype == "tree":
        tree_proposal = partial(recom, pop_col="population", pop_target=ideal_population, epsilon=0.05,
                                node_repeats=1, method=my_mst_bipartition_tree_random)

        exp_chain = MarkovChain(tree_proposal,
                                Validator([#popbound  # ,boundary_condition
                                           ]), accept=cut_accept, initial_state=grid_partition,
                                total_steps=steps)

    if chaintype == "uniform_tree":
        #tree_proposal = partial(uniform_tree_propose)
        tree_proposal = partial(recom, pop_col="population", pop_target=ideal_population, epsilon=0.05,
                                node_repeats=1, method=my_uu_bipartition_tree_random)


        #This tree proposal is returning partitions that are balanced to within epsilon
        #But its not good enough for this validator.
        exp_chain = MarkovChain(tree_proposal,
                                Validator([#popbound  # ,boundary_condition
                                           ]), accept=cut_accept, initial_state=grid_partition,
                                total_steps=steps)
def produce_gerrymanders(graph, k, tag, sample_size, chaintype):
    # Samples k-partitions of the graph
    # stores vote histograms, and returns most extreme partitions.
    for node in graph.nodes():
        graph.nodes[node]["last_flipped"] = 0
        graph.nodes[node]["num_flips"] = 0

    ideal_population = sum(graph.nodes[x][config["POPULATION_COLUMN"]] for x in graph.nodes()) / k
    election = Election(
        config['ELECTION_NAME'],
        {'PartyA': config['PARTY_A_COL'], 'PartyB': config['PARTY_B_COL']},
        alias=config['ELECTION_ALIAS']
    )
    updaters = {'population': Tally(config['POPULATION_COLUMN']),
                'cut_edges': cut_edges,
                'step_num': step_num,
                config['ELECTION_ALIAS'] : election
                }
    initial_partition = Partition(graph, assignment=config['ASSIGNMENT_COLUMN'], updaters=updaters)
    popbound = within_percent_of_ideal_population(initial_partition, config['POPULATION_EPSILON'])

    if chaintype == "tree":
        tree_proposal = partial(recom, pop_col=config["POPULATION_COLUMN"], pop_target=ideal_population,
                           epsilon=config['POPULATION_EPSILON'], node_repeats=config['NODE_REPEATS'],
                           method=facefinder.my_mst_bipartition_tree_random)

    elif chaintype == "uniform_tree":
        tree_proposal = partial(recom, pop_col=config["POPULATION_COLUMN"], pop_target=ideal_population,
                           epsilon=config['POPULATION_EPSILON'], node_repeats=config['NODE_REPEATS'],
                           method=facefinder.my_uu_bipartition_tree_random)
    else:
        print("Chaintype used: ", chaintype)
        raise RuntimeError("Chaintype not recognized. Use 'tree' or 'uniform_tree' instead")

    exp_chain = MarkovChain(tree_proposal, Validator([popbound]), accept=accept.always_accept, initial_state=initial_partition,
                            total_steps=sample_size)

    seats_won_table = []
    best_left = np.inf
    best_right = -np.inf
    for ctr, part in enumerate(exp_chain):
        seats_won = 0

        if ctr % 100 == 0:
            print("step ", ctr)
        for i in range(k):
            rep_votes = 0
            dem_votes = 0
            for node in graph.nodes():
                if part.assignment[node] == i:
                    rep_votes += graph.nodes[node]["EL16G_PR_R"]
                    dem_votes += graph.nodes[node]["EL16G_PR_D"]
            total_seats = int(rep_votes > dem_votes)
            seats_won += total_seats
        # total seats won by rep
        seats_won_table.append(seats_won)
        # save gerrymandered partitions
        if seats_won < best_left:
            best_left = seats_won
            left_mander = copy.deepcopy(part.parts)
        if seats_won > best_right:
            best_right = seats_won
            right_mander = copy.deepcopy(part.parts)
        # print("finished round"

    print("max", best_right, "min:", best_left)

    plt.figure()
    plt.hist(seats_won_table, bins=10)

    name = "./plots/large_sample/seats_hist/seats_histogram_orig" + tag + ".png"
    plt.savefig(name)
    plt.close()
    return left_mander, right_mander
def produce_sample(graph, k, tag, sample_size = 500, chaintype='tree'):
    #Samples k partitions of the graph, stores the cut edges and records them graphically
    #Also stores vote histograms, and returns most extreme partitions.

    print("producing sample")
    updaters = {'population': Tally('population'),
                'cut_edges': cut_edges,
                'step_num': step_num,
                }
    for edge in graph.edges():
        graph[edge[0]][edge[1]]['cut_times'] = 0

    
    for n in graph.nodes():
        #graph.nodes[n]["population"] = 1 #graph.nodes[n]["POP10"] #This is something gerrychain will refer to for checking population balance
        graph.nodes[n]["last_flipped"] = 0
        graph.nodes[n]["num_flips"] = 0
    print("set up chain")
    ideal_population= sum( graph.nodes[x]["population"] for x in graph.nodes())/k

    initial_partition = Partition(graph, assignment='part', updaters=updaters)
    pop1 = .05
    print("popbound")
    popbound = within_percent_of_ideal_population(initial_partition, pop1)

    if chaintype == "tree":
        tree_proposal = partial(recom, pop_col="population", pop_target=ideal_population, epsilon=pop1,
                                node_repeats=1, method=facefinder.my_mst_bipartition_tree_random)

    elif chaintype == "uniform_tree":
        tree_proposal = partial(recom, pop_col="population", pop_target=ideal_population, epsilon=pop1,
                                node_repeats=1, method=facefinder.my_uu_bipartition_tree_random)
    else:
        print("Chaintype used: ", chaintype)
        raise RuntimeError("Chaintype not recongized. Use 'tree' or 'uniform_tree' instead")

    
    exp_chain = MarkovChain(tree_proposal, Validator([popbound]), accept=always_true, initial_state=initial_partition, total_steps=sample_size)
    
    

    z = 0
    num_cuts_list = []
    seats_won_table = []
    best_left = np.inf
    best_right = -np.inf
    print("begin chain")
    for part in exp_chain:


        
        z += 1
        if z % 100 == 0:
            print("step ", z)
        seats_won = 0

        for edge in part["cut_edges"]:
            graph[edge[0]][edge[1]]["cut_times"] += 1
        for i in range(k):
            rep_votes = 0
            dem_votes = 0
            for n in graph.nodes():
                if part.assignment[n] == i:
                    rep_votes += graph.nodes[n]["EL16G_PR_R"]
                    dem_votes += graph.nodes[n]["EL16G_PR_D"]
            total_seats = int(rep_votes > dem_votes)
            seats_won += total_seats
        # total seats won by rep
        seats_won_table.append(seats_won)
        # save gerrymandered partitionss
        if seats_won < best_left:
            best_left = seats_won
            #left_mander = copy.deepcopy(part.parts)
        if seats_won > best_right:
            best_right = seats_won

            #right_mander = copy.deepcopy(part.parts)
        #print("finished round"
    
    print("max", best_right, "min:", best_left)
    
    plt.figure()
    plt.hist(seats_won_table, bins = 10)
    
    name = "./plots/seats_histogram_metamander" + tag +".png"
    plt.savefig(name)
    plt.close()    
        
    edge_colors = [graph[edge[0]][edge[1]]["cut_times"] for edge in graph.edges()]
    
    pos=nx.get_node_attributes(graph, 'pos')
    

    plt.figure()
    plt.hist(seats_won_table, bins=10)
    mean = sum(seats_won_table) / len(seats_won_table)
    std = np.std(seats_won_table)
    # plt.close()
    # title = 'mean: ' + str(mean) + ' standard deviation: ' + str(std)
    # plt.title(title)
    # name = "./plots/seats_hist/seats_histogram" + tag + ".png"
    # plt.savefig(name)
    # plt.close()

    # edge_colors = [graph[edge[0]][edge[1]]["cut_times"] for edge in graph.edges()]
    #
    # plt.figure()
    # nx.draw(graph, pos=nx.get_node_attributes(graph, 'pos'), node_size=0,
    #         edge_color=edge_colors, node_shape='s',
    #         cmap='magma', width=1)
    # plt.savefig("./plots/edges_plots/edges" + tag + ".png")
    # plt.close()

    return mean, std, graph
def run_simple(graph):
    election = Election("2016 President", {
        "Democratic": "T16PRESD",
        "Republican": "T16PRESR"
    },
                        alias="2016_President")

    initial_partition = Partition(graph,
                                  assignment="2011_PLA_1",
                                  updaters={
                                      "2016_President":
                                      election,
                                      "population":
                                      Tally("TOT_POP", alias="population"),
                                      "perimeter":
                                      perimeter,
                                      "exterior_boundaries":
                                      exterior_boundaries,
                                      "interior_boundaries":
                                      interior_boundaries,
                                      "boundary_nodes":
                                      boundary_nodes,
                                      "cut_edges":
                                      cut_edges,
                                      "area":
                                      Tally("area", alias="area"),
                                      "cut_edges_by_part":
                                      cut_edges_by_part,
                                      "county_split":
                                      county_splits('county_split',
                                                    "COUNTYFP10"),
                                      "black_population":
                                      Tally("BLACK_POP",
                                            alias="black_population"),
                                  })

    efficiency_gaps = []
    wins = []
    beta = 0
    wp = 1
    wi = 1
    wc = 0
    wm = 1

    def accept(partition):
        return (metro_scoring_prob(partition, beta, wp, wi, wc, wm))

    is_valid = Validator([single_flip_contiguous, districts_within_tolerance])
    chain = MarkovChain(
        proposal=propose_random_flip,
        is_valid=is_valid,
        #accept = always_accept,
        accept=
        accept,  #THe acceptance criteria is what needs to be defined ourselves - to match the paper
        initial_state=initial_partition,
        total_steps=30)

    for partition in chain:
        if (hasattr(partition, 'accepted') and partition.accepted):
            efficiency_gaps.append(
                gerrychain.scores.efficiency_gap(partition["2016_President"]))
            wins.append(partition["2016_President"].wins("Democratic"))
    return (efficiency_gaps, wins, partition)
Beispiel #17
0
def run_simple(graph, num_samples = 80000, wp = 3000, wi = 2.5, wc = 0.4, wm = 800, get_parts = 5):
    election = Election(
        "2014 Senate",
        {"Democratic": "sen_blue", "Republican": "sen_red"},
        alias="2014_Senate"
    )
    
    initial_partition = Partition(
            graph,
            assignment="2014_CD",
            updaters={
                "2014_Senate": election,
                "population": Tally("population", alias="population"), 
                "exterior_boundaries": exterior_boundaries,
                "interior_boundaries": interior_boundaries,
                "perimeter": perimeter,
                "boundary_nodes": boundary_nodes,
               "cut_edges": cut_edges,
                "area": Tally("area", alias="area"),
               "cut_edges_by_part": cut_edges_by_part, 
                "county_split" : county_splits( 'county_split', "COUNTY_ID"),
                "black_population": Tally("black_pop", alias = "black_population"),
            }
        )
    
    def vra_validator(part):
        if(vra_district_requirement(part, 2, [0.4, 0.335]) > 0):
            return False
        return True

    districts_within_tolerance_2 = lambda part : districts_within_tolerance(part, 'population', 0.12)
    is_valid = Validator([single_flip_contiguous, districts_within_tolerance_2, vra_validator ])
    def accept(partition, counter):
        if(counter < 40000):
            beta = 0
        elif(counter < 60000):
            beta = (counter - 40000) /(60000-40000)
        else:
            beta = 1
        return(metro_scoring_prob(partition, beta, wp, wi, wc, wm))

    chain = MarkovChainAnneal(
        proposal=propose_random_flip,
        is_valid=is_valid,
        accept=accept,
        initial_state=initial_partition,
        total_steps= num_samples * 100,
    )
    # going to show 5 partitions from this sample
    part_con = max(1, num_samples / get_parts)
    
    efficiency_gaps = []
    wins = []
    voting_percents = []
    sample_parts = []
    tbefore = time.time()
    for partition in chain:
        if(hasattr(partition, 'accepted') and partition.accepted):
            efficiency_gaps.append(gerrychain.scores.efficiency_gap(partition["2014_Senate"]))
            wins.append(partition["2014_Senate"].wins("Democratic"))
            voting_percents.append(partition["2014_Senate"].percents("Democratic"))
            num_s = len(wins)
            if(num_s % part_con) == 0:
                sample_parts.append(partition)
            if(num_s> num_samples):
                break
            if(num_s % 1000 == 0):
                tnext = time.time()
                print("It took %i time to get 1000 samples" % (tnext - tbefore))
                tbefore = time.time()
    return(efficiency_gaps, wins, voting_percents, sample_parts)
Beispiel #18
0
    A MarkovChain with propose_random_flips proposal and always_accept
    acceptance function. Also instantiates a Validator for you from a
    list of constraints.
    """

    def __init__(self, partition, constraints, total_steps):
        if len(constraints) > 0:
            validator = Validator(constraints)
        else:
            validator = lambda x: True
        super().__init__(
            propose_random_flip, validator, always_accept, partition, total_steps
        )


grid_validator = Validator([single_flip_contiguous, no_vanishing_districts])


class GridChain(MarkovChain):
    """
    A basic MarkovChain for use with the Grid partition. The proposal is a single random flip
    at the boundary of a district. A step is valid if the districts are connected and no
    districts disappear. Requires a 'cut_edges' updater.
    """

    def __init__(self, initial_grid, total_steps=1000):
        if not initial_grid["cut_edges"]:
            raise ValueError(
                "BasicChain needs the Partition to have a cut_edges updater."
            )
Beispiel #19
0
def produce_gerrymanders(graph, k, tag, sample_size, chaintype):
    #Samples k partitions of the graph
    #stores vote histograms, and returns most extreme partitions.
    for n in graph.nodes():
        graph.nodes[n]["last_flipped"] = 0
        graph.nodes[n]["num_flips"] = 0
      
    ideal_population= sum( graph.nodes[x]["population"] for x in graph.nodes())/k
    updaters = {'population': Tally('population'),
                        'cut_edges': cut_edges,
                        'step_num': step_num,
                        }
    initial_partition = Partition(graph, assignment='part', updaters=updaters)
    pop1 = .05
    popbound = within_percent_of_ideal_population(initial_partition, pop1)
    
    if chaintype == "tree":
        tree_proposal = partial(recom, pop_col="population", pop_target=ideal_population, epsilon=pop1,
                                node_repeats=1, method=my_mst_bipartition_tree_random)
    
    elif chaintype == "uniform_tree":
        tree_proposal = partial(recom, pop_col="population", pop_target=ideal_population, epsilon=pop1,
                                node_repeats=1, method=my_uu_bipartition_tree_random)
    else: 
        print("Chaintype used: ", chaintype)
        raise RuntimeError("Chaintype not recongized. Use 'tree' or 'uniform_tree' instead")
    
    
    
    exp_chain = MarkovChain(tree_proposal, Validator([popbound]), accept=always_true, initial_state=initial_partition, total_steps=sample_size)
        
    seats_won_table = []
    best_left = np.inf
    best_right = -np.inf
    ctr = 0
    for part in exp_chain:
        ctr += 1
        seats_won = 0

        if ctr % 100 == 0:
            print("step ", ctr)
        for i in range(k):
            rep_votes = 0
            dem_votes = 0
            for n in graph.nodes():
                if part.assignment[n] == i:
                    rep_votes += graph.nodes[n]["EL16G_PR_R"]
                    dem_votes += graph.nodes[n]["EL16G_PR_D"]
            total_seats = int(rep_votes > dem_votes)
            seats_won += total_seats
        #total seats won by rep
        seats_won_table.append(seats_won)
        # save gerrymandered partitionss
        if seats_won < best_left:
            best_left = seats_won
            left_mander = copy.deepcopy(part.parts)
        if seats_won > best_right:
            best_right = seats_won
            right_mander = copy.deepcopy(part.parts)
        #print("finished round"
    
    print("max", best_right, "min:", best_left)
    
    #plt.figure()
    #plt.hist(seats_won_table, bins = 10)
    
    name = "./plots/seats_histogram" + tag +".png"
    #plt.savefig(name)
    #plt.close() 
    sns_plot = sns.distplot(seats_won_table, label="North Carolina Republican Vote Distribution").get_figure()
    plt.legend()
    sns_plot.savefig(name)
    return left_mander, right_mander
print("Finished ReCom")
# ########BUILD SECOND PARTITION

squiggle_partition = Partition(graph,
                               assignment=part.assignment,
                               updaters=updaters)

# ADD CONSTRAINTS
popbound = within_percent_of_ideal_population(squiggle_partition, 0.1)

# ######BUILD AND RUN SECOND MARKOV CHAIN

squiggle_chain = MarkovChain(
    propose_random_flip,
    Validator([single_flip_contiguous, popbound]),
    accept=always_accept,
    initial_state=squiggle_partition,
    total_steps=100_000,
)

for part2 in squiggle_chain:
    pass

plt.figure()
nx.draw(
    graph,
    pos={x: x
         for x in graph.nodes()},
    node_color=[part2.assignment[x] for x in graph.nodes()],
    node_size=ns,
Beispiel #21
0
def produce_sample(graph, k, tag, sample_size = 500):
    #Samples k partitions of the graph, stores the cut edges and records them graphically
    #Also stores vote histograms, and returns most extreme partitions.
    for edge in graph.edges():
        graph[edge[0]][edge[1]]['cut_times'] = 0
    
        for n in graph.nodes():
            #graph.nodes[n]["population"] = 1 #graph.nodes[n]["POP10"] #This is something gerrychain will refer to for checking population balance
            graph.nodes[n]["last_flipped"] = 0
            graph.nodes[n]["num_flips"] = 0
    
    #sierp_partition = build_balanced_partition(g_sierpinsky, "population", ideal_population, .01)
    
    
    
    ideal_population= sum( graph.nodes[x]["population"] for x in graph.nodes())/k
    updaters = {'population': Tally('population'),
                        'cut_edges': cut_edges,
                        'step_num': step_num,
                        }
    initial_partition = Partition(graph, assignment='part', updaters=updaters)
    #viz(g_sierpinsky, set([]), sierp_partition.parts)
    
    
    #ideal_population = sum(sierp_partition["population"].values()) / len(sierp_partition)
    print(ideal_population)
    
    steps = sample_size
    pop1 = .1
    popbound = within_percent_of_ideal_population(initial_partition, pop1)

    
    chaintype = "tree"
    
    if chaintype == "tree":
        tree_proposal = partial(recom, pop_col="population", pop_target=ideal_population, epsilon=pop1,
                                node_repeats=1, method=my_mst_bipartition_tree_random)
    
    if chaintype == "uniform_tree":
        tree_proposal = partial(recom, pop_col="population", pop_target=ideal_population, epsilon=pop1,
                                node_repeats=1, method=my_uu_bipartition_tree_random)
    
    
    
    exp_chain = MarkovChain(tree_proposal, Validator([popbound]), accept=always_true, initial_state=initial_partition, total_steps=steps)
    
    
    z = 0
    num_cuts_list = []
    
    
    seats_won_table = []
    best_left = np.inf
    best_right = -np.inf
    ctr = 0
    for part in exp_chain:
        ctr += 1
        print(ctr)
    #for i in range(steps):
    #    part = build_balanced_partition(g_sierpinsky, "population", ideal_population, .05)
    
        seats_won = 0
        z += 1
        
        if z % 100 == 0:
            print("step ", z)
    
        for edge in part["cut_edges"]:
            graph[edge[0]][edge[1]]["cut_times"] += 1
    
        for i in range(k):
            rural_pop = 0
            urban_pop = 0
            for n in graph.nodes():
                if part.assignment[n] == i:
                    rural_pop += graph.nodes[n]["EL16G_PR_R"]
                    urban_pop += graph.nodes[n]["EL16G_PR_D"]
            total_seats = int(rural_pop > urban_pop)
            seats_won += total_seats
        #total seats won by rual pop 
        seats_won_table.append(seats_won)
        # save gerrymandered partitionss
        if seats_won < best_left:
            best_left = seats_won
            left_mander = copy.deepcopy(part.parts)
        if seats_won > best_right:
            best_right = seats_won
            right_mander = copy.deepcopy(part.parts)
        #print("finished round"
    
    print("max", best_right, "min:", best_left)
    
    edge_colors = [graph[edge[0]][edge[1]]["cut_times"] for edge in graph.edges()]
    
    pos=nx.get_node_attributes(graph, 'pos')
    
    plt.figure()
    nx.draw(graph, pos=nx.get_node_attributes(graph, 'pos'), node_size=1,
                        edge_color=edge_colors, node_shape='s',
                        cmap='magma', width=3)
    plt.savefig("./plots/edges" + tag + ".png")
    plt.close()
    
    plt.figure()
    plt.hist(seats_won_table, bins = 10)
    
    name = "./plots/seats_histogram" + tag +".png"
    plt.savefig(name)
    plt.close()    

    return left_mander, right_mander
Beispiel #22
0
            #########Setup Proposal
            ideal_population = sum(
                grid_partition["population"].values()) / len(grid_partition)

            tree_proposal = partial(recom,
                                    pop_col="population",
                                    pop_target=ideal_population,
                                    epsilon=0.05,
                                    node_repeats=1)

            #######BUILD MARKOV CHAINS

            exp_chain = MarkovChain(
                slow_reversible_propose_bi,
                Validator([
                    single_flip_contiguous,
                    popbound  #,boundary_condition
                ]),
                accept=cut_accept,
                initial_state=grid_partition,
                total_steps=1000000)

            #########Run MARKOV CHAINS

            rsw = []
            rmm = []
            reg = []
            rce = []
            rbn = []
            waits = []

            slopes = []
Beispiel #23
0
    grid_partition["population"].values()) / len(grid_partition)

tree_proposal = partial(
    recom,
    pop_col="population",
    pop_target=ideal_population,
    epsilon=0.05,
    node_repeats=1,
)

# ######BUILD MARKOV CHAINS
numIters = 10000

recom_chain = MarkovChain(
    tree_proposal,
    Validator([popbound]),
    accept=always_accept,
    initial_state=grid_partition,
    total_steps=numIters,
)

# ########Run MARKOV CHAINS

rsw = []
rmm = []
reg = []
rce = []

expectednumberseats = 0

for part in recom_chain:
    # print(len(clusters))
    flips = {}
    for val in clusters.keys():
        flips[val] = edd[clusters[val]]

    # print(len(flips))
    # print(partition.assignment)
    # print(flips)
    return partition.flip(flips)


# ######BUILD AND RUN FINAL MARKOV CHAIN

final_chain = MarkovChain(
    propose_spectral_merge,
    Validator([]),
    accept=always_accept,
    initial_state=initial_partition,
    total_steps=10,
)

for part in final_chain:
    df["clusters"] = df.index.map(dict(part.assignment))
    df.plot(column="clusters")
    plt.axis('off')
    plt.savefig(f"./plots/spectral_step{part['step_num']:02d}.png")

    plt.close()
    #plt.show()

    #plt.figure()
Beispiel #25
0
    def min_pop(part):
        return min(list(part["population"].values()))

    def sd_pop(part):
        return np.std(list(part["population"].values()))

    mean_one_sd_up = mean_pop(init_partition) + (2 /
                                                 3) * sd_pop(init_partition)
    mean_one_sd_down = mean_pop(init_partition) - (2 /
                                                   3) * sd_pop(init_partition)

    # initalize and run both chains
    is_valid = Validator([
        LowerBound(min_pop,
                   min_pop(init_partition) % 50),
        UpperBound(mean_pop, mean_one_sd_up),
        LowerBound(mean_pop, mean_one_sd_down),
        WithinPercentRangeOfBounds(sd_pop, 25),
    ])

    chunk = Process(
        target=generate_entropies,
        args=("chunk", STEP_COUNT, BURN_IN, results, int(TOT_WORKERS / 2)),
    )
    random = Process(
        target=generate_entropies,
        args=("random", STEP_COUNT, BURN_IN, results, int(TOT_WORKERS / 2)),
    )
    chunk.start(), random.start()
    chunk.join(), random.join()
    save_results(CITY_NAME, STEP_COUNT, results["chunk"], results["random"])
Beispiel #26
0
def main():
    """ Contains majority of expermiment. Runs a markov chain on the state dual graph, determining how the distribution is affected to changes in the
     state dual graph.
     Raises:
        RuntimeError if PROPOSAL_TYPE of config file is neither 'sierpinski'
        nor 'convex'
    """
    output_directory = createDirectory(config)
    epsilon = config["epsilon"]
    k = config["NUM_DISTRICTS"]
    updaters = {'population': Tally('population'),
                'cut_edges': cut_edges,
                "Blue-Red": Election("Blue-Red", {"Blue": "blue", "Red": "red"})
                }
    graph, dual = preprocessing(output_directory)
    cddict = {x: int(x[0] / gn) for x in graph.nodes()}

    plt.figure()
    nx.draw(
        graph,
        pos={x: x for x in graph.nodes()},
        node_color=[cddict[x] for x in graph.nodes()],
        node_size=ns,
        node_shape="s",
        cmap="tab20",
    )
    plt.show()

    ideal_population = sum(graph.nodes[x]["population"] for x in graph.nodes()) / k
    faces = graph.graph["faces"]
    faces = list(faces)
    square_faces = [face for face in faces if len(face) == 4]
    totpop = 0
    for node in graph.nodes():
        totpop += int(graph.nodes[node]['population'])
    # length of chain
    steps = config["CHAIN_STEPS"]

    # length of each gerrychain step
    gerrychain_steps = config["GERRYCHAIN_STEPS"]
    # faces that are currently modified. Code maintains list of modified faces, and at each step selects a face. if face is already in list,
    # the face is un-modified, and if it is not, the face is modified by the specified proposal type.
    special_faces = set([face for face in square_faces if np.random.uniform(0, 1) < .5])
    #chain_output = {'dem_seat_data': [], 'rep_seat_data': [], 'score': []}
    chain_output = defaultdict(list)

    # start with small score to move in right direction
    print("Choosing", math.floor(len(faces) * config['PERCENT_FACES']), "faces of the dual graph at each step")
    max_score = -math.inf
    # this is the main markov chain
    for i in tqdm.tqdm(range(1, steps + 1), ncols=100, desc="Chain Progress"):
        special_faces_proposal = copy.deepcopy(special_faces)
        proposal_graph = copy.deepcopy(graph)
        if (config["PROPOSAL_TYPE"] == "sierpinski"):
            for i in range(math.floor(len(faces) * config['PERCENT_FACES'])):
                face = random.choice(faces)
                ##Makes the Markov chain lazy -- this just makes the chain aperiodic.
                if random.random() > .5:
                    if not (face in special_faces_proposal):
                        special_faces_proposal.append(face)
                    else:
                        special_faces_proposal.remove(face)
            chain_on_subsets_of_faces.face_sierpinski_mesh(proposal_graph, special_faces_proposal)
        elif (config["PROPOSAL_TYPE"] == "add_edge"):
            for j in range(math.floor(len(square_faces) * config['PERCENT_FACES'])):
                face = random.choice(square_faces)
                ##Makes the Markov chain lazy -- this just makes the chain aperiodic.
                if random.random() > .5:
                    if not (face in special_faces_proposal):
                        special_faces_proposal.add(face)
                    else:
                        special_faces_proposal.remove(face)
            chain_on_subsets_of_faces.add_edge_proposal(proposal_graph, special_faces_proposal)
        else:
            raise RuntimeError('PROPOSAL TYPE must be "sierpinski" or "convex"')

        initial_partition = Partition(proposal_graph, assignment=cddict, updaters=updaters)

        # Sets up Markov chain
        popbound = within_percent_of_ideal_population(initial_partition, epsilon)
        tree_proposal = partial(recom, pop_col=config['POP_COL'], pop_target=ideal_population, epsilon=epsilon,
                                node_repeats=1)

        # make new function -- this computes the energy of the current map
        exp_chain = MarkovChain(tree_proposal, Validator([popbound]), accept=accept.always_accept,
                                initial_state=initial_partition, total_steps=gerrychain_steps)
        seats_won_for_republicans = []
        seats_won_for_democrats = []
        for part in exp_chain:
            for u, v in part["cut_edges"]:
                proposal_graph[u][v]["cut_times"] += 1

            rep_seats_won = 0
            dem_seats_won = 0
            for j in range(k):
                rep_votes = 0
                dem_votes = 0
                for n in graph.nodes():
                    if part.assignment[n] == j:
                        rep_votes += graph.nodes[n]["blue"]
                        dem_votes += graph.nodes[n]["red"]
                total_seats_dem = int(dem_votes > rep_votes)
                total_seats_rep = int(rep_votes > dem_votes)
                rep_seats_won += total_seats_rep
                dem_seats_won += total_seats_dem
            seats_won_for_republicans.append(rep_seats_won)
            seats_won_for_democrats.append(dem_seats_won)

        seat_score = statistics.mean(seats_won_for_republicans)

        # implement mattingly simulated annealing scheme, from evaluating partisan gerrymandering in wisconsin
        if i <= math.floor(steps * .67):
            beta = i / math.floor(steps * .67)
        else:
            beta = (i / math.floor(steps * 0.67)) * 100
        temperature = 1 / (beta)

        weight_seats = 1
        weight_flips = -.2
        config['PERCENT_FACES'] = config['PERCENT_FACES']  # what is this?
        flip_score = len(special_faces)  # This is the number of edges being swapped

        score = weight_seats * seat_score + weight_flips * flip_score

        ##This is the acceptance step of the Metropolis-Hasting's algorithm. Specifically, rand < min(1, P(x')/P(x)), where P is the energy and x' is proposed state
        # if the acceptance criteria is met or if it is the first step of the chain
        def update_outputs():
            chain_output['dem_seat_data'].append(seats_won_for_democrats)
            chain_output['rep_seat_data'].append(seats_won_for_republicans)
            chain_output['score'].append(score)
            chain_output['seat_score'].append(seat_score)
            chain_output['flip_score'].append(flip_score)


        def propagate_outputs():
            for key in chain_output.keys():
                chain_output[key].append(chain_output[key][-1])

        if i == 1:
            update_outputs()
            special_faces = copy.deepcopy(special_faces_proposal)
        # this is the simplified form of the acceptance criteria, for intuitive purposes
        # exp((1/temperature) ( proposal_score - previous_score))
        elif np.random.uniform(0, 1) < (math.exp(score) / math.exp(chain_output['score'][-1])) ** (1 / temperature):
            update_outputs()
            special_faces = copy.deepcopy(special_faces_proposal)
        else:
            propagate_outputs()

        # if score is highest seen, save map.
        if score > max_score:
            # todo: all graph coloring for graph changes that produced this score
            nx.write_gpickle(proposal_graph, "obj/graphs/"+str(score)+'sc_'+str(config['CHAIN_STEPS'])+'mcs_'+ str(config["GERRYCHAIN_STEPS"])+ "gcs_" +
                config['PROPOSAL_TYPE']+'_'+ str(len(special_faces)), pickle.HIGHEST_PROTOCOL)
            save_obj(special_faces, output_directory, 'north_carolina_highest_found')
            nx.write_gpickle(proposal_graph, output_directory + '/' +  "max_score", pickle.HIGHEST_PROTOCOL)
            f=open(output_directory + "/max_score_data.txt","w+")
            f.write("maximum score: " + str(score) + "\n" + "edges changed: " + str(len(special_faces)) + "\n" + "Seat Score: " + str(seat_score))
            save_obj(special_faces, output_directory + '/', "special_faces")
            max_score = score

    plt.plot(range(len(chain_output['score'])), chain_output['score'])
    plt.xlabel("Meta-Chain Step")
    plt.ylabel("Score")
    plt.show()
    plt.close()

    plt.plot(range(len(chain_output['seat_score'])), chain_output['seat_score'])
    plt.xlabel("Meta-Chain Step")
    plt.ylabel("Number of average seats republicans won")
    plt.show()
    plt.close()
Beispiel #27
0

            #########Setup Proposal
            ideal_population = sum(grid_partition["population"].values()) / len(grid_partition)

            tree_proposal = partial(recom,
                                   pop_col="population",
                                   pop_target=ideal_population,
                                   epsilon=0.05,
                                   node_repeats=1
                                  )

            #######BUILD MARKOV CHAINS


            exp_chain = MarkovChain(slow_reversible_propose_bi ,Validator([single_flip_contiguous, popbound#,boundary_condition
            ]), accept = cut_accept, initial_state=grid_partition,
            total_steps =  10000000)





            #########Run MARKOV CHAINS

            rsw = []
            rmm = []
            reg = []
            rce = []
            rbn=[]
            waits= []
def run_ensemble_on_distro(graph, min_pop_col, maj_pop_col, tot_pop_col, num_districts, initial_plan, num_steps, pop_tol = 0.05, min_win_thresh = 0.5):
    """Runs a Recom chain on a given graph with a given minority/majority population distribution and returns lists of cut edges, minority seat wins, and tuples of minority percentage by district for each step of the chain.
    
    Parameters:
    graph (networkx.Graph) -- a NetworkX graph object representing the dual graph on which to run the chain. The nodes should have attributes for majority population, minority population, and total population.
    min_pop_col (string) -- the key/column name for the minority population attribute in graph
    maj_pop_col (string) -- the key/column name for the majority population attribute in graph
    tot_pop_col (string) -- the key/column name for the total population attribute in graph
    num_districts (int) -- number of districts to run for the chain
    initial_plan (gerrychain.Partition) -- an initial partition for the chain (which does not need updaters since the function will supply its own updaters)
    num_steps (int) -- the number of steps for which to run the chain
    pop_tol (float, default 0.05) -- tolerance for deviation from perfectly balanced populations between districts
    min_win_thresh (float, default 0.5) -- percent of minority population needed in a district for it to be considered a minority win. If the minority percentage in a district is greater than or equal to min_win_thresh then that district is considered a minority win.
    
    Returns:
    [cut_edges_list,min_seats_list,min_percents_list] (list)
        WHERE
        cut_edges_list (list) -- list where cut_edges_list[i] is the number of cut edges in the partition at step i of the Markov chain
        min_seats_list -- list where min_seats_list[i] is the number of districts won by the minority (according to min_win_thresh) at step i of the chain
        min_percents_list -- list where min_percents_list[i] is a tuple, with min_percents_list[i][j] being the minority percentage in district j at step i of the chain
    """
    my_updaters = {
        "population": Tally(tot_pop_col, alias = "population"),
        "cut_edges": cut_edges,
        "maj-min": Election("maj-min", {"maj": maj_pop_col, "min": min_pop_col}),
    }
    
    initial_partition = Partition(graph = initial_plan.graph, assignment = initial_plan.assignment, updaters = my_updaters)
    
    # ADD CONSTRAINTS
    popbound = within_percent_of_ideal_population(initial_partition, 0.1)
    
    # ########Setup Proposal
    ideal_population = sum(initial_partition["population"].values()) / len(initial_partition)
    
    tree_proposal = partial(
        recom,
        pop_col=tot_pop_col,
        pop_target=ideal_population,
        epsilon=pop_tol,
        node_repeats=1,
    )
    
    # ######BUILD MARKOV CHAINS
    
    recom_chain = MarkovChain(
        tree_proposal,
        Validator([popbound]),
        accept=always_accept,
        initial_state=initial_partition,
        total_steps=num_steps,
    )
    
    cut_edges_list = []
    min_seats_list = []
    min_percents_list = []
    
    for part in recom_chain:
        cut_edges_list.append(len(part["cut_edges"]))
        min_percents_list.append(part["maj-min"].percents("min"))
        min_seats = (np.array(part["maj-min"].percents("min")) >= min_win_thresh).sum()
        min_seats_list.append(min_seats)
    
    return [cut_edges_list,min_seats_list,min_percents_list]
def run_chain(init_part, chaintype, length, ideal_population, id):
    """Runs a Recom chain, and saves the seats won histogram to a file and
       returns the most Gerrymandered plans for both PartyA and PartyB

    Args:
        init_part (Gerrychain Partition): initial partition of chain
        chaintype (String): indicates which proposal to be used to generate
        spanning tree during Recom. Must be either "tree" or "uniform_tree"
        length (int): total steps of chain
        id (String): id of experiment, used when printing progress

    Raises:
        RuntimeError: If chaintype is not "tree" nor 'uniform_tree"

    Returns:
        list of partitions generated by chain
    """

    graph = init_part.graph

    popbound = within_percent_of_ideal_population(init_part, config['EPSILON'])

    # Determine proposal for generating spanning tree based upon parameter
    if chaintype == "tree":
        tree_proposal = partial(
            recom,
            pop_col=config["POP_COL"],
            pop_target=ideal_population,
            epsilon=config['EPSILON'],
            node_repeats=config['NODE_REPEATS'],
            method=facefinder.my_mst_bipartition_tree_random)

    elif chaintype == "uniform_tree":
        tree_proposal = partial(
            recom,
            pop_col=config["POP_COL"],
            pop_target=ideal_population,
            epsilon=config['EPSILON'],
            node_repeats=config['NODE_REPEATS'],
            method=facefinder.my_uu_bipartition_tree_random)
    else:
        print("Chaintype used: ", chaintype)
        raise RuntimeError(
            "Chaintype not recognized. Use 'tree' or 'uniform_tree' instead")

    # Chain to be run
    chain = MarkovChain(tree_proposal,
                        Validator([popbound]),
                        accept=accept.always_accept,
                        initial_state=init_part,
                        total_steps=length)

    electionDict = {
        'seats': (lambda x: x[config['ELECTION_NAME']].seats('PartyA')),
        'won': (lambda x: x[config['ELECTION_NAME']].seats('PartyA')),
        'efficiency_gap':
        (lambda x: x[config['ELECTION_NAME']].efficiency_gap()),
        'mean_median': (lambda x: x[config['ELECTION_NAME']].mean_median()),
        'mean_thirdian':
        (lambda x: x[config['ELECTION_NAME']].mean_thirdian()),
        'partisan_bias':
        (lambda x: x[config['ELECTION_NAME']].partisan_bias()),
        'partisan_gini': (lambda x: x[config['ELECTION_NAME']].partisan_gini())
    }

    # Run chain, save each desired statistic, and keep track of cuts. Save most
    # left gerrymandered partition
    statistics = {statistic: [] for statistic in config['ELECTION_STATISTICS']}
    for i, partition in enumerate(chain):
        # Save statistics of partition
        for statistic in config['ELECTION_STATISTICS']:
            statistics[statistic].append(electionDict[statistic](partition))
        if i % 500 == 0:
            print('{}: {}'.format(id, i))
    saveRunStatistics(statistics, id)
def run_experiment(bases=[2 * 2.63815853],
                   pops=[.1],
                   time_between_outputs=10000,
                   total_run_length=100000000000000):

    mu = 2.63815853
    subsequence_step_size = 10000
    balances_burn_in = 1000000  #ignore the first 10000 balances

    # creating the boundary figure plot
    plt.figure()
    fig = plt.figure()
    #fig_intervals = plt.figure()
    #ax2=fig.add_axes([0,0,1,1])
    ax = plt.subplot(111, projection='polar')
    #ax.set_axis_off()
    #ax.xaxis.set_ticklabels([])
    ax.yaxis.set_ticklabels([])

    for pop1 in pops:
        for base in bases:
            for alignment in [1]:

                gn = 20
                k = 2
                ns = 120
                p = .5

                graph = nx.grid_graph([k * gn, k * gn])

                ########## BUILD ASSIGNMENT
                #cddict = {x: int(x[0]/gn)  for x in graph.nodes()}
                cddict = {x: 1 - 2 * int(x[0] / gn) for x in graph.nodes()}

                for n in graph.nodes():
                    if alignment == 0:
                        if n[0] > 19:
                            cddict[n] = 1
                        else:
                            cddict[n] = -1
                    elif alignment == 1:
                        if n[1] > 19:
                            cddict[n] = 1
                        else:
                            cddict[n] = -1
                    elif alignment == 2:
                        if n[0] > n[1]:
                            cddict[n] = 1
                        elif n[0] == n[1] and n[0] > 19:
                            cddict[n] = 1
                        else:
                            cddict[n] = -1
                    elif alignment == 10:
                        #This is for debugging the case of reaching trivial partitions.
                        if n[0] == 10 and n[1] == 10:
                            cddict[n] = 1
                        else:
                            cddict[n] = -1

                for n in graph.nodes():
                    graph.nodes[n]["population"] = 1
                    graph.nodes[n]["part_sum"] = cddict[n]
                    graph.nodes[n]["last_flipped"] = 0
                    graph.nodes[n]["num_flips"] = 0

                    if random.random() < p:
                        graph.nodes[n]["pink"] = 1
                        graph.nodes[n]["purple"] = 0
                    else:
                        graph.nodes[n]["pink"] = 0
                        graph.nodes[n]["purple"] = 1
                    if 0 in n or k * gn - 1 in n:
                        graph.nodes[n]["boundary_node"] = True
                        graph.nodes[n]["boundary_perim"] = 1

                    else:
                        graph.nodes[n]["boundary_node"] = False

                #graph.add_edges_from([((0,1),(1,0)), ((0,38),(1,39)), ((38,0),(39,1)), ((38,39),(39,38))])

                for edge in graph.edges():
                    graph[edge[0]][edge[1]]['cut_times'] = 0

                #this part adds queen adjacency
                #for i in range(k*gn-1):
                #    for j in range(k*gn):
                #        if j<(k*gn-1):
                #            graph.add_edge((i,j),(i+1,j+1))
                #            graph[(i,j)][(i+1,j+1)]["shared_perim"]=0
                #        if j >0:
                #            graph.add_edge((i,j),(i+1,j-1))
                #            graph[(i,j)][(i+1,j-1)]["shared_perim"]=0

                #graph.remove_nodes_from([(0,0),(0,39),(39,0),(39,39)])

                #del cddict[(0,0)]

                #del cddict[(0,39)]

                # cddict[(39,0)]

                #del cddict[(39,39)]
                ######PLOT GRIDS
                """
                plt.figure()
                nx.draw(graph, pos = {x:x for x in graph.nodes()} ,node_size = ns, node_shape ='s')
                plt.show()

                cdict = {1:'pink',0:'purple'}

                plt.figure()
                nx.draw(graph, pos = {x:x for x in graph.nodes()}, node_color = [cdict[graph.nodes[x]["pink"]] for x in graph.nodes()],node_size = ns, node_shape ='s' )
                plt.show()

                plt.figure()
                nx.draw(graph, pos = {x:x for x in graph.nodes()}, node_color = [cddict[x] for x in graph.nodes()] ,node_size = ns, node_shape ='s',cmap = 'tab20')
                plt.show()
                """

                ####CONFIGURE UPDATERS

                def new_base(partition):
                    return base

                def step_num(partition):
                    parent = partition.parent

                    if not parent:
                        return 0

                    return parent["step_num"] + 1

                bnodes = [
                    x for x in graph.nodes()
                    if graph.nodes[x]["boundary_node"] == 1
                ]

                def bnodes_p(partition):

                    return [
                        x for x in graph.nodes()
                        if graph.nodes[x]["boundary_node"] == 1
                    ]

                updaters = {
                    'population': Tally('population'),
                    "boundary": bnodes_p,
                    #"slope": boundary_slope,
                    'cut_edges': cut_edges,
                    'step_num': step_num,
                    'b_nodes': b_nodes_bi,
                    'base': new_base,
                    'geom': geom_wait,
                    #"Pink-Purple": Election("Pink-Purple", {"Pink":"pink","Purple":"purple"})
                }

                balances = []

                #########BUILD PARTITION

                grid_partition = Partition(graph,
                                           assignment=cddict,
                                           updaters=updaters)

                #ADD CONSTRAINTS
                popbound = within_percent_of_ideal_population(
                    grid_partition, pop1)

                #plt.figure()
                #nx.draw(graph, pos = {x:x for x in graph.nodes()}, node_color = [dict(grid_partition.assignment)[x] for x in graph.nodes()] ,node_size = ns, node_shape ='s',cmap = 'tab20')
                #plt.savefig("./plots/"+str(alignment)+"B"+str(int(100*base))+"P"+str(int(100*pop1))+"start.png")
                #plt.close()

                #########Setup Proposal
                ideal_population = sum(grid_partition["population"].values()
                                       ) / len(grid_partition)

                tree_proposal = partial(recom,
                                        pop_col="population",
                                        pop_target=ideal_population,
                                        epsilon=pop1,
                                        node_repeats=1)

                #######BUILD MARKOV CHAINS

                exp_chain = MarkovChain(
                    slow_reversible_propose_bi,
                    Validator([
                        single_flip_contiguous,
                        popbound  #,boundary_condition
                    ]),
                    accept=cut_accept,
                    initial_state=grid_partition,
                    total_steps=total_run_length)

                #########Run MARKOV CHAINS

                rsw = []
                rmm = []
                reg = []
                rce = []
                rbn = []
                waits = []

                slopes = []
                angles = []
                angles_safe = []
                ends_vectors_normalized = LinkedList()
                ends_vectors_normalized_bloated = LinkedList()
                import time

                st = time.time()

                total_waits = 0
                last_total_waits = 0
                t = 0

                subsequence_timer = 0

                balances = {}
                for b in np.linspace(0, 2, 100001):
                    balances[int(b * 100) / 100] = 0

                #first_partition = True
                for part in exp_chain:
                    rce.append(len(part["cut_edges"]))
                    wait_time_rv = part.geom
                    waits.append(wait_time_rv)
                    total_waits += wait_time_rv
                    rbn.append(len(list(part["b_nodes"])))

                    if total_waits > subsequence_timer + subsequence_step_size:

                        last_total_waits = total_waits

                        ends = boundary_ends(part)
                        if len(ends) == 2:
                            ends_vector = np.asarray(ends[1]) - np.asarray(
                                ends[0])
                            ends_vector_normalized = ends_vector / np.linalg.norm(
                                ends_vector)

                            #if first_partition == True:
                            #    ends_vectors_normalized.last_vector = ends_vector_normalized
                            #    first_partition = False

                            if ends_vectors_normalized.last:
                                # We choose the vector that preserves continuity
                                # previous_angle = ends_vectors_normalized.last_value()
                                previous = ends_vectors_normalized.last_vector

                                d_previous = np.linalg.norm(
                                    previous - ends_vector_normalized)
                                d_previous_neg = np.linalg.norm(
                                    previous + ends_vector_normalized)
                                if d_previous < d_previous_neg:
                                    continuous_lift = ends_vector_normalized
                                else:
                                    continuous_lift = -1 * ends_vector_normalized
                                    #print(previous, ends_vector_normalized)

                            else:
                                continuous_lift = ends_vector_normalized  # *random.choice([-1,1])
                                # just to debias it, in the regime of very unbalanced partitions
                                # that touch the empty partition frequently

                        else:
                            continuous_lift = [0, 0]

                        ##############For Debugging#############
                        '''
                        if total_waits > subsequence_timer + subsequence_step_size:

                            last_total_waits = total_waits

                            ends = boundary_ends(part)
                            if ends:
                                ends_vector = np.asarray(ends[1]) - np.asarray(ends[0])
                                ends_vector_normalized = ends_vector / np.linalg.norm(ends_vector)

                                if ends_vectors_normalized_bloated.last:
                                    # We choose the vector that preserves continuity
                                    previous = ends_vectors_normalized_bloated.last_value()
                                    d_previous = np.linalg.norm( ends_vector_normalized - previous)
                                    d_previous_neg = np.linalg.norm( ends_vector_normalized + previous )
                                    if d_previous < d_previous_neg:
                                        continuous_lift_bloated = ends_vector_normalized
                                    else:
                                        continuous_lift_bloated = -1* ends_vector_normalized

                                else:
                                    continuous_lift_bloated = ends_vector_normalized # *random.choice([-1,1])
                                    # just to debias it, in the regime of very unbalanced partitions
                                    # that touch the empty partition frequently

                            else:
                                continuous_lift_bloated = [0,0]
                                '''
                        ################

                        # Pop balance stuff:
                        left_pop, right_pop = part["population"].values()
                        ideal_population = (left_pop + right_pop) / 2
                        left_bal = (left_pop / ideal_population)
                        right_bal = (right_pop / ideal_population)

                        while subsequence_timer < total_waits:
                            subsequence_timer += subsequence_step_size
                            if (continuous_lift == np.asarray([0, 0])).all():
                                lifted_angle = False
                                print("false")
                                draw_other_plots(balances, graph, alignment,
                                                 "NonSimplyConnected", base,
                                                 pop1, part, ns)
                                #Flag to hold the exceptional case of the boundary vanishing
                            else:
                                lifted_angle = np.arctan2(
                                    continuous_lift[1], continuous_lift[0])
                                #+ np.pi
                                ends_vectors_normalized.last_vector = continuous_lift
                            ends_vectors_normalized.append(lifted_angle)

                            if subsequence_timer > balances_burn_in:
                                left_bal_rounded = int(left_bal * 100) / 100
                                right_bal_rounded = int(right_bal * 100) / 100
                                balances[
                                    left_bal_rounded] += 1  #left_bal_rounded
                                balances[
                                    right_bal_rounded] += 1  # right_bal_rounded
                                #NB wait times are accounted for by the while loops

                    for edge in part["cut_edges"]:
                        graph[edge[0]][edge[1]]["cut_times"] += wait_time_rv
                        #print(graph[edge[0]][edge[1]]["cut_times"])

                    if part.flips is not None:
                        f = list(part.flips.keys())[0]

                        graph.nodes[f]["part_sum"] = graph.nodes[f][
                            "part_sum"] - part.assignment[f] * (
                                total_waits - graph.nodes[f]["last_flipped"])
                        graph.nodes[f]["last_flipped"] = total_waits
                        graph.nodes[f]["num_flips"] = graph.nodes[f][
                            "num_flips"] + wait_time_rv

                    t += 1

                    if t % time_between_outputs == 0:

                        #ends_vectors_normalized[1:] #Remove the first one because it will overlap with last one of previous dump

                        identifier_string = "state_after_num_steps" + str(
                            t) + "and_time" + str(st - time.time())

                        #print("finished no", st-time.time())
                        with open(
                                "./plots/" + str(alignment) + "B" +
                                str(int(100 * base)) + "P" +
                                str(int(100 * pop1)) + "wait.txt",
                                'w') as wfile:
                            wfile.write(str(sum(waits)))

                        #with open("./plots/"+str(alignment)+"B"+str(int(100*base))+"P"+str(int(100*pop1)) + "ends_vectors.txt",'w') as wfile:
                        #    wfile.write(str(ends_vectors_normalized))

                        #with open("./plots/"+str(alignment)+"B"+str(int(100*base))+"P"+str(int(100*pop1)) + "ends_vectors.pkl",'wb') as wfile:
                        #    pickle.dump(ends_vectors_normalized, wfile)

                        with open(
                                "./plots/" + str(alignment) + "B" +
                                str(int(100 * base)) + "P" +
                                str(int(100 * pop1)) + "balances.txt",
                                'w') as wfile:
                            wfile.write(str(balances))
                        with open(
                                "./plots/" + str(alignment) + "B" +
                                str(int(100 * base)) + "P" +
                                str(int(100 * pop1)) + "balances.pkl",
                                'wb') as wfile:
                            pickle.dump(balances, wfile)

                        for n in graph.nodes():
                            if graph.nodes[n]["last_flipped"] == 0:
                                graph.nodes[n][
                                    "part_sum"] = total_waits * part.assignment[
                                        n]
                            graph.nodes[n]["lognum_flips"] = math.log(
                                graph.nodes[n]["num_flips"] + 1)

                        total_part_sum = 0
                        for n in graph.nodes():
                            total_part_sum += graph.nodes[n]["part_sum"]

                        for n in graph.nodes():
                            if total_part_sum != 0:
                                graph.nodes[n][
                                    "normalized_part_sum"] = graph.nodes[n][
                                        "part_sum"] / total_part_sum
                            else:
                                graph.nodes[n]["normalized_part_sum"] = 0

                        #print(len(rsw[-1]))
                        #print(graph[(1,0)][(0,1)]["cut_times"])

                        print("creating boundary plot, ", time.time())

                        max_time = ends_vectors_normalized.last.end_time
                        non_simply_connected_intervals = [
                            [x.start_time, x.end_time]
                            for x in ends_vectors_normalized
                            if type(x.data) == bool
                        ]

                        for x in ends_vectors_normalized:
                            if type(x.data) != bool:

                                #times = np.linspace(x.start_time, x.end_time, 100)
                                #times.append(x.end_time)

                                times = [x.start_time, x.end_time]
                                angles = [x.data] * len(times)
                                plt.polar(angles, times, lw=.1, color='b')

                                next_point = x.next
                                #'''
                                if next_point != None:
                                    if type(next_point.data) != bool:
                                        if np.abs(
                                            (x.data - next_point.data)) % (
                                                2 * np.pi) < .1:
                                            # added that last if to avoid
                                            # the big jumps that happen with
                                            # small size subcritical
                                            plt.polar(
                                                [x.data, next_point.data], [
                                                    x.end_time,
                                                    next_point.start_time
                                                ],
                                                lw=.1,
                                                color='b')
                                #'''

                        # Create the regular segments corresponding to time
                        '''
                        for k in range(11):
                            plt.polar ( np.arange(0, (2 * np.pi), 0.01), [int(max_time/10) * k ] * len( np.arange(0, (2 * np.pi), 0.01)), lw = .2, color = 'g' )
                        '''
                        # Create the intervals representing when the partition is null.
                        # Removing these might be just as good, and a little cleaner.
                        #'''
                        for interval in non_simply_connected_intervals:
                            start = interval[0]
                            end = interval[1]
                            for s in np.linspace(start, end, 10):
                                plt.polar(
                                    np.arange(0, (2 * np.pi), 0.01),
                                    s * np.ones(
                                        len(np.arange(0, (2 * np.pi), 0.01))),
                                    lw=.3,
                                    color='r')
                        #'''

                        #plt.savefig("./plots/"+str(alignment)+"B"+str(int(100*base))+"P"+str(int(100*pop1)) + str("proposals_") + str( max_time * subsequence_step_size ) + "boundary_slope.svg")
                        plt.savefig("./plots/" + str(alignment) + "B" +
                                    str(int(100 * base)) + "P" +
                                    str(int(100 * pop1)) + str("proposals_") +
                                    identifier_string + "boundary_slope.png",
                                    dpi=500)

                        # now clear the ends vectors list
                        last = ends_vectors_normalized.last
                        last_non_zero = ends_vectors_normalized.last_non_zero
                        last_vector = ends_vectors_normalized.last_vector
                        # Explicit Garbage collection https://stackoverflow.com/questions/1316767/how-can-i-explicitly-free-memory-in-python

                        print(
                            "finished boundary plot, doing garbage collection",
                            time.time())
                        del ends_vectors_normalized
                        gc.collect()

                        ends_vectors_normalized = LinkedList()
                        ends_vectors_normalized.head = last
                        ends_vectors_normalized.last = last
                        ends_vectors_normalized.last_non_zero = last_non_zero  # can be ahead of head...
                        ends_vectors_normalized.last_vector = last_vector
                        #print(last)

                        print("drawing other plots", time.time())
                        draw_other_plots(balances, graph, alignment,
                                         identifier_string, base, pop1, part,
                                         ns)
                        print("finished drawing other plots, ", time.time())