@@ -1081,34 +1081,89 @@ TEST(MaxFlow, ShortestPath_MustSaturateAllEqualCostPaths) {
10811081}
10821082
10831083// =============================================================================
1084- // TEST MATRIX SUMMARY
1085- // =============================================================================
1086- //
1087- // Coverage Matrix (Proportional placement with shortest_path=True):
1088- //
1089- // | Path Structure | Equal Cap | Asym Cap | Zero Cap | Validated |
1090- // |---------------------------|-----------|----------|----------|-----------|
1091- // | Single path | ✓ | ✓ | - | ✓ |
1092- // | 2 disjoint equal-cost | ✓ | ✓ | ✓ | ✓ |
1093- // | 3 disjoint equal-cost | ✓ | - | - | ✓ |
1094- // | 4 disjoint equal-cost | ✓ | - | - | ✓ |
1095- // | 10 disjoint equal-cost | ✓ | - | - | ✓ |
1096- // | 2 cost tiers | ✓ | - | - | ✓ |
1097- // | 3 cost tiers | ✓ | - | - | ✓ |
1098- // | Grid topology | ✓ | - | - | ✓ |
1099- // | Shared bottleneck | ✓ | - | - | ✓ |
1100- // | 3-tier Clos fabric | ✓ | - | - | ✓ |
1101- // | Triangle (combine mode) | ✓ | - | - | ✓ |
1102- // | Simple parallel paths | ✓ | - | - | ✓ |
1103- //
1104- // Additional validation dimensions:
1105- // - Placement modes: Proportional ✓, EqualBalanced ✓
1106- // - shortest_path: True ✓, False ✓
1107- // - Optional features: edge flows ✓, residuals ✓, reachable ✓, min-cut ✓
1108- // - Masks: node mask ✓, edge mask ✓
1109- // - Batch operations: ✓
1110- // - Real-world topologies: Clos ✓, Triangle ✓, Parallel paths ✓
1111- //
1112- // TOTAL TEST COUNT: 45 tests
1113- // COVERAGE: All critical parameter combinations + NetGraph integration validated
1084+ // SECTION 11: SENSITIVITY ANALYSIS
11141085// =============================================================================
1086+
1087+ TEST (MaxFlow, Sensitivity_SinglePath) {
1088+ auto g = make_line_graph (3 );
1089+ auto be = make_cpu_backend ();
1090+ Algorithms algs (be);
1091+ auto gh = algs.build_graph (g);
1092+
1093+ MaxFlowOptions opts;
1094+ opts.placement = FlowPlacement::Proportional;
1095+
1096+ auto results = algs.sensitivity_analysis (gh, 0 , 2 , opts);
1097+
1098+ // Path: 0->1->2. Both edges saturated and critical.
1099+ // Capacity is 1.0. Flow is 1.0.
1100+ // Removing any edge drops flow to 0. Delta = 1.0.
1101+ EXPECT_EQ (results.size (), 2u );
1102+ for (const auto & p : results) {
1103+ EXPECT_NEAR (p.second , 1.0 , 1e-9 );
1104+ }
1105+ }
1106+
1107+ TEST (MaxFlow, Sensitivity_TwoParallelPaths) {
1108+ // Two paths, capacity 10 each. Total 20.
1109+ auto g = make_n_disjoint_paths (2 , 10.0 );
1110+ auto be = make_cpu_backend ();
1111+ Algorithms algs (be);
1112+ auto gh = algs.build_graph (g);
1113+
1114+ MaxFlowOptions opts;
1115+ opts.placement = FlowPlacement::Proportional;
1116+
1117+ auto results = algs.sensitivity_analysis (gh, 0 , 3 , opts);
1118+
1119+ // All 4 edges are saturated.
1120+ // Removing any edge kills one path (flow 20 -> 10). Delta = 10.
1121+ EXPECT_EQ (results.size (), 4u );
1122+ for (const auto & p : results) {
1123+ EXPECT_NEAR (p.second , 10.0 , 1e-9 );
1124+ }
1125+ }
1126+
1127+ TEST (MaxFlow, Sensitivity_PartialSaturation) {
1128+ // Topology: S->A (cap 10), S->B (cap 5)
1129+ // A->T (cap 5), B->T (cap 10)
1130+ // Flow: S->A->T limited by A->T (5). S->B->T limited by S->B (5). Total 10.
1131+ // Saturated: A->T (edge 2), S->B (edge 1).
1132+ // Non-Saturated: S->A (edge 0), B->T (edge 3).
1133+
1134+ std::int32_t src[4 ] = {0 , 0 , 1 , 2 };
1135+ std::int32_t dst[4 ] = {1 , 2 , 3 , 3 };
1136+ double cap[4 ] = {10.0 , 5.0 , 5.0 , 10.0 };
1137+ std::int64_t cost[4 ] = {1 , 1 , 1 , 1 };
1138+
1139+ auto g = StrictMultiDiGraph::from_arrays (4 ,
1140+ std::span (src, 4 ), std::span (dst, 4 ),
1141+ std::span (cap, 4 ), std::span (cost, 4 ));
1142+
1143+ auto be = make_cpu_backend ();
1144+ Algorithms algs (be);
1145+ auto gh = algs.build_graph (g);
1146+
1147+ MaxFlowOptions opts;
1148+ opts.placement = FlowPlacement::Proportional;
1149+ opts.shortest_path = false ;
1150+
1151+ auto results = algs.sensitivity_analysis (gh, 0 , 3 , opts);
1152+
1153+ // Should only report saturated edges: S->B and A->T.
1154+ // (We assume edge indices follow insertion order: 0, 1, 2, 3)
1155+ // S->B is index 1. A->T is index 2.
1156+
1157+ // Check results
1158+ int count = 0 ;
1159+ for (const auto & p : results) {
1160+ if (p.first == 1 || p.first == 2 ) {
1161+ EXPECT_NEAR (p.second , 5.0 , 1e-9 );
1162+ count++;
1163+ } else {
1164+ // If non-saturated edges are reported (which they shouldn't be based on logic), fail.
1165+ FAIL () << " Reported sensitivity for non-saturated edge " << p.first ;
1166+ }
1167+ }
1168+ EXPECT_EQ (count, 2 );
1169+ }
0 commit comments