def test_matrix_to_adj(self): a = Matrix(size=2) a.add_edge(0, 1) nodes = [ Node(colour=Colour.BLUE, id=1), Node(colour=Colour.BLACK, id=2) ] result = utils.matrix_to_adj(nodes, a) assert result.get(nodes[0], []) == [nodes[1]]
def test_equal_size(self): a = Matrix(size = 3) a.add_edge(0, 1) b = Matrix(size = 4) b.add_edge(0, 1) assert not a.equals(b)
def combine_adj_matrix(starting_node: int, graph1: Matrix, graph2: Matrix, target_nodes: List[int]) -> Matrix: if graph1.equals(graph2): return graph1 elif graph1.subset(graph2): return graph1 elif graph2.subset(graph1): return graph2 out = Matrix(graph1.size()) def does_lead_to_target(curr_node, visited: Set[int]): if curr_node not in target_nodes: if curr_node not in visited: all_outgoing = set( graph1.get_connected_lst(curr_node) + graph2.get_connected_lst(curr_node)) visited.add(curr_node) for outgoing in all_outgoing: if outgoing not in visited: # out.add_edge(curr_node, outgoing) if does_lead_to_target(outgoing, visited): return True return False else: return True def traverse(curr_node, visited: Set[int]): # Do nothing if we already visited the node if curr_node not in visited: # Get all outgoing edges all_outgoing = set( graph1.get_connected_lst(curr_node) + graph2.get_connected_lst(curr_node)) visited.add(curr_node) for outgoing in all_outgoing: # Make sure we don't go in circles, make sure to prune any extraneous edges if outgoing not in visited and does_lead_to_target( outgoing, deepcopy(visited)): out.add_edge(curr_node, outgoing) traverse(outgoing, visited) traverse(starting_node, set()) return out
def find_children(graph: Matrix, starting_node: int, has_visited: List[int], remaining_targets: List[int], curr_matrix: Matrix) -> Iterator[Matrix]: # Get all children of root. children_of_root = graph.get_connected_lst(starting_node) for child in children_of_root: if child == starting_node: logging.debug("Skipping duplicate node: {0}".format(child)) continue if child in has_visited: logging.debug("Skipping visited child: {0}".format(child)) continue if child in remaining_targets: # We found one of our target nodes! logging.debug("Found a child node: {0}".format(child)) # TODO: Bug here is two found nodes have the same parent, (not in serial), this algorithm fails. # Remove child from target nodes, and recurse remaining_children = [x for x in remaining_targets if x != child] else: # We didn't hit a child, so no changes. remaining_children = remaining_targets # Add an edge between the current node and the child new_matrix = deepcopy(curr_matrix) new_matrix.add_edge(starting_node, child) if len(remaining_children) == 0: # We've found all children, we're done! yield new_matrix else: yield from find_children( graph=graph, starting_node=child, # Make sure we don't go in circles has_visited=has_visited + [child], remaining_targets=remaining_children, curr_matrix=new_matrix)
def traverse_graph(graph: Dict[Node, List[Node]], target_colours: List[Colour]) \ -> Iterator[Dict[Node, List[Node]]]: nodes = list(graph.keys()) num_nodes = len(nodes) graph_matrix = utils.adj_to_matrix(graph) # Lookup all nodes with that same colour node_colour_lookup: Dict[Colour, List[Node]] = build_node_colour_lookup( list(graph.keys())) target_node_sets: Iterator[List[int]] = get_all_target_nodes( nodes=nodes, targets=target_colours, node_colours=node_colour_lookup) green_nodes: List[int] = utils.node_to_index( nodes, node_colour_lookup.get(Colour.GREEN, [])) all_final_mat_output: List[Matrix] = [] # We must start at the green nodes, so if they aren't present, we know there's no solution # Try it from every green node for green in green_nodes: # Get every set of end-nodes, try and build a graph to each set for target_nodes_set in target_node_sets: # store adjacency matrix from the particular green node to the particular target node green_to_target: List[Matrix] = [] # Go to each individual target node for target_node in target_nodes_set: green_to_target.extend( list( find_children( graph=graph_matrix, starting_node=green, has_visited=[green ], # Make sure we don't go in circles remaining_targets=[target_node], curr_matrix=Matrix(size=num_nodes)))) if len(target_nodes_set) == 1: # There's only one node we're looking for, so output all paths to that node for completed_matrix in green_to_target: completed_adj = utils.matrix_to_adj( nodes, completed_matrix) yield completed_adj else: # Once we've gotten paths between that green node and every target node in the set, it's time to combine them and check! for combination in itertools.combinations( range(len(green_to_target)), r=len(target_nodes_set)): # Create the merged graph merged = Matrix(size=num_nodes) for c in combination: merged = combine_adj_matrix(green, green_to_target[c], merged, target_nodes_set) # G1 and G2 are the two adjacency lists to combine from green_to_target # They will have the same starting node, but different ending node # Determine if we've already output it if not utils.has_prev_output_graph(all_final_mat_output, merged): # Determine if it meets all criteria # (does it contain all of the target nodes) did_get_all_targets = True for target in target_nodes_set: did_get_all_targets = did_get_all_targets and len( merged.inward_edges_lst(target)) > 0 # Determine if we got them all if did_get_all_targets: all_final_mat_output.append(merged) merged_adj = utils.matrix_to_adj(nodes, merged) yield merged_adj
def test_add_edge(self): m = Matrix(size = 3) m.add_edge(0, 1) assert m.get_edge(0, 1)
def test_create_matrix_size(self): m = Matrix(size = 3) assert m.size() == 3
def test_connected(self): m = Matrix(size = 3) m.add_edge(0, 1) assert 1 in m.get_connected_lst(0)
def test_matrix_to_adj_debug(self): a = Matrix(size=2) a.add_edge(0, 1) result = utils.matrix_to_adj_debug(a) assert result.get(0, []) == [1]
def test_has_prev_output(self): a = Matrix(size=3) b = Matrix(size=3) c = Matrix(size=3) a.add_edge(0, 1) b.add_edge(1, 2) c.add_edge(0, 2) prev_output = [a, b] assert not utils.has_prev_output_graph(prev_output, c) assert utils.has_prev_output_graph(prev_output, a)