[PATCH NNGT] IO: graphml support for missing attributes
Export this patch
From: Tanguy Fardet <tanguyfardet@protonmail.com>
---
nngt/io/loading_helpers.py | 20 ++++++++++++++++-
nngt/lib/converters.py | 9 ++++++++
nngt/simulation/nest_utils.py | 4 ++++
testing/Networks/missing_attrs.graphml | 31 ++++++++++++++++++++++++++
testing/test_io.py | 22 ++++++++++++++++++
5 files changed, 85 insertions(+), 1 deletion(-)
create mode 100644 testing/Networks/missing_attrs.graphml
diff --git a/nngt/io/loading_helpers.py b/nngt/io/loading_helpers.py
index c07b18d..1448a4b 100755
--- a/nngt/io/loading_helpers.py
+++ b/nngt/io/loading_helpers.py
@@ -30,7 +30,7 @@ import types
import numpy as np
from ..lib.converters import (_np_dtype, _to_int, _to_string, _to_list,
- _string_from_object, _python_type)
+ _string_from_object, _python_type, _default_value)
__all__ = [
@@ -389,9 +389,16 @@ def _get_edges_graphml(lst_lines, attributes, *args, di_attributes=None,
key_to_name = di_notif["eattr_keytoname"]
+ eattrs = {k: v for k, v in
+ zip(di_notif["edge_attributes"], di_notif["edge_attr_types"])}
+
for attr in elt.findall("data", ns):
name = key_to_name[attr.get("key")]
di_attributes[name].append(convertor[name](attr.text))
+ del eattrs[name]
+
+ for k, v in eattrs.items():
+ di_attributes[k].append(_default_value(v))
return edges
@@ -429,13 +436,24 @@ def _get_node_attr(di_notif, separator, fmt=None, lines=None, convertor=None):
ns = di_notif["namespace"]
key_to_name = di_notif["nattr_keytoname"]
+ nattrs = {k: v for k, v in
+ zip(di_notif["node_attributes"], di_notif["node_attr_types"])}
+
for elt in di_notif["nodes"]:
+ all_attrs = nattrs.copy()
+
for attr in elt.findall("data", ns):
name = key_to_name[attr.get("key")]
+
if name not in di_nattr:
di_nattr[name] = []
+ del all_attrs[name]
+
di_nattr[name].append(convertor[name](attr.text))
+
+ for k, v in all_attrs.items():
+ di_nattr[k].append(_default_value(v))
else:
# edge list and neighbors formatting
nattr_name = {str("na_" + k): k for k in di_notif["node_attributes"]}
diff --git a/nngt/lib/converters.py b/nngt/lib/converters.py
index 041e371..97b6b73 100755
--- a/nngt/lib/converters.py
+++ b/nngt/lib/converters.py
@@ -122,3 +122,12 @@ def _type_converter(attribute_type):
return attribute_type
return _np_dtype(attribute_type)
+
+
+def _default_value(attribute_type):
+ if attribute_type in ("double", "float", "real"):
+ return np.NaN
+ elif attribute_type in ("int", "integer"):
+ return 0
+
+ return ""
diff --git a/nngt/simulation/nest_utils.py b/nngt/simulation/nest_utils.py
index 9da02b9..70f43f1 100755
--- a/nngt/simulation/nest_utils.py
+++ b/nngt/simulation/nest_utils.py
@@ -295,8 +295,12 @@ def randomize_neural_states(network, instructions, groups=None, nodes=None,
if nodes is not None and groups is not None:
raise InvalidArgument('`nodes` and `groups` cannot be set together.')
elif groups is not None:
+ if isinstance(groups, nngt.Group):
+ groups = [groups]
+
for group in groups:
gids.extend(group.nest_gids)
+
gids = list(set(gids))
else:
gids.extend(
diff --git a/testing/Networks/missing_attrs.graphml b/testing/Networks/missing_attrs.graphml
new file mode 100644
index 0000000..198ae07
--- /dev/null
+++ b/testing/Networks/missing_attrs.graphml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<graphml xmlns="http://graphml.graphdrawing.org/xmlns"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns
+ http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
+<!-- Created by igraph -->
+ <key id="v_name" for="node" attr.name="name" attr.type="string"/>
+ <key id="e_weight" for="edge" attr.name="eattr" attr.type="double"/>
+ <graph id="G" edgedefault="directed">
+ <node id="n0">
+ <data key="v_name">A</data>
+ </node>
+ <node id="n1"></node>
+ <node id="n2">
+ <data key="v_name">C</data>
+ </node>
+ <node id="n3">
+ <data key="v_name">D</data>
+ </node>
+ <edge source="n0" target="n1">
+ <data key="e_weight">4</data>
+ </edge>
+ <edge source="n0" target="n2">
+ <data key="e_weight">1</data>
+ </edge>
+ <edge source="n2" target="n3"></edge>
+ <edge source="n3" target="n1">
+ <data key="e_weight">0.5</data>
+ </edge>
+ </graph>
+</graphml>
diff --git a/testing/test_io.py b/testing/test_io.py
index c751d81..088339b 100644
--- a/testing/test_io.py
+++ b/testing/test_io.py
@@ -303,6 +303,27 @@ def test_node_attributes():
h.node_attributes["size"])
+@pytest.mark.mpi_skip
+def test_partial_graphml():
+ '''
+ Check that the GraphML file loads fine if some attributes are missing.
+ '''
+ g = nngt.load_from_file(os.path.join(current_dir,
+ "Networks/missing_attrs.graphml"))
+
+ nattrs = {0: "A", 1: "", 2: "C", 3: "D"}
+ eattrs = {(0, 1): 4, (0, 2): 1, (2, 3): np.NaN, (3, 1): 0.5}
+
+ for n, name in nattrs.items():
+ assert g.node_attributes["name"][n] == name
+
+ for e, value in eattrs.items():
+ if np.isnan(value):
+ assert np.isnan(g.get_edge_attributes(edges=e, name="eattr"))
+ else:
+ assert g.get_edge_attributes(edges=e, name="eattr") == value
+
+
# ---------- #
# Test suite #
# ---------- #
@@ -316,4 +337,5 @@ if __name__ == "__main__":
test_structure()
test_node_attributes()
test_spatial()
+ test_partial_graphml()
unittest.main()
--
2.32.0
NNGT/patches/.build.yml: SUCCESS in 28m2s
[IO: graphml support for missing attributes][0] from [~tfardet][1]
[0]: https://lists.sr.ht/~tfardet/nngt-developers/patches/26537
[1]: mailto:tanguyfardet@protonmail.com
✓ #628317 SUCCESS NNGT/patches/.build.yml https://builds.sr.ht/~tfardet/job/628317