diff --git a/CHANGELOG.md b/CHANGELOG.md index 17f25c0..bb88958 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # CHANGELOG +## [v2.2.0 (January 11 2022)](https://pypi.org/project/grandiso/2.2.0/) + +- Features + - (#39) Adds streaming match generators (thanks @davidmezzetti!) +- Fixes + - (#37) Improve performance of `find_motifs` by remembering to actually update `_greatest_backbone_count` (thanks @nobutoba!) + ## [v2.1.1 (December 21 2021)](https://pypi.org/project/grandiso/2.1.0/) - Fixes diff --git a/grandiso/__init__.py b/grandiso/__init__.py index 1a3355c..e45e5e1 100644 --- a/grandiso/__init__.py +++ b/grandiso/__init__.py @@ -17,13 +17,13 @@ attribute search. """ -from typing import Dict, Generator, Hashable, List, Optional, Union, Tuple +from typing import Dict, Generator, Hashable, List, Union, Tuple import itertools from functools import lru_cache import networkx as nx -__version__ = "2.1.1" +__version__ = "2.2.0" @lru_cache() @@ -161,8 +161,9 @@ def get_next_backbone_candidates( # Let's return ALL possible node choices for this next_node. To do this # without being an insane person, let's filter on max degree in host: for n in host.nodes: - if is_node_attr_match(next_node, n, motif, host) \ - and is_node_structural_match(next_node, n, motif, host): + if is_node_attr_match( + next_node, n, motif, host + ) and is_node_structural_match(next_node, n, motif, host): yield {next_node: n} return @@ -248,7 +249,7 @@ def get_next_backbone_candidates( # This is neato :) It means that there are multiple edges in the host # graph that we can use to downselect the number of candidate nodes. candidate_nodes_set = set() - for (source, _, target) in required_edges: + for source, _, target in required_edges: if directed: if source is not None: # this is a "from" edge: @@ -281,9 +282,11 @@ def get_next_backbone_candidates( def tentative_results(): for c in candidate_nodes: - if c not in backbone.values() \ - and is_node_attr_match(next_node, c, motif, host) \ - and is_node_structural_match(next_node, c, motif, host): + if ( + c not in backbone.values() + and is_node_attr_match(next_node, c, motif, host) + and is_node_structural_match(next_node, c, motif, host) + ): yield {**backbone, next_node: c} # One last filtering step here. This is to catch the cases where you have @@ -323,7 +326,7 @@ def monomorphism_candidates(): # to confirm that no spurious edges exist in the induced subgraph: def isomorphism_candidates(): for result in monomorphism_candidates(): - for (motif_u, motif_v) in itertools.product(result.keys(), result.keys()): + for motif_u, motif_v in itertools.product(result.keys(), result.keys()): # if the motif has this edge, then it doesn't rule any of the # above results out as an isomorphism. # if the motif does NOT have the edge, then NO RESULT may have @@ -338,6 +341,7 @@ def isomorphism_candidates(): yield from isomorphism_candidates() + def uniform_node_interestingness(motif: nx.Graph) -> dict: """ Sort the nodes in a motif by their interestingness. @@ -422,6 +426,7 @@ def walk(path): for path in paths: yield from walk(path) + def find_motifs( motif: nx.Graph, host: nx.Graph, diff --git a/grandiso/queues.py b/grandiso/queues.py index d5e3afc..1d299b4 100644 --- a/grandiso/queues.py +++ b/grandiso/queues.py @@ -5,7 +5,7 @@ try: SimpleQueue = queue.SimpleQueue -except: +except ImportError: SimpleQueue = queue.Queue diff --git a/grandiso/test_grandiso.py b/grandiso/test_grandiso.py index d082b9a..fb2e297 100644 --- a/grandiso/test_grandiso.py +++ b/grandiso/test_grandiso.py @@ -1,5 +1,3 @@ -import time -import copy import random import pytest @@ -318,7 +316,7 @@ def _random_host(directed=False, n=20, p=0.1): def _random_directed_motif(): motif = _random_motif() dmotif = nx.DiGraph() - for (u, v) in motif.edges(): + for u, v in motif.edges(): dmotif.add_edge(*random.choice([(u, v), (v, u)])) return dmotif diff --git a/grandiso/test_queues.py b/grandiso/test_queues.py index 4aec80a..ea27038 100644 --- a/grandiso/test_queues.py +++ b/grandiso/test_queues.py @@ -11,19 +11,21 @@ @pytest.mark.parametrize( - "queue,queue_args", all_queues, + "queue,queue_args", + all_queues, ) def test_empty(queue, queue_args): q = queue(*queue_args) assert q.empty() q.put(1) - assert q.empty() == False + assert q.empty() is False q.get() assert q.empty() @pytest.mark.parametrize( - "queue,queue_args", all_queues, + "queue,queue_args", + all_queues, ) def test_can_put_and_pop(queue, queue_args): q = queue(*queue_args) diff --git a/setup.py b/setup.py index 5646228..1d2865b 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="grandiso", - version="2.1.1", + version="2.2.0", author="Jordan Matelsky", author_email="opensource@matelsky.com", description="Performant subgraph isomorphism", @@ -18,4 +18,5 @@ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], + install_requires=["networkx>=2.5.1"], )