def compute_estimated_cut(G, partition_dict, marked_nodes): """Compute overestimated cut value on a partial marked graph""" buffer_dict = dict(partition_dict) # cut marked nodes using strong minority gc.marked_nodes_could_be_cut(G, buffer_dict, marked_nodes) bk = set() # decided nodes in partition_dict m = set() # marked nodes in partition_dict for node in partition_dict: if partition_dict[node] == gc.MARKED: m.add(node) else: bk.add(node) b1k1 = set() # decided and undecided nodes in buffer_dict for node in m: if buffer_dict[node] != gc.MARKED: b1k1.add(node) # nx.set_node_attributes(G, gc.PARTITION, buffer_dict) nx.set_node_attributes(G, buffer_dict, gc.PARTITION) result = (G.subgraph(m).number_of_edges() + gc.cut_edges(G.subgraph(bk | b1k1)) - gc.cut_edges(G.subgraph(b1k1))) return result
def complete_cut(G, partition_dict, nodes_stack): """Compute the maximum cut from a partial partitioned graph.""" marked_nodes_stack = [] while gc.could_be_cut(G, partition_dict): pass # filter away the decided nodes for i in nodes_stack: if partition_dict[i] is gc.UNDECIDED or partition_dict[i] is gc.MARKED: marked_nodes_stack.append(i) if not marked_nodes_stack: return partition_dict, gc.cut_edges(G, partition_dict) candidate = marked_nodes_stack.pop() # Complete cut blue_cut, blue_cut_val = aux_local_consistent_max_cut( G, dict(partition_dict), list(marked_nodes_stack), int(candidate), gc.BLUE) black_cut, black_cut_val = aux_local_consistent_max_cut( G, dict(partition_dict), list(marked_nodes_stack), int(candidate), gc.BLACK) if blue_cut_val > black_cut_val: return blue_cut, blue_cut_val return black_cut, black_cut_val
def aux_pruning_local_consistent_max_cut(G, partition_dict, degree_node_seq, candidate, partition_attribute): """Helper function to pruning_local_consistent_max_cut""" partition_dict[candidate] = partition_attribute # check consistency in the candidate and its neighbors only check_nodes = nx.neighbors(G, candidate) if not gc.is_cut_consistent(G, partition_dict, check_nodes): return None, 0 if not degree_node_seq: return partition_dict, gc.cut_edges(G, partition_dict) while (gc.could_be_cut(G, partition_dict)): pass # pick a new candidate candidate = choose_new_candidate(partition_dict, degree_node_seq) blue_cut, blue_cut_val = aux_pruning_local_consistent_max_cut( G, dict(partition_dict), list(degree_node_seq), candidate, gc.BLUE) black_cut, black_cut_val = aux_pruning_local_consistent_max_cut( G, dict(partition_dict), list(degree_node_seq), candidate, gc.BLACK) if blue_cut is None and black_cut is None: return None, 0 # Choose best cut according to the effective cut value if blue_cut_val > black_cut_val: return blue_cut, blue_cut_val return black_cut, black_cut_val
def greedy_choice(G, candidate, blue_nodes, black_nodes, visited): """Helper function to greedy cut""" G.node[candidate][gc.PARTITION] = gc.BLUE blue_cut_val = gc.cut_edges(nx.subgraph(G, visited)) G.node[candidate][gc.PARTITION] = gc.BLACK black_cut_val = gc.cut_edges(nx.subgraph(G, visited)) if blue_cut_val > black_cut_val: G.node[candidate][gc.PARTITION] = gc.BLUE blue_nodes.add(candidate) else: black_nodes.add(candidate) return blue_nodes, black_nodes
def ising_ground_truth(cf, info_mtx, fig_save_path=""): print("Running the Ground Truth...") ising_model = Ising_model(cf, info_mtx) dim = info_mtx.shape[0] if cf.pb_type == "maxcut": # 1/2\sum_edges 1-node1*node2 = 1/2*num_edges - 1/2*\sum_edges node1*node2 graph = ising_model.graph start_time = time.time() result = ising.search(graph, num_states=3, show_progress=True, chunk_size=0) end_time = time.time() energy = result.energies[0] state = decode_state(result.states[0], dim, list(range(dim))) num_edges = info_mtx.sum() / 2 score = num_edges / 2 - energy score1 = ising_model(state) if abs((score - score1) / score) > 1e-2: raise ("Mismatched energy - result1={}, result2={}".format( score, score1)) # plot the graph # Laplacian matrices are real and symmetric, so we can use eigh, # the variation on eig specialized for Hermetian matrices. G = laplacian_to_graph(info_mtx) # Laplacian matrices are real and symmetric, so we can use eigh, # the variation on eig specialized for Hermetian matrices. # method from maxCutPy gt.execution_time(mc.local_consistent_max_cut, 1, G) score1 = gc.cut_edges(G) if abs((score - score1)) != 0: raise ("Mismatched maxcut - result1={}, result2={}".format( score, score1)) nbunch = G.nodes() for i in nbunch: G.node[i]['partition'] = state[i] gd.draw_cut_graph(G) plt.savefig(fig_save_path) plt.close() if cf.pb_type == "spinglass": # \sum_edges J_ij*node1*node2 graph = ising_model.graph start_time = time.time() result = ising.search(graph, num_states=3, show_progress=True, chunk_size=0) end_time = time.time() energy = result.energies[0] state = decode_state(result.states[0], dim, list(range(dim))) energy1 = ising_model(state) if abs((energy - energy1) / energy) > 1e-2: raise ("Mismatched energy - result1={}, result2={}".format( energy, energy1)) score = energy time_elapsed = end_time - start_time return score, state, time_elapsed
def second_lemma(G): """Compute second lemma.""" cut_edges = gc.cut_edges(G) uncut_edges = G.number_of_edges() - cut_edges numerator = float(uncut_edges - cut_edges) denominator = float(G.number_of_edges()) return numerator / denominator
def compare_cut_algorithm_results(cut_alg, results_graphs, test_name=''): graphs_dict = gg.read_from_file(results_graphs + '.dat') location = cut_alg.__name__ error_list = [] results_dict = defaultdict(list) print('Algorithm: ' + str(cut_alg.__name__)) print('Benchamark: ' + results_graphs) print('Starting..:)') print('Graph\Result Epsilon\tCurrent Epsilon') i = 0 for opt_eps in sorted(graphs_dict.iterkeys()): for A, c in graphs_dict[opt_eps]: G = nx.from_numpy_matrix(A) cut_alg(G) if gc.cut_edges(G) < G.number_of_edges() / 2: print('####### ERROR 1 #######') error_list.append(G) if gc.are_undecided_nodes(G): print('####### ERROR 2 #######') error_list.append(G) this_eps = gc.compute_epsilon(G) print(str(i) + '\t' + str(opt_eps) + '\t' + str(this_eps)) results_dict[opt_eps].append(this_eps) i += 1 gg.write_to_file(results_dict, test_name + 'results_' + results_graphs + '.dat', location) if len(error_list) != 0: gg.write_to_file(error_list, test_name + 'errors_' + results_graphs + '.dat', location)
def brute_force_max_cut(G): """Compute maximum cut of a graph considering all the possible cuts.""" max_cut_value = 0 max_cut_ind = 0 n = G.number_of_nodes() for i in range(1, 2**(n - 1)): cut_graph = nx.Graph(G) gc.binary_cut(cut_graph, i) value = gc.cut_edges(cut_graph) if value > max_cut_value: max_cut_value = value max_cut_ind = i gc.binary_cut(G, max_cut_ind) return gc.partition_dictionary(G), max_cut_value
def plot_graph(cf, L, save_path): if cf.pb_type == "maxcut": seed = cf.random_seed # Laplacian matrices are real and symmetric, so we can use eigh, # the variation on eig specialized for Hermetian matrices. # method from maxCutPy time = gt.execution_time(mc.local_consistent_max_cut, 1, G) result = gc.cut_edges(G) return time, result if cf.pb_type == "spinglass": def decode_state(state_repr, no_spins, labels): state = {} for i in range(no_spins): state[labels[no_spins - (i + 1)]] = 1 if state_repr % 2 else -1 state_repr //= 2 return state def check(dic, graph): total = 0 for edge, energy in graph.items(): total += energy * dic[edge[0]] * dic[edge[1]] return total J = L graph = {} shape = J.shape size = int(shape[0] * shape[1]) for i in range(shape[0]): for j in range(shape[1]): graph[(i, j)] = J[i, j] result = ising.search(graph, num_states=4) print(result.energies, result.states) print(decode_state(result.states[0], size, list(range(size)))) print( check(decode_state(result.states[0], size, list(range(size))), graph))
def trevisan_approximation_alg(G): B, K = recursive_spectral_cut(G) # set blue and black nodes in graph G gc.set_partitions(G, B, K) return gc.cut_edges(G)