def UpdateTreeFromDiGraph(self, root, forest=False): try: self.DiGraph except: print('Run UpdateDiGraph!') return if forest == False: self.Tree = nx.maximum_spanning_arborescence(self.DiGraph.copy()) # set tree root self.TreeRoot = root self.TransferAttributes(self.Tree, self.DiGraph) self.UpdateReducedTree() else: self.Tree = nx.maximum_branching(self.DiGraph.copy()) # set tree root self.TreeRoot = None self.TransferAttributes(self.Tree, self.DiGraph) self.UpdateReducedTree() self.Tree.node[root]['root'] = '1'
def optimal_branching(self, model: 'ModelSuite') -> Branching: root_costs = model.roots_cost(self.lexicon) edge_costs = model.edges_cost(self.edge_set) graph = nx.DiGraph() for edge in self.edge_set: # weight is the negative cost, i.e. how much is "saved" # by including this edge (because we look for maximum branching) weight = root_costs[self.lexicon.get_id(edge.target)] -\ edge_costs[self.edge_set.get_id(edge)] if graph.has_edge(edge.source, edge.target): if weight > graph[edge.source][edge.target]['weight']: graph[edge.source][edge.target]['weight'] = weight else: graph.add_edge(edge.source, edge.target, weight=weight) branching = nx.maximum_branching(graph) result = Branching() for source, target in branching.edges_iter(): result.add_edge(\ max(self.find_edges(source, target), \ key=lambda e: root_costs[self.lexicon.get_id(e.target)] -\ edge_costs[self.edge_set.get_id(e)])) return result
def branching_rest(): graph, devices, vulns = generate_graph(5, 2, 3, chance=75) branching = maximum_branching(graph) arborescence = maximum_spanning_arborescence(graph) pass
def build_tree(self, cprob: np.ndarray, mode='connection_probability', as_matrix=False, recalculate_probs=False, max_slaves=0, max_depth=0): ''' Get structure from conention probability mx `mode`: 'connection_probability' -- method takes `connection_probability` on input and calculate connection_power itself 'connection_power' -- method takes `connection_power`on input; it suppose to correctness of cpower (actually it is needed to implement some features - you really should not use it) `as_matrix`: True -- returns adjacency matrix instead of networkx.DiGraph False -- returns networkx.DiGraph object `recalculate_probs`: -- recalculate cpowers feature `max_slaves` -- max slaves feature. If it > 0 => for each node number of connections will be limited `max_depth` -- max depth feature. If it > 0 => tree_depth will be limited ''' msize = len(cprob[0]) # Mode checking if mode == 'connection_probability': cpower = self.connection_power(cprob) elif mode == 'connection_power': cpower = cprob # recalculating_probs feature (look on `DynamicHierarhy` function in Wolfram Notebook) if recalculate_probs: # Build base tree base_tree = self.build_tree(cprob, mode=mode, as_matrix=True, max_slaves=max_slaves, max_depth=max_depth) # Searching nodes to reweight nodes_to_reweight = [] for i in range(msize): # Row bypass connection_counter = 0 # counter of i node for j in range(msize): # Column bypass if base_tree[i][j] > 0: connection_counter += 1 if connection_counter > 1: # add node to reweight list if more than 1 connection nodes_to_reweight.append(i) # Reweighting nodes for i in nodes_to_reweight: div = 1 # divider of conn power for j in range(msize): if base_tree[i][j] > 0: #cpower[i][j] = cpower[i][j] / div cprob[i][j] = cprob[i][j] / div div *= 2 cpower = self.connection_power(cprob) # max_slaves feature # bug: # With standart nodesCoord matrix: # I. # 1. Set max_slaves = 2, max_depth = 2 # 2. Program prints: `No optimal tree found` # II. # 1. Set max_slaves = 3 or 4, max_depth = 2 # 2. Program build a tree with with no more, than 2 slaves # UPD: can not reproduce if max_slaves > 0: cpower_changed = True while cpower_changed: # Build base tree from existing cpower mx base_tree = self.build_tree( cpower, mode='connection_power', as_matrix=True, recalculate_probs=recalculate_probs, max_depth=max_depth) # Searching first meeting of node with more than `max_slaves` connection cpower_changed = False for i in range(msize): current_connections_index = [] current_connections_power = [] for j in range(msize): # all exist connections are adding to list to get one with min cpower to drop it if base_tree[i][j] > 0: current_connections_index.append( (i, j) ) # need to write index of node and its power current_connections_power.append(base_tree[i][j]) # If node with too many slaves is found, then we fix cpower -> cprob # and rebuild tree (kind of recursive way) if len(current_connections_power) > max_slaves: # Setting flag cpower_changed = True # Search min connection min_listindex = current_connections_power.index( min(current_connections_power)) min_i = current_connections_index[min_listindex][0] min_j = current_connections_index[min_listindex][1] # and drop it cpower[min_i][min_j] = 0 # max_depth feature if max_depth > 0: cpower_changed = True while cpower_changed: # Build base tree from existing cpower mx base_tree = self.build_tree( cpower, mode='connection_power', as_matrix=False, recalculate_probs=recalculate_probs, max_slaves=max_slaves) cpower_changed = False # If we have too far node, then we fix cpower -> cprob (cpower [-2][-1] := 0) # and rebuild tree (kind of recursive way) if Utils.tree_depth(base_tree) > max_depth: # Setting flag cpower_changed = True longest_path = nx.dag_longest_path(base_tree) # Farest node far_i = longest_path[-2] far_j = longest_path[-1] # Drop it cpower[far_i][far_j] = 0 # Make structure # NOTE: # - maximum_spanning_arborescence -- if there is not possible to build with this set of nodes # then it throws an exception - no arborescence can be built # - maximum_branching -- it builds an branching even if there is all zero weights G = nx.from_numpy_array(cpower, create_using=nx.DiGraph) try: #res = nx.maximum_spanning_arborescence(G, default=0) res = nx.maximum_branching(G, default=0) if as_matrix: res = nx.to_numpy_array(res) except nx.exception.NetworkXException: # if no tree found raise # handle this in interface part # We find out which node has no connection and drop it return res