aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/networkx/algorithms/isomorphism/matchhelpers.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/networkx/algorithms/isomorphism/matchhelpers.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are hereHEADmaster
Diffstat (limited to '.venv/lib/python3.12/site-packages/networkx/algorithms/isomorphism/matchhelpers.py')
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/isomorphism/matchhelpers.py352
1 files changed, 352 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/isomorphism/matchhelpers.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/isomorphism/matchhelpers.py
new file mode 100644
index 00000000..b48820d4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/isomorphism/matchhelpers.py
@@ -0,0 +1,352 @@
+"""Functions which help end users define customize node_match and
+edge_match functions to use during isomorphism checks.
+"""
+
+import math
+import types
+from itertools import permutations
+
+__all__ = [
+ "categorical_node_match",
+ "categorical_edge_match",
+ "categorical_multiedge_match",
+ "numerical_node_match",
+ "numerical_edge_match",
+ "numerical_multiedge_match",
+ "generic_node_match",
+ "generic_edge_match",
+ "generic_multiedge_match",
+]
+
+
+def copyfunc(f, name=None):
+ """Returns a deepcopy of a function."""
+ return types.FunctionType(
+ f.__code__, f.__globals__, name or f.__name__, f.__defaults__, f.__closure__
+ )
+
+
+def allclose(x, y, rtol=1.0000000000000001e-05, atol=1e-08):
+ """Returns True if x and y are sufficiently close, elementwise.
+
+ Parameters
+ ----------
+ rtol : float
+ The relative error tolerance.
+ atol : float
+ The absolute error tolerance.
+
+ """
+ # assume finite weights, see numpy.allclose() for reference
+ return all(math.isclose(xi, yi, rel_tol=rtol, abs_tol=atol) for xi, yi in zip(x, y))
+
+
+categorical_doc = """
+Returns a comparison function for a categorical node attribute.
+
+The value(s) of the attr(s) must be hashable and comparable via the ==
+operator since they are placed into a set([]) object. If the sets from
+G1 and G2 are the same, then the constructed function returns True.
+
+Parameters
+----------
+attr : string | list
+ The categorical node attribute to compare, or a list of categorical
+ node attributes to compare.
+default : value | list
+ The default value for the categorical node attribute, or a list of
+ default values for the categorical node attributes.
+
+Returns
+-------
+match : function
+ The customized, categorical `node_match` function.
+
+Examples
+--------
+>>> import networkx.algorithms.isomorphism as iso
+>>> nm = iso.categorical_node_match("size", 1)
+>>> nm = iso.categorical_node_match(["color", "size"], ["red", 2])
+
+"""
+
+
+def categorical_node_match(attr, default):
+ if isinstance(attr, str):
+
+ def match(data1, data2):
+ return data1.get(attr, default) == data2.get(attr, default)
+
+ else:
+ attrs = list(zip(attr, default)) # Python 3
+
+ def match(data1, data2):
+ return all(data1.get(attr, d) == data2.get(attr, d) for attr, d in attrs)
+
+ return match
+
+
+categorical_edge_match = copyfunc(categorical_node_match, "categorical_edge_match")
+
+
+def categorical_multiedge_match(attr, default):
+ if isinstance(attr, str):
+
+ def match(datasets1, datasets2):
+ values1 = {data.get(attr, default) for data in datasets1.values()}
+ values2 = {data.get(attr, default) for data in datasets2.values()}
+ return values1 == values2
+
+ else:
+ attrs = list(zip(attr, default)) # Python 3
+
+ def match(datasets1, datasets2):
+ values1 = set()
+ for data1 in datasets1.values():
+ x = tuple(data1.get(attr, d) for attr, d in attrs)
+ values1.add(x)
+ values2 = set()
+ for data2 in datasets2.values():
+ x = tuple(data2.get(attr, d) for attr, d in attrs)
+ values2.add(x)
+ return values1 == values2
+
+ return match
+
+
+# Docstrings for categorical functions.
+categorical_node_match.__doc__ = categorical_doc
+categorical_edge_match.__doc__ = categorical_doc.replace("node", "edge")
+tmpdoc = categorical_doc.replace("node", "edge")
+tmpdoc = tmpdoc.replace("categorical_edge_match", "categorical_multiedge_match")
+categorical_multiedge_match.__doc__ = tmpdoc
+
+
+numerical_doc = """
+Returns a comparison function for a numerical node attribute.
+
+The value(s) of the attr(s) must be numerical and sortable. If the
+sorted list of values from G1 and G2 are the same within some
+tolerance, then the constructed function returns True.
+
+Parameters
+----------
+attr : string | list
+ The numerical node attribute to compare, or a list of numerical
+ node attributes to compare.
+default : value | list
+ The default value for the numerical node attribute, or a list of
+ default values for the numerical node attributes.
+rtol : float
+ The relative error tolerance.
+atol : float
+ The absolute error tolerance.
+
+Returns
+-------
+match : function
+ The customized, numerical `node_match` function.
+
+Examples
+--------
+>>> import networkx.algorithms.isomorphism as iso
+>>> nm = iso.numerical_node_match("weight", 1.0)
+>>> nm = iso.numerical_node_match(["weight", "linewidth"], [0.25, 0.5])
+
+"""
+
+
+def numerical_node_match(attr, default, rtol=1.0000000000000001e-05, atol=1e-08):
+ if isinstance(attr, str):
+
+ def match(data1, data2):
+ return math.isclose(
+ data1.get(attr, default),
+ data2.get(attr, default),
+ rel_tol=rtol,
+ abs_tol=atol,
+ )
+
+ else:
+ attrs = list(zip(attr, default)) # Python 3
+
+ def match(data1, data2):
+ values1 = [data1.get(attr, d) for attr, d in attrs]
+ values2 = [data2.get(attr, d) for attr, d in attrs]
+ return allclose(values1, values2, rtol=rtol, atol=atol)
+
+ return match
+
+
+numerical_edge_match = copyfunc(numerical_node_match, "numerical_edge_match")
+
+
+def numerical_multiedge_match(attr, default, rtol=1.0000000000000001e-05, atol=1e-08):
+ if isinstance(attr, str):
+
+ def match(datasets1, datasets2):
+ values1 = sorted(data.get(attr, default) for data in datasets1.values())
+ values2 = sorted(data.get(attr, default) for data in datasets2.values())
+ return allclose(values1, values2, rtol=rtol, atol=atol)
+
+ else:
+ attrs = list(zip(attr, default)) # Python 3
+
+ def match(datasets1, datasets2):
+ values1 = []
+ for data1 in datasets1.values():
+ x = tuple(data1.get(attr, d) for attr, d in attrs)
+ values1.append(x)
+ values2 = []
+ for data2 in datasets2.values():
+ x = tuple(data2.get(attr, d) for attr, d in attrs)
+ values2.append(x)
+ values1.sort()
+ values2.sort()
+ for xi, yi in zip(values1, values2):
+ if not allclose(xi, yi, rtol=rtol, atol=atol):
+ return False
+ else:
+ return True
+
+ return match
+
+
+# Docstrings for numerical functions.
+numerical_node_match.__doc__ = numerical_doc
+numerical_edge_match.__doc__ = numerical_doc.replace("node", "edge")
+tmpdoc = numerical_doc.replace("node", "edge")
+tmpdoc = tmpdoc.replace("numerical_edge_match", "numerical_multiedge_match")
+numerical_multiedge_match.__doc__ = tmpdoc
+
+
+generic_doc = """
+Returns a comparison function for a generic attribute.
+
+The value(s) of the attr(s) are compared using the specified
+operators. If all the attributes are equal, then the constructed
+function returns True.
+
+Parameters
+----------
+attr : string | list
+ The node attribute to compare, or a list of node attributes
+ to compare.
+default : value | list
+ The default value for the node attribute, or a list of
+ default values for the node attributes.
+op : callable | list
+ The operator to use when comparing attribute values, or a list
+ of operators to use when comparing values for each attribute.
+
+Returns
+-------
+match : function
+ The customized, generic `node_match` function.
+
+Examples
+--------
+>>> from operator import eq
+>>> from math import isclose
+>>> from networkx.algorithms.isomorphism import generic_node_match
+>>> nm = generic_node_match("weight", 1.0, isclose)
+>>> nm = generic_node_match("color", "red", eq)
+>>> nm = generic_node_match(["weight", "color"], [1.0, "red"], [isclose, eq])
+
+"""
+
+
+def generic_node_match(attr, default, op):
+ if isinstance(attr, str):
+
+ def match(data1, data2):
+ return op(data1.get(attr, default), data2.get(attr, default))
+
+ else:
+ attrs = list(zip(attr, default, op)) # Python 3
+
+ def match(data1, data2):
+ for attr, d, operator in attrs:
+ if not operator(data1.get(attr, d), data2.get(attr, d)):
+ return False
+ else:
+ return True
+
+ return match
+
+
+generic_edge_match = copyfunc(generic_node_match, "generic_edge_match")
+
+
+def generic_multiedge_match(attr, default, op):
+ """Returns a comparison function for a generic attribute.
+
+ The value(s) of the attr(s) are compared using the specified
+ operators. If all the attributes are equal, then the constructed
+ function returns True. Potentially, the constructed edge_match
+ function can be slow since it must verify that no isomorphism
+ exists between the multiedges before it returns False.
+
+ Parameters
+ ----------
+ attr : string | list
+ The edge attribute to compare, or a list of node attributes
+ to compare.
+ default : value | list
+ The default value for the edge attribute, or a list of
+ default values for the edgeattributes.
+ op : callable | list
+ The operator to use when comparing attribute values, or a list
+ of operators to use when comparing values for each attribute.
+
+ Returns
+ -------
+ match : function
+ The customized, generic `edge_match` function.
+
+ Examples
+ --------
+ >>> from operator import eq
+ >>> from math import isclose
+ >>> from networkx.algorithms.isomorphism import generic_node_match
+ >>> nm = generic_node_match("weight", 1.0, isclose)
+ >>> nm = generic_node_match("color", "red", eq)
+ >>> nm = generic_node_match(["weight", "color"], [1.0, "red"], [isclose, eq])
+
+ """
+
+ # This is slow, but generic.
+ # We must test every possible isomorphism between the edges.
+ if isinstance(attr, str):
+ attr = [attr]
+ default = [default]
+ op = [op]
+ attrs = list(zip(attr, default)) # Python 3
+
+ def match(datasets1, datasets2):
+ values1 = []
+ for data1 in datasets1.values():
+ x = tuple(data1.get(attr, d) for attr, d in attrs)
+ values1.append(x)
+ values2 = []
+ for data2 in datasets2.values():
+ x = tuple(data2.get(attr, d) for attr, d in attrs)
+ values2.append(x)
+ for vals2 in permutations(values2):
+ for xi, yi in zip(values1, vals2):
+ if not all(map(lambda x, y, z: z(x, y), xi, yi, op)):
+ # This is not an isomorphism, go to next permutation.
+ break
+ else:
+ # Then we found an isomorphism.
+ return True
+ else:
+ # Then there are no isomorphisms between the multiedges.
+ return False
+
+ return match
+
+
+# Docstrings for numerical functions.
+generic_node_match.__doc__ = generic_doc
+generic_edge_match.__doc__ = generic_doc.replace("node", "edge")