Skip to content

Commit 027fbc7

Browse files
committed
Update to v0.11.1
1 parent e62ad6f commit 027fbc7

14 files changed

Lines changed: 202 additions & 397 deletions

File tree

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [0.11.1] - 2025-12-06
9+
10+
### Added
11+
12+
- **AnalysisContext API**: `analyze()` now returns an `AnalysisContext` for max-flow, shortest paths, and sensitivity analysis with reusable state.
13+
14+
### Changed
15+
16+
- **Performance runner & workflows**: Reuse bound `AnalysisContext` to avoid rebuilding Core graphs across repeated analyses.
17+
- **Docs & examples**: Updated guides and reference docs to describe the new analysis API and bound-context workflow.
18+
- **Failure handling**: More consistent tracking of disabled nodes and links during analysis.

docs/reference/api-full.md

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

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

17-
Modules auto-discovered: 43
17+
Modules auto-discovered: 42
1818

1919
---
2020

@@ -2448,37 +2448,6 @@ Returns:
24482448

24492449
---
24502450

2451-
## ngraph.exec.analysis.types
2452-
2453-
Typed protocols for analysis IPC payloads.
2454-
2455-
Defines lightweight, serializable structures used across worker boundaries
2456-
during parallel analysis execution.
2457-
2458-
### FlowResult
2459-
2460-
Normalized result record for a flow pair in one iteration.
2461-
2462-
Keys:
2463-
src: Source label
2464-
dst: Destination label
2465-
metric: Name of metric ('capacity' or 'placement_ratio')
2466-
value: Numeric value for the metric
2467-
stats: Optional FlowStats with compact details
2468-
priority: Optional demand priority (only for placement results)
2469-
2470-
### FlowStats
2471-
2472-
Compact per-flow statistics for aggregation.
2473-
2474-
Keys:
2475-
cost_distribution: Mapping of path cost to flow volume.
2476-
edges: List of edge identifiers (string form).
2477-
edges_kind: Meaning of edges list: 'min_cut' for capacity analysis,
2478-
'used' for demand placement edge usage.
2479-
2480-
---
2481-
24822451
## ngraph.exec.demand.builder
24832452

24842453
Builders for traffic matrices.
@@ -2592,8 +2561,8 @@ all iterations.
25922561

25932562
Parallelism: The C++ Core backend releases the GIL during computation,
25942563
enabling true parallelism with Python threads. With graph caching, most
2595-
per-iteration work happens in GIL-free C++ code, achieving near-linear
2596-
scaling with thread count.
2564+
per-iteration work runs in GIL-free C++ code; speedup depends on workload
2565+
and parallelism level.
25972566

25982567
### AnalysisFunction
25992568

docs/reference/api.md

Lines changed: 109 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,40 @@ Quick links:
1010

1111
This section provides a curated guide to NetGraph's Python API, organized by typical usage patterns.
1212

13-
## Performance Notes
13+
## 1. Programmatic Quickstart
1414

15-
NetGraph uses a **hybrid Python+C++ architecture**:
15+
Minimal, copy-pastable start: build a tiny network, run max-flow, and reuse a bound context.
1616

