Skip to content

Commit 0b03cb3

Browse files
thierry-martinezIvanIsCoding
authored andcommitted
Fix Qiskit#840: Add GraphML serializer (Qiskit#1464)
* Fix Qiskit#840: Add GraphML serializer This commit adds a GraphML serializer: ```python def write_graphml( graphs: list[PyGraph | PyDiGraph], keys: list[tuple[str, Domain, str, Type, Any]], path: str, /, compression: str | None = ..., ) -> None: ... ``` `keys` is a list of tuples: id, domain, name of the key, type, and default value. This commit also introduces the `read_graphml_with_keys` function, which returns the key definitions in the same format, along with the list of parsed graphs. The implementation preserves the ids of graphs, nodes, and edges when possible. If some ids conflict, fresh ids are generated in the written GraphML file. The `read_graphml` function has also been updated to store the graph id in the graph attributes, just like node and edge ids are stored in the corresponding attributes. The `write_graphml` function supports gzip compression, as does `read_graphml`. Note that the JSON node-link serializer (the other part of Qiskit#840) was already implemented in Qiskit#1091. Compared to Qiskit#1462: - Keys are passed explicitly instead of being inferred (which allows to use the types `float` and `int`, and to use default values); - Attributes for graphs, nodes, and edges are taken from the weight of elements, instead of relying on callbacks. This allows write_graphml to act as a proper reciprocal of read_graphml. Round-trip tests have been added. - IDs are taken from attributes when possible, instead of being generated from indices. - Multiple graphs can be written to the same file. - Gzip compression is supported. - Tests have been added. Regarding @IvanIsCoding's comment (Qiskit#1462 (comment)), about using https://github.com/jonasbb/petgraph-graphml: - Rustworkx's `graphml.rs` introduces an internal `Graph` data structure, which is used for `read_graphml`. It is natural to have `write_graphml` rely on the same data structure. - `petgraph-graphml` generates ids from indices, which prevents us from preserving ids accross the `read_graphml`/`write_graphml` round trip. * Fix clippy comments * Prefix types with GraphML Suggested by @IvanIsCoding: Qiskit#1464 (comment) * Black * Add release notes * Fix stubs error * Add documentation * Remove read_graphml_with_keys / write_graphml for single graph only * Use `DictMap` everywhere Suggested by @IvanIsCoding: Qiskit#1464 (comment) * rustfmt and clippy * Remove unused math module (ruff check) * Use `from __future__ import annotations` for Python <3.10 * Add stub for `write_graphml` * Remove `read_graphml_with_keys` from documentation * Apply suggestions from code review * Update rustworkx/__init__.py * Apply suggestions from code review --------- Co-authored-by: Ivan Carvalho <[email protected]>
1 parent 041cb69 commit 0b03cb3

File tree

8 files changed

+876
-74
lines changed

8 files changed

+876
-74
lines changed

docs/source/api/serialization.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ Serialization
88

99
rustworkx.node_link_json
1010
rustworkx.read_graphml
11+
rustworkx.write_graphml
1112
rustworkx.from_node_link_json_file
1213
rustworkx.parse_node_link_json
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
features:
3+
- |
4+
Added a new function :func:`~rustworkx.write_graphml` that writes
5+
a list of rustworkx graph objects to a file in GraphML format.
6+
other:
7+
- |
8+
When graphs read with :func:`~rustworkx.read_graphml` include IDs,
9+
these IDs are now stored in the graph attributes.

rustworkx/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2279,3 +2279,9 @@ def single_source_all_shortest_paths(
22792279
For most use cases, consider using `dijkstra_shortest_paths` for a single shortest path, which runs much faster.
22802280
"""
22812281
raise TypeError(f"Invalid Input Type {type(graph)} for graph")
2282+
2283+
2284+
@_rustworkx_dispatch
2285+
def write_graphml(graph, path, /, keys=None, compression=None):
2286+
""" """
2287+
raise TypeError(f"Invalid Input Type {type(graph)} for graph")

rustworkx/__init__.pyi

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,9 @@ from .rustworkx import directed_barabasi_albert_graph as directed_barabasi_alber
163163
from .rustworkx import undirected_random_bipartite_graph as undirected_random_bipartite_graph
164164
from .rustworkx import directed_random_bipartite_graph as directed_random_bipartite_graph
165165
from .rustworkx import read_graphml as read_graphml
166+
from .rustworkx import graph_write_graphml as graph_write_graphml
167+
from .rustworkx import digraph_write_graphml as digraph_write_graphml
168+
from .rustworkx import GraphMLKey as GraphMLKey
166169
from .rustworkx import digraph_node_link_json as digraph_node_link_json
167170
from .rustworkx import graph_node_link_json as graph_node_link_json
168171
from .rustworkx import from_node_link_json_file as from_node_link_json_file
@@ -662,3 +665,10 @@ def is_bipartite(graph: PyGraph[_S, _T] | PyDiGraph[_S, _T]) -> bool: ...
662665
def condensation(
663666
graph: PyDiGraph | PyGraph, /, sccs: list[int] | None = ...
664667
) -> PyDiGraph | PyGraph: ...
668+
def write_graphml(
669+
graph: PyGraph | PyDiGraph,
670+
path: str,
671+
/,
672+
keys: list[GraphMLKey] | None = ...,
673+
compression: str | None = ...,
674+
) -> None: ...

rustworkx/rustworkx.pyi

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,30 @@ class ColoringStrategy:
6868
Saturation: Any
6969
IndependentSet: Any
7070

71+
@final
72+
class GraphMLDomain:
73+
Node: GraphMLDomain
74+
Edge: GraphMLDomain
75+
Graph: GraphMLDomain
76+
All: GraphMLDomain
77+
78+
@final
79+
class GraphMLType:
80+
Boolean: GraphMLType
81+
Int: GraphMLType
82+
Float: GraphMLType
83+
Double: GraphMLType
84+
String: GraphMLType
85+
Long: GraphMLType
86+
87+
@final
88+
class GraphMLKey:
89+
id: str
90+
domain: GraphMLDomain
91+
name: str
92+
ty: GraphMLType
93+
default: Any
94+
7195
# Cartesian product
7296

7397
def digraph_cartesian_product(
@@ -685,6 +709,20 @@ def read_graphml(
685709
/,
686710
compression: str | None = ...,
687711
) -> list[PyGraph | PyDiGraph]: ...
712+
def graph_write_graphml(
713+
graph: PyGraph,
714+
path: str,
715+
/,
716+
keys: list[GraphMLKey] | None = ...,
717+
compression: str | None = ...,
718+
) -> None: ...
719+
def digraph_write_graphml(
720+
graph: PyDiGraph,
721+
path: str,
722+
/,
723+
keys: list[GraphMLKey] | None = ...,
724+
compression: str | None = ...,
725+
) -> None: ...
688726
def digraph_node_link_json(
689727
graph: PyDiGraph[_S, _T],
690728
/,

0 commit comments

Comments
 (0)