def _choose_spanhead_from_heuristics(self,span_nodes,pos_precedence_list): distancestoroot = [len(nx.ancestors(self,x)) for x in span_nodes] shortestdistancetoroot = min(distancestoroot) distance_counter = Counter(distancestoroot) highest_nodes_in_span = [] # Heuristic Nr 1: If there is one single highest node in the span, it becomes the head # N.B. no need for the subspan to be a tree if there is one single highest element if distance_counter[shortestdistancetoroot] == 1: spanhead = span_nodes[distancestoroot.index(shortestdistancetoroot)] return spanhead # Heuristic Nr 2: Choose by POS ranking the best head out of the highest nodes for x in span_nodes: if len(nx.ancestors(self,x)) == shortestdistancetoroot: highest_nodes_in_span.append(x) best_rank = len(pos_precedence_list) + 1 candidate_head = - 1 span_upos = [self.node[x]["cpostag"]for x in highest_nodes_in_span] for upos, idx in zip(span_upos,highest_nodes_in_span): if pos_precedence_list.index(upos) < best_rank: best_rank = pos_precedence_list.index(upos) candidate_head = idx return candidate_head
def _find_necessary_steps(self, outputs, inputs): """ Determines what graph steps need to pe run to get to the requested outputs from the provided inputs. Eliminates steps that come before (in topological order) any inputs that have been provided. Also eliminates steps that are not on a path from the provided inputs to the requested outputs. :param list outputs: A list of desired output names. This can also be ``None``, in which case the necessary steps are all graph nodes that are reachable from one of the provided inputs. :param dict inputs: A dictionary mapping names to values for all provided inputs. :returns: Returns a list of all the steps that need to be run for the provided inputs and requested outputs. """ if not outputs: # If caller requested all outputs, the necessary nodes are all # nodes that are reachable from one of the inputs. Ignore input # names that aren't in the graph. necessary_nodes = set() for input_name in iter(inputs): if self.graph.has_node(input_name): necessary_nodes |= nx.descendants(self.graph, input_name) else: # If the caller requested a subset of outputs, find any nodes that # are made unecessary because we were provided with an input that's # deeper into the network graph. Ignore input names that aren't # in the graph. unnecessary_nodes = set() for input_name in iter(inputs): if self.graph.has_node(input_name): unnecessary_nodes |= nx.ancestors(self.graph, input_name) # Find the nodes we need to be able to compute the requested # outputs. Raise an exception if a requested output doesn't # exist in the graph. necessary_nodes = set() for output_name in outputs: if not self.graph.has_node(output_name): raise ValueError("graphkit graph does not have an output " "node named %s" % output_name) necessary_nodes |= nx.ancestors(self.graph, output_name) # Get rid of the unnecessary nodes from the set of necessary ones. necessary_nodes -= unnecessary_nodes # Return an ordered list of the needed steps. return [step for step in self.steps if step in necessary_nodes]
def ancestors(self, nbunch): self.validate_input_nodes(nbunch) if not self.acceptable_iterable(nbunch): # single input node return nx.ancestors(self, nbunch) else: if len(nbunch) == 1: # still a single node return nx.ancestors(self, nbunch[0]) else: # multiple input nodes DG = self.copy() t = DG.add_node_unique() for node in nbunch: DG.add_edge(node, t) # this automatically adds t to DG too return nx.ancestors(DG, t) - set(nbunch) # returns a SET
def filter_graph(graph): from_s = nx.descendants(graph, start_id) from_s.add(start_id) to_e = nx.ancestors(graph, end_id) to_e.add(end_id) del_cross = (from_s | to_e) - (from_s & to_e) graph.remove_nodes_from(del_cross)
def as_dependency_list(self, limit_to=None): """returns a list of list of nodes, eg. [[0,1], [2], [4,5,6]]. Each element contains nodes whose dependenices are subsumed by the union of all lists before it. In this way, all nodes in list `i` can be run simultaneously assuming that all lists before list `i` have been completed""" if limit_to is None: graph_nodes = set(self.graph.nodes()) else: graph_nodes = set() for node in limit_to: graph_nodes.add(node) if node in self.graph: graph_nodes.update(nx.descendants(self.graph, node)) else: raise RuntimeError("Couldn't find model '{}' -- does it exist or is it diabled?".format(node)) depth_nodes = defaultdict(list) for node in graph_nodes: num_ancestors = len(nx.ancestors(self.graph, node)) depth_nodes[num_ancestors].append(node) dependency_list = [] for depth in sorted(depth_nodes.keys()): dependency_list.append(depth_nodes[depth]) return dependency_list
def in_component(G, target): r'''creates the in_component by basically reversing out_component. Parameters ---------- G : NetworkX Graph The network the disease will transmit through. target : a target node The node whose infection we are interested in. In principle target could be an iterable, but in this case we would be finding those possible sources whose infection leads to infection of at least one target, not all. Returns ------- source_nodes : set the set of nodes (including target) from which target is reachable ''' try: #testing whether this is an iterable iterator = iter(target) except TypeError: #It's not an iterable. It "must" be a node. if G.has_node(target): target_nodes = set([target]) else: #it's an iterable. target_nodes = set(target) source_nodes = set([]) for node in target_nodes: source_nodes = source_nodes.union(set(nx.ancestors(G, node))) return source_nodes
def subtree(G, node): GS = G.copy() GS.remove_node(node) sd = nx.descendants(G, node) sd.add(node) s = set(sd) S = G.subgraph(s).copy() for n in sd: if n == node: continue ns = nx.ancestors(GS, n) if not ns.issubset(sd): S.remove_node(n) s.discard(n) pn = set(G.predecessors_iter(node)) gs = set( itertools.chain.from_iterable( nx.shortest_path(G, "REPO", n) for n in pn )) GS = G.subgraph(gs.union(s)).copy() for n in pn.difference(s): GS.node[n]["fontcolor"] = "#FF0000" for n in s: GS.node[n]["fontcolor"] = "#006600" GS.remove_node("REPO") return S, GS
def OnClick(self, node_id): self.color_nodes() self._current_node_id = node_id node_ea = self[node_id] self._remove_target_handler.unregister() self._disable_source_handler.unregister() self._enable_source_handler.unregister() if node_ea in self._targets: self._remove_target_handler.register() self._attach_to_popup(self._remove_target_handler.get_name()) for ea in nx.ancestors(self._lca_graph, node_ea): if ea not in self._targets and ea not in self._sources: self._set_node_bg_color(self._node_ids[ea], COLOR_PATH) if node_ea in self._sources: if node_ea in self._disabled_sources: self._enable_source_handler.register() self._attach_to_popup(self._enable_source_handler.get_name()) else: self._disable_source_handler.register() self._attach_to_popup(self._disable_source_handler.get_name()) for ea in nx.descendants(self._lca_graph, node_ea): if ea not in self._targets and ea not in self._sources: self._set_node_bg_color(self._node_ids[ea], COLOR_PATH) return False
def backward_reachable(self, state): """Return states from which the given state can be reached. A wrapper of networkx.ancestors. """ ancestors = nx.ancestors(self, state) return ancestors
def print_impacted_modules(single_node=None, json_out=None): """ For each module, print a list of modules that depend on the module, i.e. modules that would be impacted by a change in this module. The function shows all levels of dependency, not just the immediately impacted modules. If the json_out argument is not None, then the output will be recorded there rather than printed on stdout. :return: """ if json_out is None: print('\n===Impacted Modules===') else: json_out['impacted_modules'] = {} for node_name in G.nodes_iter(): if single_node and (node_name!=single_node): continue ancestors = nx.ancestors(G, node_name) if len(ancestors) > 0: if json_out is None: print(augment_format_string(node_name, '\n%s:') % node_name) else: json_out['impacted_modules'][node_name] = [] for a in ancestors: if json_out is None: print(augment_format_string(a, ' %s') % a) else: json_out['impacted_modules'][node_name].append(a)
def get_ancestor_groups(self, pipeline_name): groups = [] ancestors = nx.ancestors(self.value_stream.pipeline_graph, pipeline_name) for ancestor in ancestors: logger.debug('ancestor of %s: %s', pipeline_name, ancestor) groups.append(self.pipeline_groups.pipelines[ancestor][0]) return list(set(groups))
def subgraph_needed_for(self, start_at, end_at): """Find the subgraph of all dependencies to run these tasks. Returns a new graph. """ assert start_at or end_at, "one of {start_at,end_at} must be a task id" start, end = map(self.task_dict.get, [start_at, end_at]) if None in [start, end]: graph = self.get_networkx_graph() if start: task_subset = nx.descendants(graph, start) task_subset.add(start) elif end: task_subset = nx.ancestors(graph, end) task_subset.add(end) elif start == end: task_subset = set([start]) else: graph = self.get_networkx_graph() task_subset = set() for path in nx.all_simple_paths(graph, start, end): task_subset.update(path) # make sure the tasks are added to the subgraph in the same # order as the original configuration file tasks_kwargs_list = [task.yaml_data for task in self.task_list if task in task_subset] subgraph = TaskGraph(self.config_path, tasks_kwargs_list) return subgraph
def __init__(self, go_server, pipeline_name, label = None): self.go_server = go_server self.pipeline = gocd_parser.pipeline.Pipeline(pipeline_name, go_server) if label is None: # get the last passing pipeline label = self.pipeline.get_last_passing()['label'] self.label = label self.value_stream = value_stream.ValueStream( self.go_server, self.pipeline.name, self.label) self.ancestors = nx.ancestors(self.value_stream.pipeline_graph, self.pipeline.name) self.dashboard = gocd_parser.handler.dashboard.Dashboard(go_server) self.pipeline_groups = pipeline_groups.PipelineGroups(go_server) self.pipeline.set_from_groups_handler(self.pipeline_groups) self.pipeline.set_failing_comparison() self.set_blockers() self.status = 'passing' if len(self.blockers) > 0: self.status = 'blocked' if self.pipeline.is_failing(): self.status = 'failing'
def ensure_dependencies(request): r""" CommandLine: python -m dtool.base --exec-BaseRequest.ensure_dependencies Example: >>> # ENABLE_DOCTEST >>> from dtool.base import * # NOQA >>> from dtool.example_depcache import testdata_depc >>> depc = testdata_depc() >>> request = depc.new_request('vsmany', [1, 2], [2, 3, 4]) >>> request.ensure_dependencies() """ import networkx as nx depc = request.depc if False: dependencies = nx.ancestors(depc.graph, request.tablename) subgraph = depc.graph.subgraph(set.union(dependencies, {request.tablename})) dependency_order = nx.topological_sort(subgraph) root = dependency_order[0] [nx.algorithms.dijkstra_path(subgraph, root, start)[:-1] + nx.algorithms.dijkstra_path(subgraph, start, request.tablename) for start in dependency_order] graph = depc.graph root = list(nx.topological_sort(graph))[0] edges = graph.edges() #parent_to_children = ut.edges_to_adjacency_list(edges) child_to_parents = ut.edges_to_adjacency_list([t[::-1] for t in edges]) to_root = {request.tablename: ut.paths_to_root(request.tablename, root, child_to_parents)} from_root = ut.reverse_path(to_root, root, child_to_parents) dependency_levels_ = ut.get_levels(from_root) dependency_levels = ut.longest_levels(dependency_levels_) true_order = ut.flatten(dependency_levels)[1:-1] #print('[req] Ensuring %s request dependencies: %r' % (request, true_order,)) ut.colorprint( '[req] Ensuring request %s dependencies: %r' % (request, true_order,), 'yellow') for tablename in true_order: table = depc[tablename] if table.ismulti: pass else: # HACK FOR IBEIS all_aids = ut.flat_unique(request.qaids, request.daids) depc.get_rowids(tablename, all_aids) pass pass #zip(depc.get_implicit_edges()) #zip(depc.get_implicit_edges()) #raise NotImplementedError('todo') #depc = request.depc #parent_rowids = request.parent_rowids #config = request.config #rowid_dict = depc.get_all_descendant_rowids( # request.tablename, root_rowids, config=config) pass
def getcentral(g1): # get different centrality return pd.DataFrame({ u'anc': {x: len(nx.ancestors(g1, x)) for x in g1.nodes()}, u'des': {x: len(nx.descendants(g1, x)) for x in g1.nodes()}, u'indeg': g1.in_degree(), u'outdeg': g1.out_degree() })
def descends(self, e, root): """Does the envo term `e` descend from the node `root`? Returns True or False.""" # Auto conversion # if isinstance(e, int): e = "ENVO:%08d" % e if isinstance(root, int): root = "ENVO:%08d" % root # Return # return e in networkx.ancestors(self.networkx, root)
def filter_recipe_dag(dag, names): name_set = set(names) nodes = set() for recipe in dag: if recipe.reldir in name_set and recipe not in nodes: nodes.add(recipe) nodes |= nx.ancestors(dag, recipe) return nx.subgraph(dag, nodes)
def get_upstream_paths(self, *requested_paths): subgraph_members = set(requested_paths) for path in requested_paths: subgraph_members.update(nx.ancestors(self._graph, path)) subgraph_paths = self._paths.intersection(subgraph_members) full_subgraph = nx.subgraph(self._graph, subgraph_members) path_subgraph = nx.projected_graph(full_subgraph, subgraph_paths) return(nx.topological_sort(path_subgraph))
def hierarchical_f_measure(tr, y_true, y_pred): """ Calculate hierarchical f-measure. This is defined as the f-measure precision and recall calculated with the union of the ancestors of the given labels (including the labels themselves and excluding the root). Parameters ---------- tr: ThesaursReader The thesaurus. y_true: {sparse matrix, array-like} The true labels y_pred: {sparse matrix, array-like} The predicited labels Returns ------- float The hierarchical f_measure """ graph = tr.nx_graph root = tr.nx_root if not sp.issparse(y_true): y_true = sp.coo_matrix(y_true) y_pred = sp.coo_matrix(y_pred) label_scores = [] for i in range(0, y_true.shape[0]): row_true = y_true.getrow(i) row_pred = y_pred.getrow(i) true_ancestors = set.union(set(row_true.indices), *[nx.ancestors(graph, index) for index in row_true.indices]) true_ancestors.discard(root) pred_ancestors = set.union(set(row_pred.indices), *[nx.ancestors(graph, index) for index in row_pred.indices]) pred_ancestors.discard(root) intersection = len(pred_ancestors & true_ancestors) try: p = intersection / len(pred_ancestors) r = intersection / len(true_ancestors) label_scores.append(2 * p * r / (p + r)) except ZeroDivisionError: warn('F_score is ill-defined and being set to 0.0 on samples with no predicted labels', UndefinedMetricWarning, stacklevel=2) label_scores.append(0) return np.mean(label_scores)
def ensure_complete(self): ## find input at start of graph ## add CopyTask for each such input for node in nx.nodes(self._graph): if len(nx.ancestors(self._graph, node)) == 0: if not isinstance(node, Task): t = CopyTask(node) self._graph.add_edge(t, node) self._paths.update(node) self._tasks.add(t)
def get_roots(graph): """ Get the roots of a graph. :param `DiGraph` graph: the graph :returns: the roots of the graph :rtype: list of `Node` """ return [n for n in graph if not nx.ancestors(graph, n)]
def get_subgraph_for_node(node_name): """ Prints the dependency graph for only the specified node_name (a full dependency graph can be difficult to read). :param node_name: Node for which to print the sub-graph :return: """ ancestors = nx.ancestors(G, node_name) ancestors.add(node_name) return nx.subgraph(G, ancestors)
def architecture_subtree_names(self, node_name): """ returns an unordered set of descendant names of the current node in the architectural tree """ # NOTE: this is actually the descendants, despite the name, because # of the way we set up the tree descendant_names = nx.ancestors(self.architectural_tree, node_name) subtree_names = descendant_names | {node_name} return subtree_names
def _transfer_graph_as_worker(self, graph): worker = GWorker(debug=self._debug, priority=1) for node in graph.nodes(): worker.add_node(node, {'ptr': graph.node[node]['ptr']}) if not nx.ancestors(graph, node): worker.set_start_to_run(node) for edge in graph.edges(): u, v = edge worker.add_edge(u, v, weight=1) return worker
def print_test(self, e=None): """Just a method to see a bit how the different libraries work.""" # Test node # if e is None: e = test_envos[0] # Goa # print "Goa: " print self.goatools[e] # Pygraphviz # print "pygraphviz: " print self.pygraphviz[e] print self.pygraphviz.successors(e) print self.pygraphviz.predecessors(e) print self.pygraphviz.get_node(e) # Networkx # import networkx print "networkx: " print self.networkx[e] print self.networkx.successors(e) print self.networkx.predecessors(e) print networkx.ancestors(self.networkx, e) # same as predecessors print networkx.descendants(self.networkx, e) # almost as child_to_parents
def find_subgraphs_to_delete(graph, delete_pattern=DELETE_PATTERN): # this doesn't work: it can also find the root even if there are # subtree starting from the root that cannot be deleted. leafs = [n for n,d in graph.out_degree().items() if d == 0] want_to_delete = {} to_visit_again = set() for leaf in leafs: if delete_pattern not in leaf: want_to_delete[leaf] = False continue else: want_to_delete[leaf] = True # Now, go up subtree = graph.subgraph(nx.ancestors(graph, leaf)) currently_deleting = True for node in reversed(nx.topological_sort(subtree)): # * If node has been visited already, skip to next leaf # * (avoid visiting ancestors twice) # # * If node cannot be deleted, continue and mark all nodes # as cannot be deleted. # # * If node has been never visited, and it should be deleted, # # - if has other outlets, add it to the list of nodes to # check later on # - if has no other outles, mark it for deletion and # continue if node in want_to_delete: # We already visited this node, we should stop. break if delete_pattern not in leaf or currently_deleting is False: # None of the ancestors should be deleted want_to_delete[node] = False currently_deleting = False continue # Apparently, DELETE_PATTERN is in leaf, we are currently # deleting and this is the first time we visit the node. if len(subtree.edges(node)) == 1: # This is the only children. want_to_delete[node] = True else: to_visit_again.add(node) break to_delete = [i for i in want_to_delete if want_to_delete.get(i)] if to_visit_again: # Remove nodes we already visited and call again this function. sub = graph.subgraph([g for g in graph if g not in to_delete]) to_delete.extend(find_subgraphs_to_delete(sub, delete_pattern=delete_pattern).nodes()) return graph.subgraph(to_delete)
def ancestors_dag(graph, root): """ Return a directed, acyclic graph of all the ancestors of 'root'. """ assert_dag(graph) ancestors = networkx.ancestors(graph, root) non_ancestors = set(graph.nodes()) - ancestors non_ancestors.remove(root) new_graph = graph.copy() new_graph.remove_nodes_from(non_ancestors) assert_dag(new_graph) return new_graph
def update_flow_graphs(self, node_uids=None): if self.is_flowbuilder_active: return self.flowfunctions = [] startpoints = [] endpoints = [] pythonnodes = set() toposort = nx.topological_sort(self.flowgraph) self.flow_toposort = toposort for uid in toposort: node = self.flow_module_instances.get(uid) if node is not None: if node.implementation == 'python': pythonnodes.add(uid) if node.is_input_node(): startpoints.append(uid) if node.is_output_node(): endpoints.append(uid) graphs = [] for enduid in endpoints: ancestors = nx.ancestors(self.flowgraph, enduid) node = self.flow_module_instances[enduid] if ancestors or node.inputs == []: path = [uid for uid in toposort if uid in ancestors] + [enduid] if path: graphs.append(path) # worldadapter_names = [] # if self.worldadapter_instance is not None: # worldadapter_names += self.worldadapter_instance.get_available_flow_datasources() + self.worldadapter_instance.get_available_flow_datatargets() flowfunctions = {} floworder = OrderedSet() for idx, graph in enumerate(graphs): # split graph in parts: # node_uids = [uid for uid in graph if uid not in worldadapter_names] node_uids = [uid for uid in graph] nodes = [self.get_node(uid) for uid in node_uids] paths = self.split_flow_graph_into_implementation_paths(nodes) for p in paths: floworder.add(p['hash']) if p['hash'] not in flowfunctions: func, dang_in, dang_out = self.compile_flow_subgraph([n.uid for n in p['members']], use_unique_input_names=True) if func: flowfunctions[p['hash']] = {'callable': func, 'members': p['members'], 'endnodes': set([nodes[-1]]), 'inputs': dang_in, 'outputs': dang_out} else: flowfunctions[p['hash']]['endnodes'].add(nodes[-1]) for funcid in floworder: self.flowfunctions.append(flowfunctions[funcid]) self.logger.debug("Compiled %d flowfunctions" % len(self.flowfunctions))
def get_parameterized_intercitation_dag(self,old_node,new_node,dag): desc = nx.descendants(dag,old_node) desc.add(old_node) anc = nx.ancestors(dag,new_node) anc.add(new_node) # Intersect lineages to get ad tree intersect = desc.intersection(anc) if (len(intersect) == 0): print "No common intercitations between ",old_node," and ",new_node else: rev_dag = nx.reverse(dag,copy=True) # Strength of weighting due to impact (# citations) impact_param = 1.0 #Strength of weighting due to network relevance of paper's citations network_relevance_param = 1.0 #Strength of weighting due to redundancy in citation network network_robustness_param = 1.0 sum_citations = sum([pow(dag.in_degree(w),impact_param) for w in intersect]) #Store importance score importance_dict = {} for w in intersect: importance_dict[w] = pow(dag.in_degree(w),impact_param) #Calculate network relevance net_relevance = {} for w in intersect: cited_reach_cnt = 0 for cited in dag.neighbors(w): #If we can reach old node through cited node add to count if (nx.has_path(dag,cited,old_node)): cited_reach_cnt += 1 net_relevance[w] = pow(float(cited_reach_cnt)/dag.out_degree(w),network_relevance_param) #Calculate network robustness net_robustness = {} for w in intersect: citer_alt_path = 0 cited_alt_path = 0 for citer in rev_dag.neighbors(w): #If we can reach old node through citer node (without using that citation as a link) if (nx.has_path(dag,citer,old_node)): citer_alt_path += 1 for cited in dag.neighbors(w): if (nx.has_path(rev_dag,cited,new_node)): cited_alt_path += 1 net_robustness[w] = pow(float(cited_alt_path + citer_alt_path)/(dag.out_degree(w) + dag.in_degree(w)),network_robustness_param)
def _sanitize(): """Analyze all rules for possible inconsistencies. This function serves primarily as a tool for developers who intend to add new rules or modify existing ones. Ideally, it will help you identify and correct logical inconsistencies as early as possible. Additionally, it suggests other rules that you may want to consider blacklisting. """ supported_elements = find_all_supported_elements() rule_matches = find_all_rule_matches(supported_elements) # Build directed graphs showing which rules blacklist each other. for key, rules in rule_matches.items(): # Only consider patterns matched by multiple rules. if len(rules) < 2: continue element_type, pattern = key graph = nx.DiGraph() for rule_number in rules: graph.add_node(rule_number) blacklisted_rules = set() decorators = get_decorators(RULE_NUMBER_TO_RULE[rule_number]) for dec in decorators: if isinstance(dec, Blacklist): blacklisted_rules.update(dec.rule_numbers) for blacklisted_rule in blacklisted_rules: graph.add_edge(rule_number, blacklisted_rule) if not nx.is_connected(graph.to_undirected()): draw_rule_graph('unconnected', graph, element_type, pattern) if not nx.is_directed_acyclic_graph(graph): draw_rule_graph('not_DAG', graph, element_type, pattern) # Check for multiple sinks. This is not necessarily incorrect. sinks = [] for node in graph.nodes(): if len(nx.descendants(graph, node)) == 0: sinks.append(node) if len(sinks) > 1: draw_rule_graph('multiple_sinks', graph, element_type, pattern, sinks=sinks) # Check for multiple sources. This is not necessarily incorrect. sources = [] for node in graph.nodes(): if len(nx.ancestors(graph, node)) == 0: sources.append(node) if len(sources) > 1: draw_rule_graph('multiple_sources', graph, element_type, pattern, sources=sources)
def successors_til_node(self, g, node): """Like node_successors but only until the given node, and ignoring all nodes that are not its ancestors.""" bunch = list(nx.ancestors(g, node)) + [node] return self.node_successors(g.subgraph(bunch))
def base_inference_algorithm(g, q_desc_ids=None): # Convert the graph to its functions sorted_list = list(nx.topological_sort(g)) msg = """ sorted_list: {} """.format(sorted_list) debug_print(msg, level=1, V=VERBOSITY) functions = {} if q_desc_ids is None: # desc ids not provided => all attributes which are diagrammatically identified as descriptive, are assumed # to be given as inputs q_desc_ids = list(get_ids(g, kind="desc")) # q_desc_ids.sort() print(q_desc_ids) for node_name in sorted_list: node = g.nodes(data=True)[node_name] if node.get("kind", None) == "data": if len(nx.ancestors(g, node_name)) == 0: functions[node_name] = _select_numeric( q_desc_ids.index(node["idx"])) else: # Select the relevant output previous_node = [t[0] for t in g.in_edges(node_name)][0] previous_t_idx = g.nodes()[previous_node]["tgt"] relevant_idx = previous_t_idx.index(node["idx"]) functions[node_name] = o(_select_numeric(relevant_idx), functions[previous_node]) elif node.get("kind", None) == "imputation": functions[node_name] = node["function"] elif node.get("kind", None) == "model": previous_nodes = [t[0] for t in g.in_edges(node_name)] inputs = { g.nodes()[n]["tgt"][0]: functions[n] for n in previous_nodes } inputs = [ inputs[k] for k in sorted(inputs) ] # We need to sort to get the inputs in the correct order. inputs = o(np.transpose, x(*inputs, return_type=np.array)) f = node["function"] functions[node_name] = o(f, inputs) elif node.get("kind", None) == "prob": # Select the relevant output prob_idx = node["idx"] prob_classes = node["classes"] previous_nodes = [t[0] for t in g.in_edges(node_name)] previous_classes = [ g.edges[t]["classes"] for t in g.in_edges(node_name) ] previous_t_idx = [g.nodes()[n]["tgt"] for n in previous_nodes] inputs = [(functions[n], t, c) for n, t, c in zip( previous_nodes, previous_t_idx, previous_classes)] for idx, (f1, t, c) in enumerate(inputs): f2 = o(_select_nominal(t.index(prob_idx)), f1) if len(c) < len(prob_classes): f2 = o(_pad_proba(c, prob_classes), f2) inputs[idx] = f2 f = partial(np.sum, axis=0) functions[node_name] = o(f, x(*inputs, return_type=np.array)) elif node.get("kind", None) == "vote": # Convert probabilistic votes to single prediction previous_node = [t[0] for t in g.in_edges(node_name)][0] f = partial(node["function"], classes=node["classes"]) functions[node_name] = o(f, functions[previous_node]) elif node.get("kind", None) == "merge": merge_idx = node["idx"] previous_nodes = [t[0] for t in g.in_edges(node_name)] previous_t_idx = [g.nodes()[n]["tgt"] for n in previous_nodes] inputs = [(functions[n], t) for n, t in zip(previous_nodes, previous_t_idx)] inputs = [ o(_select_numeric(t_idx.index(merge_idx)), f) for f, t_idx in inputs ] inputs = o(np.transpose, x(*inputs, return_type=np.array)) f = partial(np.mean, axis=1) functions[node_name] = o(f, inputs) return functions
def filter_dfg_on_activities_percentage(dfg0, start_activities0, end_activities0, activities_count0, percentage): """ Filters a DFG (complete, and so connected) on the specified percentage of activities (but ensuring that every node is still reachable from the start and to the end) Parameters ---------------- dfg0 (Complete, and so connected) DFG start_activities0 Start activities end_activities0 End activities activities_count0 Activities of the DFG along with their count percentage Percentage of activities Returns ---------------- dfg (Filtered) DFG start_activities (Filtered) start activities end_activities (Filtered) end activities activities_count (Filtered) activities of the DFG along with their count """ import networkx as nx # since the dictionaries/sets are modified, a deepcopy is the best option to ensure data integrity dfg = deepcopy(dfg0) start_activities = deepcopy(start_activities0) end_activities = deepcopy(end_activities0) activities_count = deepcopy(activities_count0) if len(activities_count) > 1 and len(dfg) > 1: activities_count_sorted_list = sorted( [(x, y) for x, y in activities_count.items()], key=lambda x: x[1], reverse=True) # retrieve the minimum list of activities to keep in the graph, according to the percentage min_set_activities_to_keep = set( x[0] for x in activities_count_sorted_list[:math.ceil( (len(activities_count) - 1) * percentage) + 1]) # retrieve the activities that can be possibly discarded, according to the percentage activities_to_possibly_discard = list( x[0] for x in activities_count_sorted_list[math.ceil( (len(activities_count) - 1) * percentage) + 1:]) activities_to_possibly_discard.reverse() # build a graph structure that helps in deciding whether the activities can be discarded safely graph, start_node, end_node = generate_nx_graph_from_dfg( dfg, start_activities, end_activities, activities_count) for act in activities_to_possibly_discard: new_graph = nx.DiGraph(graph) # try to remove the node new_graph.remove_node(act) # check whether all the activities to keep can be reached from the start and can reach the end reachable_from_start = set(nx.descendants(new_graph, start_node)) reachable_to_end = set(nx.ancestors(new_graph, end_node)) if min_set_activities_to_keep.issubset( reachable_from_start ) and min_set_activities_to_keep.issubset(reachable_to_end): # if that is the case, try to elaborate the new DFG (without the activity) new_dfg = { x: y for x, y in dfg.items() if x[0] != act and x[1] != act } # if that is still not empty ... if new_dfg: # ... then the activity can be safely removed dfg = new_dfg del activities_count[act] if act in start_activities: del start_activities[act] if act in end_activities: del end_activities[act] graph = new_graph # at the end of the previous step, some nodes may be remaining that are not reachable from the start # or cannot reach the end. obviously the previous steps ensured that at least the activities in min_set_activities_to_keep # are connected reachable_from_start = set(nx.descendants(graph, start_node)) reachable_to_end = set(nx.ancestors(graph, end_node)) reachable_start_end = reachable_from_start.intersection( reachable_to_end) activities_set = set(activities_count.keys()) non_reachable_activities = activities_set.difference( reachable_start_end) # remove these non reachable activities for act in non_reachable_activities: dfg = {x: y for x, y in dfg.items() if x[0] != act and x[1] != act} del activities_count[act] if act in start_activities: del start_activities[act] if act in end_activities: del end_activities[act] return dfg, start_activities, end_activities, activities_count
import sys with open('/home/demo/fyg-iota/branch_trunk/branch_link_MS8_1.json') as f1: branch_link = json.load(f1) with open('/home/demo/fyg-iota/branch_trunk/trunk_link_MS8_1.json') as f2: trunk_link = json.load(f2) with open('/home/demo/fyg-iota/sorted/MS8_1_sorted.json') as f: topology = json.load(f2) G = nx.DiGraph() for e in range(len(trunk_link)): a = trunk_link[e][0] b = trunk_link[e][1] G.add_edge(a, b) for e in range(len(branch_link)): a = branch_link[e][0] b = branch_link[e][1] G.add_edge(a, b) c_w = {} para = int(sys.argv[1]) for i in topology[((para - 1) * 10000):(para * 10000)]: n = len(list(nx.ancestors(G, i))) + 1 c_w[i] = n # print(c_w) with open( "/home/demo/fyg-iota/cw/MS8_1_cw_{i}_{j}.json".format( i=((para - 1) * 10000), j=(para * 10000)), "w+") as f: json.dump(c_w, f) print(para)
def _validate_data_health(self): r""" Check whether A and b are well-defined, i.e. doesn't contain nans. """ import networkx as nx from pandas import unique # Short-circuit subsequent checks if data are healthy if np.isfinite(self.A.data).all() and np.isfinite(self.b).all(): return True # Validate network topology health self._validate_topology_health() # Validate geometry health self._validate_geometry_health() # Fetch phase/geometries/physics prj = self.network.project phase = prj.find_phase(self) geometries = prj.geometries().values() physics = prj.physics().values() # Locate the root of NaNs unaccounted_nans = [] for geom, phys in zip(geometries, physics): objs = [phase, geom, phys] # Generate global dependency graph dg = nx.compose_all( [x.models.dependency_graph(deep=True) for x in objs]) d = {} # maps prop -> obj.name for obj in objs: for k, v in obj.check_data_health().items(): if "Has NaNs" in v: # FIXME: The next line doesn't cover multi-level props base_prop = ".".join(k.split(".")[:2]) if base_prop in dg.nodes: d[base_prop] = obj.name else: unaccounted_nans.append(base_prop) # Generate dependency subgraph for props with NaNs dg_nans = nx.subgraph(dg, d.keys()) # Find prop(s)/object(s) from which NaNs have propagated root_props = [n for n in d.keys() if not nx.ancestors(dg_nans, n)] root_objs = unique([d[x] for x in nx.topological_sort(dg_nans)]) # Throw error with helpful info on how to resolve the issue if root_props: raise Exception( r"Found NaNs in A matrix, possibly caused by NaNs in" f" {', '.join(root_props)}. The issue might get resolved if you call" r" regenerate_models on the following object(s):" f" {', '.join(root_objs)}") # Raise Exception for unaccounted properties if unaccounted_nans: raise Exception( r"Found NaNs in A matrix, possibly caused by NaNs in" f" {', '.join(unaccounted_nans)}.") # Raise Exception otherwise if root cannot be found raise Exception( "Found NaNs in A matrix but couldn't locate the root object(s) that might" " have caused it. It's likely that disabling caching of A matrix via" " alg.settings['cache_A'] = False after instantiating the algorithm object" " fixes the problem.")
def count_orbits(g: nx.DiGraph) -> int: """ Count the total number of orbits in a graph. """ return sum([len(nx.ancestors(g, n)) for n in g.nodes])
def get_ancestors(graph, parent_id): ancestors = networkx.ancestors(graph, parent_id) return ancestors
def load_data(data_folder): url = "http://current.geneontology.org/ontology/go-basic.obo" graph = obonet.read_obo(url) for item in graph.nodes(): if item.startswith("GO:") and graph.nodes[item][ 'namespace'] == "biological_process": rec = graph.nodes[item] rec["_id"] = item rec['go'] = item if rec.get("is_a"): rec["parents"] = [ parent for parent in rec.pop("is_a") if parent.startswith("GO:") ] if rec.get("xref"): xrefs = defaultdict(set) for val in rec.get("xref"): if ":" in val: prefix, id = val.split(':', 1) if prefix in ["http", "https"]: continue if prefix.lower() in [ 'biocyc', 'kegg_pathway', 'kegg_reaction', 'metacyc', 'reactome' ]: xrefs[prefix.lower()].add(id) else: xrefs[prefix.lower()].add(val) for k, v in xrefs.items(): xrefs[k] = list(v) rec.pop("xref") rec["xrefs"] = dict(xrefs) rec["children"] = [ child for child in graph.predecessors(item) if child.startswith("GO:") ] rec["ancestors"] = [ ancestor for ancestor in nx.descendants(graph, item) if ancestor.startswith("GO:") ] rec["descendants"] = [ descendant for descendant in nx.ancestors(graph, item) if descendant.startswith("GO:") ] rec["synonym"] = get_synonyms(rec) if rec.get("created_by"): rec.pop("created_by") if rec.get("creation_date"): rec.pop("creation_date") if rec.get("relationship"): rels = {} for rel in rec.get("relationship"): predicate, val = rel.split(' ') prefix = val.split(':')[0] if predicate not in rels: rels[predicate] = defaultdict(set) if prefix.lower() not in rels[predicate]: rels[predicate][prefix.lower()].add(val) for m, n in rels.items(): for p, q in n.items(): n[p] = list(q) rels[m] = dict(n) rec.update(rels) rec.pop("relationship") yield rec
def ancestors(self, node): """Returns set of the ancestors of a node as DAGNodes.""" return nx.ancestors(self._multi_graph, node)
components_sizes = Counter() isolated_nodes = set() for component in nx.connected_components(unsuper): x = len(component) components_sizes[x] += 1 if x == 1: isolated_nodes.update(component) print('Connected components of the supergraph:') print(' size : number') for size, number in components_sizes.items(): print('{:>5} : {}'.format(size, number)) print('\n{} isolated diff expr genes'.format(len(isolated_nodes & diff_expressed))) giant_component_nodes = max(nx.connected_components(unsuper), key=len) giant_component = nx.subgraph(supergraph, giant_component_nodes) giant_component.graph['name'] = 'giant component' print('\nLooking in the giant component') # nodes with diff expr genes in this graph diff_expressed_nodes = giant_component_nodes & diff_expressed first_diff_ancestors = set() for node in diff_expressed_nodes: diff_ancestors = nx.ancestors(giant_component, node) & diff_expressed_nodes if not diff_ancestors: first_diff_ancestors.add(node) print(len(first_diff_ancestors), 'diff expr genes in the giant component without diff expr ancestors') first_freq = sorted(first_diff_ancestors, key=lambda x: freq_dict[x], reverse=True) print('Top {} genes:'.format(n_top_genes)) hsa_names.preload(*first_freq[:n_top_genes]) for gene in first_freq[:n_top_genes]: print('{:>4} : {:>7} : {}'.format(freq_dict[gene], gene, hsa_names[gene]))
def log_pnml_next_step_one_two(di_graph, rep): # number of time points and number of replicates _t = np.shape(rep)[1] _r = len(rep) # partition the nodes of di_graph into two classes, those without (wo) parents and # those with at least one parent _VwoP = [_v for _v in nx.nodes(di_graph) if nx.ancestors(di_graph, _v) == set([])] _VP = [_v for _v in nx.nodes(di_graph) if _v not in _VwoP] # compute (and show) likelihood contribution for nodes with out parents # _MLwithOutParents = math.pow(2.0*math.pi*math.e, # -0.5*_r*(_t-2)*len(_VwoP))/math.e _logMLwithOutParents = (-0.5 * _r * (_t-2) * len(_VwoP)) * math.log(2.0 * math.pi * math.e) - 1.0 # compute (and show) likelihood contribution for nodes with at least one parent # dict that will be used to precompute a number of values and matrices that are used # in the later computation _nodeInfo = {} for _child in _VP: # get all time data for child ('next step one-two' paradigm) _child_data = [_zzz[2:(_t-1), _child] for _zzz in rep] # get parents for child and all parent's time data ('next step one-two' paradigm) _Parents = [_parent for _parent in nx.ancestors(di_graph, _child) if di_graph.has_edge(_parent, _child)] _parent_data = [_zzz[0:(_t-3), _Parents] for _zzz in rep] # compute average of the parents data over the replicates (t-2) X Kc if _r == 1: __avg_parent_data = _parent_data[0] else: __avg_parent_data = (1/float(_r))*functools.reduce(lambda x, y: x+y, _parent_data) # There are a number of values that used in numerous places # in the calculation of the likelihood. Some of these are # fairly computationally expensive, hence the strategy is to # compute these once, store them in a data dictionary, and then # retrieve these precomputed values as needed in the final # calculation. For each of the "r" replicates there are three # such values that will be precomputed, and there is one final # value as well. Hence "3*r+1" values will be stored in the # data store "nodeInfo". # For specific details see the closed form expression of # f(Q | DAG) on page 33 of Patton's master's thesis. # r pieces of data for child _holder = [np.dot(np.transpose(_v), _v) for _v in _child_data] # r pieces of data associated with parents of child _holder.extend([np.dot(np.transpose(_v), _v) for _v in _parent_data]) # r pieces of data associated with child and parents _holder.extend([np.dot(np.transpose(_v), _w) for _v, _w in zip(_parent_data, _child_data)]) # averages over all replicates of parents _holder.append(np.dot(np.transpose(__avg_parent_data), __avg_parent_data)) # put in dictionary _nodeInfo[_child] = _holder # Compute the Patton-Norris marginal likelihood using # all the precomputed pieces. For the details of this # computations go see Kris Patton's thesis, page 33. # This computation will use the precomputed values # stored in nodeInfo. # initialize floating point working variables # _answer1 = _answer2 = _answer3 = 1.0 _loganswer1 = _loganswer2 = _loganswer3 = 0.0 for _val in _nodeInfo.values(): # product across all nodeInfo's of inner products # of avg. of parents raised to "r/2" power # _answer1 *= math.pow(np.linalg.det(_val[3*_r]),0.5*_r) _loganswer1 += 0.5 * _r * math.log(np.linalg.det(_val[3 * _r])) # product across all nodeInfo's of square root of # determinant of the sum of inner product of parents and # inner product of avg. of parents for _index in range(_r, 2*_r): # _answer2 *= math.sqrt( # np.linalg.det(_val[_index]+_val[3*_r])) _loganswer2 += 0.5 * math.log(np.linalg.det(_val[_index] + _val[3 * _r])) # xxxtemp is the sum across all replicates, initially a "1" # xxxtemp is the sum of the: _xxxtemp = 1.0 for _index in range(0, _r): _xxxtemp += \ _val[_index] - \ np.transpose(_val[2*_r+_index]) * \ np.transpose(np.linalg.inv( (_val[_r+_index]+_val[3*_r]))) * \ _val[2*_r+_index] # xxxtemp raised to (-r*t+1)/2 power # _answer3 *= math.pow(_xxxtemp, -(_r*(_t-2)+1)/2.0) _loganswer3 += -(_r * (_t-2) + 1) * 0.5 * math.log(_xxxtemp) # putting all the three major pieces together # _MLwithParents = _answer3*_answer1/_answer2 _logMLwithParents = _loganswer3 + _loganswer1 - _loganswer2 # coef of the likelihood term # _MLcoef = math.pow(math.pi, -_r*(_t-2)*len(_VP)/2.0) * math.pow(math.gamma((_r*(_t-2)+1)/2.0), len(_VP)) _logMLcoef = -0.5 * _r * (_t-2) * len(_VP) * math.log(math.pi) + \ len(_VP) * math.log(math.gamma((_r * (_t-2) + 1) / 2.0)) # return _MLcoef*_MLwithParents*_MLwithOutParents return _logMLcoef + _logMLwithParents + _logMLwithOutParents
recipe_files = find_recipe_files(base) pkg_recipes, pkg_versions, pkg_requirements, pkg_outputs = \ load_recipes(recipe_files) root_node = "root" graph = nx.DiGraph(directed=True) graph = build_dependency_graph(graph, root_node, pkg_versions, pkg_requirements, pkg_outputs) if args.package and args.version: # Print a subgraph for a specific package and version pv = (args.package, parse(args.version)) if pv in graph: for node in nx.topological_sort( nx.subgraph(graph, nx.ancestors(graph, pv))): print_node(node, pkg_recipes) else: raise ValueError("Package {} version {} is not present " "in the graph".format(pv[0], pv[1])) elif (args.changes): # Print a unified list of all changed recipes changed_recipes = find_changed_recipe_files(base, args.changes) cpkg_recipes, cpkg_versions, cpkg_requirements, cpkg_outputs = \ load_recipes(changed_recipes) for cpkg, cversions in cpkg_versions.items(): for cversion in cversions: pv = (cpkg, cversion) if pv in graph: print_node(pv, pkg_recipes) else:
def gtd_logic(df, G, label_column, column_prefix, progress=True): """Greedy Top Down algorithm to select most relevant nodes in a Graph based on Gain Ratio. Args: df (pd.DataFrame): DataFrame. G (nx.DirectedGraph): Directed Graph containing the hierarchy. label_column (str): Name of the label column. column_prefix (str): Prefix of the columns generated by the generator (e.g. "new_link_type_"). progress (bool, optional): If True, progress bars will be shown to inform the user about the progress made by the process. Defaults to True. Returns: set: Set of nodes (as strings) that are deemed most relevant by the algorithm. """ # Create a collection of shortest Paths from the root node (VRN) to each of the leave nodes P = find_shortest_paths(G, progress=progress) # Calculate the Gain Ratio for each feature in the data (therby for each node in the Graph) gr_values = calc_gr(df, label_column, progress=progress) SF = set() # Create a dictionary to keep track of the availability of nodes node_availability = {} for node in list(G.nodes()): node_availability[node] = True if progress: iterator = tqdm( P, desc="Greedy Top Down - (3/3) Finding most relevant nodes.") else: iterator = P for path in iterator: # Get candidate_nodes for that path. Candidate Nodes are nodes that are part of the path AND are still available candidate_nodes = [ node for node in path if node_availability[node] == True ] # Check if there are any available nodes in the path. if len(candidate_nodes) > 0: # Get the Candidate Node with the highest Gain Ratio max_node = get_max_node(candidate_nodes, gr_values, column_prefix) # Add that Node to the Selected Features Set (SF) SF.add(max_node) # Change the availability of that node, it's ancestors and it's descendants to False node_availability[max_node] = False for ancestor in nx.ancestors(G, max_node): node_availability[ancestor] = False for decendant in nx.descendants(G, max_node): node_availability[decendant] = False # Check if there are any available nodes left. If not, end the algorithm. if all(availability == False for availability in node_availability.values()): break return SF
def calculateDisSim(DAG, output_file, disease_genes = None): ''' :param DAG: 二维list,表示一个有向无循环图 :param output_file: str,表示结果的存储路径 :param disease_genes: 二维list,表示disease-gene associations :return: ''' begin_time = time.clock() diseases = NetUtil.getNodes2HomoNet(DAG) disease_DAG = nx.DiGraph() disease_DAG.add_edges_from(DAG) # ---------------------------------------------------------------------------- IC = defaultdict() if disease_genes: diseases_asso, genes = NetUtil.getNodes2HeterNet(disease_genes) disease2genes = common.list2DictSet(disease_genes, key= 1, value= 2) for di in diseases: if di in diseases_asso: IC[di] = - math.log2(float(len(disease2genes[di])) / len(genes)) else: IC[di] = 0 diseases = diseases & diseases_asso print("there are {} diseases for similarity based on DAG and associations.".format(len(diseases))) else: for di in diseases: descendants = nx.ancestors(disease_DAG, di) if descendants: IC[di] = - math.log2(float(len(descendants))/len(diseases)) print("there are {} diseases for similarity based on DAG.".format(len(diseases))) # -------------------------------------------------------------------------------- print("begin to calculate disease similarity......") diseases = list(diseases) simi_matrix = np.zeros((len(diseases), len(diseases))) for i in range(0, len(diseases)): sys.stdout.flush() temp_time1 = time.clock() di_A = diseases[i] for j in range(i + 1, len(diseases)): di_B = diseases[j] commonAncestors = getCommonAncesters(disease_DAG, di_A, di_B) sectionOfDoId2Gene = getSectionoFromDic(commonAncestors, IC) newDic = sorted(sectionOfDoId2Gene.items(), key=lambda x: x[1], reverse=True) if newDic: simi_matrix[i][j] = newDic[0][1] temp_time2 = time.clock() sys.stdout.write('\r{} -> {}, {}s'.format(i, diseases[i], (temp_time2 - temp_time1))) print() # --------------------------------------------------------------------------------------------- Resnik_simi = {} for i in range(0, len(diseases)): di_A = diseases[i] for j in range(i + 1, len(diseases)): di_B = diseases[j] if simi_matrix[i][j] > 0: Resnik_simi["{}\t{}".format( di_A, di_B)] = simi_matrix[i][j] Resnik_simi = common.normalizeDict(Resnik_simi) FileUtil.writeDic2File(Resnik_simi, output_file) end_time = time.clock() print("ResnikSim costs {}s.".format(end_time - begin_time)) pass
return graph questions = input_file.read().splitlines() data = data_csv.read().splitlines() graph = nx.DiGraph() graph = populate_nx_graph(graph, data) for j in questions: j_list = j.split('(') child = j_list[1].split(')')[0] if j_list[0] == 'ancestors': # Return all nodes reachable from \(source\) in G. ancestors = nx.descendants(graph, child) x_list = list(ancestors) x_list.sort() output_file.write('{}\n'.format(str_from_list(x_list, child))) if j_list[0] == 'descendants': # Return all nodes having a path to \(source\) in G. descendants = nx.ancestors(graph, child) x_list = list(descendants) x_list.sort() output_file.write('{}\n'.format(str_from_list(x_list, child))) output_file.close() input_file.close()
def main(): parser = argparse.ArgumentParser() parser.add_argument('-p', '--prefix', default=None) parser.add_argument('-n', '--name', default=None) parser.add_argument('-v', '--version', action='version', version='%(prog)s ' + __version__) parser.add_argument('-r', '--recursive', help='show dependencies of dependencies', default=False, action='store_true') subparser = parser.add_subparsers(dest='subcmd') subparser.add_parser('leaves', help='shows leaf packages') subparser.add_parser('cycles', help='shows dependency cycles') p = subparser.add_parser( 'whoneeds', help='shows packages that depends on this package') p.add_argument('package', help='the target package') p = subparser.add_parser('depends', help='shows this package dependencies') p.add_argument('package', help='the target package') args = parser.parse_args() if args.name is not None: # Allow user to specify name, but check the environment for an # existing CONDA_EXE command. This allows a different conda # package to be installed (and imported above) but will # resolve the name using their expected conda. (The imported # conda here will find the environments, but might not name # them as the user expects.) _conda = os.environ.get('CONDA_EXE', 'conda') _info = json.loads( subprocess.check_output([_conda, 'info', '-e', '--json'])) args.prefix = conda.base.context.locate_prefix_by_name( name=args.name, envs_dirs=_info['envs_dirs']) if args.prefix is None: args.prefix = sys.prefix l = get_local_cache(args.prefix) g = make_cache_graph(l) if args.subcmd == 'cycles': for i in networkx.simple_cycles(g): print(" -> ".join(i) + " -> " + i[0]) elif args.subcmd == 'depends': if args.package not in g: print("warning: package \"%s\" not found" % (args.package), file=sys.stderr) if args.recursive: e = list(networkx.descendants(g, args.package)) else: e = list(map(lambda i: i[1], g.out_edges(args.package))) print(e) elif args.subcmd == 'whoneeds': if args.package not in g: print("warning: package \"%s\" not found" % (args.package), file=sys.stderr) if args.recursive: e = list(networkx.ancestors(g, args.package)) else: e = list(map(lambda i: i[0], g.in_edges(args.package))) print(e) elif args.subcmd == 'leaves': e = list( map(lambda i: i[0], (filter(lambda i: i[1] == 0, g.in_degree())))) print(e) else: parser.print_help() sys.exit(1)
def sjiang(self, pairids, **kwargs): """Using any variant of J&C-based approach to compute the ontology concept similarity scores Arguments: As in the sresnik method above and addition jv described above jv = 0 for the Resnik-based normalization = 1 for the Couto-based normalization = 2 for the Leacock & Chodorow normalization = 3 for the Garla & Brandt normalization = 4 for the Rada normalization = 5 for the canonical normalization """ # Setting potential parameters for concept similarity: approach and correct fact kwargs = kwargs app = kwargs['app'] if 'app' in kwargs else 'universal' cf = kwargs['cf'] if 'cf' in kwargs else 0 gr = kwargs['gr'] if 'gr' in kwargs else 0 jv = kwargs['jv'] if 'jv' in kwargs else 0 # Setting potential parameters for information content if not 'sigma' in kwargs: kwargs['sigma'] = 0.5 if not 'TermStats' in kwargs: kwargs['TermStats'] = {} if not 'TermIC' in kwargs: kwargs['TermIC'] = {} if not app in self.AppScores: self.getIC(approach = app, **kwargs) if app != 'wang': ics = self.AppScores[app] else: ics = dict([(t, sum(self.AppScores[app][t].values())) for t in self.AppScores[app]]) data = {}; icmax = max(ics.values()) if jv==0: # Resnik-based normalization for p, q in pairids: if p==q: data[(p, q)] = 1.0 else: panc = nx.ancestors(self.DagStr, p); panc.add(p) qanc = nx.ancestors(self.DagStr, q); qanc.add(q) canc = panc & qanc; canc.discard(self.oroot) if gr: canc = [a for a in canc if set(self.DagStr[a])&((panc|qanc)-canc)] icanc = [ics[t] for t in canc] vic = self.cfact(icanc, cf) if icanc else 0.0 jcnn = ics[p]+ics[q]-2*abs(max(icanc)) if icanc else 1.0 data[(p,q)] = vic*(1.0 - jcnn/(2*icmax)) if jcnn != 1.0 else 0.0 elif jv==1: # Couto-based normalization for p, q in pairids: if p==q: data[(p, q)] = 1.0 else: panc = nx.ancestors(self.DagStr, p); panc.add(p) qanc = nx.ancestors(self.DagStr, q); qanc.add(q) canc = panc & qanc; canc.discard(self.oroot) if gr: canc = [a for a in canc if set(self.DagStr[a])&((panc|qanc)-canc)] icanc = [ics[t] for t in canc] vic = self.cfact(icanc, cf) if icanc else 0.0 jcnn = ics[p]+ics[q]-2*abs(max(icanc)) if icanc else 1.0 data[(p,q)] = vic*(1.0 - min(1,jcnn/icmax)) if jcnn != 1.0 else 0.0 elif jv==2: # Leacock & Chodorow normalization for p, q in pairids: if p==q: data[(p, q)] = 1.0 else: panc = nx.ancestors(self.DagStr, p); panc.add(p) qanc = nx.ancestors(self.DagStr, q); qanc.add(q) canc = panc & qanc; canc.discard(self.oroot) if gr: canc = [a for a in canc if set(self.DagStr[a])&((panc|qanc)-canc)] icanc = [ics[t] for t in canc] vic = self.cfact(icanc, cf) if icanc else 0.0 jcnn = ics[p]+ics[q]-2*abs(max(icanc)) if icanc else 1.0 data[(p,q)] = vic*(1.0 - log(jcnn+1)/log(icmax)) elif jv==3: # Garla & Brandt normalization for p, q in pairids: if p==q: data[(p, q)] = 1.0 else: panc = nx.ancestors(self.DagStr, p); panc.add(p) qanc = nx.ancestors(self.DagStr, q); qanc.add(q) canc = panc & qanc; canc.discard(self.oroot) if gr: canc = [a for a in canc if set(self.DagStr[a])&((panc|qanc)-canc)] icanc = [ics[t] for t in canc] vic = self.cfact(icanc, cf) if icanc else 0.0 jcnn = ics[p]+ics[q]-2*abs(max(icanc)) if icanc else 1.0 data[(p,q)] = vic*(1.0 - log(jcnn+1)/log(icmax+1)) elif jv==4: # Rada normalization for p, q in pairids: if p==q: data[(p, q)] = 1.0 else: panc = nx.ancestors(self.DagStr, p); panc.add(p) qanc = nx.ancestors(self.DagStr, q); qanc.add(q) canc = panc & qanc; canc.discard(self.oroot) if gr: canc = [a for a in canc if set(self.DagStr[a])&((panc|qanc)-canc)] icanc = [ics[t] for t in canc] vic = self.cfact(icanc, cf) if icanc else 0.0 jcnn = ics[p]+ics[q]-2*abs(max(icanc)) if icanc else 1.0 data[(p,q)] = vic/(1.0 + jcnn) else: # canonical normalization for p, q in pairids: if p==q: data[(p, q)] = 1.0 else: panc = nx.ancestors(self.DagStr, p); panc.add(p) qanc = nx.ancestors(self.DagStr, q); qanc.add(q) canc = panc & qanc; canc.discard(self.oroot) if gr: canc = [a for a in canc if set(self.DagStr[a])&((panc|qanc)-canc)] icanc = [ics[t] for t in canc] vic = self.cfact(icanc, cf) if icanc else 0.0 jcnn = ics[p]+ics[q]-2*abs(max(icanc)) if icanc else 1.0 data[(p,q)] = vic*(1.0 - log(jcnn+1)/log(ics[p]+ics[q]+1)) return data
def descendants(self, identifier: str) -> Set[str]: """Return a set of identifiers for the children of the given identifier.""" return nx.ancestors(self.hierarchy, identifier) # note this is backwards
def ancestors(self, include_self=False): x = nx.ancestors(self.workflow.task_graph(), self) if include_self: return sorted({self}.union(x), key=lambda task: task.stage.number) else: return x
#!/usr/bin/env python3 import networkx as nx g = nx.DiGraph() for bag, _, content in [ line.rstrip(".\n").partition(" contain ") for line in open("./input") ]: bag = bag.replace(" bags", "").replace(" ", "") g.add_node(bag) content = content.replace("no other bags", "") for seg in [s.split(" ") for s in content.split(", ") if len(s) != 0]: count = int(seg[0]) subbag = seg[1] + seg[2] g.add_edge(bag, subbag, weight=count) def count_subbags(g, source): total = 0 for nbr in g.neighbors(source): total += g[source][nbr]["weight"] * (1 + count_subbags(g, nbr)) return total print("Part 1: ", len(nx.ancestors(g, "shinygold")), sep="") print("Part 2: ", count_subbags(g, "shinygold"), sep="")
def get_ancestors(graph, status): return ancestors(graph, status)
def SuccessorsInSubgraph(self, N, M): return ListIntersection(self.successors(N), [M] + list(nx.ancestors(self, M)))
def load_meta_info(experiment, meta_file): """If we get a meta_file, modify the configurations accordingly. Enable only instruments on the graph that connect the relevant *ReceiverChannel* objects to *Writer* or *Plotter* objects.""" calibration = experiment.calibration save_data = experiment.save_data # Create a mapping from qubits to data writers and inverse qubit_to_writer = {} writer_to_qubit = {} qubit_to_stream_sel = {} stream_sel_to_qubit = {} # shortcuts instruments = experiment.settings['instruments'] filters = experiment.settings['filters'] qubits = experiment.settings['qubits'] if 'sweeps' in experiment.settings: sweeps = experiment.settings['sweeps'] # Use the meta info to modify the parameters # loaded from the human-friendly yaml configuration. with open(meta_file, 'r') as FID: meta_info = json.load(FID) # Construct a graph of all instruments in order to properly enabled those # associated with the meta_file. We only need to use string representations # here, not actual filter and instrument objects. # Strip any spaces, since we only care about the general flow, and not any # named connectors. def strip_conn_name(text): val_list = [] # multiple sourcs are separated by commas all_vals = text.strip().split(',') for vals in all_vals: val = vals.strip().split() if len(val) == 0: raise ValueError( "Please disable filters with missing source.") elif len(val) > 2: raise ValueError( "Spaces are reserved to separate filters and connectors. Please rename {}." .format(vals)) val_list.append(val[0]) return val_list # Graph edges for the measurement filters # switch stream selector to raw (by default) before building the graph if experiment.__class__.__name__ == "SingleShotFidelityExperiment": receivers = [s for s in meta_info['receivers'].items()] if len(receivers) > 1: raise NotImplementedError( "Single shot fidelity for more than one qubit is not yet implemented." ) stream_sel_name_orig = receivers[0][0].replace('RecvChan-', '') stream_selectors = [ k for k, v in filters.items() if "AlazarStreamSelector" in v["type"] and v["source"] == filters[stream_sel_name_orig]['source'] ] stream_selectors += [k for k,v in filters.items() if v["type"] == 'X6StreamSelector' and v["source"] == filters[stream_sel_name_orig]['source']\ and v['channel'] == filters[stream_sel_name_orig]['channel'] and np.mod(v["dsp_channel"]-1,5)+1 == np.mod(filters[stream_sel_name_orig]["dsp_channel"]-1,5)+1] for s in stream_selectors: if filters[s]['stream_type'] == experiment.ss_stream_type: filters[s]['enabled'] = True stream_sel_name = s else: filters[s]['enabled'] = False edges = [[(s, k) for s in strip_conn_name(v["source"])] for k, v in filters.items() if ("enabled" not in v.keys()) or v["enabled"]] edges = [e for edge in edges for e in edge] dag = nx.DiGraph() dag.add_edges_from(edges) inst_to_enable = [] filt_to_enable = set() # Find any writer endpoints of the receiver channels for receiver_name, num_segments in meta_info['receivers'].items(): # Receiver channel name format: RecvChan-StreamSelectorName if not experiment.__class__.__name__ == "SingleShotFidelityExperiment": stream_sel_name = receiver_name.replace('RecvChan-', '') stream_sel_name_orig = stream_sel_name dig_name = filters[stream_sel_name]['source'] chan_name = filters[stream_sel_name]['channel'] if experiment.repeats is not None: num_segments *= experiment.repeats # Set the correct number of segments for the digitizer instruments[dig_name]['nbr_segments'] = num_segments # Enable the digitizer inst_to_enable.append(dig_name) # Find the enabled X6 stream selectors with the same channel as the receiver. Allow to plot/save raw/demod/int streams belonging to the same receiver if calibration: stream_selectors = [] else: stream_selectors = [ k for k, v in filters.items() if ("AlazarStreamSelector" in v["type"]) and ( v["source"] == filters[stream_sel_name_orig]['source']) ] stream_selectors += [ k for k, v in filters.items() if (v["type"] == 'X6StreamSelector' and v["source"] == filters[stream_sel_name]['source'] and v["enabled"] == True and v["channel"] == filters[stream_sel_name]["channel"] and (np.mod(v["dsp_channel"] - 1, 5) + 1 == np.mod( filters[stream_sel_name_orig]["dsp_channel"] - 1, 5) + 1 or v["dsp_channel"] > 5)) ] # Enable the tree for single-shot fidelity experiment. Change stream_sel_name to raw (by default) writers = [] plotters = [] singleshot = [] buffers = [] def check_endpoint(endpoint_name, endpoint_type): source_type = filters[filters[endpoint_name]['source'].split( ' ')[0]]['type'] return filters[endpoint_name]['type'] == endpoint_type and ( not hasattr(filters[endpoint_name], 'enabled') or filters[endpoint_name]['enabled'] ) and not (calibration and source_type == 'Correlator') and ( not source_type == 'SingleShotMeasurement' or experiment.__class__.__name__ == 'SingleShotFidelityExperiment') for filt_name, filt in filters.items(): if filt['enabled'] == False: continue if filt_name in [stream_sel_name] + stream_selectors: # Find descendants of the channel selector chan_descendants = nx.descendants(dag, filt_name) # Find endpoints within the descendants endpoints = [ n for n in chan_descendants if dag.in_degree(n) == 1 and dag.out_degree(n) == 0 ] # Find endpoints which are enabled writers, plotters or singleshot filters without an output. Disable outputs of single-shot filters when not used. writers += [ e for e in endpoints if check_endpoint(e, "WriteToHDF5") ] plotters += [ e for e in endpoints if check_endpoint(e, "Plotter") ] buffers += [ e for e in endpoints if check_endpoint(e, "DataBuffer") ] singleshot += [ e for e in endpoints if check_endpoint(e, "SingleShotMeasurement") and experiment.__class__.__name__ == "SingleShotFidelityExperiment" ] filt_to_enable.update(set().union(writers, plotters, singleshot, buffers, stream_selectors)) if calibration: # For calibrations the user should only have one writer enabled, otherwise we will be confused. if len(writers) > 1: raise Exception( "More than one viable data writer was found for a receiver channel {}. Please enable only one!" .format(receiver_name)) if len(writers) == 0 and len(plotters) == 0 and len( singleshot) == 0 and len(buffers) == 0: raise Exception( "No viable data writer, plotter or single-shot filter was found for receiver channel {}. Please enable one!" .format(receiver_name)) if writers and not save_data: # If we are calibrating we don't care about storing data, use buffers instead buffers = [] for w in writers: source_filt = filters[w]["source"].split(" ")[0] if filters[source_filt]["type"] == "Averager": sources = ", ".join([ source_filt + " final_average", source_filt + " final_variance" ]) else: sources = filters[w]["source"] buff = { "source": sources, "enabled": True, "type": "DataBuffer", } # Remove the writer filters.pop(w) # Substitute the buffer filters[w] = buff # Store buffer name for local use buffers.append(w) writers = buffers # For now we assume a single qubit, not a big change for multiple qubits qubit_name = next( k for k, v in qubits.items() if v["measure"]["receiver"] in (stream_sel_name, stream_sel_name_orig)) if calibration: if len(writers) == 1: qubit_to_writer[qubit_name] = writers[0] else: qubit_to_writer[qubit_name] = writers writer_ancestors = [] plotter_ancestors = [] singleshot_ancestors = [] buffer_ancestors = [] # Trace back our ancestors, using plotters if no writers are available if writers: writer_ancestors = set().union( *[nx.ancestors(dag, wr) for wr in writers]) if plotters: plotter_ancestors = set().union( *[nx.ancestors(dag, pl) for pl in plotters]) if singleshot: singleshot_ancestors = set().union( *[nx.ancestors(dag, ss) for ss in singleshot]) if buffers: buffer_ancestors = set().union( *[nx.ancestors(dag, bf) for bf in buffers]) filt_to_enable.update(set().union(writer_ancestors, plotter_ancestors, singleshot_ancestors, buffer_ancestors)) # remove all the digitizers, which are already taken care of filt_to_enable.difference_update( [f for f in filt_to_enable if dag.in_degree()[f] == 0]) if calibration: # One to one writers to qubits writer_to_qubit = {v: [k] for k, v in qubit_to_writer.items()} else: # Many to one writers to qubits or viceversa writer_to_qubit = {} for q, ws in qubit_to_writer.items(): for w in ws: if w not in writer_to_qubit: writer_to_qubit[w] = [] writer_to_qubit[w].append(q) # Disable digitizers and APSs and then build ourself back up with the relevant nodes for instr_name in instruments.keys(): if 'tx_channels' in instruments[instr_name].keys( ) or 'rx_channels' in instruments[instr_name].keys(): instruments[instr_name]['enabled'] = False for instr_name in inst_to_enable: instruments[instr_name]['enabled'] = True for meas_name in filters.keys(): filters[meas_name]['enabled'] = False for meas_name in filt_to_enable: filters[meas_name]['enabled'] = True #label measurement with qubit name (assuming the convention "M-"+qubit_name) for meas_name in filt_to_enable: if filters[meas_name]["type"] == "WriteToHDF5": filters[meas_name]['groupname'] = ''.join(writer_to_qubit[meas_name]) \ + "-" + filters[meas_name]['groupname'] for instr_name, chan_data in meta_info['instruments'].items(): instruments[instr_name]['enabled'] = True if isinstance(chan_data, str): instruments[instr_name][ 'seq_file'] = chan_data # Per-instrument seq file elif isinstance(chan_data, dict): for chan_name, seq_file in chan_data.items(): if "tx_channels" in instruments[ instr_name] and chan_name in instruments[ instr_name]["tx_channels"].keys(): instruments[instr_name]["tx_channels"][chan_name][ 'seq_file'] = seq_file elif "rx_channels" in instruments[ instr_name] and chan_name in instruments[ instr_name]["rx_channels"].keys(): instruments[instr_name]["rx_channels"][chan_name][ 'seq_file'] = seq_file else: raise ValueError( "Could not find channel {} in of instrument {}.". format(chan_name, instr_name)) # Now we will construct the DataAxis from the meta_info desc = meta_info["axis_descriptor"] data_axis = desc[0] # Data will always be the first axis if experiment.repeats is not None: #ovverride data axis with repeated number of segments data_axis['points'] = np.tile(data_axis['points'], experiment.repeats) # Search for calibration axis, i.e., metadata axis_names = [d['name'] for d in desc] if 'calibration' in axis_names: meta_axis = desc[axis_names.index('calibration')] # There should be metadata for each cal describing what it is if len(desc) > 1: metadata = ['data'] * len( data_axis['points']) + meta_axis['points'] # Pad the data axis with dummy equidistant x-points for the extra calibration points avg_step = (data_axis['points'][-1] - data_axis['points'][0] ) / (len(data_axis['points']) - 1) points = np.append( data_axis['points'], data_axis['points'][-1] + (np.arange(len(meta_axis['points'])) + 1) * avg_step) else: metadata = meta_axis[ 'points'] # data may consist of calibration points only points = np.arange( len(metadata)) # dummy axis for plotting purposes # If there's only one segment we can ignore this axis if len(points) > 1: experiment.segment_axis = DataAxis(data_axis['name'], points, unit=data_axis['unit'], metadata=metadata) else: if len(data_axis['points']) > 1: experiment.segment_axis = DataAxis(data_axis['name'], data_axis['points'], unit=data_axis['unit']) experiment.qubit_to_writer = qubit_to_writer experiment.writer_to_qubit = writer_to_qubit
def can_be_raised_up(self, nod, g): anc = nx.ancestors(g, nod) if any(x in anc for x in self.Nodes): return False else: return True
def get_ancestors(self, node_name, new_graph=None): if new_graph is None: graph = self._graph else: graph = new_graph return set(nx.ancestors(graph, node_name))
def contains_shiny_gold_bag(self): return len(nx.ancestors(self.bag_graph, 'shiny gold'))
def _ResolutionOrder(self, variables_to_solve): """ return a list of lists of tuples (block,output,ndof) to be solved """ # Gp=nx.DiGraph() # # for i in range(nvar): # Gp.add_node('v'+str(i),bipartite=0) # # for i in range(neq): # Gp.add_node('e'+str(i),bipartite=1) # for j in range(nvar): # if Mo[i,j]==1: # Gp.add_edge('e'+str(i),'v'+str(j)) Gp = nx.DiGraph() for variable in self.variables: Gp.add_node(variable, bipartite=0) for block in self.blocks: for iov, output_variable in enumerate(block.outputs): Gp.add_node((block, iov), bipartite=1) Gp.add_edge((block, iov), output_variable) Gp.add_edge(output_variable, (block, iov)) for input_variable in block.inputs: if not isinstance(input_variable, Signal): Gp.add_edge(input_variable, (block, iov)) # for n1,n2 in M.items(): # Gp.add_edge(n1,n2) sinks = [] sources = [] for node in Gp.nodes(): if Gp.out_degree(node) == 0: sinks.append(node) elif Gp.in_degree(node) == 0: sources.append(node) G2 = sources[:] for node in sources: for node2 in nx.descendants(Gp, node): if node2 not in G2: G2.append(node2) if G2 != []: print(G2) raise ModelError('Overconstrained variables') G3 = sinks[:] for node in sinks: for node2 in nx.ancestors(Gp, node): if node2 not in G3: G3.append(node2) if G3 != []: raise ModelError('Underconstrained variables') # vars_resolvables=[] # for var in vars_resoudre: # if not 'v'+str(var) in G2+G3: # vars_resolvables.append(var) # G1=Gp.copy() # G1.remove_nodes_from(G2+G3) # # M1=nx.bipartite.maximum_matching(G1) # G1p=nx.DiGraph() # # G1p.add_nodes_from(G1.nodes()) # for e in G1.edges(): # # equation vers variable # if e[0][0]=='v': # G1p.add_edge(e[0],e[1]) # else: # G1p.add_edge(e[1],e[0]) # # print(len(M)) # for n1,n2 in M1.items(): # # print(n1,n2) # if n1[0]=='e': # G1p.add_edge(n1,n2) # else: # G1p.add_edge(n2,n1) scc = list(nx.strongly_connected_components(Gp)) # pos=nx.spring_layout(G1p) # plt.figure() # nx.draw(G1p,pos) # nx.draw_networkx_labels(G1p,pos) # print(scc) if scc != []: C = nx.condensation(Gp, scc) isc_vars = [] for isc, sc in enumerate(scc): for var in variables_to_solve: if var in sc: isc_vars.append(isc) break ancestors_vars = isc_vars[:] for isc_var in isc_vars: for ancetre in nx.ancestors(C, isc_var): if ancetre not in ancestors_vars: ancestors_vars.append(ancetre) order_sc = [sc for sc in nx.topological_sort( C) if sc in ancestors_vars] order_ev = [] for isc in order_sc: # liste d'équations et de variables triées pour être séparées evs = list(scc[isc]) # print(evs) # levs=int(len(evs)/2) eqs = [] var = [] for element in evs: if type(element) == tuple: eqs.append(element) else: var.append(element) order_ev.append((len(eqs), eqs, var)) return order_ev raise ModelError
def ancestors(self, task): for anc in nx.ancestors(self.G, task.name): yield self.workflow[anc]
def digraph2condensationgraph(digraph: networkx.DiGraph) -> networkx.DiGraph: """ Creates the condensation graph from *digraph*. The condensation graph is similar to the SCC graph but it replaces cascades between SCCs by single edges. Its nodes are therefore non-trivial SCCs or inputs. As for the SCC graph, nodes are tuples of names that belong to the SCC. The condensation graph is cycle-free and does distinguish between inputs and constants. The graph has no additional data. **arguments**: * *digraph*: directed graph **returns**: * *condensation_graph*: the condensation graph **example**:: >>> cgraph = digraph2condensationgraph(igraph) >>> cgraph.nodes() [('Ash1', 'Cbf1'), ('Gal4',), ('Gal80',), ('Cbf1','Swi5)] >>> cgraph.edges() [(('Gal4',), ('Ash1', 'Cbf1')), (('Gal4',), ('Gal80',)), (('Gal80',),('Cbf1','Swi5))] """ sccs = sorted([ tuple(sorted(scc)) for scc in networkx.strongly_connected_components(digraph) ]) cascades = [ scc for scc in sccs if (len(scc) == 1) and not digraph.has_edge(scc[0], scc[0]) ] noncascades = [scc for scc in sccs if scc not in cascades] cgraph = networkx.DiGraph() cgraph.add_nodes_from(noncascades) # rgraph is a copy of Digraph with edges leaving noncascade components removed. # will use rgraph to decide if there is a cascade path between U and W (i.e. edge in cgraph) rgraph = networkx.DiGraph(digraph.edges()) for U, W in itertools.product(noncascades, noncascades): if U == W: continue rgraph = digraph.copy() for X in noncascades: if not X == U and not X == W: rgraph.remove_nodes_from(X) if has_path(rgraph, U, W): cgraph.add_edge(U, W) # annotate each node with its depth in the hierarchy and an integer ID for ID, target in enumerate(cgraph.nodes()): depth = 1 for source in networkx.ancestors(cgraph, target): for p in networkx.all_simple_paths(cgraph, source, target): depth = max(depth, len(p)) cgraph.nodes[target]["depth"] = depth cgraph.nodes[target]["id"] = ID return cgraph
map( lambda s: re.sub('\sbags*$', '', s), filter(lambda s: s and s != 'no other bags', re.split(r'(?:\scontain\s|,\s*|\.)', ln.strip())))) for ln in sys.stdin if ln.strip() ] graph = nx.DiGraph() for node, *edges in lns: print(node) graph.add_node(node) for edge in edges: print('\t|', edge) weight, color = edge.split(' ', 1) weight = int(weight) graph.add_edge(node, color, weight=weight) print(len(nx.ancestors(graph, 'shiny gold'))) def count_bags(node='shiny gold'): sum = 0 if node == 'shiny gold' else 1 for intern in graph.neighbors(node): m = graph[node][intern]['weight'] sum += m * count_bags(intern) return sum print(count_bags())