17-
- **High-level APIs** (Network, Scenario, Workflow) are pure Python with ergonomic interfaces
18-
- **Core algorithms** (shortest paths, max-flow, K-shortest paths) execute in optimized C++ via NetGraph-Core
19-
- **GIL released** during algorithm execution for true parallel processing
20-
- **Transparent integration**: You work with Python objects; Core acceleration is automatic
21-
22-
All public APIs accept and return Python types (Network, Node, Link, FlowSummary, etc.).
23-
The C++ layer is an implementation detail you generally don't interact with directly.
17+
```python
18+
from ngraph import Network, Node, Link, analyze, Mode
19+
20+
# Build a small directed network
21+
net = Network()
22+
net.add_node(Node(name="A"))
23+
net.add_node(Node(name="B"))
24+
net.add_node(Node(name="C"))
25+
net.add_link(Link(source="A", target="B", capacity=10.0, cost=1.0))
26+
net.add_link(Link(source="B", target="C", capacity=5.0, cost=1.0))
27+
28+
# One-off max-flow (unbound context)
29+
flow = analyze(net).max_flow("^A$", "^C$", mode=Mode.COMBINE)
30+
print(flow) # {('^A$', '^C$'): 5.0}
31+
32+
# Detailed flow with cost distribution
33+
detailed = analyze(net).max_flow_detailed("^A$", "^C$", mode=Mode.COMBINE)
34+
(_, _), summary = next(iter(detailed.items()))
35+
print(summary.total_flow, summary.cost_distribution)
36+
37+
# Bound context for repeated runs with exclusions
38+
ctx = analyze(net, source="^A$", sink="^C$", mode=Mode.COMBINE)
39+
baseline = ctx.max_flow()
40+
# Exclude first link by getting its ID from the network
41+
first_link_id = next(iter(net.links.keys()))
42+
degraded = ctx.max_flow(excluded_links={first_link_id})
43+
print("baseline", baseline, "degraded", degraded)
44+
```
2445

25-
## 1. Fundamentals
46+
## 2. Fundamentals
2647

2748
The core components that form the foundation of most NetGraph programs.
2849

@@ -66,9 +87,9 @@ from ngraph import Network, Node, Link, analyze
6687

6788
# Create a tiny network
6889
network = Network()
69-
network.add_node(Node(name="n1"))
70-
network.add_node(Node(name="n2"))
71-
network.add_link(Link(source="n1", target="n2", capacity=100.0))
90+
network.add_node(Node(name="n1", risk_groups={"rack1"}))
91+
network.add_node(Node(name="n2", risk_groups={"rack2"}))
92+
network.add_link(Link(source="n1", target="n2", capacity=100.0, risk_groups={"fiber_bundle_A"}))
7293

7394
# Calculate maximum flow using analyze()
7495
flow_result = analyze(network).max_flow("^n1$", "^n2$")
@@ -83,6 +104,7 @@ print(flow_result) # {("^n1$", "^n2$"): 100.0}
83104
**Key Concepts:**
84105

85106
- **disabled flags:** Node.disabled and Link.disabled mark components as inactive in the scenario topology (use `excluded_nodes`/`excluded_links` parameters for temporary analysis-time exclusion)
107+
- **Risk Groups:** Nodes and links can be tagged with risk group names (e.g., "rack1", "fiber_bundle") to model shared failure domains.
86108
- **Node selection:** Use regex patterns anchored at start (e.g., `"^datacenter.*"`) or attribute directives (`"attr:role"`) to select and group nodes (see DSL Node Selection)
87109

88110
### Results
@@ -110,7 +132,7 @@ print(list(all_data["steps"].keys()))
110132

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

113-
## 2. Basic Analysis
135+
## 3. Basic Analysis
114136

115137
Essential analysis capabilities for network evaluation.
116138

@@ -145,11 +167,11 @@ print(summary.cost_distribution) # Dict[float, float] mapping cost to flow volu
145167
**Key Functions:**
146168

147169
- `analyze(network, *, source=None, sink=None, mode=Mode.COMBINE)` - Create analysis context
148-
- `ctx.max_flow(source, sink, *, mode, flow_placement, shortest_path, excluded_nodes, excluded_links)` - Maximum flow
149-
- `ctx.max_flow_detailed(...)` - Maximum flow with cost distribution and optional min-cut
170+
- `ctx.max_flow(source, sink, *, mode, shortest_path, require_capacity, flow_placement, excluded_nodes, excluded_links)` - Maximum flow
171+
- `ctx.max_flow_detailed(..., include_min_cut=False)` - Maximum flow with cost distribution and optional min-cut
150172
- `ctx.sensitivity(...)` - Identify critical edges and their impact on flow
151-
- `ctx.shortest_path_cost(source, sink, *, mode, excluded_nodes, excluded_links)` - Shortest path cost
152-
- `ctx.shortest_paths(source, sink, *, mode)` - Full Path objects
173+
- `ctx.shortest_path_cost(source, sink, *, mode, edge_select=ALL_MIN_COST, excluded_nodes, excluded_links)` - Shortest path cost
174+
- `ctx.shortest_paths(source, sink, *, mode, edge_select, split_parallel_edges)` - Full Path objects
153175

