Skip to content

Commit b2451ba

Browse files
committed
Update to v0.12.1
- Added `ngraph.lib.nx` module for NetworkX integration, including `from_networkx()` and `to_networkx()` functions for graph conversion. - Introduced `NodeMap` and `EdgeMap` classes for bidirectional lookups of node and edge IDs post-conversion. - Updated documentation to reflect new features and usage examples.
1 parent 2934e8d commit b2451ba

10 files changed

Lines changed: 1196 additions & 20 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.12.1] - 2025-12-07
9+
10+
### Added
11+
12+
- **NetworkX interop**: New `ngraph.lib.nx` module with `from_networkx()` and `to_networkx()` for converting between NetworkX graphs and netgraph_core.StrictMultiDiGraph
13+
- **Mapping classes**: `NodeMap` and `EdgeMap` for bidirectional node/edge ID lookups after conversion
14+
815
## [0.12.0] - 2025-12-06
916

1017
### Changed

docs/reference/api-full.md

Lines changed: 154 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,15 @@ Quick links:
1212
- [CLI Reference](cli.md)
1313
- [DSL Reference](dsl.md)
1414

15-
Generated from source code on: December 06, 2025 at 12:38 UTC
15+
Generated from source code on: December 06, 2025 at 18:54 UTC
1616

17-
Modules auto-discovered: 42
17+
Modules auto-discovered: 44
18+
19+
---
20+
21+
## ngraph._version
22+
23+
ngraph version metadata.
1824

1925
---
2026

@@ -2600,6 +2606,152 @@ Attributes:
26002606

26012607
---
26022608

