def test_PathCollection_add_path(): """Add path to the path collection.""" a = Node('a') b = Node('b') c = Node('c') e = Edge(a, b, uid='e') f = Edge(b, c, uid='f') p1 = Path(e, f, uid='p1') p2 = Path(e, uid='p2') p3 = Path(a, uid='p3') paths = PathCollection() paths.add(p1) paths.add(p1) assert paths.counter['p1'] == 2 assert len(paths.nodes) == 2 # assert len(paths.edges) == 2 assert len(paths) == 1 assert p1 in paths paths = PathCollection() paths.add(p1, p2) assert p1 in paths assert p2 in paths
def test_PathCollection_add_edges(): """Add edge path to the path collection.""" a = Node('a') b = Node('b') c = Node('c') e = Edge(a, b, uid='e') f = Edge(b, c, uid='f') paths = PathCollection() paths.add(e, f, uid='p1') assert len(paths.nodes) == 2 # assert len(paths.edges) == 2 assert len(paths) == 1 assert 'p1' in paths paths.add(e, f, uid='p1') assert paths.counter['p1'] == 2 paths.add(e, f) assert paths.counter['p1'] == 3 with pytest.raises(Exception): paths.add(e, f, uid='p2') assert paths.counter['p1'] == 3
def test_possible_paths(): """Test to generate all possible paths.""" paths = PathCollection() paths.add('a', 'a', 'b', 'b', 'a') null = NullModel() assert len(null.possible_relations(paths, length=3)) == 16
def test_fit_path_collection(): """Fit PathCollection to a HON""" paths = PathCollection() paths.add('a', 'c', 'd', uid='acd', frequency=10) paths.add('b', 'c', 'e', uid='bce', frequency=10) hon = HigherOrderNetwork() hon.fit(paths, order=0) assert hon.order == 0 assert hon.number_of_nodes() == 5 assert hon.number_of_edges() == 0 hon = HigherOrderNetwork() hon.fit(paths, order=1) assert hon.order == 1 assert hon.number_of_nodes() == 5 assert hon.number_of_edges() == 4 hon = HigherOrderNetwork() hon.fit(paths, order=2) assert hon.order == 2 assert hon.number_of_nodes() == 4 assert hon.number_of_edges() == 2 hon = HigherOrderNetwork.from_paths(paths, order=2) assert hon.order == 2 assert hon.number_of_nodes() == 4 assert hon.number_of_edges() == 2
def test_outdegrees(): """Fit PathCollection to a HON""" paths = PathCollection() paths.add('a', 'c', 'd', uid='acd', frequency=10) paths.add('b', 'c', 'e', uid='bce', frequency=10) hon = HigherOrderNetwork() hon.fit(paths, order=2) assert sum(hon.outdegrees().values()) == 2
def _(self, data: Network, order: Optional[int] = None, subpaths: bool = True) -> None: paths = PathCollection(directed=data.directed, multipaths=data.multiedges) for edge in data.edges: paths.add(*edge, count=data.edges.counter[edge.uid]) self.fit(paths, order=order)
def routes_from(self, v, node_mapping=None) -> PathCollection: """ Constructs all paths from node v to any leaf nodes Parameters ---------- v: str uid of node from which to start node_mapping: dict an optional mapping from node to a different set. Returns ------- list a list of lists, where each list contains one path from the source node v until a leaf node is reached """ if node_mapping is None: node_mapping = {w.uid: w.uid for w in self.nodes} paths = PathCollection() # Collect temporary paths, indexed by the target node temp_paths = defaultdict(list) temp_paths[v] = [[v]] # set of unprocessed nodes queue = {v} # print('Queue = ', queue) while queue: # take one unprocessed node x = queue.pop() # print('Dequeued ', x) # successors of x expand all temporary # paths, currently ending in x if len(self.successors[x]) > 0: for w in self.successors[x]: for p in temp_paths[x]: temp_paths[w.uid].append(p + [w.uid]) # print('Adding ', w.uid) queue.add(w.uid) del temp_paths[x] #print('Queue = ', queue) # flatten list for possible_paths in temp_paths.values(): for path in possible_paths: if node_mapping: path = [node_mapping[k] for k in path] paths.add(path, count=1, uid='-'.join(path)) return paths
def test_degrees_of_reedom(): """Tets degrees of freedom""" paths = PathCollection() paths.add('a', 'c', 'd', frequency=2) paths.add('b', 'c', 'e', frequency=2) null = NullModel.from_paths(paths, order=0) assert null.degrees_of_freedom() == 4 null = NullModel.from_paths(paths, order=1) assert null.degrees_of_freedom() == 1 null = NullModel.from_paths(paths, order=2) assert null.degrees_of_freedom() == 2 null = NullModel.from_paths(paths, order=3) assert null.degrees_of_freedom() == 0
def test_basic(): """Test basic functions""" paths = PathCollection() paths.add('a', 'c', 'd', uid='a-c-d', count=10) paths.add('b', 'c', 'e', uid='b-c-e', count=10) null = NullModel() null.fit(paths, order=2) # null = NullModel.from_paths(paths, order=2) assert null.number_of_edges() == 4 assert null.number_of_nodes() == 4 for e in null.edges.uids: assert null.edges.counter[e] == 5.0
def _(self, data: PathCollection, order: Optional[int] = None, subpaths: bool = True) -> None: # update if order is not None: self._order = order # iterate over all paths for uid, path in tqdm(data.items(), desc='convert paths to hon'): # generate subpaths of order-1 for higher-order nodes nodes = path.subpaths(min_length=self.order - 1, max_length=self.order - 1, include_self=True, paths=False) # add higher-order nodes to the network for node in nodes: if node not in self.nodes: self.add_node(*node, uid='-'.join(node), count=0) self.nodes.counter[self.nodes[node].uid] += data.counter[uid] # do not create edges if order is 0 nodes = nodes if self.order > 0 else [] # generat higher-order edges for _v, _w in zip(nodes[:-1], nodes[1:]): _v, _w = self.nodes[_v], self.nodes[_w] # check if edge exist otherwise add new edge if (_v, _w) not in self.edges: self.add_edge(_v, _w, count=0) # get edge and update counters edge = self.edges[_v, _w] self.edges.counter[edge.uid] += data.counter[uid] if order == len(path): self._observed[ edge.first_order_relations] += data.counter[uid] else: self._subpaths[ edge.first_order_relations] += data.counter[uid] # calculate frequencies for a zero-order network if self.order == 0: total = sum(self.nodes.counter.values()) for key, value in self.nodes.counter.items(): self.nodes.counter[key] = value / total # create all possible higher-order nodes if subpaths and self.order > 1: for node in self.possible_relations(data, self.order - 1): if node not in self.nodes: self.add_node(*node, count=0)
def __init__(self, paths: Optional[PathCollection] = None) -> None: """Initialize sub-paths object.""" # check if inital paths are given if isinstance(paths, PathCollection): self._paths = paths else: self._paths = PathCollection() # initialize the base class super().__init__(directed=self._paths.directed, multiedges=self._paths.multiedges, multipaths=self._paths.multipaths, nodes=self._paths.nodes, edges=self._paths.edges) # initialize counters self._counter: Counter = Counter() self._observed: defaultdict = defaultdict(Counter) self._possible: defaultdict = defaultdict(Counter)
def _(self, data: Network, order: Optional[int] = None) -> None: # Check order if order is not None: self._order = order if 0 <= self.order <= 1: super().fit(data, order=self.order) elif self.order > 1: # TODO: create function to transfer base data from PathCollection object # --- START --- nc = NodeCollection() for node in data.nodes.values(): nc.add(node) ec = EdgeCollection(nodes=nc) for edge in data.edges.values(): ec.add(edge) self._nodes = HigherOrderNodeCollection(nodes=nc, edges=ec) # --- END --- # get network data network = data # generate a path representation of the data paths = PathCollection(directed=network.directed, nodes=network.nodes, edges=network.edges) for edge in data.edges: paths.add(edge, frequency=edge.attributes.get('frequency', 1)) self.calculate(network, paths) else: LOG.error('A Null Model with order %s is not supported', self.order) raise AttributeError
def _temp(self, **kwargs): """Convert a temproal netwok to paths.""" from pathpy.models.directed_acyclic_graph import DirectedAcyclicGraph #paths = PathCollection(edges=self.edges.copy()) paths = PathCollection() delta = kwargs.get('delta', 1) # generate a single time-unfolded DAG dag = DirectedAcyclicGraph.from_temporal_network(self, delta=delta) for root in dag.roots: causal_tree = _causal_tree(dag, root) _paths = causal_tree.to_paths() for path in _paths: edges = [e['original'] for e in path.edges] if edges not in paths: paths.add(*edges, frequency=1) else: paths[edges]['frequency'] += 1 return paths
def test_PathCollection_add_str(): """Add string path to the path collection.""" paths = PathCollection() paths.add('a', 'b', 'c', uid='p1') assert len(paths.nodes) == 3 # assert len(paths.edges) == 2 assert len(paths) == 1 assert 'p1' in paths paths.add('a', 'b', 'c', uid='p1') assert paths.counter['p1'] == 2 paths.add('a', 'b', 'c') assert paths.counter['p1'] == 3
def _dag(self, **kwargs): """Convert a DAG to paths.""" # check if dag is acyclic if self.acyclic is None: self.topological_sorting() if not self.acyclic: LOG.error('Cannot extract statistics from a cyclic graph') raise ValueError paths = PathCollection(edges=self.edges.copy()) for root in self.roots: paths = self.routes_from(root, paths) return paths
def test_PathCollection_add_tuple(): """Add path tuple to the path collection.""" paths = PathCollection() paths.add(('a', 'b'), ('a', 'b', 'c')) assert len(paths.nodes) == 3 # assert len(paths.edges) == 2 assert len(paths) == 2 paths.add('a', 'b', 'c') assert paths.counter[paths['a', 'b', 'c'].uid] == 2
def test_PathCollection_add_nodes(): """Add node path to the path collection.""" a = Node('a') b = Node('b') c = Node('c') paths = PathCollection() paths.add(a, b, c, uid='p1') assert len(paths.nodes) == 3 # assert len(paths.edges) == 2 assert len(paths) == 1 assert 'p1' in paths paths.add(a, b, c, uid='p1') assert paths.counter['p1'] == 2 paths.add(a, b, c) assert paths.counter['p1'] == 3
def likelihood(self, data: PathCollection, log: bool = False) -> float: """Returns the likelihood given some observation data.""" # some information for debugging LOG.debug('I\'m a likelihood of a HigherOrderNetwork') # get a list of nodes for the matrix indices idx = self.nodes.index # get the transition matrix matrix = transition_matrix(self, count=True, transposed=True) # initialize likelihood likelihood, _path_likelihood = (0, 0) # iterate over observed hon paths for uid, path in data.items(): # get frequency of the observed path frequency = data.counter[uid] # initial path likelihood path_likelihood = _path_likelihood # generate subpaths of order-1 for higher-order nodes nodes = path.subpaths(min_length=self.order - 1, max_length=self.order - 1, include_self=True, paths=False) for _v, _w in zip(nodes[:-1], nodes[1:]): path_likelihood += np.log(matrix[idx[self.nodes[_w].uid], idx[self.nodes[_v].uid]]) likelihood += path_likelihood * frequency return likelihood if log else np.exp(likelihood)
def test_PathCollection_remove_path(): """Remove path from the path collection.""" a = Node('a') b = Node('b') c = Node('c') e = Edge(a, b, uid='e') f = Edge(b, c, uid='f') p1 = Path(e, f, uid='p1') p2 = Path(e, uid='p2') p3 = Path(a, uid='p3') paths = PathCollection() paths.add(p1) paths.remove(p1) assert len(paths.nodes) == 0 # assert len(paths.edges) == 2 assert len(paths) == 0 assert p1 not in paths
class SubPathCollection(PathCollection): """Class for sub-path statistics.""" def __init__(self, paths: Optional[PathCollection] = None) -> None: """Initialize sub-paths object.""" # check if inital paths are given if isinstance(paths, PathCollection): self._paths = paths else: self._paths = PathCollection() # initialize the base class super().__init__(directed=self._paths.directed, multiedges=self._paths.multiedges, multipaths=self._paths.multipaths, nodes=self._paths.nodes, edges=self._paths.edges) # initialize counters self._counter: Counter = Counter() self._observed: defaultdict = defaultdict(Counter) self._possible: defaultdict = defaultdict(Counter) def __call__(self, min_length: int = 0, max_length: int = sys.maxsize, include_path: bool = False, recalculate: bool = False) -> SubPathCollection: """Returns a sub-pahts""" if len(self) == 0 or recalculate: self.calculate(min_length=min_length, max_length=max_length, include_path=include_path) return self def __str__(self) -> str: """Print a summary of the sub-paths.""" return self.summary() @property def observed(self) -> defaultdict: """Returns observed paths as a dict of counters.""" return self._observed @property def possible(self) -> defaultdict: """Returns possible paths as a dict of counters.""" return self._possible @property def counter(self) -> Counter: """Returns a subpath counter""" return self._counter def calculate(self, min_length: int = 0, max_length: int = sys.maxsize, include_path: bool = False) -> None: """Helper function to calculate subpaths.""" if len(self) > 0: LOG.warning('Recalculating sub-paths!') # get the default max and min path lengths _min_length: int = min_length _max_length: int = max_length # iterrate over all paths for path in tqdm(self._paths.values(), desc='sub-path calculation'): # number of counted paths frequency = path.attributes.get('frequency', 1) # if min_length is zero, account also for nodes if _min_length <= 0: for node in path.nodes: if (node, ) not in self: self._add(Path(node, possible=frequency, frequency=0)) else: self[(node, )]['possible'] += frequency # get min and max length min_length = max(_min_length, 1) max_length = min(len(path) - 1, _max_length) # get subpaths for i in range(min_length - 1, max_length): for j in range(len(path) - i): edges = tuple(path.edges[j:j + i + 1]) if edges not in self: self._add(Path(*edges, possible=frequency, frequency=0)) else: # TODO: fix the frequency assignment if self[edges]['possible'] is None: self[edges]['possible'] = 0 self[edges]['possible'] += frequency # include the path if include_path: if path not in self and _min_length <= len( path) <= _max_length: path['possible'] = 0 self._add(path) for path in self: self._observed[len(path)][path] += path['frequency'] or 0 self._possible[len(path)][path] += path['possible'] or 0 self._counter[path] += path['frequency'] or 0 self._counter[path] += path['possible'] or 0 def summary(self) -> str: """Returns a summary of the sub path statistic. Returns ------- str Retruns a summary of the sub path statistics. """ # check if sub path statistic is already calculated if len(self) == 0: return super().__str__() # initialize a data storage counter: Counter = Counter() # get max order max_order_obs = max(self.observed.keys()) max_order_pos = max(self.possible.keys()) max_order = max(max_order_obs, max_order_pos) # get data of observed paths for order in range(max_order_obs + 1): counter[order] = int(sum(self.observed[order].values())) data: list = list(counter.elements()) # TODO: Find better solution for printing # TODO: Move to util line_length = 54 row = {} row['==='] = '=' * line_length row['sf'] = '{:<25s}{:>15.3f}' row['s|sss|ss'] = '{:^6s} | {:^9s} {:^9s} {:^9s} | {:^6s} {:^6s}' row['-|---|--'] = '{:->6s} | {:->9s} {:->9s} {:->9s} | {:->6s} {:->6s}' row['d|ddd|dd'] = '{:>6d} | {:>9d} {:>9d} {:>9d} | {:>6d} {:>6d}' row['f|fff|ff'] = '{:>6.0f} | {:>9.0f} {:>9.0f} {:>9.0f} | {:>6.0f} {:>6.0f}' row['s| s | s'] = '{:^6s} | {:^29s} | {:^13s}' # initialize summary text summary: list = [ row['==='], 'Sub path statistics', ] if data: # add general statistics lines: list = [['- General '], ['Number of unique nodes:', len(self.nodes)], ['Number of unique edges:', len(self.edges)], ['Number of unique paths:', len(self._paths)], ['- Path statistics '], ['Mean path length:', np.mean(data)], ['Standard derivation:', np.std(data)], ['Min. path length:', np.min(data)], ['25% quantile:', np.quantile(data, 0.25)], ['50% quantile:', np.quantile(data, 0.50)], ['75% quantile:', np.quantile(data, 0.75)], ['Max. path length:', np.max(data)], ['- Sub path statistics ']] for line in lines: if len(line) == 1: summary.append('{}'.format(line[0]).ljust( line_length, '-')) else: summary.append(row['sf'].format(line[0], line[1])) # add sub path statistics # add headings summary.append(row['s| s | s'].format('path', 'frequencies', 'unique')) summary.append(row['s|sss|ss'].format('length', 'obs', 'pos', 'tot', 'obs', 'pos')) # add line summary.append(row['-|---|--'].format('', '', '', '', '', '')) # add row for each order data: list = [[], [], [], [], []] for order in range(max_order + 1): data[0].append(sum(self.observed[order].values())) data[1].append(sum(self.possible[order].values())) data[2].append(data[0][-1] + data[1][-1]) data[3].append( np.count_nonzero(list(self.observed[order].values()))) data[4].append( np.count_nonzero(list(self.possible[order].values()))) summary.append(row['f|fff|ff'].format(order, *[v[-1] for v in data])) # add line summary.append(row['-|---|--'].format('', '', '', '', '', '')) # add column sums summary.append(row['f|fff|ff'].format(order, *[sum(v) for v in data])) # add legend summary.append('obs ... observed paths (in the network)') summary.append('pos ... possible paths (but not observed)') summary.append('tot ... total number of paths') # add double line summary.append('=' * line_length, ) # # if logging is enabled print summary as INFO log # # TODO: Move this code to a helper function return '\n'.join(summary) @classmethod def from_paths(cls, paths: PathCollection, min_length: int = 0, max_length: int = sys.maxsize, include_path: bool = False) -> SubPathCollection: """Create sub-paths statistic from a path collection object.""" subpaths = cls(paths) subpaths.calculate(min_length=min_length, max_length=max_length, include_path=include_path) return subpaths
def read_pathcollection(filename: str, separator: str = ',', frequency: bool = False, directed: bool = True, maxlines: int = None) -> PathCollection: """Read path in edgelist format Reads data from a file containing multiple lines of *edges* of the form "v,w,frequency,X" (where frequency is optional and X are arbitrary additional columns). The default separating character ',' can be changed. Parameters ---------- filename : str path to edgelist file separator : str character separating the nodes frequency : bool is a frequency given? if ``True`` it is the last element in the edge (i.e. ``a,b,2``) directed : bool are the edges directed or undirected maxlines : int number of lines to read (useful to test large files). None means the entire file is read """ from pathpy.core.path import Path, PathCollection nodes: dict = {} edges: dict = {} paths: dict = {} with open(filename, 'r') as csv: for n, line in enumerate(csv): fields = line.rstrip().split(separator) assert len(fields) >= 1, 'Error: empty line: {0}'.format(line) if frequency: path = tuple(fields[:-1]) freq = float(fields[-1]) else: path = tuple(fields) freq = 1.0 for node in path: if node not in nodes: nodes[node] = Node(node) if len(path) == 1 and path not in paths: paths[path] = Path(nodes[path[0]], frequency=freq) else: edge_list = [] for u, v in zip(path[:-1], path[1:]): if (u, v) not in edges: edges[(u, v)] = Edge(nodes[u], nodes[v]) edge_list.append(edges[(u, v)]) if path not in paths: paths[path] = Path(*edge_list, frequency=freq) if maxlines is not None and n >= maxlines: break ncoll = NodeCollection() for node in nodes.values(): ncoll.add(node) ecoll = EdgeCollection(nodes=ncoll) for edge in edges.values(): ecoll._add(edge) _paths = PathCollection(directed=directed, nodes=ncoll, edges=ecoll) for _path in paths.values(): _paths._add(_path) return _paths
def test_fit_path_collection(): """Fit PathCollection to a HON""" paths = PathCollection() a = Node('a') b = Node('b') c = Node('c') d = Node('d') e = Node('e') # paths.add(a, c, d, uid='acd', frequency=10) # paths.add(b, c, e, uid='bce', frequency=10) paths.add('a', c, 'd', 'f', uid='acd', count=10) paths.add('b', c, 'e', 'g', uid='bce', count=10) # paths.add('a', 'c', 'd', uid='acd', frequency=10) # paths.add('b', 'c', 'e', uid='bce', frequency=10) # print(paths.counter) # hon = HigherOrderNetwork() # hon.fit(paths, order=3) # print(hon.nodes['xxx'].objects) # print(hon.nodes.counter) # for e in hon.edges: # print(e.first_order_relations) # print() # break paths = PathCollection() paths.add('a', 'c', 'b', uid='acb', count=10) paths.add('c', 'b', 'a', 'c', uid='cba', count=20) paths.add('a', 'b', 'a', 'c', uid='abac', count=30) # paths.add(a, 'b', 'c', 'd', 'e', 'f', uid='p1') # paths.add(a, 'b', 'c', 'd', 'e', 'x') hon = HigherOrderNetwork(uid='hon') hon.fit(paths, order=1) print(hon) # for n in hon.nodes: # print((n, hon.indegrees()[n.uid], hon.outdegrees()[n.uid])) ##p = paths['p1'].subpaths(min_length=0, max_length=None, paths=True) # # print(paths) # print(hon.observed) # print(hon.subpaths) # print(hon.edges.counter) print('no log', hon.likelihood(paths, log=False)) print('log', hon.likelihood(paths, log=True)) import numpy as np print(np.exp(hon.likelihood(paths, log=True)))
def test_PathCollection(): """Test the paths object""" a = Node('a') b = Node('b') c = Node('c') e = Edge(a, b, uid='e') f = Edge(b, c, uid='f') p1 = Path(e, f, uid='p1') p2 = Path(e, uid='p2') p3 = Path(a, uid='p3') paths = PathCollection() paths.add(p1) paths.add(p2) paths.add(p3) with pytest.raises(Exception): paths.add(p1) assert len(paths.nodes) == 3 assert len(paths.edges) == 2 assert len(paths) == 3 assert p1 in paths assert p2 in paths assert p3 in paths assert 'p1' in paths assert 'p2' in paths assert 'p3' in paths assert (e, f) in paths assert ('e', 'f') in paths assert [e] in paths assert ['e'] in paths assert (a, b, c) in paths assert ('a', 'b', 'c') in paths assert (a, b) in paths assert ('a', 'b') in paths assert (a, ) in paths assert ('a', ) in paths assert [a] in paths assert ['a'] in paths # print(paths['p1'] == p1) #print(paths[p1] == p1) assert paths['a', 'b', 'c'] == p1 assert paths['e', 'f'] == p1 with pytest.raises(Exception): p = paths['x', 'y'] g = Edge(b, c, uid='a') p4 = Path(g, uid='p4') paths.add(p4) # issue warning assert ['a'] in paths paths = PathCollection() paths.add(a, b) with pytest.raises(Exception): paths.add(a, b) paths = PathCollection() paths.add('a', 'b', 'c', uid='a-b-c') assert len(paths) == 1 assert 'a-b-c' in paths assert 'a' and 'b' and 'c' in paths.nodes assert ('a', 'b') and ('b', 'c') in paths.edges paths = PathCollection() paths.add(p1, p2) assert len(paths) == 2 paths = PathCollection() paths.add(('a', 'b', 'c'), ('a', 'b')) assert len(paths.nodes) == 3 assert len(paths.edges) == 2 assert len(paths) == 2 paths = PathCollection() paths.add(e, f, uid='p1') assert len(paths) == 1 assert len(paths.edges) == 2 assert len(paths.nodes) == 3 assert (e, f) in paths assert ('a', 'b', 'c') in paths with pytest.raises(Exception): paths.add(f, e, uid='p2') paths = PathCollection() paths.add('e1', uid='p1', nodes=False) assert len(paths) == 1 assert len(paths.edges) == 1 assert len(paths.nodes) == 2 assert 'p1' in paths assert 'e1' in paths.edges paths = PathCollection() paths.add('e1', 'e2', uid='p1', nodes=False) assert len(paths) == 1 assert len(paths.edges) == 2 assert len(paths.nodes) == 3 assert 'p1' in paths assert 'e1' and 'e2' in paths.edges assert paths.edges['e1'].w == paths.edges['e2'].v paths = PathCollection() paths.add(('e1', 'e2'), ('e3', 'e4'), nodes=False) assert len(paths.nodes) == 6 assert len(paths.edges) == 4 assert len(paths) == 2 paths = PathCollection() paths.add(p1, p2, p3) assert len(paths.nodes) == 3 assert len(paths.edges) == 2 assert len(paths) == 3 paths.remove(p3) assert len(paths.nodes) == 3 assert len(paths.edges) == 2 assert len(paths) == 2 assert p3 not in paths paths.remove('p1') assert len(paths.nodes) == 3 assert len(paths.edges) == 2 assert len(paths) == 1 assert p1 not in paths paths = PathCollection() paths.add(('a', 'b', 'c'), ('a', 'b')) assert len(paths) == 2 paths.remove('a', 'b') assert len(paths) == 1 paths = PathCollection() paths.add(('a', 'b'), ('b', 'c'), ('c', 'd')) paths.remove(('a', 'b'), ('b', 'c')) assert len(paths) == 1 assert ('a', 'b') not in paths assert ('b', 'c') not in paths assert ('c', 'd') in paths paths = PathCollection() paths.add(('e1', 'e2'), ('e2', 'e3'), ('e3', 'e4'), nodes=False) assert len(paths) == 3 assert len(paths.edges) == 4 paths.remove('e1', 'e2') assert len(paths) == 2 paths.remove(('e2', 'e3'), ('e3', 'e4')) assert len(paths) == 0 paths = PathCollection() paths.add('a', 'b', uid='p1') paths.add('b', 'c', uid='p2') paths.add('c', 'd', uid='p3') paths.add('d', 'e', uid='p4') assert len(paths) == 4 paths.remove('p1') assert len(paths) == 3 paths.remove('p2', 'p3') assert len(paths) == 1
def test_PathCollection_counter(): """Test the counter of the path collection""" paths = PathCollection() paths.add('a', 'b', count=5) paths.add('a', 'b', count=7) assert paths.counter[paths['a', 'b'].uid] == 12 p1 = Path('a', 'x', 'c', uid='a-x-c') p2 = Path('b', 'x', 'd', uid='b-x-d') pc = PathCollection(multipaths=True) pc.add(p1) pc.add(p2) pc.add(p2) assert 'a-x-c' and 'b-x-d' in pc.counter p3 = Path('b', 'x', 'd', uid='b-x-d-2') pc.add(p3) assert 'a-x-c' and 'b-x-d' and 'b-x-d-2' in pc.counter
def test_PathCollection(): """Test the paths object""" a = Node('a') b = Node('b') c = Node('c') e = Edge(a, b, uid='e') f = Edge(b, c, uid='f') p1 = Path(e, f, uid='p1') p2 = Path(e, uid='p2') p3 = Path(a, uid='p3') paths = PathCollection() paths.add(p1) paths.add(p2) paths.add(p3) paths.add(p1) assert paths.counter['p1'] == 2 assert len(paths.nodes) == 3 # assert len(paths.edges) == 2 assert len(paths) == 3 assert p1 in paths assert p2 in paths assert p3 in paths assert 'p1' in paths assert 'p2' in paths assert 'p3' in paths assert (e, f) in paths assert ('e', 'f') in paths assert (e, ) in paths assert ('e', ) in paths assert (a, ) in paths assert ('a', ) in paths a = Node('a') b = Node('b') c = Node('c') p1 = Path(a, b, c, uid='p1') p2 = Path(a, b, uid='p2') p3 = Path(a, uid='p3') paths = PathCollection() paths.add(p1) paths.add(p2) paths.add(p3) assert (a, b, c) in paths assert ('a', 'b', 'c') in paths assert (a, b) in paths assert ('a', 'b') in paths assert (a, ) in paths assert ('a', ) in paths # assert [a] in paths # assert ['a'] in paths assert paths['a', 'b', 'c'] == p1 assert paths['a', 'b'] == p2 with pytest.raises(Exception): p = paths['x', 'y'] p4 = Path(b, c, uid='p4') # with pytest.raises(Exception): paths.add(p4) assert paths.counter['p4'] == 1 paths = PathCollection() paths.add(a, b) # with pytest.raises(Exception): paths.add(a, b) assert paths.counter[paths['a', 'b'].uid] == 2 paths = PathCollection() paths.add('a', 'b', 'c', uid='a-b-c') assert len(paths) == 1 assert 'a-b-c' in paths assert 'a' and 'b' and 'c' in paths.nodes paths = PathCollection() paths.add(p1, p2) assert len(paths) == 2 paths = PathCollection() paths.add(('a', 'b', 'c'), ('a', 'b')) assert len(paths.nodes) == 3 #assert len(paths.edges) == 2 assert len(paths) == 2 paths = PathCollection() paths.add(e, f, uid='p1') assert len(paths) == 1 #assert len(paths.edges) == 2 assert len(paths.nodes) == 2 assert (e, f) in paths #assert ('a', 'b', 'c') in paths # with pytest.raises(Exception): paths.add(f, e, uid='p2') # paths = PathCollection() # paths.add('e1', uid='p1', nodes=False) # assert len(paths) == 1 # assert len(paths.edges) == 1 # assert len(paths.nodes) == 2 # assert 'p1' in paths # assert 'e1' in paths.edges # paths = PathCollection() # paths.add('e1', 'e2', uid='p1', nodes=False) # assert len(paths) == 1 # assert len(paths.edges) == 2 # assert len(paths.nodes) == 3 # assert 'p1' in paths # assert 'e1' and 'e2' in paths.edges # assert paths.edges['e1'].w == paths.edges['e2'].v # paths = PathCollection() # paths.add(('e1', 'e2'), ('e3', 'e4'), nodes=False) # assert len(paths.nodes) == 6 # assert len(paths.edges) == 4 # assert len(paths) == 2 paths = PathCollection() paths.add(p1, p2, p3) assert len(paths.nodes) == 3 assert len(paths) == 3 paths.remove(p3) assert len(paths.nodes) == 3 #assert len(paths.edges) == 2 assert len(paths) == 2 assert p3 not in paths paths.remove('p1') assert len(paths.nodes) == 2 #assert len(paths.edges) == 2 assert len(paths) == 1 assert p1 not in paths paths = PathCollection() paths.add(('a', 'b', 'c'), ('a', 'b')) assert len(paths) == 2 paths.remove('a', 'b') assert len(paths) == 1 paths = PathCollection() paths.add(('a', 'b'), ('b', 'c'), ('c', 'd')) paths.remove(('a', 'b'), ('b', 'c')) assert len(paths) == 1 assert ('a', 'b') not in paths assert ('b', 'c') not in paths assert ('c', 'd') in paths # paths = PathCollection() # paths.add(('e1', 'e2'), ('e2', 'e3'), ('e3', 'e4'), nodes=False) # assert len(paths) == 3 # assert len(paths.edges) == 4 # paths.remove('e1', 'e2') # assert len(paths) == 2 # paths.remove(('e2', 'e3'), ('e3', 'e4')) # assert len(paths) == 0 paths = PathCollection() paths.add('a', 'b', uid='p1') paths.add('b', 'c', uid='p2') paths.add('c', 'd', uid='p3') paths.add('d', 'e', uid='p4') assert len(paths) == 4 paths.remove('p1') assert len(paths) == 3 paths.remove(('p2', 'p3'))
def test_PathCollection_remove_edges(): """Remove edge path from the path collection.""" a = Node('a') b = Node('b') c = Node('c') e = Edge(a, b, uid='e') f = Edge(b, c, uid='f') paths = PathCollection() paths.add(e, f, uid='p1') paths.remove(e, f) assert len(paths) == 0 assert 'p1' not in paths paths.add(e, f, uid='p1') paths.remove('p1') assert len(paths) == 0 paths.add(e, f, uid='p1') paths.remove(e, f) assert len(paths) == 0 paths.add(e, f, uid='p1') paths.remove('e', 'f') assert len(paths) == 0
def test_possible_paths(): """Test to generate all possible paths.""" paths = PathCollection() paths.add('a', 'a', 'b', 'b', 'a') assert len(NullModel.possible_paths(paths.edges, order=3)) == 16