Skip to content

Commit

Permalink
Workaround is_isormorphic returning false for 2 empty graphs (#422)
Browse files Browse the repository at this point in the history
* Workaround is_isormorphic returning false for 2 empty graphs

This commit works around the bug in the vf2 implementation for empty
graphs where no mapping is found for is_isomorphic. It's just a
workaround for the issue reported in #421 and I'm sure there is a better
real fix, but in the interest of having a solution proposed in the short
term this was fast.

Fixes #421

* Bump versions and prepare for release

* Update src/isomorphism/vf2.rs

Co-authored-by: Ivan Carvalho <[email protected]>

* Add subgraph isomorphic tests too
  • Loading branch information
mtreinish authored Aug 25, 2021
1 parent a71e5bf commit b97bc14
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 3 deletions.
4 changes: 3 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "retworkx"
description = "A python graph library implemented in Rust"
version = "0.10.0"
version = "0.10.1"
authors = ["Matthew Treinish <[email protected]>"]
license = "Apache-2.0"
readme = "README.md"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
prelude: >
This is a bugfix release that fixes a regression introduced in the previous
0.10.0 release. In 0.10.0 the :func:`~retworkx.is_isomorphic` function when
comparing 2 empty graph objects would incorrectly return ``False``.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def readme():

setup(
name="retworkx",
version="0.10.0",
version="0.10.1",
description="A python graph library implemented in Rust",
long_description=readme(),
long_description_content_type='text/markdown',
Expand Down
6 changes: 6 additions & 0 deletions src/isomorphism/vf2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,12 @@ pub fn is_isomorphic<Ty: EdgeType>(
{
return Ok(false);
}
// TODO: Remove this. This is just a hacky workaround to fix #421 fast,
// we should fix VF2Algorithm.next() to return an empty hashmap for
// 2 empty graphs
if g1.node_count() == 0 && g1.edge_count() == 0 {
return Ok(true);
}

let mut vf2 = Vf2Algorithm::new(
py, g0, g1, node_match, edge_match, id_order, ordering, induced,
Expand Down
32 changes: 32 additions & 0 deletions tests/digraph/test_isomorphic.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,38 @@


class TestIsomorphic(unittest.TestCase):
def test_empty_isomorphic_identical(self):
dag_a = retworkx.PyDAG()
dag_b = retworkx.PyDAG()

for id_order in [False, True]:
with self.subTest(id_order=id_order):
self.assertTrue(
retworkx.is_isomorphic(dag_a, dag_b, id_order=id_order)
)

def test_empty_isomorphic_mismatch_node_data(self):
dag_a = retworkx.PyDAG()
dag_b = retworkx.PyDAG()

for id_order in [False, True]:
with self.subTest(id_order=id_order):
self.assertTrue(
retworkx.is_isomorphic(dag_a, dag_b, id_order=id_order)
)

def test_empty_isomorphic_compare_nodes_mismatch_node_data(self):
dag_a = retworkx.PyDAG()
dag_b = retworkx.PyDAG()

for id_order in [False, True]:
with self.subTest(id_order=id_order):
self.assertTrue(
retworkx.is_isomorphic(
dag_a, dag_b, lambda x, y: x == y, id_order=id_order
)
)

def test_isomorphic_identical(self):
dag_a = retworkx.PyDAG()
dag_b = retworkx.PyDAG()
Expand Down
28 changes: 28 additions & 0 deletions tests/digraph/test_subgraph_isomorphic.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,34 @@


class TestSubgraphIsomorphic(unittest.TestCase):
def test_empty_subgraph_isomorphic_identical(self):
g_a = retworkx.PyDiGraph()
for id_order in [False, True]:
with self.subTest(id_order=id_order):
self.assertTrue(
retworkx.is_subgraph_isomorphic(g_a, g_a, id_order=id_order)
)

def test_empty_subgraph_isomorphic_mismatch_node_data(self):
g_a = retworkx.PyDiGraph()
g_b = retworkx.PyDiGraph()
for id_order in [False, True]:
with self.subTest(id_order=id_order):
self.assertTrue(
retworkx.is_subgraph_isomorphic(g_a, g_b, id_order=id_order)
)

def test_empty_subgraph_isomorphic_compare_nodes_mismatch_node_data(self):
g_a = retworkx.PyDiGraph()
g_b = retworkx.PyDiGraph()
for id_order in [False, True]:
with self.subTest(id_order=id_order):
self.assertTrue(
retworkx.is_subgraph_isomorphic(
g_a, g_b, lambda x, y: x == y, id_order=id_order
)
)

def test_subgraph_isomorphic_identical(self):
g_a = retworkx.PyDiGraph()
nodes = g_a.add_nodes_from(["a_1", "a_2", "a_3"])
Expand Down
29 changes: 29 additions & 0 deletions tests/graph/test_isomorphic.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,35 @@


class TestIsomorphic(unittest.TestCase):
def test_empty_isomorphic_identical(self):
g_a = retworkx.PyGraph()
g_b = retworkx.PyGraph()
for id_order in [False, True]:
with self.subTest(id_order=id_order):
self.assertTrue(
retworkx.is_isomorphic(g_a, g_b, id_order=id_order)
)

def test_empty_isomorphic_mismatch_node_data(self):
g_a = retworkx.PyGraph()
g_b = retworkx.PyGraph()
for id_order in [False, True]:
with self.subTest(id_order=id_order):
self.assertTrue(
retworkx.is_isomorphic(g_a, g_b, id_order=id_order)
)

def test_empty_isomorphic_compare_nodes_mismatch_node_data(self):
g_a = retworkx.PyGraph()
g_b = retworkx.PyGraph()
for id_order in [False, True]:
with self.subTest(id_order=id_order):
self.assertTrue(
retworkx.is_isomorphic(
g_a, g_b, lambda x, y: x == y, id_order=id_order
)
)

def test_isomorphic_identical(self):
g_a = retworkx.PyGraph()
g_b = retworkx.PyGraph()
Expand Down
28 changes: 28 additions & 0 deletions tests/graph/test_subgraph_isomorphic.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,34 @@


class TestSubgraphIsomorphic(unittest.TestCase):
def test_empty_subgraph_isomorphic_identical(self):
g_a = retworkx.PyGraph()
for id_order in [False, True]:
with self.subTest(id_order=id_order):
self.assertTrue(
retworkx.is_subgraph_isomorphic(g_a, g_a, id_order=id_order)
)

def test_empty_subgraph_isomorphic_mismatch_node_data(self):
g_a = retworkx.PyGraph()
g_b = retworkx.PyGraph()
for id_order in [False, True]:
with self.subTest(id_order=id_order):
self.assertTrue(
retworkx.is_subgraph_isomorphic(g_a, g_b, id_order=id_order)
)

def test_empty_subgraph_isomorphic_compare_nodes_mismatch_node_data(self):
g_a = retworkx.PyGraph()
g_b = retworkx.PyGraph()
for id_order in [False, True]:
with self.subTest(id_order=id_order):
self.assertTrue(
retworkx.is_subgraph_isomorphic(
g_a, g_b, lambda x, y: x == y, id_order=id_order
)
)

def test_subgraph_isomorphic_identical(self):
g_a = retworkx.PyGraph()
nodes = g_a.add_nodes_from(["a_1", "a_2", "a_3"])
Expand Down

0 comments on commit b97bc14

Please sign in to comment.