def from_networkx_digraph(nx_digraph): """Returns a DirectedHypergraph object that is the graph equivalent of the given NetworkX DiGraph object. :param nx_digraph: the NetworkX directed graph object to transform. :returns: DirectedHypergraph -- hypergraph object equivalent to the NetworkX directed graph. :raises: TypeError -- Transformation only applicable to directed NetworkX graphs """ import networkx as nx if not isinstance(nx_digraph, nx.DiGraph): raise TypeError("Transformation only applicable to directed \ NetworkX graphs") G = DirectedHypergraph() for node in nx_digraph.nodes_iter(): G.add_node(node, copy.copy(nx_digraph.node[node])) for edge in nx_digraph.edges_iter(): tail_node = edge[0] head_node = edge[1] G.add_hyperedge(tail_node, head_node, copy.copy(nx_digraph[tail_node][head_node])) return G
def test_stationary_distribution(): H = DirectedHypergraph() H.read("./tests/data/basic_directed_hypergraph.txt") # Try random-walking a directed hypergraph with a node # with no outgoing hyperedges try: pi = rw.stationary_distribution(H) assert False except AssertionError: pass except BaseException as e: assert False, e # Try random-walking a valid directed hypergraph H.add_hyperedge(["u"], ["u"], weight=1) pi = rw.stationary_distribution(H) # Correctness tests go here # Try partitioning an invalid directed hypergraph try: pi = rw.stationary_distribution("H") assert False except TypeError: pass except BaseException as e: assert False, e
def test_raises_exception_if_H_not_B_hypegraph(self): H = DirectedHypergraph() H.add_nodes([1, 2, 3]) H.add_hyperedge([1], [2, 3]) source, destination = 1, 2 self.assertRaises(TypeError, ksh.k_shortest_hyperpaths, H, source, destination, 1)
def convert_pnet_to_hypergraph_andgatewayonly(pnet): hg = DirectedHypergraph() #scan all transitions and create hyperedges transitions = get_transitions(pnet) for transition in transitions: #get all incoming arcs, the source of these become the tail of hyperedge inc_arcs = get_incoming_arcs(transition, pnet) tail = [] for inc_arc in inc_arcs: source = str(get_arc_source(inc_arc)) tail.append(source) #get all outgoing arcs, the target of these become the head of the hyperedge out_arcs = get_outgoing_arcs(transition, pnet) head = [] for out_arc in out_arcs: target = str(get_arc_target(out_arc)) head.append(target) name = get_transition_name(transition) hg.add_hyperedge(tail, head, name=name, phero=0.5, cost=0.4, avail=0.6, qual=0.2, time=0.99) #print the result before exit print_hg_std_out_only(hg) return hg
def from_networkx_digraph(nx_digraph): """Returns a DirectedHypergraph object that is the graph equivalent of the given NetworkX DiGraph object. :param nx_digraph: the NetworkX directed graph object to transform. :returns: DirectedHypergraph -- hypergraph object equivalent to the NetworkX directed graph. :raises: TypeError -- Transformation only applicable to directed NetworkX graphs """ import networkx as nx if not isinstance(nx_digraph, nx.DiGraph): raise TypeError("Transformation only applicable to directed \ NetworkX graphs") G = DirectedHypergraph() for node in nx_digraph.nodes(): G.add_node(node, **copy.copy(nx_digraph.nodes[node])) for edge in nx_digraph.edges(): tail_node = edge[0] head_node = edge[1] G.add_hyperedge(tail_node, head_node, copy.copy(nx_digraph[tail_node][head_node])) return G
def reset_pheromone(hg): ''' Reset the pheromone level to a default (0.5) :param hg: ''' hg_copy = DirectedHypergraph() for edge in hg.get_hyperedge_id_set(): #hg_copy = hg.copy() PHERO_DEFAULT = 0.5 head_edge = hg.get_hyperedge_head(edge) #head_edge = ast.literal_eval(head_edge) #print(head_edge) for head_node in head_edge: #print(head_node) attrs = hg.get_node_attributes(head_node) hg_copy.add_node(head_node, attrs) tail_edge = hg.get_hyperedge_tail(edge) #tail_edge = ast.literal_eval(tail_edge) for tail_node in tail_edge: attrs = hg.get_node_attributes(tail_node) hg_copy.add_node(tail_node, attrs) hg_copy.add_hyperedge(tail_edge, head_edge, phero=PHERO_DEFAULT, id=edge, name=edge) return hg_copy
def test_returns_empty_list_if_no_s_t_path(self): H = DirectedHypergraph() H.add_node('s') H.add_node('1') H.add_node('2') H.add_node('t') H.add_hyperedge({'s'}, {'1'}, weight=1) H.add_hyperedge({'1', '2'}, {'t'}, weight=1) output = ksh.k_shortest_hyperpaths(H, 's', 't', 1) self.assertEqual(output, [])
def test_add_hyperedge(): node_a = 'A' node_b = 'B' node_c = 'C' node_d = 'D' tail = set([node_a, node_b]) head = set([node_c, node_d]) frozen_tail = frozenset(tail) frozen_head = frozenset(head) attrib = {'weight': 6, 'color': 'black'} H = DirectedHypergraph() H.add_node(node_a, label=1337) hyperedge_name = H.add_hyperedge(tail, head, attrib, weight=5) assert hyperedge_name == 'e1' # Test that all hyperedge attributes are correct assert H._hyperedge_attributes[hyperedge_name]['tail'] == tail assert H._hyperedge_attributes[hyperedge_name]['head'] == head assert H._hyperedge_attributes[hyperedge_name]['weight'] == 5 assert H._hyperedge_attributes[hyperedge_name]['color'] == 'black' # Test that successor list contains the correct info assert frozen_head in H._successors[frozen_tail] assert hyperedge_name in H._successors[frozen_tail][frozen_head] # Test that the precessor list contains the correct info assert frozen_tail in H._predecessors[frozen_head] assert hyperedge_name in H._predecessors[frozen_head][frozen_tail] # Test that forward-stars and backward-stars contain the correct info for node in frozen_tail: assert hyperedge_name in H._forward_star[node] for node in frozen_head: assert hyperedge_name in H._backward_star[node] # Test that adding same hyperedge will only update attributes new_attrib = {'weight': 10} H.add_hyperedge(tail, head, new_attrib) assert H._hyperedge_attributes[hyperedge_name]['weight'] == 10 assert H._hyperedge_attributes[hyperedge_name]['color'] == 'black' try: H.add_hyperedge(set(), set()) assert False except ValueError: pass except BaseException as e: assert False, e
def trim(H, F, e, source, taildistancelist): ''' Takes the edge list F, which is a super path from the source vertex to e and repeatedly removes an edge and checks reachability for each edge in F Returns the trimmed edge list and prints that the sink is not reachable from the source if the original edge set is not a superpath ''' sortededges = [] if e[0] != 'e': reachingset = [e] else: reachingset = H.get_hyperedge_tail(e) for f in F: sortededges.append((taildistancelist[f], f)) sortededges.sort() justedges = [s[1] for s in sortededges] H2 = DirectedHypergraph() H2.add_node(source) H2edgeids = {} for f in justedges: newf = H2.add_hyperedge(H.get_hyperedge_tail(f), H.get_hyperedge_head(f)) H2edgeids[f] = newf nodeset, _, __, ___ = b_visit(H2, source) isereached = True for v in reachingset: if v not in nodeset: isereached = False if isereached == False: print('invalid edge set given to trim. Sink not reachable from source') print(nodeset) F2 = [] tailedges = [] for f in justedges: H2.remove_hyperedge(H2edgeids[f]) nodeset, _, __, ___ = b_visit(H2, source) isereached = True for v in reachingset: if v not in nodeset: isereached = False if isereached == False: #This means we cannot remove f newf = H2.add_hyperedge(H.get_hyperedge_tail(f), H.get_hyperedge_head(f)) H2edgeids[f] = newf F2.append(f) if e[0] == 'e': for v in H.get_hyperedge_tail(e): if v in H.get_hyperedge_head(f): tailedges.append(f) if e[0] == 'e': F2.append(e) return F2, tailedges
def test_returns_hyperpath_for_simple_tree(self): s1, s2, s3, s4 = 1, 2, 3, 4 H = DirectedHypergraph() H.add_nodes([s1, s2, s3, s4]) e1 = H.add_hyperedge([s1], [s2]) e2 = H.add_hyperedge([s1], [s3]) e3 = H.add_hyperedge([s3], [s4]) T = {s4: e3, s3: e2, s2: e1, s1: None} path = directed_paths.get_hyperpath_from_predecessors(H, T, s1, s4) # validate nodes self.assertEqual(path.get_node_set(), {s1, s3, s4}) # validate hyperedges self.assertEqual(len(path.get_hyperedge_id_set()), 2) self.assertTrue(path.get_hyperedge_id([1], [3])) self.assertTrue(path.get_hyperedge_id([3], [4]))
def test_get_hyperedge_attributes(): node_a = 'A' node_b = 'B' node_c = 'C' node_d = 'D' tail = set([node_a, node_b]) head = set([node_c, node_d]) frozen_tail = frozenset(tail) frozen_head = frozenset(head) attrib = {'weight': 6, 'color': 'black'} H = DirectedHypergraph() H.add_node(node_a, label=1337) hyperedge_name = H.add_hyperedge(tail, head, attrib, weight=5) assert H.get_hyperedge_attributes(hyperedge_name) == \ {'tail': tail, 'head': head, 'weight': 5, 'color': 'black'} # Test getting non-existent hyperedge's attributes try: H.get_hyperedge_attributes("e10") assert False except ValueError: pass except BaseException as e: assert False, e
def test_returns_hyperpath_when_node_is_in_tail_of_two_edges(self): s1, s2, s3 = 1, 2, 3 s4 = 4 H = DirectedHypergraph() e1 = H.add_hyperedge([s1], [s2]) e2 = H.add_hyperedge([s2], [s3]) e3 = H.add_hyperedge([s2, s3], [s4]) T = {s4: e3, s3: e2, s2: e1, s1: None} path = directed_paths.get_hyperpath_from_predecessors(H, T, s1, s4) # validate nodes self.assertEqual(path.get_node_set(), {s1, s2, s3, s4}) # validate hyperedges self.assertEqual(len(path.get_hyperedge_id_set()), 3) self.assertTrue(path.get_hyperedge_id([2, 3], [4])) self.assertTrue(path.get_hyperedge_id([2], [3])) self.assertTrue(path.get_hyperedge_id([1], [2]))
def test_raises_exception_if_all_nodes_have_predecessors(self): s1, s2, s3 = 1, 2, 3 H = DirectedHypergraph() H.add_nodes([s1, s2, s3]) e1 = H.add_hyperedge([s1], [s2]) T = {s1: e1, s2: e1, s3: e1} self.assertRaises(ValueError, directed_paths.get_hyperpath_from_predecessors, H, T, s1, s2)
def test_raises_exception_if_values_of_function_are_not__in_hypergraph( self): s1, s2, s3 = 1, 2, 3 H = DirectedHypergraph() H.add_nodes([s1, s2]) e1 = H.add_hyperedge([s1], [s2]) T = {s1: None, s2: 'e2'} self.assertRaises(KeyError, directed_paths.get_hyperpath_from_predecessors, H, T, s1, s2)
def makeHypergraph(model): S = create_stoichiometric_matrix(model, array_type='DataFrame') H = DirectedHypergraph() for i in range(len(S.columns)): nodes_df = S.iloc[:, i][S.iloc[:, i] != 0] edge_name = nodes_df.name head_nodes = set(nodes_df[nodes_df > 0].index) tail_nodes = set(nodes_df[nodes_df < 0].index) H.add_hyperedge(head_nodes, tail_nodes, {'reaction': edge_name}) if model.reactions.get_by_id(edge_name).reversibility: H.add_hyperedge(tail_nodes, head_nodes, {'reaction': edge_name + '_rev'}) return H
def test_returns_disconnected_nodes_on_graph_with_two_nodes(self): H = DirectedHypergraph() s, t = 's', 't' H.add_node(s) H.add_node(t) e1 = H.add_hyperedge({s}, {t}) predecessor = {s: None, t: e1} ordering = [s, t] branch = ksh._branching_step(H, predecessor, ordering)[0] self.assertEqual(branch.get_hyperedge_id_set(), set([])) self.assertEqual(branch.get_node_set(), {'s', 't'})
def test_returns_hyperpath_for_tree_with_multiple_nodes_in_tail(self): s1, s2, s3 = 1, 2, 3 s4, s5, s6 = 4, 5, 6 H = DirectedHypergraph() H.add_nodes([s1, s2, s3, s4, s5, s6]) e1 = H.add_hyperedge([s1], [s2]) e2 = H.add_hyperedge([s1], [s3]) e3 = H.add_hyperedge([s1], [s4]) e4 = H.add_hyperedge([s2, s3], [s5]) e5 = H.add_hyperedge([s5], [s6]) T = {s6: e5, s5: e4, s4: e3, s3: e2, s2: e1, s1: None} path = directed_paths.get_hyperpath_from_predecessors(H, T, s1, s6) # validate nodes self.assertEqual(path.get_node_set(), {s1, s2, s3, s5, s6}) # validate hyperedges self.assertEqual(len(path.get_hyperedge_id_set()), 4) self.assertTrue(path.get_hyperedge_id([5], [6])) self.assertTrue(path.get_hyperedge_id([2, 3], [5])) self.assertTrue(path.get_hyperedge_id([1], [3])) self.assertTrue(path.get_hyperedge_id([1], [2]))
def random_generate_hg(level_size, block_size_min, block_size_max): ''' Generate a random hypergraph :param level_size: number of rewriting levels (at each level an 'and' or 'xor' block is randomly chosen for rewriting) :param block_size: min size of a block (number of branches) :param block_size: max size of a block (number of branches) ''' hg = DirectedHypergraph() # create source and sink nodes source_attrs = generate_random_node_attributes() source_attrs['source'] = True sink_attrs = generate_random_node_attributes() sink_attrs['sink'] = True source = 'source' hg.add_node(source, source_attrs) sink = 'sink' hg.add_node(sink, sink_attrs) edge_attrs = {'phero' : 0.5} source_list = [] sink_list = [] source_list.append(source) sink_list.append(sink) hg.add_hyperedge(source_list, sink_list, edge_attrs) node_start_list = source_list node_end_list = sink_list # add 1 node node_middle = generate_random_node_list(hg, 1) hg.add_hyperedge(source_list, node_middle, edge_attrs) hg.add_hyperedge(node_middle, sink_list, edge_attrs) hg.remove_hyperedge(hg.get_hyperedge_id(source_list, sink_list)) #print_hg_std_out_only(hg) # add one xor block #hg = rewrite_xor_block(hg, node_middle[0], 2)[0] #hg = rewrite_and_block(hg, node_middle[0], 4)[0] #print_hg_std_out_only(hg) # generation of test hypergraph current_node = node_middle[0] SIZE = level_size for i in range(0, SIZE, 1): size = randint(block_size_min, block_size_max) # pick size randomly if uniform(0,1) > 0.5: # make an and block out = rewrite_and_block(hg, current_node, size) hg, current_node = out[0], out[1] else: # make a xor block out = rewrite_xor_block(hg, current_node, size) hg, current_node = out[0], out[1] #print_hg_std_out_only(hg) return hg
def convert_pnet_to_hypergraph_andgatewayonly(pnet): hg = DirectedHypergraph() #scan all transitions and create hyperedges transitions = get_transitions(pnet) for transition in transitions: #get all incoming arcs, the source of these become the tail of hyperedge inc_arcs = get_incoming_arcs(transition,pnet) tail = [] for inc_arc in inc_arcs: source = str(get_arc_source(inc_arc)) tail.append(source) #get all outgoing arcs, the target of these become the head of the hyperedge out_arcs = get_outgoing_arcs(transition,pnet) head = [] for out_arc in out_arcs: target = str(get_arc_target(out_arc)) head.append(target) name = get_transition_name(transition) hg.add_hyperedge(tail, head, name = name, phero = 0.5, cost = 0.4, avail = 0.6, qual = 0.2, time = 0.99) #print the result before exit print_hg_std_out_only(hg) return hg
def read_hg_from_file(file_name): ''' returns a hypergraph based on the info in a file. Assumption: file_name has been written using write_hg_on_file :param file_name: ''' hg = DirectedHypergraph() in_file = open(file_name, 'r') lines = in_file.readlines() sep = '\t' for line in lines: #line.strip() values = line.split(sep) #for value in values: if values[0] == 'node': # I am processing a node node_id = values[1] node_attrs = ast.literal_eval(values[2]) hg.add_node(node_id, node_attrs) if values[0] == 'edge': tail = None head = None edge = values[1] edge_attrs = ast.literal_eval(values[2]) # I am processsing an edge for j in range(2, len(values), 1): values[j] = values[j].strip() tail = edge_attrs['tail'] #print(tail) #tail_list = eval(tail) head = edge_attrs['head'] # turn string representation of head into an actual list #head_list = ast.literal_eval(head) #print("ID, TAIL, HEAD: {2} - {0} - {1} - {3}".format(tail,head,values[1], edge_attrs)) hg.add_hyperedge(tail, head, edge_attrs) print_hg_std_out_only(hg) in_file.close() return hg
def test_returns_only_one_hyperpath_for_k_equals_one(self): H = DirectedHypergraph() H.add_node('s') H.add_node('1') H.add_node('2') H.add_node('3') H.add_node('t') H.add_hyperedge({'s'}, {'1'}, weight=1) H.add_hyperedge({'s'}, {'2'}, weight=1) H.add_hyperedge({'s'}, {'3'}, weight=1) H.add_hyperedge({'1'}, {'t'}, weight=1) H.add_hyperedge({'2', '3'}, {'t'}, weight=1) output = ksh.k_shortest_hyperpaths(H, 's', 't', 1) self.assertEqual(len(output), 1)
def test_is_BF_hypergraph(): H = DirectedHypergraph() H.read("tests/data/basic_directed_hypergraph.txt") assert not H.is_BF_hypergraph() H = DirectedHypergraph() H.add_hyperedge(['a', 'b'], ['c']) assert H.is_BF_hypergraph() H = DirectedHypergraph() H.add_hyperedge(['x'], ['y', 'z']) assert H.is_BF_hypergraph() H = DirectedHypergraph() H.add_hyperedge(['a', 'b'], ['c']) H.add_hyperedge(['x'], ['y', 'z']) assert H.is_BF_hypergraph()
def test_returns_shortest_hyperpath_for_k_equals_one(self): H = DirectedHypergraph() H.add_node('s') H.add_node('1') H.add_node('2') H.add_node('3') H.add_node('t') H.add_hyperedge({'s'}, {'1'}, weight=1) H.add_hyperedge({'s'}, {'2'}, weight=1) H.add_hyperedge({'s'}, {'3'}, weight=1) H.add_hyperedge({'1'}, {'t'}, weight=1) H.add_hyperedge({'2', '3'}, {'t'}, weight=1) output = ksh.k_shortest_hyperpaths(H, 's', 't', 1) hyperpath = output[0] self.assertEqual(hyperpath.get_node_set(), {'s', '1', 't'}) self.assertEqual(len(hyperpath.get_hyperedge_id_set()), 2) self.assertTrue(hyperpath.get_hyperedge_id({'s'}, {'1'})) self.assertTrue(hyperpath.get_hyperedge_id({'1'}, {'t'}))
class TestComputeLowerBound(unittest.TestCase): def setUp(self): self.nielsenGraph = DirectedHypergraph() self.nielsenGraph.add_node('s') self.nielsenGraph.add_node('1') self.nielsenGraph.add_node('2') self.nielsenGraph.add_node('3') self.nielsenGraph.add_node('4') self.nielsenGraph.add_node('t') self.nielsenGraph.add_hyperedge({'s'}, {'1'}, weight=1) self.nielsenGraph.add_hyperedge({'s'}, {'2'}, weight=1) self.nielsenGraph.add_hyperedge({'s'}, {'3'}, weight=1) self.nielsenGraph.add_hyperedge({'1'}, {'2'}, weight=1) self.nielsenGraph.add_hyperedge({'2'}, {'3'}, weight=1) self.nielsenGraph.add_hyperedge({'1', '2'}, {'t'}, weight=1) self.nielsenGraph.add_hyperedge({'4'}, {'t'}, weight=1) self.nielsenGraph.add_hyperedge({'2', '3'}, {'4'}, weight=1) self.nielsenGraph.add_hyperedge({'4'}, {'1'}, weight=1) def test_returns_12_for_lower_bound_for_nielsen_H_21(self): ''' Test graph is discussed in Section 3.2 example 2 of Nielsen et al.\ ''' H_2 = self.nielsenGraph.copy() e1 = H_2.get_hyperedge_id({'s'}, {'1'}) e2 = H_2.get_hyperedge_id({'1'}, {'2'}) e3 = H_2.get_hyperedge_id({'1', '2'}, {'t'}) H_2.remove_hyperedge(H_2.get_hyperedge_id({'s'}, {'2'})) H_2.remove_hyperedge(H_2.get_hyperedge_id({'4'}, {'t'})) # weight vector W = {'s': 0, '1': 1, '2': 2, '3': 1, '4': 4, 't': 4} # predecessor function pred = {'s': None, '1': e1, '2': e2, 't': e3} # ordering ordering = ['s', '1', '2', 't'] # branch of H_2 for the test H_2_1 = H_2.copy() H_2_1.remove_hyperedge(H_2_1.get_hyperedge_id({'s'}, {'1'})) self.assertEqual( ksh._compute_lower_bound(H_2_1, 0, pred, ordering, W, 't'), 12)
class TestBranchingStep(unittest.TestCase): def setUp(self): self.nielsenGraph = DirectedHypergraph() self.nielsenGraph.add_node('s') self.nielsenGraph.add_node('1') self.nielsenGraph.add_node('2') self.nielsenGraph.add_node('3') self.nielsenGraph.add_node('4') self.nielsenGraph.add_node('t') self.nielsenGraph.add_hyperedge({'s'}, {'1'}, weight=1) self.nielsenGraph.add_hyperedge({'s'}, {'2'}, weight=1) self.nielsenGraph.add_hyperedge({'s'}, {'3'}, weight=1) self.nielsenGraph.add_hyperedge({'1'}, {'2'}, weight=1) self.nielsenGraph.add_hyperedge({'2'}, {'3'}, weight=1) self.nielsenGraph.add_hyperedge({'1', '2'}, {'t'}, weight=1) self.nielsenGraph.add_hyperedge({'4'}, {'t'}, weight=1) self.nielsenGraph.add_hyperedge({'2', '3'}, {'4'}, weight=1) self.nielsenGraph.add_hyperedge({'4'}, {'1'}, weight=1) def test_returns_disconnected_nodes_on_graph_with_two_nodes(self): H = DirectedHypergraph() s, t = 's', 't' H.add_node(s) H.add_node(t) e1 = H.add_hyperedge({s}, {t}) predecessor = {s: None, t: e1} ordering = [s, t] branch = ksh._branching_step(H, predecessor, ordering)[0] self.assertEqual(branch.get_hyperedge_id_set(), set([])) self.assertEqual(branch.get_node_set(), {'s', 't'}) def test_returns_correct_branching_for_nielsen_graph(self): H = self.nielsenGraph ordering = ['s', '1', '2', 't'] pred = { 's': None, '1': H.get_hyperedge_id({'s'}, {'1'}), '2': H.get_hyperedge_id({'s'}, {'2'}), 't': H.get_hyperedge_id({'2', '1'}, {'t'}) } branches = ksh._branching_step(H, pred, ordering) self.assertEqual(len(branches), 3) # branch 1 b1 = branches[0] self.assertEqual(b1.get_node_set(), H.get_node_set()) hyperedges = b1.get_hyperedge_id_set() hyperedgesTuple = [(b1.get_hyperedge_tail(e), b1.get_hyperedge_head(e)) for e in hyperedges] self.assertEqual(len(hyperedges), 6) self.assertIn(({'s'}, {'2'}), hyperedgesTuple) self.assertIn(({'s'}, {'3'}), hyperedgesTuple) self.assertIn(({'2'}, {'3'}), hyperedgesTuple) self.assertIn(({'2', '3'}, {'4'}), hyperedgesTuple) self.assertIn(({'1', '2'}, {'t'}), hyperedgesTuple) self.assertIn(({'4'}, {'1'}), hyperedgesTuple) # branch 2 b2 = branches[1] self.assertEqual(b2.get_node_set(), H.get_node_set()) hyperedges = b2.get_hyperedge_id_set() hyperedgesTuple = [(b2.get_hyperedge_tail(e), b2.get_hyperedge_head(e)) for e in hyperedges] self.assertEqual(len(hyperedges), 7) self.assertIn(({'s'}, {'1'}), hyperedgesTuple) self.assertIn(({'s'}, {'3'}), hyperedgesTuple) self.assertIn(({'1'}, {'2'}), hyperedgesTuple) self.assertIn(({'2'}, {'3'}), hyperedgesTuple) self.assertIn(({'2', '3'}, {'4'}), hyperedgesTuple) self.assertIn(({'1', '2'}, {'t'}), hyperedgesTuple) self.assertIn(({'4'}, {'1'}), hyperedgesTuple) # branch 3 b3 = branches[2] self.assertEqual(b3.get_node_set(), H.get_node_set()) hyperedges = b3.get_hyperedge_id_set() hyperedgesTuple = [(b3.get_hyperedge_tail(e), b3.get_hyperedge_head(e)) for e in hyperedges] self.assertEqual(len(hyperedges), 8) self.assertIn(({'s'}, {'1'}), hyperedgesTuple) self.assertIn(({'s'}, {'2'}), hyperedgesTuple) self.assertIn(({'s'}, {'3'}), hyperedgesTuple) self.assertIn(({'1'}, {'2'}), hyperedgesTuple) self.assertIn(({'2'}, {'3'}), hyperedgesTuple) self.assertIn(({'2', '3'}, {'4'}), hyperedgesTuple) self.assertIn(({'4'}, {'t'}), hyperedgesTuple) self.assertIn(({'4'}, {'1'}), hyperedgesTuple)
class TestBranchingStep(unittest.TestCase): def setUp(self): self.nielsenGraph = DirectedHypergraph() self.nielsenGraph.add_node('s') self.nielsenGraph.add_node('1') self.nielsenGraph.add_node('2') self.nielsenGraph.add_node('3') self.nielsenGraph.add_node('4') self.nielsenGraph.add_node('t') self.nielsenGraph.add_hyperedge({'s'}, {'1'}, weight=1) self.nielsenGraph.add_hyperedge({'s'}, {'2'}, weight=1) self.nielsenGraph.add_hyperedge({'s'}, {'3'}, weight=1) self.nielsenGraph.add_hyperedge({'1'}, {'2'}, weight=1) self.nielsenGraph.add_hyperedge({'2'}, {'3'}, weight=1) self.nielsenGraph.add_hyperedge({'1', '2'}, {'t'}, weight=1) self.nielsenGraph.add_hyperedge({'4'}, {'t'}, weight=1) self.nielsenGraph.add_hyperedge({'2', '3'}, {'4'}, weight=1) self.nielsenGraph.add_hyperedge({'4'}, {'1'}, weight=1) def test_returns_disconnected_nodes_on_graph_with_two_nodes(self): H = DirectedHypergraph() s, t = 's', 't' H.add_node(s) H.add_node(t) e1 = H.add_hyperedge({s}, {t}) predecessor = {s: None, t: e1} ordering = [s, t] branch = ksh._branching_step(H, predecessor, ordering)[0] self.assertEqual(branch.get_hyperedge_id_set(), set([])) self.assertEqual(branch.get_node_set(), {'s', 't'}) def test_returns_correct_branching_for_nielsen_graph(self): H = self.nielsenGraph ordering = ['s', '1', '2', 't'] pred = {'s': None, '1': H.get_hyperedge_id({'s'}, {'1'}), '2': H.get_hyperedge_id({'s'}, {'2'}), 't': H.get_hyperedge_id({'2', '1'}, {'t'})} branches = ksh._branching_step(H, pred, ordering) self.assertEqual(len(branches), 3) # branch 1 b1 = branches[0] self.assertEqual(b1.get_node_set(), H.get_node_set()) hyperedges = b1.get_hyperedge_id_set() hyperedgesTuple = [(b1.get_hyperedge_tail(e), b1.get_hyperedge_head(e)) for e in hyperedges] self.assertEqual(len(hyperedges), 6) self.assertIn(({'s'}, {'2'}), hyperedgesTuple) self.assertIn(({'s'}, {'3'}), hyperedgesTuple) self.assertIn(({'2'}, {'3'}), hyperedgesTuple) self.assertIn(({'2', '3'}, {'4'}), hyperedgesTuple) self.assertIn(({'1', '2'}, {'t'}), hyperedgesTuple) self.assertIn(({'4'}, {'1'}), hyperedgesTuple) # branch 2 b2 = branches[1] self.assertEqual(b2.get_node_set(), H.get_node_set()) hyperedges = b2.get_hyperedge_id_set() hyperedgesTuple = [(b2.get_hyperedge_tail(e), b2.get_hyperedge_head(e)) for e in hyperedges] self.assertEqual(len(hyperedges), 7) self.assertIn(({'s'}, {'1'}), hyperedgesTuple) self.assertIn(({'s'}, {'3'}), hyperedgesTuple) self.assertIn(({'1'}, {'2'}), hyperedgesTuple) self.assertIn(({'2'}, {'3'}), hyperedgesTuple) self.assertIn(({'2', '3'}, {'4'}), hyperedgesTuple) self.assertIn(({'1', '2'}, {'t'}), hyperedgesTuple) self.assertIn(({'4'}, {'1'}), hyperedgesTuple) # branch 3 b3 = branches[2] self.assertEqual(b3.get_node_set(), H.get_node_set()) hyperedges = b3.get_hyperedge_id_set() hyperedgesTuple = [(b3.get_hyperedge_tail(e), b3.get_hyperedge_head(e)) for e in hyperedges] self.assertEqual(len(hyperedges), 8) self.assertIn(({'s'}, {'1'}), hyperedgesTuple) self.assertIn(({'s'}, {'2'}), hyperedgesTuple) self.assertIn(({'s'}, {'3'}), hyperedgesTuple) self.assertIn(({'1'}, {'2'}), hyperedgesTuple) self.assertIn(({'2'}, {'3'}), hyperedgesTuple) self.assertIn(({'2', '3'}, {'4'}), hyperedgesTuple) self.assertIn(({'4'}, {'t'}), hyperedgesTuple) self.assertIn(({'4'}, {'1'}), hyperedgesTuple)
def get_hyperpath_from_predecessors(H, Pv, source_node, destination_node, node_weights=None, attr_name="weight"): """Gives the hyperpath (DirectedHypergraph) representing the shortest B-hyperpath from the source to the destination, given a predecessor function and source and destination nodes. :note: The IDs of the hyperedges in the subhypergraph returned may be different than those in the original hypergraph (even though the tail and head sets are identical). :param H: the hypergraph which the path algorithm was executed on. :param Pv: dictionary mapping each node to the ID of the hyperedge that preceeded it in the path. :param source_node: the source node of the path. :param destination_node: the destination node of the path. :returns: DirectedHypergraph -- shortest B-hyperpath from source_node to destination_node. :raises: TypeError -- Algorithm only applicable to directed hypergraphs :raises: KeyError -- Node key in predecessor is not in H :raises: KeyError -- Hyperedge key in predecessor is not in H :raises: ValueError -- Multiple nodes without predecessor :raises: ValueError -- Hypertree does not have source node """ if not isinstance(H, DirectedHypergraph): raise TypeError("Algorithm only applicable to directed hypergraphs") # Check that Pv is a valid predecessor function: # - keys must be nodes in H mapping to hyperedges in H # - exactly one node must map to None (i.e., only one node # without predecessor) nodes_without_predecessor = 0 for node, hyperedge_id in Pv.items(): if not H.has_node(node): raise KeyError("Node key %s in predecessor is not in H" % node) if hyperedge_id is None: nodes_without_predecessor += 1 elif not H.has_hyperedge_id(hyperedge_id): raise KeyError("Hyperedge key %s in predecessor is not in H" % hyperedge_id) if nodes_without_predecessor > 1: raise ValueError("Multiple nodes without predecessor. %s received" % Pv) elif nodes_without_predecessor == 0: raise ValueError("Hypertree does not have source node. %s received" % Pv) path = DirectedHypergraph() # keep track of which nodes are or have been processed processedOrInQueue = {n: False for n in Pv} nodesToProcess = [destination_node] processedOrInQueue[destination_node] = True while nodesToProcess: node = nodesToProcess.pop(0) hyperedge_id = Pv[node] if hyperedge_id: for n in H.get_hyperedge_tail(hyperedge_id): if not processedOrInQueue[n]: nodesToProcess.append(n) processedOrInQueue[n] = True path.add_hyperedge(H.get_hyperedge_tail(hyperedge_id), H.get_hyperedge_head(hyperedge_id), weight=H.get_hyperedge_weight(hyperedge_id)) elif not path.has_node(node): path.add_node(node) return path
def convert_pnet_to_hypergraph(pnet): """ pre-process pnet to number tau-split and tau-join transitions""" tau_pre_processing_pnet(pnet) """ Convert a Petri net (in pnml format) into a hypergraph (Halp format) """ hg = DirectedHypergraph() transitions = get_transitions(pnet) places = get_places(pnet) """STEP 1: Pre-process places to find xor places (splits and joints) If input/output of a transitions is 2 or more places, then mark those places as "X" and put in hypergraph""" for place in places: inc_arcs = get_incoming_arcs(place,pnet) out_arcs = get_outgoing_arcs(place,pnet) isSink = False isSource = False if len(inc_arcs) > 1: #create node for place in hypergraph node_id = get_id(place) #check if join is end event (sink) if (len(out_arcs) == 0): isSink = True logger.debug("STEP 1 - Creating xor-join node -- {0}".format(node_id)) hg.add_node(node_id, source = isSource, sink = isSink, type = 'xor-join', name = " ") head = [] head.append(node_id) isSink = False isSource = False #create node for all source of incoming arcs for arc in inc_arcs: node_id2 = get_id(get_element(get_arc_source(arc), pnet)) node_name = get_transition_name(get_element(get_arc_source(arc), pnet)) logger.debug("STEP 1 - Creating transition node -- {0} -- {1}".format(node_id, node_name)) hg.add_node(node_name, source = isSource, sink = isSink, type = 'transition', name = node_name) tail = [] tail.append(node_name) #create hyperedge logger.debug("STEP 1 - Creating hyperedge from {0} to {1}".format(str(tail), str(head))) hg.add_hyperedge(tail, head, name = " ", phero = 0.5) if len(out_arcs) > 1: node_id = get_id(place) #create node for place in hypergraph (if it does not exist already) tail = [] tail.append(node_id) if(not hg.has_node(node_id)): #check if source (start event) if (len(inc_arcs) == 0): isSource = True logger.debug("STEP 1 - Creating xor-split node -- {0}".format(node_id)) hg.add_node(node_id, source = isSource, sink = isSink, type = 'xor-split', name = " ") #create node for all targets of outgoing arcs isSink = False isSource = False for arc in out_arcs: node_id2 = get_id(get_element(get_arc_target(arc), pnet)) node_name = get_transition_name(get_element(get_arc_target(arc),pnet)) if(not hg.has_node(node_id2)): logger.debug("STEP 1 - Creating transition node -- {0} -- {1}".format(node_id, node_name)) hg.add_node(node_name, source = isSource, sink = isSink, type = 'transition', name = node_name) head = [] head.append(node_name) #create hyperedge logger.debug("STEP 1 - Creating hyperedge from {0} to {1}".format(str(tail), str(head))) hg.add_hyperedge(tail, head, name = " ", phero = 0.5) """ STEP2 : Process each transition """ for transition in transitions: logger.debug("######## Processing transition {0}".format(get_transition_name(transition))) isSink = False isSource = False #check if transition is not a node in hg and add if needed #if (not hg.has_node(get_transition_name(transition))): #check if transition is start inc_arcs = get_incoming_arcs(transition,pnet) for inc_arc in inc_arcs: source_place = get_element(get_arc_source(inc_arc),pnet) place_inc = get_incoming_arcs(source_place,pnet) if not place_inc: isSource = True logger.debug("Transition is START: {0}".format(get_transition_name(transition))) #check if trsnasition is end event out_arcs = get_outgoing_arcs(transition,pnet) for out_arc in out_arcs: sink_place = get_element(get_arc_target(out_arc),pnet) place_out = get_outgoing_arcs(sink_place,pnet) if not place_out: isSink = True logger.debug("Transition is END: {0}".format(get_transition_name(transition))) #create node in hypergraph logger.debug("STEP 2 - Creating transition node") hg.add_node(get_transition_name(transition), source = isSource, sink = isSink, type = 'transition', name = get_transition_name(transition)) #look BACKWARD if not isSource: inc_arcs = get_incoming_arcs(transition,pnet) tail = [] x_head = [get_transition_name(transition)] xplace_list = [] otherp_list = [] xplace_tail = [] for inc_arc in inc_arcs: place = get_element(get_arc_source(inc_arc),pnet) #separate xor places from other forward places of this transition if(hg.has_node(get_id(place))): xplace_list.append(place) xplace_tail.append(get_id(place)) else: otherp_list.append(place) #create forward hyperedge to possibly multiple xor nodes he_from_xors_needed = False for place in xplace_tail: temp_tail = [] temp_tail.append(place) if(not hg.has_hyperedge(temp_tail,x_head)): he_from_xors_needed = True if(he_from_xors_needed): logger.debug("STEP 2 - Creating backward hyperedge to (multiple) xor - TAIL {0} -- HEAD {1} ".format(str(xplace_tail),str(x_head))) hg.add_hyperedge(xplace_tail, x_head, name = " ", phero = 0.5) #create forward normal hyperdge tail = [] # for place in otherp_list: # inc_arcs_l2 = get_incoming_arcs(place) # for inc_arc_l2 in inc_arcs_l2: # trans2 = get_element(get_arc_source(inc_arc_l2)) # tail.append(get_transition_name(trans2)) # if(tail): # logger.info("STEP 2 - Creating real backward hyperedge - TAIL {0} -- HEAD {1} ".format(str(tail),str(x_head))) # hg.add_hyperedge(tail, x_head, name = " ", phero = 0.0, cost = 0.4, avail = 0.6, qual = 0.2, time = 0.99) #look FORWARD if not isSink: out_arcs = get_outgoing_arcs(transition,pnet) head = [] x_tail = [get_transition_name(transition)] xplace_list = [] otherp_list = [] xplace_head = [] for out_arc in out_arcs: place = get_element(get_arc_target(out_arc),pnet) #separate xor places from other forward places of this transition if(hg.has_node(get_id(place))): xplace_list.append(place) xplace_head.append(get_id(place)) else: otherp_list.append(place) #create forward hyperedge to possibly multiple xor nodes he_to_xors_needed = False for place in xplace_head: temp_head = [] temp_head.append(place) if(not hg.has_hyperedge(x_tail,temp_head)): he_to_xors_needed = True if(he_to_xors_needed): logger.debug("STEP 2 - Creating forward hyperedge to (multiple) xor - TAIL {0} -- HEAD {1} ".format(str(x_tail),str(xplace_head))) hg.add_hyperedge(x_tail, xplace_head, name = " ", phero = 0.5) #create forward normal hyperdge head = [] for place in otherp_list: out_arcs_l2 = get_outgoing_arcs(place,pnet) for out_arc_l2 in out_arcs_l2: trans2 = get_element(get_arc_target(out_arc_l2),pnet) head.append(get_transition_name(trans2)) if(head): logger.debug("STEP 2 - Creating real forward hyperedge - TAIL {0} -- HEAD {1} ".format(str(x_tail),str(head))) hg.add_hyperedge(x_tail, head, name = " ", phero = 0.5, cost = 0.4, avail = 0.6, qual = 0.2, time = 0.99) """ POST PROCESSING of tau-split/join generated by inductive miner """ hg = tau_post_processing(hg) """ reduction of tau split/join """ #hg = tau_reduction(hg) return hg
def convert_pnet_to_hypergraph(pnet): """ pre-process pnet to number tau-split and tau-join transitions""" tau_pre_processing_pnet(pnet) """ Convert a Petri net (in pnml format) into a hypergraph (Halp format) """ hg = DirectedHypergraph() transitions = get_transitions(pnet) places = get_places(pnet) """STEP 1: Pre-process places to find xor places (splits and joints) If input/output of a transitions is 2 or more places, then mark those places as "X" and put in hypergraph""" for place in places: inc_arcs = get_incoming_arcs(place, pnet) out_arcs = get_outgoing_arcs(place, pnet) isSink = False isSource = False if len(inc_arcs) > 1: #create node for place in hypergraph node_id = get_id(place) #check if join is end event (sink) if (len(out_arcs) == 0): isSink = True logger.debug( "STEP 1 - Creating xor-join node -- {0}".format(node_id)) hg.add_node(node_id, source=isSource, sink=isSink, type='xor-join', name=" ") head = [] head.append(node_id) isSink = False isSource = False #create node for all source of incoming arcs for arc in inc_arcs: node_id2 = get_id(get_element(get_arc_source(arc), pnet)) node_name = get_transition_name( get_element(get_arc_source(arc), pnet)) logger.debug( "STEP 1 - Creating transition node -- {0} -- {1}".format( node_id, node_name)) hg.add_node(node_name, source=isSource, sink=isSink, type='transition', name=node_name) tail = [] tail.append(node_name) #create hyperedge logger.debug( "STEP 1 - Creating hyperedge from {0} to {1}".format( str(tail), str(head))) hg.add_hyperedge(tail, head, name=" ", phero=0.5) if len(out_arcs) > 1: node_id = get_id(place) #create node for place in hypergraph (if it does not exist already) tail = [] tail.append(node_id) if (not hg.has_node(node_id)): #check if source (start event) if (len(inc_arcs) == 0): isSource = True logger.debug( "STEP 1 - Creating xor-split node -- {0}".format(node_id)) hg.add_node(node_id, source=isSource, sink=isSink, type='xor-split', name=" ") #create node for all targets of outgoing arcs isSink = False isSource = False for arc in out_arcs: node_id2 = get_id(get_element(get_arc_target(arc), pnet)) node_name = get_transition_name( get_element(get_arc_target(arc), pnet)) if (not hg.has_node(node_id2)): logger.debug( "STEP 1 - Creating transition node -- {0} -- {1}". format(node_id, node_name)) hg.add_node(node_name, source=isSource, sink=isSink, type='transition', name=node_name) head = [] head.append(node_name) #create hyperedge logger.debug( "STEP 1 - Creating hyperedge from {0} to {1}".format( str(tail), str(head))) hg.add_hyperedge(tail, head, name=" ", phero=0.5) """ STEP2 : Process each transition """ for transition in transitions: logger.debug("######## Processing transition {0}".format( get_transition_name(transition))) isSink = False isSource = False #check if transition is not a node in hg and add if needed #if (not hg.has_node(get_transition_name(transition))): #check if transition is start inc_arcs = get_incoming_arcs(transition, pnet) for inc_arc in inc_arcs: source_place = get_element(get_arc_source(inc_arc), pnet) place_inc = get_incoming_arcs(source_place, pnet) if not place_inc: isSource = True logger.debug("Transition is START: {0}".format( get_transition_name(transition))) #check if trsnasition is end event out_arcs = get_outgoing_arcs(transition, pnet) for out_arc in out_arcs: sink_place = get_element(get_arc_target(out_arc), pnet) place_out = get_outgoing_arcs(sink_place, pnet) if not place_out: isSink = True logger.debug("Transition is END: {0}".format( get_transition_name(transition))) #create node in hypergraph logger.debug("STEP 2 - Creating transition node") hg.add_node(get_transition_name(transition), source=isSource, sink=isSink, type='transition', name=get_transition_name(transition)) #look BACKWARD if not isSource: inc_arcs = get_incoming_arcs(transition, pnet) tail = [] x_head = [get_transition_name(transition)] xplace_list = [] otherp_list = [] xplace_tail = [] for inc_arc in inc_arcs: place = get_element(get_arc_source(inc_arc), pnet) #separate xor places from other forward places of this transition if (hg.has_node(get_id(place))): xplace_list.append(place) xplace_tail.append(get_id(place)) else: otherp_list.append(place) #create forward hyperedge to possibly multiple xor nodes he_from_xors_needed = False for place in xplace_tail: temp_tail = [] temp_tail.append(place) if (not hg.has_hyperedge(temp_tail, x_head)): he_from_xors_needed = True if (he_from_xors_needed): logger.debug( "STEP 2 - Creating backward hyperedge to (multiple) xor - TAIL {0} -- HEAD {1} " .format(str(xplace_tail), str(x_head))) hg.add_hyperedge(xplace_tail, x_head, name=" ", phero=0.5) #create forward normal hyperdge tail = [] # for place in otherp_list: # inc_arcs_l2 = get_incoming_arcs(place) # for inc_arc_l2 in inc_arcs_l2: # trans2 = get_element(get_arc_source(inc_arc_l2)) # tail.append(get_transition_name(trans2)) # if(tail): # logger.info("STEP 2 - Creating real backward hyperedge - TAIL {0} -- HEAD {1} ".format(str(tail),str(x_head))) # hg.add_hyperedge(tail, x_head, name = " ", phero = 0.0, cost = 0.4, avail = 0.6, qual = 0.2, time = 0.99) #look FORWARD if not isSink: out_arcs = get_outgoing_arcs(transition, pnet) head = [] x_tail = [get_transition_name(transition)] xplace_list = [] otherp_list = [] xplace_head = [] for out_arc in out_arcs: place = get_element(get_arc_target(out_arc), pnet) #separate xor places from other forward places of this transition if (hg.has_node(get_id(place))): xplace_list.append(place) xplace_head.append(get_id(place)) else: otherp_list.append(place) #create forward hyperedge to possibly multiple xor nodes he_to_xors_needed = False for place in xplace_head: temp_head = [] temp_head.append(place) if (not hg.has_hyperedge(x_tail, temp_head)): he_to_xors_needed = True if (he_to_xors_needed): logger.debug( "STEP 2 - Creating forward hyperedge to (multiple) xor - TAIL {0} -- HEAD {1} " .format(str(x_tail), str(xplace_head))) hg.add_hyperedge(x_tail, xplace_head, name=" ", phero=0.5) #create forward normal hyperdge head = [] for place in otherp_list: out_arcs_l2 = get_outgoing_arcs(place, pnet) for out_arc_l2 in out_arcs_l2: trans2 = get_element(get_arc_target(out_arc_l2), pnet) head.append(get_transition_name(trans2)) if (head): logger.debug( "STEP 2 - Creating real forward hyperedge - TAIL {0} -- HEAD {1} " .format(str(x_tail), str(head))) hg.add_hyperedge(x_tail, head, name=" ", phero=0.5, cost=0.4, avail=0.6, qual=0.2, time=0.99) """ POST PROCESSING of tau-split/join generated by inductive miner """ hg = tau_post_processing(hg) """ reduction of tau split/join """ #hg = tau_reduction(hg) return hg
class TestComputeLowerBound(unittest.TestCase): def setUp(self): self.nielsenGraph = DirectedHypergraph() self.nielsenGraph.add_node('s') self.nielsenGraph.add_node('1') self.nielsenGraph.add_node('2') self.nielsenGraph.add_node('3') self.nielsenGraph.add_node('4') self.nielsenGraph.add_node('t') self.nielsenGraph.add_hyperedge({'s'}, {'1'}, weight=1) self.nielsenGraph.add_hyperedge({'s'}, {'2'}, weight=1) self.nielsenGraph.add_hyperedge({'s'}, {'3'}, weight=1) self.nielsenGraph.add_hyperedge({'1'}, {'2'}, weight=1) self.nielsenGraph.add_hyperedge({'2'}, {'3'}, weight=1) self.nielsenGraph.add_hyperedge({'1', '2'}, {'t'}, weight=1) self.nielsenGraph.add_hyperedge({'4'}, {'t'}, weight=1) self.nielsenGraph.add_hyperedge({'2', '3'}, {'4'}, weight=1) self.nielsenGraph.add_hyperedge({'4'}, {'1'}, weight=1) def test_returns_12_for_lower_bound_for_nielsen_H_21(self): ''' Test graph is discussed in Section 3.2 example 2 of Nielsen et al.\ ''' H_2 = self.nielsenGraph.copy() e1 = H_2.get_hyperedge_id({'s'}, {'1'}) e2 = H_2.get_hyperedge_id({'1'}, {'2'}) e3 = H_2.get_hyperedge_id({'1', '2'}, {'t'}) H_2.remove_hyperedge(H_2.get_hyperedge_id({'s'}, {'2'})) H_2.remove_hyperedge(H_2.get_hyperedge_id({'4'}, {'t'})) # weight vector W = {'s': 0, '1': 1, '2': 2, '3': 1, '4': 4, 't': 4} # predecessor function pred = {'s': None, '1': e1, '2': e2, 't': e3} # ordering ordering = ['s', '1', '2', 't'] # branch of H_2 for the test H_2_1 = H_2.copy() H_2_1.remove_hyperedge( H_2_1.get_hyperedge_id({'s'}, {'1'})) self.assertEqual(ksh._compute_lower_bound( H_2_1, 0, pred, ordering, W, 't'), 12)
class TestKShortestHyperpaths(unittest.TestCase): def setUp(self): self.nielsenGraph = DirectedHypergraph() self.nielsenGraph.add_node('s') self.nielsenGraph.add_node('1') self.nielsenGraph.add_node('2') self.nielsenGraph.add_node('3') self.nielsenGraph.add_node('4') self.nielsenGraph.add_node('t') self.nielsenGraph.add_hyperedge({'s'}, {'1'}, weight=1) self.nielsenGraph.add_hyperedge({'s'}, {'2'}, weight=1) self.nielsenGraph.add_hyperedge({'s'}, {'3'}, weight=1) self.nielsenGraph.add_hyperedge({'1'}, {'2'}, weight=1) self.nielsenGraph.add_hyperedge({'2'}, {'3'}, weight=1) self.nielsenGraph.add_hyperedge({'1', '2'}, {'t'}, weight=1) self.nielsenGraph.add_hyperedge({'4'}, {'t'}, weight=1) self.nielsenGraph.add_hyperedge({'2', '3'}, {'4'}, weight=1) self.nielsenGraph.add_hyperedge({'4'}, {'1'}, weight=1) # valid input tests def test_raises_exception_if_H_not_B_hypegraph(self): H = DirectedHypergraph() H.add_nodes([1, 2, 3]) H.add_hyperedge([1], [2, 3]) source, destination = 1, 2 self.assertRaises(TypeError, ksh.k_shortest_hyperpaths, H, source, destination, 1) def test_raises_exception_if_H_not_hypegraph(self): H = "DirectedHypergraph" source, destination = 1, 2 self.assertRaises(TypeError, ksh.k_shortest_hyperpaths, H, source, destination, 1) def test_raises_exception_if_source_not_in_graph(self): H = DirectedHypergraph() source, destination = 1, 2 H.add_nodes([source, destination]) self.assertRaises(ValueError, ksh.k_shortest_hyperpaths, H, 3, destination, 1) def test_raises_exception_if_destination_not_in_graph(self): H = DirectedHypergraph() source, destination = 1, 2 H.add_nodes([source, destination]) self.assertRaises(ValueError, ksh.k_shortest_hyperpaths, H, source, 3, 1) def test_raises_exception_if_k_not_integer(self): H = DirectedHypergraph() source, destination = 1, 2 H.add_nodes([source, destination]) self.assertRaises(TypeError, ksh.k_shortest_hyperpaths, H, source, destination, 0.1) def test_raises_exception_if_k_not_positive(self): H = DirectedHypergraph() source, destination = 1, 2 H.add_nodes([source, destination]) self.assertRaises(ValueError, ksh.k_shortest_hyperpaths, H, source, destination, -4) self.assertRaises(ValueError, ksh.k_shortest_hyperpaths, H, source, destination, 0) # various cases def test_returns_only_one_hyperpath_for_k_equals_one(self): H = DirectedHypergraph() H.add_node('s') H.add_node('1') H.add_node('2') H.add_node('3') H.add_node('t') H.add_hyperedge({'s'}, {'1'}, weight=1) H.add_hyperedge({'s'}, {'2'}, weight=1) H.add_hyperedge({'s'}, {'3'}, weight=1) H.add_hyperedge({'1'}, {'t'}, weight=1) H.add_hyperedge({'2', '3'}, {'t'}, weight=1) output = ksh.k_shortest_hyperpaths(H, 's', 't', 1) self.assertEqual(len(output), 1) def test_returns_shortest_hyperpath_for_k_equals_one(self): H = DirectedHypergraph() H.add_node('s') H.add_node('1') H.add_node('2') H.add_node('3') H.add_node('t') H.add_hyperedge({'s'}, {'1'}, weight=1) H.add_hyperedge({'s'}, {'2'}, weight=1) H.add_hyperedge({'s'}, {'3'}, weight=1) H.add_hyperedge({'1'}, {'t'}, weight=1) H.add_hyperedge({'2', '3'}, {'t'}, weight=1) output = ksh.k_shortest_hyperpaths(H, 's', 't', 1) hyperpath = output[0] self.assertEqual(hyperpath.get_node_set(), {'s', '1', 't'}) self.assertEqual(len(hyperpath.get_hyperedge_id_set()), 2) self.assertTrue(hyperpath.get_hyperedge_id({'s'}, {'1'})) self.assertTrue(hyperpath.get_hyperedge_id({'1'}, {'t'})) def test_returns_empty_list_if_no_s_t_path(self): H = DirectedHypergraph() H.add_node('s') H.add_node('1') H.add_node('2') H.add_node('t') H.add_hyperedge({'s'}, {'1'}, weight=1) H.add_hyperedge({'1', '2'}, {'t'}, weight=1) output = ksh.k_shortest_hyperpaths(H, 's', 't', 1) self.assertEqual(output, []) def test_returns_3_shortest_hypergraphs_for_nielsen_example_with_k_equal_3( self): threeShortest = ksh.k_shortest_hyperpaths(self.nielsenGraph, 's', 't', 3) self.assertEqual(len(threeShortest), 3) # shortest path hyperpath = threeShortest[0] self.assertEqual(hyperpath.get_node_set(), {'s', '1', '2', 't'}) self.assertEqual(len(hyperpath.get_hyperedge_id_set()), 3) self.assertTrue(hyperpath.get_hyperedge_id({'s'}, {'1'})) self.assertTrue(hyperpath.get_hyperedge_id({'s'}, {'2'})) self.assertTrue(hyperpath.get_hyperedge_id({'1', '2'}, {'t'})) # second shortest path hyperpath = threeShortest[1] self.assertEqual(hyperpath.get_node_set(), {'s', '1', '2', 't'}) self.assertEqual(len(hyperpath.get_hyperedge_id_set()), 3) self.assertTrue(hyperpath.get_hyperedge_id({'s'}, {'1'})) self.assertTrue(hyperpath.get_hyperedge_id({'1'}, {'2'})) self.assertTrue(hyperpath.get_hyperedge_id({'1', '2'}, {'t'})) # third shortest path hyperpath = threeShortest[2] self.assertEqual(hyperpath.get_node_set(), {'s', '2', '3', '4', 't'}) self.assertEqual(len(hyperpath.get_hyperedge_id_set()), 4) self.assertTrue(hyperpath.get_hyperedge_id({'s'}, {'2'})) self.assertTrue(hyperpath.get_hyperedge_id({'s'}, {'3'})) self.assertTrue(hyperpath.get_hyperedge_id({'2', '3'}, {'4'})) self.assertTrue(hyperpath.get_hyperedge_id({'4'}, {'t'}))
class TestKShortestHyperpaths(unittest.TestCase): def setUp(self): self.nielsenGraph = DirectedHypergraph() self.nielsenGraph.add_node('s') self.nielsenGraph.add_node('1') self.nielsenGraph.add_node('2') self.nielsenGraph.add_node('3') self.nielsenGraph.add_node('4') self.nielsenGraph.add_node('t') self.nielsenGraph.add_hyperedge({'s'}, {'1'}, weight=1) self.nielsenGraph.add_hyperedge({'s'}, {'2'}, weight=1) self.nielsenGraph.add_hyperedge({'s'}, {'3'}, weight=1) self.nielsenGraph.add_hyperedge({'1'}, {'2'}, weight=1) self.nielsenGraph.add_hyperedge({'2'}, {'3'}, weight=1) self.nielsenGraph.add_hyperedge({'1', '2'}, {'t'}, weight=1) self.nielsenGraph.add_hyperedge({'4'}, {'t'}, weight=1) self.nielsenGraph.add_hyperedge({'2', '3'}, {'4'}, weight=1) self.nielsenGraph.add_hyperedge({'4'}, {'1'}, weight=1) # valid input tests def test_raises_exception_if_H_not_B_hypegraph(self): H = DirectedHypergraph() H.add_nodes([1, 2, 3]) H.add_hyperedge([1], [2, 3]) source, destination = 1, 2 self.assertRaises(TypeError, ksh.k_shortest_hyperpaths, H, source, destination, 1) def test_raises_exception_if_H_not_hypegraph(self): H = "DirectedHypergraph" source, destination = 1, 2 self.assertRaises(TypeError, ksh.k_shortest_hyperpaths, H, source, destination, 1) def test_raises_exception_if_source_not_in_graph(self): H = DirectedHypergraph() source, destination = 1, 2 H.add_nodes([source, destination]) self.assertRaises(ValueError, ksh.k_shortest_hyperpaths, H, 3, destination, 1) def test_raises_exception_if_destination_not_in_graph(self): H = DirectedHypergraph() source, destination = 1, 2 H.add_nodes([source, destination]) self.assertRaises(ValueError, ksh.k_shortest_hyperpaths, H, source, 3, 1) def test_raises_exception_if_k_not_integer(self): H = DirectedHypergraph() source, destination = 1, 2 H.add_nodes([source, destination]) self.assertRaises(TypeError, ksh.k_shortest_hyperpaths, H, source, destination, 0.1) def test_raises_exception_if_k_not_positive(self): H = DirectedHypergraph() source, destination = 1, 2 H.add_nodes([source, destination]) self.assertRaises(ValueError, ksh.k_shortest_hyperpaths, H, source, destination, -4) self.assertRaises(ValueError, ksh.k_shortest_hyperpaths, H, source, destination, 0) # various cases def test_returns_only_one_hyperpath_for_k_equals_one(self): H = DirectedHypergraph() H.add_node('s') H.add_node('1') H.add_node('2') H.add_node('3') H.add_node('t') H.add_hyperedge({'s'}, {'1'}, weight=1) H.add_hyperedge({'s'}, {'2'}, weight=1) H.add_hyperedge({'s'}, {'3'}, weight=1) H.add_hyperedge({'1'}, {'t'}, weight=1) H.add_hyperedge({'2', '3'}, {'t'}, weight=1) output = ksh.k_shortest_hyperpaths(H, 's', 't', 1) self.assertEqual(len(output), 1) def test_returns_shortest_hyperpath_for_k_equals_one(self): H = DirectedHypergraph() H.add_node('s') H.add_node('1') H.add_node('2') H.add_node('3') H.add_node('t') H.add_hyperedge({'s'}, {'1'}, weight=1) H.add_hyperedge({'s'}, {'2'}, weight=1) H.add_hyperedge({'s'}, {'3'}, weight=1) H.add_hyperedge({'1'}, {'t'}, weight=1) H.add_hyperedge({'2', '3'}, {'t'}, weight=1) output = ksh.k_shortest_hyperpaths(H, 's', 't', 1) hyperpath = output[0] self.assertEqual(hyperpath.get_node_set(), {'s', '1', 't'}) self.assertEqual(len(hyperpath.get_hyperedge_id_set()), 2) self.assertTrue(hyperpath.get_hyperedge_id({'s'}, {'1'})) self.assertTrue(hyperpath.get_hyperedge_id({'1'}, {'t'})) def test_returns_empty_list_if_no_s_t_path(self): H = DirectedHypergraph() H.add_node('s') H.add_node('1') H.add_node('2') H.add_node('t') H.add_hyperedge({'s'}, {'1'}, weight=1) H.add_hyperedge({'1', '2'}, {'t'}, weight=1) output = ksh.k_shortest_hyperpaths(H, 's', 't', 1) self.assertEqual(output, []) def test_returns_3_shortest_hypergraphs_for_nielsen_example_with_k_equal_3( self): threeShortest = ksh.k_shortest_hyperpaths( self.nielsenGraph, 's', 't', 3) self.assertEquals(len(threeShortest), 3) # shortest path hyperpath = threeShortest[0] self.assertEqual(hyperpath.get_node_set(), {'s', '1', '2', 't'}) self.assertEqual(len(hyperpath.get_hyperedge_id_set()), 3) self.assertTrue(hyperpath.get_hyperedge_id({'s'}, {'1'})) self.assertTrue(hyperpath.get_hyperedge_id({'s'}, {'2'})) self.assertTrue(hyperpath.get_hyperedge_id({'1', '2'}, {'t'})) # second shortest path hyperpath = threeShortest[1] self.assertEqual(hyperpath.get_node_set(), {'s', '1', '2', 't'}) self.assertEqual(len(hyperpath.get_hyperedge_id_set()), 3) self.assertTrue(hyperpath.get_hyperedge_id({'s'}, {'1'})) self.assertTrue(hyperpath.get_hyperedge_id({'1'}, {'2'})) self.assertTrue(hyperpath.get_hyperedge_id({'1', '2'}, {'t'})) # third shortest path hyperpath = threeShortest[2] self.assertEqual(hyperpath.get_node_set(), {'s', '2', '3', '4', 't'}) self.assertEqual(len(hyperpath.get_hyperedge_id_set()), 4) self.assertTrue(hyperpath.get_hyperedge_id({'s'}, {'2'})) self.assertTrue(hyperpath.get_hyperedge_id({'s'}, {'3'})) self.assertTrue(hyperpath.get_hyperedge_id({'2', '3'}, {'4'})) self.assertTrue(hyperpath.get_hyperedge_id({'4'}, {'t'}))
def make_hypergraph(file_prefix, delim=';', sep='\t', keep_singleton_nodes=False): ''' Creates the directed hypergraph object from the hypernodes and hyperedges files. Returns the hypergraph object. ''' #NOTE: This is directly copied from https://github.com/annaritz/pathway-connectivity hypernodes = {} with open(file_prefix + '-hypernodes.txt') as fin: for line in fin: if line[0] == '#': continue row = line.strip().split(sep) if len(row) == 1: hypernodes[row[0]] = ['OtherComplexes-FIX'] else: hypernodes[row[0]] = row[1].split(delim) print('%d hypernodes from hypernodes file' % (len(hypernodes))) identifier2id = {} id2identifier = {} H = DirectedHypergraph() if keep_singleton_nodes: for n in hypernodes: H.add_node(n) skipped1 = 0 skipped2 = 0 tailsizes = [] headsizes = [] selfloops = [] noinselfloops = 0 indegree = [] outdegree = [] numtargets = 0 with open(file_prefix + '-hyperedges.txt') as fin: for line in fin: if line[0] == '#': continue row = line.strip().split(sep) tail = set() head = set() ## Tail includes tail and regulators. ## Head includes head. if row[0] != 'None': tail.update(row[0].split(delim)) if row[1] != 'None': head.update(row[1].split(delim)) if row[2] != 'None': tail.update(row[2].split(delim)) #NOTE: This line was commented out to exclude negative regulators in the tail #if row[3] != 'None': #tail.update(row[3].split(delim)) hedge_id = row[4] ## THIS IS A HACK FOR NOW ( should be incorporated in the make-hypergraph.py code) ## IGnore any reactions that have a Reactome Identifier (e.g. has "HSA") instead of ## a PAthway Commons identifier. if any(['HSA' in s for s in tail] + ['HSA' in s for s in head]): skipped1 += 1 elif len(tail) == 0 or len(head) == 0: skipped2 += 1 else: hid = H.add_hyperedge(tail, head, identifier=hedge_id) tailsizes.append(len(tail)) headsizes.append(len(head)) intersection = tail.intersection(head) if len(intersection) > 0: selfloops.append([v for v in intersection]) identifier2id[hedge_id] = hid id2identifier[hid] = hedge_id print('%d reactions skipped because of Reactome identifier' % (skipped1)) print('%d reactions skipped because of an empty tail or head' % (skipped2)) ## annotate nodes num_hypernodes = 0 for node in H.get_node_set(): if node in hypernodes and hypernodes[node] != [node]: H.add_node(node, hypernode_members=hypernodes[node], is_hypernode=True) num_hypernodes += 1 else: H.add_node(node, is_hypernode=False, hypernode_members=[]) H.add_node(node) return H, identifier2id, id2identifier
def initialize(H, source, target, node_dict={}): ''' Finds the set of reachable and backwards-recoverable edges, sets up the tail reached counters, the inedge lists, and the heap pointer for each edge. Initializes the taillength for each edge, and the heap, and the reachable edge counter ''' edgedict = {} #find reachable and backwards-recoverable edge list reachableedges = findreachableandbackrecoverable(H, source, target) #trim H H2 = DirectedHypergraph() if len(node_dict) == 0: for v in H.node_iterator(): H2.add_node(v, H.get_node_attributes(v)) for edge in reachableedges: H2.add_hyperedge(H.get_hyperedge_tail(edge), H.get_hyperedge_head(edge), weight=H.get_hyperedge_weight(edge)) H2.add_node('SUPERSOURCE', {'label': 'SUPERSOURCE'}) for edge in H2.get_forward_star('SUPERSOURCE'): forwardstarlist = [] for v in H2.get_hyperedge_head(edge): if len(H2.get_forward_star(v)) > 0: forwardstarlist.append(v) H2.remove_hyperedge(edge) if len(forwardstarlist) > 0: H2.add_hyperedge(['SUPERSOURCE'], forwardstarlist, weight=0) else: H2.add_hyperedge(['SUPERSOURCE'], [], weight=0) H = H2 for v in H.get_node_set(): #need to remap the edges because just calling remove_node(v) removes also all the hyperedges v is in if len(H.get_forward_star(v)) == 0 and v != 'SUPERTARGET': #find edges that need to be replaced backedges = H.get_backward_star(v) for e in backedges: tail = H.get_hyperedge_tail(e) head = H.get_hyperedge_head(e) head.remove(v) w = H.get_hyperedge_weight(e) H.remove_hyperedge(e) H.add_hyperedge(tail, head, weight=w) H.remove_node(v) reachableedges = [] H2.add_node('SUPERSOURCE', {'label': 'SUPERSOURCE'}) for edge in H.hyperedge_id_iterator(): reachableedges.append(edge) #initialize edgedict for e in H.hyperedge_id_iterator(): edgedict[e] = { 'isremoved': False, 'bestinedges': [], 'candidateinedges': [], 'tailcount': len(H.get_hyperedge_tail(e)) } #initialize taildistancelist taildistancelist = {} for e in reachableedges: taildistancelist[e] = 'inf' #initialize reachableedgecounter reachableedgecounter = len(reachableedges) #initialize heap heap = [] entry_finder = { } # mapping of tasks to entries, this and the line below are strictly for the heap counter = itertools.count() # unique sequence count for e in H.get_forward_star(source): if e in reachableedges: add_node(heap, e, weight(H, [e]), counter, entry_finder) #initialize reachedtable reachedtable = {} for e in H.hyperedge_id_iterator(): reachedtable[e] = [] return reachableedges, edgedict, taildistancelist, heap, reachableedgecounter, reachedtable, entry_finder, counter, H
def get_hyperpath_from_predecessors(H, Pv, source_node, destination_node, node_weights=None, attr_name="weight"): """Gives the hyperpath (DirectedHypergraph) representing the shortest B-hyperpath from the source to the destination, given a predecessor function and source and destination nodes. :note: The IDs of the hyperedges in the subhypergraph returned may be different than those in the original hypergraph (even though the tail and head sets are identical). :param H: the hypergraph which the path algorithm was executed on. :param Pv: dictionary mapping each node to the ID of the hyperedge that preceeded it in the path. :param source_node: the source node of the path. :param destination_node: the destination node of the path. :returns: DirectedHypergraph -- shortest B-hyperpath from source_node to destination_node. :raises: TypeError -- Algorithm only applicable to directed hypergraphs :raises: KeyError -- Node key in predecessor is not in H :raises: KeyError -- Hyperedge key in predecessor is not in H :raises: ValueError -- Multiple nodes without predecessor :raises: ValueError -- Hypertree does not have source node """ if not isinstance(H, DirectedHypergraph): raise TypeError("Algorithm only applicable to directed hypergraphs") # Check that Pv is a valid predecessor function: # - keys must be nodes in H mapping to hyperedges in H # - exactly one node must map to None (i.e., only one node # without predecessor) nodes_without_predecessor = 0 for node, hyperedge_id in Pv.items(): if not H.has_node(node): raise KeyError( "Node key %s in predecessor is not in H" % node) if hyperedge_id is None: nodes_without_predecessor += 1 elif not H.has_hyperedge_id(hyperedge_id): raise KeyError( "Hyperedge key %s in predecessor is not in H" % hyperedge_id) if nodes_without_predecessor > 1: raise ValueError( "Multiple nodes without predecessor. %s received" % Pv) elif nodes_without_predecessor == 0: raise ValueError( "Hypertree does not have source node. %s received" % Pv) path = DirectedHypergraph() # keep track of which nodes are or have been processed processedOrInQueue = {n: False for n in Pv} nodesToProcess = [destination_node] processedOrInQueue[destination_node] = True while nodesToProcess: node = nodesToProcess.pop(0) hyperedge_id = Pv[node] if hyperedge_id: for n in H.get_hyperedge_tail(hyperedge_id): if not processedOrInQueue[n]: nodesToProcess.append(n) processedOrInQueue[n] = True path.add_hyperedge(H.get_hyperedge_tail(hyperedge_id), H.get_hyperedge_head(hyperedge_id), weight=H.get_hyperedge_weight( hyperedge_id)) elif not path.has_node(node): path.add_node(node) return path