def test_single_edge(): # create a graph consisting of a single edge graph = Graph([(0, 1)], directed=True) graph.es['type'] = ['up'] node = most_detrimental(graph, 0, 1) assert node is None
def test_two_options(): # create a graph consisting of four nodes connected in sequence graph = Graph([(0, 1), (1, 2), (2, 3)], directed=True) graph.es['type'] = ['up'] * 3 node = most_detrimental(graph, 0, 3) assert node in (1, 2)
def test_not_connected(): # create a graph with two unconnected edges graph = Graph([(0, 1), (2, 3)], directed=True) graph.es['type'] = ['up', 'up'] node = most_detrimental(graph, 0, 3) assert node is None
def test_example(): # create a graph consisting of three nodes connected in sequence # every edge will be upregulating graph = Graph.Formula('A->B->C') graph.es['type'] = ['up'] * 2 # the most detrimental node should be B node = most_detrimental(graph, 'A', 'C') assert node == 'B'
def test_stray_edge_source(): # create a graph consisting of three nodes connected in sequence, plus an extra # edge pointing out from the source graph = Graph([(0, 1), (1, 2), (0, 3)], directed=True) graph.es['type'] = ['up'] * 3 node = most_detrimental(graph, 0, 2) assert node == 1
def test_two_paths_by_len_and_color(): # create a graph with two possible paths to the source # where the best path isn't clear and depends on alpha graph = Graph([(0, 1), (1, 4), (0, 2), (2, 3), (3, 4)], directed=True) graph.es['type'] = ['up', 'down', 'up', 'up', 'up'] # case 1: alpha == 1 (the default) node = most_detrimental(graph, 0, 4) # any of the intermediate nodes could be most detrimental assert node in (1, 2, 3) # also check that it calculated the correct damages # they should all be the same (ie 0) damages = damage(graph, 0, 4) assert damages.get(1, 0) == 0 assert damages.get(2, 0) == 0 assert damages.get(3, 0) == 0 # case 2: alpha == 1/2 node = most_detrimental(graph, 0, 4, alpha=0.5) # the path that passes through node 1 is better assert node == 1 # what are the influences of the two possible paths? influences = [ influence(graph.es['type'][:2], alpha=0.5), influence(graph.es['type'][2:], alpha=0.5) ] # also check that it calculated the correct damages damages = damage(graph, 0, 4, alpha=0.5) assert damages.get(node, 0) == (influences[1] - influences[0]) # case 3: alpha == 2 node = most_detrimental(graph, 0, 4, alpha=2) # the path that passes through nodes 2 and 3 is better assert node in (2, 3) # what are the influences of the two possible paths? influences = [ influence(graph.es['type'][:2], alpha=2), influence(graph.es['type'][2:], alpha=2) ] # also check that it calculated the correct damages damages = damage(graph, 0, 4, alpha=2) assert damages.get(node, 0) == (influences[0] - influences[1])
def test_two_paths_no_node(): # create a graph consisting of three nodes # where the other two nodes are pointing towards the middle one: # 0->1<-2 graph = Graph([(0, 1), (2, 1), (0, 2)], directed=True) graph.es['type'] = ['up'] * 3 node = most_detrimental(graph, 0, 1) assert node is None
def test_example_other_types(): for edge_types in (('up', 'down'), (0, 1), ('activates', 'inactivates'), (100, -200)): # create a graph consisting of three nodes connected in sequence # every edge will be upregulating graph = Graph.Formula('A->B->C') graph.es['type'] = edge_types # the most detrimental node should be B node = most_detrimental(graph, 'A', 'C') assert node == 'B', 'Failed with edge types {} and {}'.format( *edge_types)
def test_all_same_damages(): graph = Graph([(0,1), (0,2), (2,3), (1,3), (1,4), (3,5), (4,5)], directed=True) graph.es['type'] = ['up', 'up', 'down', 'down', 'down', 'down', 'down'] node = most_detrimental(graph, 0, 5) assert node in (1, 2, 3, 4) # also check that each node's damage comes out to 0, as desired damages = damage(graph, 0, 5) for node in (1, 2, 3, 4): assert damages.get(node, 0) == 0
def test_two_paths_by_color(): # create a graph with two possible paths to the source # where the best path is solely determined by the color graph = Graph([(0, 1), (1, 3), (0, 2), (2, 3)], directed=True) graph.es['type'] = ['up'] * 3 + ['down'] node = most_detrimental(graph, 0, 3) assert node == 1 # what are the influences of the two possible paths? influences = [ influence(graph.es['type'][:2]), influence(graph.es['type'][2:]) ] # also check that it calculated the correct damages damages = damage(graph, 0, 3) assert damages.get(node, 0) == (influences[1] - influences[0])
def test_two_paths_by_len(): # create a graph with two possible paths to the source # where the best path is solely determined by length graph = Graph([(0, 1), (1, 4), (0, 2), (2, 3), (3, 4)], directed=True) graph.es['type'] = ['up'] * 5 # check that it chose the correct path by looking at the most damaging node node = most_detrimental(graph, 0, 4) assert node == 1 # what are the influences of the two possible paths? influences = [ influence(graph.es['type'][:2]), influence(graph.es['type'][2:]) ] # also check that it calculated the correct damages damages = damage(graph, 0, 4) assert damages.get(node, 0) == (influences[1] - influences[0])
def test_infinity(): # create a more complicated graph: # All 3 paths must pass through node 1, the first internal node (after the source) graph = Graph([(0,1), (1,3), (3,2), (1,2), (1,4), (2,5), (4,5)], directed=True) graph.es['type'] = ['up', 'up', 'down', 'down', 'down', 'up', 'down'] node = most_detrimental(graph, 0, 5) assert node == 1 # also check that the damage comes out to infinity, as desired damages = damage(graph, 0, 5) assert damages.get(node, 0) == float('inf') # check that the damage of node 4 is 1, since the path that passes through node 4 # is the best one and has influence 4 but there's two more that both have an # influence of 5: # 5-4 = 1 assert damages.get(4, 0) == 1 # the damages of nodes 2 and 3 should be 0, since the most influential path doesn't # pass through those nodes assert damages.get(2, 0) == 0 assert damages.get(3, 0) == 0
def test_cycle(): # This graph consists of four consecutive nodes 0, 1, 2, and 3 and one more extra # node 4. A cycle begins at node 2, passes through node 4, and then ends at node 1. # All edges have the same color. graph = Graph([(0, 1), (1, 2), (2, 3), (2, 4), (4, 1)], directed=True) graph.es['type'] = ['up']*5 # for all values of alpha, the results should be the same # so let's test a bunch of them for alpha in (1, 2, 100, 0.5, 0.1): # Nodes 1 and 2 will be the most detrimental because they are the internal nodes # within the path that connects source to sink. The presence of the cycle does # not change the fact that they are absolutely required. node = most_detrimental(graph, 0, 3, alpha) assert node in (1, 2) # also check that the most damage comes out to infinity, as desired damages = damage(graph, 0, 3, alpha) assert damages.get(1, 0) == float('inf') assert damages.get(2, 0) == float('inf') # and check that the damage of node 4 is 0, since the most influential path # should not pass through it! assert damages.get(4, 0) == 0
def test_bowtie(): # This graph has two paths that split from the source and then converge on node 3. # The first of the two paths consists of three alternating edges and the second # path consists of four non-alternating edges. # There's a single edge from node 3 to node 7 and then there are two paths (each of # length: 2 edges) that split out from node 7 before converging again on node 9. # One of the final two paths has alternating edges while the other does not. ''' graph = Graph([ (0, 1), (1, 2), (2, 3), (0, 4), (4, 5), (5, 6), (6, 11), (11, 3), (3, 7), (7, 8), (8, 9), (7, 10), (10, 9) ], directed=True) graph.es['type'] = [ 'down', 'up', 'down', 'down', 'down', 'down', 'down', 'down', 'down', 'down', 'down', 'up', 'down' ] ''' graph = Graph([ (0, 1), (1, 2), (2, 3), (0, 4), (4, 5), (5, 6), (6, 3), (3, 7), (7, 8), (8, 9), (7, 10), (10, 9) ], directed=True) graph.es['type'] = [ 'down', 'up', 'down', 'down', 'down', 'down', 'down', 'down', 'down', 'down', 'up', 'down' ] # first, try the pincer node = most_detrimental(graph, 0, 7) assert node == 3 # also check that the most damage comes out to infinity, as desired damages = damage(graph, 0, 7) assert damages.get(node, 0) == float('inf') # check that the damages of nodes 4, 5, and 6 is 1 since the best path passes # through those nodes and it has an influence of 4, whereas the next best path has # an influence of 5 (because it alternates colors, even though it is shorter) # 5-4 = 1 assert damages.get(4, 0) == 1 assert damages.get(5, 0) == 1 assert damages.get(6, 0) == 1 # the damage of the other nodes should be 0, since the most influential path # doesn't pass through them assert damages.get(1, 0) == 0 assert damages.get(2, 0) == 0 # now, let's try the bowtie! node = most_detrimental(graph, 0, 9) assert node in (3, 7) # also check that the most damage comes out to infinity, as desired damages = damage(graph, 0, 9) assert damages.get(7, 0) == float('inf') # check that the damages of node 8 is 1 since the best path passes # through that node and it has a total influence of 7, whereas the next best path # has an influence of 8 (because it has one alternating node) # 8-7 = 1 assert damages.get(8, 0) == 2 # the damage of the other node should be 0, since the most influential path # doesn't pass it assert damages.get(10, 0) == 0 # just in case, assert that the damages of the other nodes haven't changed assert damages.get(1, 0) == 0 assert damages.get(2, 0) == 0 assert damages.get(3, 0) == float('inf') assert damages.get(4, 0) == 1 assert damages.get(5, 0) == 1 assert damages.get(6, 0) == 1