2609+
## ngraph.lib.nx
2610+
2611+
NetworkX graph conversion utilities.
2612+
2613+
This module provides functions to convert between NetworkX graphs and the
2614+
internal graph representation used by ngraph for high-performance algorithms.
2615+
2616+
Example:
2617+
>>> import networkx as nx
2618+
>>> from ngraph.lib.nx import from_networkx, to_networkx
2619+
>>>
2620+
>>> # Create a NetworkX graph
2621+
>>> G = nx.DiGraph()
2622+
>>> G.add_edge("A", "B", capacity=100.0, cost=10)
2623+
>>> G.add_edge("B", "C", capacity=50.0, cost=5)
2624+
>>>
2625+
>>> # Convert to ngraph format for analysis
2626+
>>> graph, node_map, edge_map = from_networkx(G)
2627+
>>>
2628+
>>> # Use with ngraph algorithms...
2629+
>>>
2630+
>>> # Convert back to NetworkX
2631+
>>> G_out = to_networkx(graph, node_map)
2632+
2633+
### EdgeMap
2634+
2635+
Bidirectional mapping between internal edge IDs and original edge references.
2636+
2637+
When converting a NetworkX graph, each edge is assigned an internal integer ID
2638+
(ext_edge_id). This class preserves the mapping for interpreting algorithm
2639+
results and updating the original graph.
2640+
2641+
Attributes:
2642+
to_ref: Maps internal edge ID to original (source, target, key) tuple
2643+
from_ref: Maps original (source, target, key) to list of internal edge IDs
2644+
(list because bidirectional=True creates two IDs per edge)
2645+
2646+
Example:
2647+
>>> graph, node_map, edge_map = from_networkx(G)
2648+
>>> # After running algorithms, map flow results back to original edges
2649+
>>> for ext_id, flow in enumerate(flow_state.edge_flow_view()):
2650+
... if flow > 0:
2651+
... u, v, key = edge_map.to_ref[ext_id]
2652+
... G.edges[u, v, key]["flow"] = flow
2653+
2654+
**Attributes:**
2655+
2656+
- `to_ref` (Dict[int, EdgeRef]) = {}
2657+
- `from_ref` (Dict[EdgeRef, List[int]]) = {}
2658+
2659+
### NodeMap
2660+
2661+
Bidirectional mapping between node names and integer indices.
2662+
2663+
When converting a NetworkX graph to the internal representation, node names
2664+
(which can be any hashable type) are mapped to contiguous integer indices
2665+
starting from 0. This class preserves the mapping for result interpretation
2666+
and back-conversion.
2667+
2668+
Attributes:
2669+
to_index: Maps original node names to integer indices
2670+
to_name: Maps integer indices back to original node names
2671+
2672+
Example:
2673+
>>> node_map = NodeMap.from_names(["A", "B", "C"])
2674+
>>> node_map.to_index["A"]
2675+
0
2676+
>>> node_map.to_name[1]
2677+
'B'
2678+
2679+
**Attributes:**
2680+
2681+
- `to_index` (Dict[Hashable, int]) = {}
2682+
- `to_name` (Dict[int, Hashable]) = {}
2683+
2684+
**Methods:**
2685+
2686+
- `from_names(names: 'List[Hashable]') -> "'NodeMap'"` - Create a NodeMap from a list of node names.
2687+
2688+
### from_networkx(G: 'NxGraph', *, capacity_attr: 'str' = 'capacity', cost_attr: 'str' = 'cost', default_capacity: 'float' = 1.0, default_cost: 'int' = 1, bidirectional: 'bool' = False) -> 'Tuple[netgraph_core.StrictMultiDiGraph, NodeMap, EdgeMap]'
2689+
2690+
Convert a NetworkX graph to ngraph's internal graph format.
2691+
2692+
Converts any NetworkX graph (DiGraph, MultiDiGraph, Graph, MultiGraph) to
2693+
netgraph_core.StrictMultiDiGraph. Node names are mapped to integer indices;
2694+
the returned NodeMap and EdgeMap preserve mappings for result interpretation.
2695+
2696+
Args:
2697+
G: NetworkX graph (DiGraph, MultiDiGraph, Graph, or MultiGraph)
2698+
capacity_attr: Edge attribute name for capacity (default: "capacity")
2699+
cost_attr: Edge attribute name for cost (default: "cost")
2700+
default_capacity: Capacity value when attribute is missing (default: 1.0)
2701+
default_cost: Cost value when attribute is missing (default: 1)
2702+
bidirectional: If True, add reverse edge for each edge. Useful for
2703+
undirected connectivity analysis. (default: False)
2704+
2705+
Returns:
2706+
Tuple of (graph, node_map, edge_map) where:
2707+
2708+
- graph: netgraph_core.StrictMultiDiGraph ready for algorithms
2709+
- node_map: NodeMap for converting node indices back to names
2710+
- edge_map: EdgeMap for converting edge IDs back to (u, v, key) refs
2711+
2712+
Raises:
2713+
TypeError: If G is not a NetworkX graph
2714+
ValueError: If graph has no nodes
2715+
2716+
Example:
2717+
>>> import networkx as nx
2718+
>>> G = nx.DiGraph()
2719+
>>> G.add_edge("src", "dst", capacity=100.0, cost=10)
2720+
>>> graph, node_map, edge_map = from_networkx(G)
2721+
>>> graph.num_nodes()
2722+
2
2723+
>>> node_map.to_index["src"]
2724+
0
2725+
>>> edge_map.to_ref[0] # First edge
2726+
('dst', 'src', 0) # sorted node order: dst < src
2727+
2728+
### to_networkx(graph: 'netgraph_core.StrictMultiDiGraph', node_map: 'Optional[NodeMap]' = None, *, capacity_attr: 'str' = 'capacity', cost_attr: 'str' = 'cost') -> "'nx.MultiDiGraph'"
2729+
2730+
Convert ngraph's internal graph format back to NetworkX MultiDiGraph.
2731+
2732+
Reconstructs a NetworkX graph from the internal representation. If a
2733+
NodeMap is provided, original node names are restored; otherwise, nodes
2734+
are labeled with integer indices.
2735+
2736+
Args:
2737+
graph: netgraph_core.StrictMultiDiGraph to convert
2738+
node_map: Optional NodeMap to restore original node names.
2739+
If None, nodes are labeled 0, 1, 2, ...
2740+
capacity_attr: Edge attribute name for capacity (default: "capacity")
2741+
cost_attr: Edge attribute name for cost (default: "cost")
2742+
2743+
Returns:
2744+
nx.MultiDiGraph with edges and attributes from the internal graph
2745+
2746+
Example:
2747+
>>> graph, node_map, edge_map = from_networkx(G)
2748+
>>> # ... run algorithms ...
2749+
>>> G_out = to_networkx(graph, node_map)
2750+
>>> list(G_out.nodes())
2751+
['A', 'B', 'C']
2752+
2753+
---
2754+
26032755

