Skip to content

Commit 85cf9b7

Browse files
committed
improve handling of equal-cost paths in single-path mode for better capacity management
1 parent d3eff07 commit 85cf9b7

2 files changed

Lines changed: 12 additions & 7 deletions

File tree

include/netgraph/core/shortest_paths.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ struct PredDAG {
2626

2727
// Compute shortest paths from src using Dijkstra's algorithm.
2828
// Returns (distances, predecessor_dag) where distances[v] is the shortest cost to reach v
29-
// (or inf if unreachable), and predecessor_dag encodes all equal-cost paths.
29+
// (or inf if unreachable), and predecessor_dag encodes the shortest path structure.
3030
//
3131
// Parameters:
3232
// - dst: if provided, algorithm may exit early once destination is reached
33-
// - multipath: if true, keep all equal-cost predecessors; if false, keep only one per node
33+
// - multipath: if true, keep all equal-cost predecessors; if false, keep one per node
34+
// (in single-path mode, ties are broken by preferring higher bottleneck capacity)
3435
// - selection: edge selection policy (multi-edge, capacity filtering, tie-breaking)
3536
// - residual: if provided, use these capacities instead of graph's original capacities.
3637
// Passing a non-empty 'residual' span forces capacity gating:

src/shortest_paths.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Implementation helpers for shortest_paths.hpp extras. */
1+
/* Path enumeration from PredDAG (resolve_to_paths). */
22
#include "netgraph/core/shortest_paths.hpp"
33

44
#include <algorithm>
@@ -236,10 +236,12 @@ shortest_paths_core(const StrictMultiDiGraph& g, NodeId src,
236236
// Extract min-cost node from priority queue.
237237
// Structured binding: auto [d_u, neg_res_u, u] = ... destructures the tuple.
238238
auto [d_u, neg_res_u, u] = pq.top(); pq.pop();
239-
(void)neg_res_u; // Residual was only for tie-breaking in queue, not needed here
240239
if (u < 0 || u >= N) continue;
241240
// Skip stale entries (node already processed at a lower cost).
242241
if (d_u > dist[static_cast<std::size_t>(u)]) continue;
242+
// Skip residual-stale entries in single-path mode (same cost but outdated residual).
243+
if (!multipath && d_u == dist[static_cast<std::size_t>(u)] &&
244+
-neg_res_u < min_residual_to_node[static_cast<std::size_t>(u)] - kEpsilon) continue;
243245

244246
// Early exit optimization: record when we first reach destination.
245247
if (early_exit && u == dst_node && !have_best_dst) { best_dst_cost = d_u; have_best_dst = true; }
@@ -311,7 +313,7 @@ shortest_paths_core(const StrictMultiDiGraph& g, NodeId src,
311313
selected_edges.clear();
312314
selected_edges.push_back(static_cast<EdgeId>(best_edge_id));
313315
}
314-
// Update distance and predecessors if we found a better path.
316+
// Update distance and predecessors if we found a better path (or equal-cost with better capacity).
315317
if (!selected_edges.empty()) {
316318
Cost new_cost = static_cast<Cost>(d_u + min_edge_cost);
317319
auto v_idx = static_cast<std::size_t>(v);
@@ -327,8 +329,10 @@ shortest_paths_core(const StrictMultiDiGraph& g, NodeId src,
327329
}
328330
Cap path_residual = std::min(min_residual_to_node[static_cast<std::size_t>(u)], max_edge_residual);
329331

330-
// Relaxation: found shorter path to v.
331-
if (new_cost < dist[v_idx]) {
332+
// Relaxation: found shorter path to v, or equal-cost path with better capacity (single-path mode).
333+
if (new_cost < dist[v_idx] ||
334+
(!multipath && new_cost == dist[v_idx] &&
335+
path_residual > min_residual_to_node[v_idx] + kEpsilon)) {
332336
dist[v_idx] = new_cost;
333337
min_residual_to_node[v_idx] = path_residual;
334338
pred_lists[v_idx].clear();

0 commit comments

Comments
 (0)