def find_paths(self, input_set, target, max_paths=1, max_path_length=5, loop=False): """Check for a source/target path in the model. Parameters ---------- input_set : list or None A list of potenital sources or None if the test statement subject is None. target : tuple Tuple representing the target node (usually common target node). max_paths : int The maximum number of specific paths to return. max_path_length : int The maximum length of specific paths to return. loop : bool Whether we are looking for a loop path. Returns ------- PathResult PathResult object indicating the results of the attempt to find a path. """ # # -- Route to the path sampling function -- # NOTE this is not generic at this point! # if self.do_sampling: # if not has_pg: # raise Exception('The paths_graph package could not be ' # 'imported.') # return self._sample_paths(input_set, obj, target_polarity, # max_paths, max_path_length) # -- Do Breadth-First Enumeration -- # Generate the predecessors to our observable and count the paths path_lengths = [] path_metrics = [] sources = [] for source, path_length in find_sources(self.graph, target, input_set): # Path already includes an edge from targets to common target, so # we need to subtract one edge. In case of loops, we are # already missing one edge, there's no need to subtract one more. if not loop: path_length = path_length - 1 # There might be a case when sources and targets contain the same # nodes (e.g. different agent state in PyBEL networks) that would # show up as paths of length 0. We only want to include meaningful # paths that contain at least one edge. if path_length > 0: pm = PathMetric(source, target, path_length) path_metrics.append(pm) path_lengths.append(path_length) # Keep unique sources but use a list (not set) to preserve order if source not in sources: sources.append(source) # Now, look for paths if path_metrics and max_paths == 0: pr = PathResult(True, 'MAX_PATHS_ZERO', max_paths, max_path_length) pr.path_metrics = path_metrics return pr elif path_metrics: if min(path_lengths) <= max_path_length: if not loop: search_path_length = min(path_lengths) + 1 else: search_path_length = min(path_lengths) pr = PathResult(True, 'PATHS_FOUND', max_paths, max_path_length) pr.path_metrics = path_metrics # Get the first path # Try to find paths of fixed length using sources found above for source in sources: logger.info('Finding paths between %s and %s' % (str(source), target)) path_iter = get_path_iter(self.graph, source, target, search_path_length, loop) for path in path_iter: pr.add_path(tuple(path)) # Do not get next path if reached max_paths if len(pr.paths) >= max_paths: break # Do not check next source if reached max_paths if len(pr.paths) >= max_paths: break return pr # There are no paths shorter than the max path length, so we # don't bother trying to get them else: pr = PathResult(True, 'MAX_PATH_LENGTH_EXCEEDED', max_paths, max_path_length) pr.path_metrics = path_metrics return pr else: return PathResult(False, 'NO_PATHS_FOUND', max_paths, max_path_length)
def find_paths(self, subj, obj, max_paths=1, max_path_length=5, loop=False, filter_func=None, allow_direct=True): """Check for a source/target path in the model. Parameters ---------- subj : indra.explanation.model_checker.NodesContainer NodesContainer representing test statement subject. obj : indra.explanation.model_checker.NodesContainer NodesContainer representing test statement object. max_paths : int The maximum number of specific paths to return. max_path_length : int The maximum length of specific paths to return. loop : bool Whether we are looking for a loop path. filter_func : function or None A function to constrain the search. A function should take a node as a parameter and return True if the node is allowed to be in a path and False otherwise. If None, then no filtering is done. allow_direct : Optional[bool] Whether to allow direct path of length 1 (edge between source and target) to be returned as a result. Default: True. Returns ------- PathResult PathResult object indicating the results of the attempt to find a path. """ # # -- Route to the path sampling function -- # NOTE this is not generic at this point! # if self.do_sampling: # if not has_pg: # raise Exception('The paths_graph package could not be ' # 'imported.') # return self._sample_paths(input_set, obj, target_polarity, # max_paths, max_path_length) # -- Do Breadth-First Enumeration -- # Generate the predecessors to our observable and count the paths path_lengths = [] path_metrics = [] sources = [] if obj.common_target: target = obj.common_target dummy_target = True else: target = obj.all_nodes[0] dummy_target = False for source, path_length in find_sources(self.graph, target, subj.all_nodes, filter_func): # If a dummy target is used, we need to subtract one edge. # In case of loops, we are already missing one edge, there's no # need to subtract one more. if dummy_target and not loop: path_length = path_length - 1 # There might be a case when sources and targets contain the same # nodes (e.g. different agent state in PyBEL networks) that would # show up as paths of length 0. We only want to include meaningful # paths that contain at least one edge. if path_length > 0: pm = PathMetric(source, target, path_length) path_metrics.append(pm) path_lengths.append(path_length) # Keep unique sources but use a list, not set to preserve order if source not in sources: sources.append(source) # Now, look for paths if path_metrics and max_paths == 0: pr = PathResult(True, 'MAX_PATHS_ZERO', max_paths, max_path_length) pr.path_metrics = path_metrics return pr elif path_metrics: min_path_length = min(path_lengths) # If we don't want to get direct connections as paths, we need # to increase the desired path length to get paths with # intermediate nodes (if they exist) if not allow_direct and min_path_length == 1 and \ len(path_lengths) > 1: min_path_length = min([pl for pl in path_lengths if pl != 1]) if min_path_length <= max_path_length: if dummy_target and not loop: search_path_length = min_path_length + 1 else: search_path_length = min_path_length pr = PathResult(True, 'PATHS_FOUND', max_paths, max_path_length) pr.path_metrics = path_metrics # Get the first path # Try to find paths of fixed length using sources found above for source in sources: logger.info('Finding paths between %s and %s' % (str(source), target)) path_iter = get_path_iter(self.graph, source, target, search_path_length, loop, dummy_target, filter_func) for path in path_iter: # Check if the path starts with a refinement if subj.is_ref(path[0]): path.insert( 0, self.get_ref(subj.main_agent, path[0], 'has_ref')) # Check if the path ends with a refinement if obj.is_ref(path[-1]): path.append( self.get_ref(obj.main_agent, path[-1], 'is_ref')) pr.add_path(tuple(path)) # Do not get next path if reached max_paths if len(pr.paths) >= max_paths: break # Do not check next source if reached max_paths if len(pr.paths) >= max_paths: break return pr # There are no paths shorter than the max path length, so we # don't bother trying to get them else: pr = PathResult(True, 'MAX_PATH_LENGTH_EXCEEDED', max_paths, max_path_length) pr.path_metrics = path_metrics return pr else: return PathResult(False, 'NO_PATHS_FOUND', max_paths, max_path_length)