26042756
## Error Handling
26052757

docs/reference/api.md

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,74 @@ print(list(all_data["steps"].keys()))
132132

133133
**Integration:** Used by all workflow steps for result storage. Provides consistent access pattern for analysis outputs.
134134

135-
## 3. Basic Analysis
135+
## 3. NetworkX Integration
136+
137+
Convert between NetworkX graphs and the internal graph format for algorithm execution.
138+
139+
### Converting from NetworkX
140+
141+
```python
142+
import networkx as nx
143+
from ngraph import from_networkx, to_networkx
144+
import netgraph_core
145+
146+
# Create or load a NetworkX graph
147+
G = nx.DiGraph()
148+
G.add_edge("A", "B", capacity=100.0, cost=10)
149+
G.add_edge("B", "C", capacity=50.0, cost=5)
150+
G.add_edge("A", "C", capacity=30.0, cost=25)
151+
152+
# Convert to internal format
153+
graph, node_map, edge_map = from_networkx(G)
154+
155+
# Use with Core algorithms
156+
backend = netgraph_core.Backend.cpu()
157+
algorithms = netgraph_core.Algorithms(backend)
158+
handle = algorithms.build_graph(graph)
159+
160+
# Run shortest path
161+
src_idx = node_map.to_index["A"]
162+
dst_idx = node_map.to_index["C"]
163+
dists, _ = algorithms.spf(handle, src=src_idx, dst=dst_idx)
164+
print(f"Shortest path cost A->C: {dists[dst_idx]}") # 15 (via B)
165+
```
166+
167+
**Key Functions:**
168+
169+
- `from_networkx(G, *, capacity_attr, cost_attr, default_capacity, default_cost, bidirectional)` - Convert NetworkX graph to internal format
170+
- `to_networkx(graph, node_map, *, capacity_attr, cost_attr)` - Convert back to NetworkX MultiDiGraph
171+
172+
**Mapping Classes:**
173+
174+
- `NodeMap` - Bidirectional mapping between node names and integer indices
175+
- `to_index[name]` - Get integer index for node name
176+
- `to_name[idx]` - Get node name for integer index
177+
- `EdgeMap` - Bidirectional mapping between edge IDs and original edge references
178+
- `to_ref[edge_id]` - Get (source, target, key) tuple for edge ID
179+
- `from_ref[(u, v, key)]` - Get list of edge IDs for original edge
180+
181+
**Options:**
182+
183+
- `bidirectional=True` - Add reverse edge for each edge (for undirected analysis)
184+
- `capacity_attr` / `cost_attr` - Custom attribute names for capacity and cost
185+
- `default_capacity` / `default_cost` - Default values when attributes missing
186+
187+
### Writing Results Back
188+
189+
```python
190+
# After algorithm execution, map results back to original graph
191+
flow_state = netgraph_core.FlowState(graph)
192+
# ... place flow ...
193+
194+
# Use edge_map to update original NetworkX graph
195+
edge_flows = flow_state.edge_flow_view()
196+
for edge_id, flow in enumerate(edge_flows):
197+
if flow > 0:
198+
u, v, key = edge_map.to_ref[edge_id]
199+
G.edges[u, v, key]["flow"] = float(flow)
200+
```
201+
202+
## 4. Basic Analysis
136203

137204
Essential analysis capabilities for network evaluation.
138205

