@@ -10,10 +10,10 @@ See [Tutorial](../getting-started/tutorial.md) for CLI usage and bundled scenari
1010
1111``` text
1212 [1,1] & [1,2] [1,1] & [1,2]
13- A ─────────────────── B ────────────── C
14- │ │
15- │ [2,3] │ [2,3]
16- └──────────────────── D ───────────────┘
13+ A -------------------- B ---------------- C
14+ | |
15+ | [2,3] | [2,3]
16+ +-------------------- D -----------------+
1717
1818[1,1] and [1,2] are parallel edges between A and B.
1919They have the same metric of 1 but different capacities (1 and 2).
@@ -23,8 +23,7 @@ Let's create this network by using NetGraph's scenario system:
2323
2424``` python
2525from ngraph.scenario import Scenario
26- from ngraph.types.base import FlowPlacement
27- from ngraph.solver.maxflow import max_flow, max_flow_with_details
26+ from ngraph import analyze, Mode, FlowPlacement
2827
2928# Define network topology with parallel paths
3029scenario_yaml = """
@@ -42,7 +41,7 @@ network:
4241
4342 # Create links with different capacities and costs
4443 links:
45- # Parallel edges between A→ B
44+ # Parallel edges between A-> B
4645 - source: A
4746 target: B
4847 link_params:
@@ -54,7 +53,7 @@ network:
5453 capacity: 2
5554 cost: 1
5655
57- # Parallel edges between B→ C
56+ # Parallel edges between B-> C
5857 - source: B
5958 target: C
6059 link_params:
@@ -66,7 +65,7 @@ network:
6665 capacity: 2
6766 cost: 1
6867
69- # Alternative path A→D→ C
68+ # Alternative path A->D-> C
7069 - source: A
7170 target: D
7271 link_params:
@@ -88,34 +87,34 @@ Note that here we used a simple `nodes` and `links` structure to directly define
8887
8988### Flow Analysis Variants
9089
91- Now let's run MaxFlow using the high-level Network API:
90+ Now let's run MaxFlow using the ` analyze() ` API:
9291
9392``` python
9493# 1. "True" maximum flow (uses all available paths)
95- max_flow_all = max_flow (network, source_path = " A " , sink_path = " C " )
94+ max_flow_all = analyze (network).max_flow( " ^A$ " , " ^C$ " , mode = Mode. COMBINE )
9695print (f " Maximum flow (all paths): { max_flow_all} " )
97- # Result: {('A ', 'C '): 6.0} (uses both A→B→ C path capacity of 3 and A→D→ C path capacity of 3)
96+ # Result: {('^A$ ', '^C$ '): 6.0} (uses both A->B-> C path capacity of 3 and A->D-> C path capacity of 3)
9897
9998# 2. Flow along shortest paths only
100- max_flow_shortest = max_flow(
101- network ,
102- source_path = " A " ,
103- sink_path = " C " ,
99+ max_flow_shortest = analyze(network). max_flow(
100+ " ^A$ " ,
101+ " ^C$ " ,
102+ mode = Mode. COMBINE ,
104103 shortest_path = True
105104)
106105print (f " Flow on shortest paths: { max_flow_shortest} " )
107- # Result: {('A ', 'C '): 3.0} (only uses A→B→ C path, ignoring higher-cost A→D→ C path)
106+ # Result: {('^A$ ', '^C$ '): 3.0} (only uses A->B-> C path, ignoring higher-cost A->D-> C path)
108107
109108# 3. Equal-balanced flow placement on shortest paths
110- max_flow_shortest_balanced = max_flow(
111- network ,
112- source_path = " A " ,
113- sink_path = " C " ,
109+ max_flow_shortest_balanced = analyze(network). max_flow(
110+ " ^A$ " ,
111+ " ^C$ " ,
112+ mode = Mode. COMBINE ,
114113 shortest_path = True ,
115114 flow_placement = FlowPlacement.EQUAL_BALANCED
116115)
117116print (f " Equal-balanced flow: { max_flow_shortest_balanced} " )
118- # Result: {('A ', 'C '): 2.0} (splits flow equally across parallel edges in A→ B and B→ C)
117+ # Result: {('^A$ ', '^C$ '): 2.0} (splits flow equally across parallel edges in A-> B and B-> C)
119118```
120119
121120## Results Interpretation
@@ -132,11 +131,10 @@ Cost distribution shows how flow splits across path costs for latency/span analy
132131
133132``` python
134133# Get flow analysis with cost distribution
135- result = max_flow_with_details(
136- network,
137- source_path = " A" ,
138- sink_path = " C" ,
139- mode = " combine"
134+ result = analyze(network).max_flow_detailed(
135+ " ^A$" ,
136+ " ^C$" ,
137+ mode = Mode.COMBINE
140138)
141139
142140# Extract flow value and summary
@@ -150,83 +148,102 @@ print(f"Cost distribution: {summary.cost_distribution}")
150148# Cost distribution: {2.0: 3.0, 4.0: 3.0}
151149#
152150# This means:
153- # - 3.0 units of flow use paths with total cost 2.0 (A→B→ C path)
154- # - 3.0 units of flow use paths with total cost 4.0 (A→D→ C path)
151+ # - 3.0 units of flow use paths with total cost 2.0 (A->B-> C path)
152+ # - 3.0 units of flow use paths with total cost 4.0 (A->D-> C path)
155153```
156154
157155### Latency Span Analysis
158156
159157If link costs approximate latency, derive span summary from cost distribution:
160158
161159``` python
162- def analyze_latency_span (cost_distribution ):
163- """ Analyze latency characteristics from cost distribution."""
164- if not cost_distribution:
165- return " No flow paths available"
166-
167- total_flow = sum (cost_distribution.values())
168- weighted_avg_latency = sum (
169- cost * flow for cost, flow in cost_distribution.items()
170- ) / total_flow
171-
172- min_latency = min (cost_distribution.keys())
173- max_latency = max (cost_distribution.keys())
174- latency_span = max_latency - min_latency
175-
176- print (f " Latency Analysis: " )
177- print (f " Average latency: { weighted_avg_latency:.2f } " )
178- print (f " Latency range: { min_latency:.1f } - { max_latency:.1f } " )
179- print (f " Latency span: { latency_span:.1f } " )
180- print (f " Flow distribution: " )
181- for cost, flow in sorted (cost_distribution.items()):
182- percentage = (flow / total_flow) * 100
183- print (f " { percentage:.1f } % uses paths with latency { cost:.1f } " )
184-
185- # Example usage
186- analyze_latency_span(summary.cost_distribution)
160+ # Example cost distribution analysis
161+ cost_dist = summary.cost_distribution # {2.0: 3.0, 4.0: 3.0}
162+ total_flow = summary.total_flow # 6.0
163+
164+ # Calculate weighted average latency
165+ avg_latency = sum (cost * flow for cost, flow in cost_dist.items()) / total_flow
166+ print (f " Average latency: { avg_latency} " ) # 3.0
167+
168+ # Find min/max latency tiers
169+ min_latency = min (cost_dist.keys())
170+ max_latency = max (cost_dist.keys())
171+ print (f " Latency range: { min_latency} - { max_latency} " ) # 2.0 - 4.0
187172```
188173
189- This helps identify traffic concentration, latency span, and potential bottlenecks.
174+ ## Efficient Repeated Analysis
190175
191- ## Advanced Analysis: Failure Simulation
176+ For scenarios requiring multiple analyses with different exclusions (e.g., failure testing), use a bound context:
192177
193- You can analyze the network under different failure scenarios by excluding nodes or links:
178+ ``` python
179+ # Create bound context - graph built once
180+ ctx = analyze(network, source = " ^A$" , sink = " ^C$" , mode = Mode.COMBINE )
181+
182+ # Baseline capacity
183+ baseline = ctx.max_flow()
184+ print (f " Baseline: { baseline} " )
185+
186+ # Test various failure scenarios
187+ for node in [" B" , " D" ]:
188+ degraded = ctx.max_flow(excluded_nodes = {node})
189+ print (f " Without { node} : { degraded} " )
190+
191+ # Output:
192+ # Baseline: {('^A$', '^C$'): 6.0}
193+ # Without B: {('^A$', '^C$'): 3.0}
194+ # Without D: {('^A$', '^C$'): 3.0}
195+ ```
196+
197+ ## Sensitivity Analysis
198+
199+ Identify which edges are critical for the flow:
194200
195201``` python
196- # Identify link to fail
197- failed_links = set ()
198- for link_id, link in network.links.items():
199- if link.source == " A" and link.target == " D" :
200- failed_links.add(link_id)
201- break
202-
203- # Compare flows: baseline vs. with failure
204- baseline_flow_dict = max_flow(network, source_path = " A" , sink_path = " C" )
205- baseline_flow = baseline_flow_dict[(' A' , ' C' )]
206-
207- degraded_flow_dict = max_flow(
208- network,
209- source_path = " A" ,
210- sink_path = " C" ,
211- excluded_links = failed_links
202+ # Get sensitivity analysis
203+ sensitivity = analyze(network).sensitivity(
204+ " ^A$" ,
205+ " ^C$" ,
206+ mode = Mode.COMBINE ,
207+ shortest_path = False # Full max-flow mode
212208)
213- degraded_flow = degraded_flow_dict[(' A' , ' C' )]
214209
215- print (f " Baseline flow: { baseline_flow} " )
216- print (f " Flow with A->D link failed: { degraded_flow} " )
217- print (f " Impact: { baseline_flow - degraded_flow} units lost " )
210+ for pair, edge_impacts in sensitivity.items():
211+ print (f " Critical edges for { pair} : " )
212+ for edge_key, flow_reduction in sorted (edge_impacts.items(), key = lambda x : - x[1 ]):
213+ print (f " { edge_key} : - { flow_reduction:.1f } " )
218214```
219215
220- This analysis helps identify:
216+ ## Shortest Paths
221217
222- - ** Critical links** : Links whose failure significantly impacts flow
223- - ** Redundancy** : How well the network handles failures
224- - ** Vulnerability assessment** : Network resilience under different failure scenarios
218+ Get actual path objects for routing analysis:
225219
226- ## Next Steps
220+ ``` python
221+ from ngraph import EdgeSelect
222+
223+ # Get all equal-cost shortest paths
224+ paths = analyze(network).shortest_paths(
225+ " ^A$" ,
226+ " ^C$" ,
227+ mode = Mode.COMBINE ,
228+ edge_select = EdgeSelect.ALL_MIN_COST
229+ )
230+
231+ for pair, path_list in paths.items():
232+ print (f " Paths from { pair[0 ]} to { pair[1 ]} : " )
233+ for path in path_list:
234+ nodes = [elem[0 ] for elem in path.path]
235+ print (f " { ' -> ' .join(nodes)} (cost: { path.cost} ) " )
236+
237+ # Get k-shortest paths
238+ k_paths = analyze(network).k_shortest_paths(
239+ " ^A$" ,
240+ " ^C$" ,
241+ max_k = 3 ,
242+ mode = Mode.PAIRWISE
243+ )
227244
228- - ** [ Bundled Scenarios ] ( bundled-scenarios.md ) ** - Ready-to-run examples
229- - ** [ Clos Fabric Analysis ] ( clos-fabric.md ) ** - More complex example
230- - ** [ Workflow Reference ] ( ../reference/workflow.md ) ** - Analysis workflows and Monte Carlo simulation
231- - ** [ DSL Reference ] ( ../reference/dsl.md ) ** - Learn the full YAML syntax for scenarios
232- - ** [ API Reference ] ( ../reference/api.md ) ** - Explore the Python API for advanced usage
245+ for pair, path_list in k_paths.items():
246+ print ( f " Top { len (path_list) } paths from { pair[ 0 ] } to { pair[ 1 ] } : " )
247+ for i, path in enumerate (path_list, 1 ):
248+ print ( f " { i } . Cost: { path.cost } " )
249+ ```
0 commit comments