154176
**Key Concepts:**
155177

@@ -159,6 +181,8 @@ print(summary.cost_distribution) # Dict[float, float] mapping cost to flow volu
159181
- **FlowPlacement.EQUAL_BALANCED (ECMP):** Equal split across parallel paths
160182
- **shortest_path=True:** Restricts flow to lowest-cost paths only (IP/IGP routing semantics)
161183
- **shortest_path=False:** Uses all paths progressively (TE/SDN semantics)
184+
- **require_capacity=True:** Flow cannot exceed link capacity (default)
185+
- **require_capacity=False:** Unconstrained flow for capacity-free analysis
162186

163187
### Efficient Repeated Analysis (Bound Context)
164188

@@ -217,9 +241,9 @@ k_paths = analyze(network).k_shortest_paths(
217241

218242
**Key Functions:**
219243

220-
- `ctx.shortest_path_cost(source, sink, *, mode, edge_select)` - Cost only, no path objects
221-
- `ctx.shortest_paths(source, sink, *, mode, edge_select, split_parallel_edges)` - Full Path objects
222-
- `ctx.k_shortest_paths(source, sink, *, max_k, max_path_cost, max_path_cost_factor)` - Multiple paths per pair
244+
- `ctx.shortest_path_cost(source, sink, *, mode, edge_select=ALL_MIN_COST)` - Cost only, no path objects
245+
- `ctx.shortest_paths(source, sink, *, mode, edge_select=ALL_MIN_COST, split_parallel_edges=False)` - Full Path objects
246+
- `ctx.k_shortest_paths(source, sink, *, mode=PAIRWISE, max_k=3, max_path_cost, max_path_cost_factor, excluded_nodes, excluded_links)` - Multiple paths per pair
223247

224248
### Sensitivity Analysis
225249

@@ -242,7 +266,7 @@ for pair, edge_impacts in sensitivity.items():
242266
print(f" {edge_key}: -{flow_reduction:.2f}")
243267
```
244268

245-
## 3. Monte Carlo Analysis
269+
## 4. Monte Carlo Analysis
246270

247271
Probabilistic failure analysis using FailureManager.
248272

@@ -251,30 +275,44 @@ Probabilistic failure analysis using FailureManager.
251275
**Purpose:** Execute Monte Carlo failure scenarios and aggregate results across multiple iterations.
252276

253277
```python
254-
from ngraph import Network, FailureManager
278+
from ngraph import Network, Node, Link, FailureManager
279+
from ngraph.model.failure.policy import FailurePolicy, FailureMode, FailureRule
255280
from ngraph.model.failure.policy_set import FailurePolicySet
256281

282+
# Build a simple network
283+
network = Network()
284+
for name in ["A", "B", "C"]:
285+
network.add_node(Node(name=name))
286+
network.add_link(Link("A", "B", capacity=100.0))
287+
network.add_link(Link("B", "C", capacity=100.0))
288+
289+
# Define failure policy: randomly choose 1 link to fail
290+
rule = FailureRule(entity_scope="link", rule_type="choice", count=1) # scope can be "node", "link", or "risk_group"
291+
mode = FailureMode(weight=1.0, rules=[rule])
292+
policy = FailurePolicy(modes=[mode])
293+
policy_set = FailurePolicySet(policies={"single_link": policy})
294+
257295
# Create failure manager
258296
fm = FailureManager(
259297
network=network,
260-
failure_policy_set=failure_policy_set,
261-
policy_name="single_link_failure"
298+
failure_policy_set=policy_set,
299+
policy_name="single_link"
262300
)
263301

264302
# Run max-flow Monte Carlo analysis
265303
results = fm.run_max_flow_monte_carlo(
266-
source_path="^dc/.*",
267-
sink_path="^edge/.*",
304+
source_path="^A$",
305+
sink_path="^C$",
268306
mode="combine",
269-
iterations=1000,
270-
parallelism="auto",
307+
iterations=100,
308+
parallelism=1,
271309
baseline=True, # Include no-failure baseline
272-
seed=42 # For reproducibility
310+
seed=42 # For reproducibility
273311
)
274312

275313
# Access results
276314
for iter_result in results["results"]:
277-
print(f"Flow: {iter_result.summary.total_placed}")
315+
print(f"Flow: {iter_result.summary.total_placed:.1f}")
278316
```
279317

280318
**Key Methods:**
@@ -283,7 +321,7 @@ for iter_result in results["results"]:
283321
- `run_demand_placement_monte_carlo(...)` - Traffic demand placement under failures
284322
- `run_monte_carlo_analysis(analysis_func, ...)` - Generic Monte Carlo with custom function
285323

286-
## 4. Workflow Steps
324+
## 5. Workflow Steps
287325

288326
Pre-built analysis steps for YAML-driven workflows.
289327

@@ -324,11 +362,33 @@ workflow:
324362
- step_type: MaximumSupportedDemand
325363
name: "find_alpha_star"
326364
matrix_name: "peak_traffic"
327-
search_strategy: "bisection"
328-
precision: 0.01
365+
alpha_start: 1.0
366+
growth_factor: 2.0
367+
resolution: 0.01
368+
seeds_per_alpha: 1
329369
```
330370
331-
## 5. Types Reference
371+
### NetworkStats Step
372+
373+
```yaml
374+
workflow:
375+
- step_type: NetworkStats
376+
name: "baseline_stats"
377+
include_disabled: false
378+
excluded_nodes: ["n1"]
379+
```
380+
381+
### CostPower Step
382+
383+
```yaml
384+
workflow:
385+
- step_type: CostPower
386+
name: "cost_power_analysis"
387+
include_disabled: false
388+
aggregation_level: 2
389+
```
390+
391+
## 6. Types Reference
332392
333393
### Enums
334394
@@ -375,10 +435,10 @@ iter_result.flows # List[FlowEntry]
375435
iter_result.summary # FlowSummary
376436
```
377437

378-
## 6. Complete Example
438+
## 7. Complete Example
379439

380440
```python
381-
from ngraph import Network, Node, Link, analyze, Mode, FlowPlacement
441+
from ngraph import Network, Node, Link, analyze, Mode
382442

383443
# Build network
384444
network = Network()
@@ -419,3 +479,15 @@ for pair, impacts in sensitivity.items():
419479
for edge, impact in sorted(impacts.items(), key=lambda x: -x[1])[:3]:
420480
print(f" {edge}: {impact:.1f}")
421481
```
482+
483+
## 8. Performance Notes
484+
485+
NetGraph uses a hybrid Python+C++ architecture:
486+
487+
- **High-level APIs** (Network, Scenario, Workflow) are pure Python
488+
- **Core algorithms** (shortest paths, max-flow, K-shortest paths) execute in optimized C++ via NetGraph-Core
489+
- **GIL released** during algorithm execution for parallel processing
490+
- **Transparent integration**: You work with Python objects; Core acceleration is automatic
491+
492+
All public APIs accept and return Python types (Network, Node, Link, FlowSummary, etc.).
493+
The C++ layer is an implementation detail you generally don't interact with directly.

0 commit comments

Comments
 (0)