Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial working multigraph edge validator plus tests #107

Merged
merged 12 commits into from
Sep 30, 2021
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

- **0.10.0**
- Features:
- `GrandIsoExecutor` and `NetworkXExecutor`: Support for `networkx.MultiGraph` and `networkx.MultiDiGraph` search through the use of the `multigraph_edge_match` executor argument (#107).
- Deprecations:
- Removed `dotmotif.dotmotif` method-style API (#107).
- **0.9.2** (May 28 2021)
- Features:
- `GrandIsoExecutor`: Utilizes the node attribute matching flow available in grandiso≥2.0.0 to reduce complexity of attribute-heavy searches (#104)
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/dotmotif/dotmotif.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Create a new dotmotif object.
> - **parser** (`dotmotif.parsers.Parser`: `DEFAULT_MOTIF_PARSER`): The parser
to use to parse the document. Defaults to the v2 parser.
> - **exclude_automorphisms** (`bool`: `False`): Whether to exclude automorphism
variants of the motif when rturning results.
variants of the motif when returning results.
> - **validators** (`List[Validator]`: `None`): A list of dotmotif.Validators to use
when verifying the motif for correctness and executability.

Expand Down
2 changes: 1 addition & 1 deletion docs/reference/dotmotif/utils.py.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Draw a dotmotif motif object.

### Arguments
> - **dm** (`None`: `None`): dotmotif.DotMotif
> - **dm** (`None`: `None`): dotmotif.Motif
> - **negative_edge_color** (`str`: `r`): Color used to represent negative edges
> - **pos** (`dict`: `None`): The position to use. If unset, uses nx.spring_layout

Expand Down
17 changes: 16 additions & 1 deletion docs/reference/executors/GrandIsoExecutor.py.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
## *Class* `GrandIsoExecutor(NetworkXExecutor)`


A DotMotif executor that uses grandiso for subgraph monomorphism.

This executor is dramatically fast than the NetworkX search, and is still a pure-Python implementation.

[GrandIso](https://github.com/aplbrain/grandiso-networkx)



## *Function* `find(self, motif, limit: int = None)`


Find a motif in a larger graph.

### Arguments
motif (dotmotif.dotmotif)
motif (dotmotif.Motif)
> - **int** (`None`: `None`): None)

### Returns
List[dict]

8 changes: 4 additions & 4 deletions docs/reference/executors/Neo4jExecutor.py.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,21 +72,21 @@ You should usually ignore this, and use .find() instead.



## *Function* `count(self, motif: "dotmotif", limit=None) -> int`
## *Function* `count(self, motif: "dotmotif.Motif", limit=None) -> int`


Count a motif in a larger graph.

### Arguments
motif (dotmotif.dotmotif)
motif (dotmotif.Motif)



## *Function* `find(self, motif: "dotmotif", limit=None, cursor=True)`
## *Function* `find(self, motif: "dotmotif.Motif", limit=None, cursor=True)`


Find a motif in a larger graph.

### Arguments
motif (dotmotif.dotmotif)
motif (dotmotif.Motif)

12 changes: 9 additions & 3 deletions docs/reference/executors/NetworkXExecutor.py.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
Check if a single edge satisfies the constraints.


## *Function* `_edge_satisfies_many_constraints_for_muligraph_any_edges(edge_attributes: dict, constraints: dict) -> List[Tuple[str, str, str]]`


Returns a subset of constraints that this edge matches, in the form (key, op, val).


## *Function* `_node_satisfies_constraints(node_attributes: dict, constraints: dict) -> bool`


Expand Down Expand Up @@ -32,19 +38,19 @@ Create a new NetworkXExecutor.



## *Function* `count(self, motif: "dotmotif", limit: int = None)`
## *Function* `count(self, motif: "dotmotif.Motif", limit: int = None)`


Count the occurrences of a motif in a graph.

See NetworkXExecutor#find for more documentation.


## *Function* `find(self, motif: "dotmotif", limit: int = None)`
## *Function* `find(self, motif: "dotmotif.Motif", limit: int = None)`


Find a motif in a larger graph.

### Arguments
motif (dotmotif.dotmotif)
motif (dotmotif.Motif)

8 changes: 4 additions & 4 deletions docs/reference/executors/NeuPrintExecutor.py.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,26 +42,26 @@ You should usually ignore this, and use .find() instead.



## *Function* `count(self, motif: dotmotif, limit=None) -> int`
## *Function* `count(self, motif: Motif, limit=None) -> int`


Count a motif in a larger graph.

### Arguments
> - **motif** (`dotmotif.dotmotif`: `None`): The motif to search for
> - **motif** (`dotmotif.Motif`: `None`): The motif to search for

### Returns
> - **int** (`None`: `None`): The count of this motif in the host graph



## *Function* `find(self, motif: dotmotif, limit=None) -> pd.DataFrame`
## *Function* `find(self, motif: Motif, limit=None) -> pd.DataFrame`


Find a motif in a larger graph.

### Arguments
> - **motif** (`dotmotif.dotmotif`: `None`): The motif to search for
> - **motif** (`dotmotif.Motif`: `None`): The motif to search for

### Returns
> - **pd.DataFrame** (`None`: `None`): The results of the search
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/ingest/ingest.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ An abstract base class for import to the NetworkX format.



## *Class* `CSVEdgelistConverter(NetworkXConverter)`
## *Class* `EdgelistConverter(NetworkXConverter)`


A converter that takes an arbitrary CSV file on disk and converts it to a graph.
Convert an edgelist dataframe or CSV to a graph.



Expand Down
Empty file added docs/reference/tests/tests.md
Empty file.
7 changes: 2 additions & 5 deletions dotmotif/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def from_motif(self, cmd: str):

return self

def from_nx(self, graph: nx.DiGraph) -> "dotmotif":
def from_nx(self, graph: nx.DiGraph) -> "Motif":
"""
Ingest directly from a graph.

Expand Down Expand Up @@ -197,7 +197,7 @@ def save(self, fname: Union[str, IO[bytes]]) -> Union[str, IO[bytes]]:
return fname

@staticmethod
def load(fname: Union[str, IO[bytes]]) -> "dotmotif":
def load(fname: Union[str, IO[bytes]]) -> "Motif":
"""
Load the motif from a file on disk.

Expand All @@ -215,6 +215,3 @@ def load(fname: Union[str, IO[bytes]]) -> "dotmotif":
result = pickle.load(f)
f.close()
return result


dotmotif = Motif
11 changes: 10 additions & 1 deletion dotmotif/executors/GrandIsoExecutor.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,19 @@ def _node_attr_match_fn(
is_node_attr_match=_node_attr_match_fn,
)

_edge_constraint_validator = (
self._validate_edge_constraints if not self._host_is_multigraph
else (
self._validate_multigraph_all_edge_constraints
if self._multigraph_edge_match == "all"
else self._validate_multigraph_any_edge_constraints
)
)

results = []
for r in graph_matches:
if _doesnt_have_any_of_motifs_negative_edges(r) and (
self._validate_edge_constraints(
_edge_constraint_validator(
r, self.graph, motif.list_edge_constraints()
)
# and self._validate_node_constraints(
Expand Down
12 changes: 6 additions & 6 deletions dotmotif/executors/Neo4jExecutor.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Copyright 2020 The Johns Hopkins University Applied Physics Laboratory.
Copyright 2021 The Johns Hopkins University Applied Physics Laboratory.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -280,12 +280,12 @@ def run(self, cypher: str, cursor=True):
return self.G.run(cypher).to_table()
return self.G.run(cypher)

def count(self, motif: "dotmotif", limit=None) -> int:
def count(self, motif: "dotmotif.Motif", limit=None) -> int:
"""
Count a motif in a larger graph.

Arguments:
motif (dotmotif.dotmotif)
motif (dotmotif.Motif)

"""
qry = self.motif_to_cypher(
Expand All @@ -295,12 +295,12 @@ def count(self, motif: "dotmotif", limit=None) -> int:
qry += f" LIMIT {limit}"
return int(self.G.run(qry).to_ndarray())

def find(self, motif: "dotmotif", limit=None, cursor=True):
def find(self, motif: "dotmotif.Motif", limit=None, cursor=True):
"""
Find a motif in a larger graph.

Arguments:
motif (dotmotif.dotmotif)
motif (dotmotif.Motif)

"""
qry = self.motif_to_cypher(motif, static_entity_labels=self._entity_labels)
Expand All @@ -312,7 +312,7 @@ def find(self, motif: "dotmotif", limit=None, cursor=True):

@staticmethod
def motif_to_cypher(
motif: "dotmotif", count_only: bool = False, static_entity_labels: dict = None,
motif: "dotmotif.Motif", count_only: bool = False, static_entity_labels: dict = None,
) -> str:
"""
Output a query suitable for Cypher-compatible engines (e.g. Neo4j).
Expand Down
Loading