def test_chimera(self): G = dnx.chimera_graph(1, 1, 4) true_tw = 4 tw, order = dnx.treewidth_branch_and_bound(G) self.check_order(G, order) self.assertEqual(tw, true_tw) G = dnx.chimera_graph(2, 2, 3) true_tw = 6 tw, order = dnx.treewidth_branch_and_bound(G) self.check_order(G, order) self.assertEqual(tw, true_tw) G = dnx.chimera_graph(1, 2, 4) true_tw = 4 tw, order = dnx.treewidth_branch_and_bound(G) self.check_order(G, order) self.assertEqual(tw, true_tw) G = dnx.chimera_graph(2, 2, 4) true_tw = 8 tw, order = dnx.treewidth_branch_and_bound(G) self.check_order(G, order) self.assertEqual(tw, true_tw)
def test_upperbound_parameter(self): """We want to be able to give an upper bound to make execution faster.""" graph = nx.complete_graph(3) # providing a treewidth without an order should still work, although # the order might not be found tw, order = dnx.treewidth_branch_and_bound(graph, treewidth_upperbound=2) self.assertEqual(len(order), 3) # all nodes should be in the order tw, order = dnx.treewidth_branch_and_bound(graph, [0, 1, 2]) self.assertEqual(len(order), 3) # all nodes should be in the order # try with both tw, order = dnx.treewidth_branch_and_bound(graph, [0, 1, 2], 2) self.assertEqual(len(order), 3) # all nodes should be in the order
def test_grid(self): G = nx.grid_2d_graph(4, 6) true_tw = 4 tw, order = dnx.treewidth_branch_and_bound(G) self.check_order(G, order) self.assertEqual(tw, true_tw)
def test_cycle(self): G = nx.cycle_graph(167) true_tw = 2 tw, order = dnx.treewidth_branch_and_bound(G) self.check_order(G, order) self.assertEqual(tw, true_tw)
def test_complete(self): G = nx.complete_graph(100) true_tw = 99 tw, order = dnx.treewidth_branch_and_bound(G) self.check_order(G, order) self.assertEqual(tw, true_tw)
def test_empty(self): G = nx.Graph() true_tw = 0 tw, order = dnx.treewidth_branch_and_bound(G) self.check_order(G, order) self.assertEqual(tw, true_tw)
def test_graphs(self): H = nx.complete_graph(2) H.add_edge(2, 3) graphs = [ nx.complete_graph(7), dnx.chimera_graph(2, 1, 3), nx.balanced_tree(5, 3), nx.barbell_graph(8, 11), nx.cycle_graph(5), H ] for G in graphs: tw, order = dnx.treewidth_branch_and_bound(G) self.assertEqual(dnx.elimination_order_width(G, order), tw) tw, order = dnx.min_width_heuristic(G) self.assertEqual(dnx.elimination_order_width(G, order), tw) tw, order = dnx.min_fill_heuristic(G) self.assertEqual(dnx.elimination_order_width(G, order), tw) tw, order = dnx.max_cardinality_heuristic(G) self.assertEqual(dnx.elimination_order_width(G, order), tw)
def test_self_loop(self): graph = nx.complete_graph(3) graph.add_edge(0, 0) graph.add_edge(2, 2) tw, order = dnx.treewidth_branch_and_bound(graph) self.assertEqual(tw, 2)
def test_complete_minus_1(self): G = nx.complete_graph(50) G.remove_edge(7, 6) true_tw = 48 tw, order = dnx.treewidth_branch_and_bound(G) self.check_order(G, order) self.assertEqual(tw, true_tw)
def test_disjoint(self): graph = nx.complete_graph(1) graph.add_node(1) tw, order = dnx.treewidth_branch_and_bound(graph) self.check_order(graph, order) self.assertEqual(tw, 0) graph = nx.complete_graph(4) graph.add_node(4) tw, order = dnx.treewidth_branch_and_bound(graph) self.check_order(graph, order) self.assertEqual(tw, 3) graph = nx.complete_graph(4) graph.add_edge(4, 5) tw, order = dnx.treewidth_branch_and_bound(graph) self.check_order(graph, order) self.assertEqual(tw, 3)
def _elimination_trees(theta, decision_variables): """From Theta and the decision variables, determine the elimination order and the induced trees. """ # auxiliary variables are any variables that are not decision auxiliary_variables = set(n for n in theta.linear if n not in decision_variables) # get the adjacency of the auxiliary subgraph adj = { v: {u for u in theta.adj[v] if u in auxiliary_variables} for v in theta.adj if v in auxiliary_variables } # get the elimination order that minimizes treewidth tw, order = dnx.treewidth_branch_and_bound(adj) ancestors = {} for n in order: ancestors[n] = set(adj[n]) # now make v simplicial by making its neighborhood a clique, then # continue neighbors = adj[n] for u, v in itertools.combinations(neighbors, 2): adj[u].add(v) adj[v].add(u) for v in neighbors: adj[v].discard(n) del adj[n] roots = {} nodes = {v: {} for v in ancestors} for vidx in range(len(order) - 1, -1, -1): v = order[vidx] if ancestors[v]: for u in order[vidx + 1:]: if u in ancestors[v]: # v is a child of u nodes[u][v] = nodes[v] # nodes[u][v] = children of v break else: roots[v] = nodes[v] # roots[v] = children of v return roots, ancestors
def main(args): print('--- Prediction by existing algorithm ---') print('Index\t|V|\t|E|\ttw(G)\ttime\tevaltw\tprunenum\tfunccallnum') output_file = "exist.dat" result = ["Index\t|V|\t|E|\ttw\ttime\tevaltw\tfunccallnum"] # make some graphs graphs = [] for idx in range(0, args.data_num): n = np.random.randint(5, 10) # [5, 10] |V| m = np.random.randint(n - 1, (n * (n - 1)) // 2) # |E| g = nx.dense_gnm_random_graph(n, m) while not nx.is_connected(g): g = nx.dense_gnm_random_graph(n, m) graphs.append(g) for idx, eval_graph in enumerate(graphs): # exact computation tw = dnx.treewidth_branch_and_bound(eval_graph)[0] for calc_type in ["upper", "lower"]: graphstat = "{3}\t{0}\t{1}\t{2}".format( eval_graph.number_of_nodes(), eval_graph.number_of_edges(), tw, str(idx).rjust(5)) print('{0}\t{1}\t{2}\t{3}'.format( str(idx).rjust(5), eval_graph.number_of_nodes(), eval_graph.number_of_edges(), tw), end='\t') try: tm, evtw, fcn = linear_search(eval_graph, calc_type) res = "{0}\t{1}\t{2}".format(tm, evtw, fcn) except TimeoutError: res = "TimeOut" print(res) assert evtw == tw result.append(graphstat + "\t" + res) # write results to a file if args.out_to_file: with open("./{}/".format(args.out) + output_file, "w") as f: f.write('\n'.join(result))
def test_singleton(self): G = nx.Graph() G.add_node('a') tw, order = dnx.treewidth_branch_and_bound(G)
def test_incorrect_lowerbound(self): graph = nx.complete_graph(3) tw, order = dnx.treewidth_branch_and_bound(graph, treewidth_upperbound=1) self.assertEqual(order, []) # no order produced