Skip to content

Commit 35e3853

Browse files
committed
add graphml tests
1 parent 8af5e24 commit 35e3853

File tree

1 file changed

+114
-0
lines changed

1 file changed

+114
-0
lines changed

tests/test_graph.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,119 @@ def test_to_networkx_types_and_node_attributes():
365365
assert pytest.approx(nxG[0][1]["weight"]) == G.adj[0, 1]
366366

367367

368+
@pytest.mark.skipif(not HAS_NX, reason="networkx not installed")
369+
def test_graphml_roundtrip_undirected_with_meta(tmp_path, S_dense, meta_df):
370+
"""Round-trip via GraphML for undirected, weighted graph with metadata."""
371+
G = Graph.from_dense(
372+
S_dense,
373+
directed=False,
374+
weighted=True,
375+
mode="similarity",
376+
meta=meta_df,
377+
)
378+
path = tmp_path / "graph_undirected.graphml"
379+
380+
# Export to GraphML
381+
G.to_graphml(path)
382+
383+
# Import back
384+
G2 = Graph.from_graphml(path)
385+
386+
assert G2.n_nodes == G.n_nodes
387+
assert G2.directed == G.directed
388+
assert G2.weighted == G.weighted
389+
assert G2.mode == G.mode
390+
391+
# Adjacency must be equal
392+
np.testing.assert_array_almost_equal(G2.adj.toarray(), G.adj.toarray())
393+
394+
# Metadata should be preserved (content-wise)
395+
assert G2.meta is not None
396+
397+
# Compare metadata ignoring column order and dtype
398+
meta1 = G2.meta.reindex(sorted(G2.meta.columns), axis=1).reset_index(drop=True)
399+
meta2 = G.meta.reindex(sorted(G.meta.columns), axis=1).reset_index(drop=True)
400+
pd.testing.assert_frame_equal(meta1, meta2, check_dtype=False)
401+
402+
403+
@pytest.mark.skipif(not HAS_NX, reason="networkx not installed")
404+
def test_graphml_roundtrip_directed(tmp_path):
405+
"""Round-trip via GraphML for a directed weighted graph."""
406+
A = _csr([1.0, 2.0, 3.0], [0, 1, 2], [1, 2, 0], 3)
407+
G = Graph.from_csr(A, directed=True, weighted=True, mode="distance")
408+
path = tmp_path / "graph_directed.graphml"
409+
410+
G.to_graphml(path)
411+
G2 = Graph.from_graphml(path)
412+
413+
assert G2.n_nodes == G.n_nodes
414+
assert G2.directed is True
415+
assert G2.weighted is True
416+
assert G2.mode == "distance"
417+
np.testing.assert_array_almost_equal(G2.adj.toarray(), G.adj.toarray())
418+
419+
420+
@pytest.mark.skipif(not HAS_NX, reason="networkx not installed")
421+
def test_from_graphml_uses_default_mode_when_missing(tmp_path):
422+
"""
423+
Importing GraphML created directly by networkx without a 'mode' attribute
424+
should fall back to default_mode.
425+
"""
426+
import networkx as nx
427+
428+
G_nx = nx.Graph()
429+
G_nx.add_edge(0, 1, weight=1.5)
430+
path = tmp_path / "no_mode.graphml"
431+
nx.write_graphml(G_nx, path)
432+
433+
# No 'mode' in graph attributes, so default_mode is used
434+
G = Graph.from_graphml(path, default_mode="distance")
435+
assert G.mode == "distance"
436+
assert not G.directed
437+
assert G.weighted
438+
assert G.n_nodes == 2
439+
np.testing.assert_array_almost_equal(
440+
G.adj.toarray(),
441+
np.array([[0.0, 1.5], [1.5, 0.0]], dtype=float),
442+
)
443+
444+
445+
@pytest.mark.skipif(not HAS_NX, reason="networkx not installed")
446+
def test_from_graphml_builds_metadata_from_node_attributes(tmp_path):
447+
"""
448+
Node attributes in a GraphML file should become meta columns in Graph.
449+
"""
450+
import networkx as nx
451+
452+
G_nx = nx.Graph()
453+
G_nx.add_node(0, name="a", group=1)
454+
G_nx.add_node(1, name="b", group=2)
455+
G_nx.add_edge(0, 1, weight=2.0)
456+
457+
path = tmp_path / "with_node_attrs.graphml"
458+
nx.write_graphml(G_nx, path)
459+
460+
G = Graph.from_graphml(path, default_mode="similarity")
461+
462+
assert G.n_nodes == 2
463+
assert G.meta is not None
464+
assert list(G.meta.columns) == ["group", "name"] or sorted(G.meta.columns) == ["group", "name"]
465+
# Check content, ignoring column order and dtype
466+
meta_sorted = G.meta.reindex(sorted(G.meta.columns), axis=1)
467+
expected = pd.DataFrame({"name": ["a", "b"], "group": [1, 2]})
468+
expected_sorted = expected.reindex(sorted(expected.columns), axis=1)
469+
pd.testing.assert_frame_equal(
470+
meta_sorted.reset_index(drop=True),
471+
expected_sorted,
472+
check_dtype=False,
473+
)
474+
# adjacency matches the edge
475+
np.testing.assert_array_almost_equal(
476+
G.adj.toarray(),
477+
np.array([[0.0, 2.0], [2.0, 0.0]], dtype=float),
478+
)
479+
480+
368481
@pytest.mark.skipif(not HAS_IG, reason="python-igraph not installed")
369482
def test_to_igraph_types_and_attributes():
370483
A = _csr([0.2, 0.9, 0.3], [0, 1, 2], [1, 2, 0], 3)
@@ -382,6 +495,7 @@ def test_to_igraph_types_and_attributes():
382495
assert "weight" in igG.es.attributes()
383496

384497

498+
385499
# ----------------- Distance/similarity conversion -----------------
386500
def test_convert_mode_distance_to_similarity_and_back_dense(S_dense, meta_df):
387501
G = Graph.from_dense(

0 commit comments

Comments
 (0)