@@ -266,7 +333,7 @@ for pair, edge_impacts in sensitivity.items():
266333
print(f" {edge_key}: -{flow_reduction:.2f}")
267334
```
268335

269-
## 4. Monte Carlo Analysis
336+
## 5. Monte Carlo Analysis
270337

271338
Probabilistic failure analysis using FailureManager.
272339

@@ -321,7 +388,7 @@ for iter_result in results["results"]:
321388
- `run_demand_placement_monte_carlo(...)` - Traffic demand placement under failures
322389
- `run_monte_carlo_analysis(analysis_func, ...)` - Generic Monte Carlo with custom function
323390

324-
## 5. Workflow Steps
391+
## 6. Workflow Steps
325392

326393
Pre-built analysis steps for YAML-driven workflows.
327394

@@ -388,7 +455,7 @@ workflow:
388455
aggregation_level: 2
389456
```
390457
391-
## 6. Types Reference
458+
## 7. Types Reference
392459
393460
### Enums
394461
@@ -435,7 +502,7 @@ iter_result.flows # List[FlowEntry]
435502
iter_result.summary # FlowSummary
436503
```
437504

438-
## 7. Complete Example
505+
## 8. Complete Example
439506

440507
```python
441508
from ngraph import Network, Node, Link, analyze, Mode
@@ -480,7 +547,7 @@ for pair, impacts in sensitivity.items():
480547
print(f" {edge}: {impact:.1f}")
481548
```
482549

483-
## 8. Performance Notes
550+
## 9. Performance Notes
484551

485552
NetGraph uses a hybrid Python+C++ architecture:
486553

ngraph/__init__.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
analyze() - Create an analysis context for network queries
88
AnalysisContext - Prepared state for efficient repeated analysis
99
Network, Node, Link - Network topology model
10+
from_networkx() - Convert NetworkX graph to internal format
11+
to_networkx() - Convert internal format back to NetworkX
1012
1113
Example:
1214
from ngraph import Network, Node, Link, analyze
@@ -28,29 +30,22 @@
2830

2931
from __future__ import annotations
3032

31-
# Utilities
3233
from ngraph import cli, logging
33-
34-
# Analysis (primary API)
34+
from ngraph._version import __version__
3535
from ngraph.analysis import AnalysisContext, analyze
36-
37-
# Execution
3836
from ngraph.exec.failure.manager import FailureManager
37+
from ngraph.lib.nx import EdgeMap, NodeMap, from_networkx, to_networkx
3938
from ngraph.model.demand.matrix import TrafficMatrixSet
40-
41-
# Model
4239
from ngraph.model.network import Link, Network, Node, RiskGroup
4340
from ngraph.model.path import Path
4441
from ngraph.results.artifacts import CapacityEnvelope
45-
46-
# Results
4742
from ngraph.results.flow import FlowEntry, FlowIterationResult, FlowSummary
48-
49-
# Types
5043
from ngraph.types.base import EdgeSelect, FlowPlacement, Mode
5144
from ngraph.types.dto import EdgeRef, MaxFlowResult
5245

5346
__all__ = [
47+
# Version
48+
"__version__",
5449
# Model
5550
"Network",
5651
"Node",
@@ -74,6 +69,11 @@
7469
"CapacityEnvelope",
7570
# Execution
7671
"FailureManager",
72+
# Library integrations (NetworkX)
73+
"EdgeMap",
74+
"NodeMap",
75+
"from_networkx",
76+
"to_networkx",
7777
# Utilities
7878
"cli",
7979
"logging",

ngraph/_version.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""ngraph version metadata."""
2+
3+
__all__ = ["__version__"]
4+
5+
__version__ = "0.12.1"

ngraph/lib/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
"""Library utilities for ngraph.
2+
3+
This package contains integration modules for external libraries.
4+
"""
5+
6+
from ngraph.lib.nx import EdgeMap, NodeMap, from_networkx, to_networkx
7+
8+
__all__ = [
9+
"EdgeMap",
10+
"NodeMap",
11+
"from_networkx",
12+
"to_networkx",
13+
]

0 commit comments

Comments
 (0)