nxpp
Header-only graph utilities on top of Boost Graph Library
Loading...
Searching...
No Matches
flow.hpp
Go to the documentation of this file.
1#pragma once
2
11#include <boost/graph/edmonds_karp_max_flow.hpp>
12#include <boost/graph/push_relabel_max_flow.hpp>
13#include <boost/graph/cycle_canceling.hpp>
14#include <boost/graph/find_flow_cost.hpp>
15#include <boost/graph/successive_shortest_path_nonnegative_weights.hpp>
16
17#include <memory>
18#include <queue>
19#include <mutex>
20#include <set>
21
22#include "attributes.hpp"
23#include "multigraph.hpp"
24
25namespace nxpp {
26
48template <typename NodeID>
50 long value = 0;
51 std::map<std::pair<NodeID, NodeID>, long> flow;
52 std::map<std::size_t, long> edge_flows_by_id;
53};
54
76template <typename NodeID>
78 long flow = 0;
79 long cost = 0;
80 std::map<std::pair<NodeID, NodeID>, long> edge_flows;
81 std::map<std::size_t, long> edge_flows_by_id;
82};
83
84namespace detail {
85
86template <typename NodeID, typename FlowEdgeDesc>
88 std::size_t edge_id;
89 NodeID source_id;
90 NodeID target_id;
91 FlowEdgeDesc edge_desc;
92};
93
94template <typename NodeID>
96 using FlowTraits = boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS>;
97 using FlowGraph = boost::adjacency_list<
98 boost::vecS,
99 boost::vecS,
100 boost::directedS,
101 boost::no_property,
102 boost::property<
103 boost::edge_capacity_t,
104 long,
105 boost::property<
106 boost::edge_residual_capacity_t,
107 long,
108 boost::property<boost::edge_reverse_t, typename FlowTraits::edge_descriptor>
109 >
110 >
111 >;
112 using CapacityMap = typename boost::property_map<FlowGraph, boost::edge_capacity_t>::type;
113 using ResidualMap = typename boost::property_map<FlowGraph, boost::edge_residual_capacity_t>::type;
114 using ReverseMap = typename boost::property_map<FlowGraph, boost::edge_reverse_t>::type;
115 using FlowEdgeDesc = typename boost::graph_traits<FlowGraph>::edge_descriptor;
116
117 FlowGraph flow_graph;
118 CapacityMap capacity;
119 ResidualMap residual;
120 ReverseMap reverse;
121 std::vector<FlowEdgeRecord<NodeID, FlowEdgeDesc>> original_edges;
122
124 : flow_graph(),
125 capacity(boost::get(boost::edge_capacity, flow_graph)),
126 residual(boost::get(boost::edge_residual_capacity, flow_graph)),
127 reverse(boost::get(boost::edge_reverse, flow_graph)) {}
128};
129
130template <typename NodeID>
132 using FlowTraits = boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS>;
133 using FlowGraph = boost::adjacency_list<
134 boost::vecS,
135 boost::vecS,
136 boost::directedS,
137 boost::no_property,
138 boost::property<
139 boost::edge_weight_t,
140 long,
141 boost::property<
142 boost::edge_capacity_t,
143 long,
144 boost::property<
145 boost::edge_residual_capacity_t,
146 long,
147 boost::property<boost::edge_reverse_t, typename FlowTraits::edge_descriptor>
148 >
149 >
150 >
151 >;
152 using WeightMap = typename boost::property_map<FlowGraph, boost::edge_weight_t>::type;
153 using CapacityMap = typename boost::property_map<FlowGraph, boost::edge_capacity_t>::type;
154 using ResidualMap = typename boost::property_map<FlowGraph, boost::edge_residual_capacity_t>::type;
155 using ReverseMap = typename boost::property_map<FlowGraph, boost::edge_reverse_t>::type;
156 using FlowEdgeDesc = typename boost::graph_traits<FlowGraph>::edge_descriptor;
157
158 FlowGraph flow_graph;
159 WeightMap weight;
160 CapacityMap capacity;
161 ResidualMap residual;
162 ReverseMap reverse;
163 std::vector<FlowEdgeRecord<NodeID, FlowEdgeDesc>> original_edges;
164
166 : flow_graph(),
167 weight(boost::get(boost::edge_weight, flow_graph)),
168 capacity(boost::get(boost::edge_capacity, flow_graph)),
169 residual(boost::get(boost::edge_residual_capacity, flow_graph)),
170 reverse(boost::get(boost::edge_reverse, flow_graph)) {}
171};
172
173template <typename FlowState>
174void add_flow_vertices(FlowState& state, std::size_t count) {
175 for (std::size_t i = 0; i < count; ++i) {
176 boost::add_vertex(state.flow_graph);
177 }
178}
179
180template <typename FlowState>
181auto add_capacity_edge_pair(FlowState& state, std::size_t source_index, std::size_t target_index, long capacity_value) {
182 auto [edge, added] = boost::add_edge(source_index, target_index, state.flow_graph);
183 auto [reverse_edge, reverse_added] = boost::add_edge(target_index, source_index, state.flow_graph);
184 (void)added;
185 (void)reverse_added;
186 state.capacity[edge] = capacity_value;
187 state.capacity[reverse_edge] = 0;
188 state.reverse[edge] = reverse_edge;
189 state.reverse[reverse_edge] = edge;
190 return edge;
191}
192
193template <typename GraphWrapper, typename FlowState, typename EdgeIdFn, typename CapacityFn>
194void add_capacity_flow_edges(
195 const GraphWrapper& graph,
196 FlowState& state,
197 EdgeIdFn&& edge_id_for,
198 CapacityFn&& capacity_for
199) {
200 const auto& impl = graph.get_impl();
201 for (auto [eit, eend] = boost::edges(impl); eit != eend; ++eit) {
202 const auto source = boost::source(*eit, impl);
203 const auto target = boost::target(*eit, impl);
204 const auto source_index = graph.get_vertex_index(source);
205 const auto target_index = graph.get_vertex_index(target);
206 const auto& source_id = graph.get_node_id(source);
207 const auto& target_id = graph.get_node_id(target);
208 const auto edge_id = edge_id_for(*eit);
209 const auto edge = add_capacity_edge_pair(state, source_index, target_index, capacity_for(edge_id));
210 state.original_edges.push_back({edge_id, source_id, target_id, edge});
211 }
212}
213
214template <typename GraphWrapper, typename FlowState, typename EdgeIdFn, typename CapacityFn, typename WeightFn>
215void add_weighted_flow_edges(
216 const GraphWrapper& graph,
217 FlowState& state,
218 EdgeIdFn&& edge_id_for,
219 CapacityFn&& capacity_for,
220 WeightFn&& weight_for
221) {
222 const auto& impl = graph.get_impl();
223 for (auto [eit, eend] = boost::edges(impl); eit != eend; ++eit) {
224 const auto source = boost::source(*eit, impl);
225 const auto target = boost::target(*eit, impl);
226 const auto source_index = graph.get_vertex_index(source);
227 const auto target_index = graph.get_vertex_index(target);
228 const auto& source_id = graph.get_node_id(source);
229 const auto& target_id = graph.get_node_id(target);
230 const auto edge_id = edge_id_for(*eit);
231 const auto edge = add_capacity_edge_pair(state, source_index, target_index, capacity_for(edge_id));
232 const auto reverse_edge = state.reverse[edge];
233 state.weight[edge] = weight_for(edge_id);
234 state.weight[reverse_edge] = -state.weight[edge];
235 state.original_edges.push_back({edge_id, source_id, target_id, edge});
236 }
237}
238
239template <typename NodeID, typename FlowEdgeDesc, typename CapacityMap, typename ResidualMap>
240void fill_flow_views(
241 const std::vector<FlowEdgeRecord<NodeID, FlowEdgeDesc>>& original_edges,
242 const CapacityMap& capacity,
243 const ResidualMap& residual,
244 std::map<std::pair<NodeID, NodeID>, long>& endpoint_flows,
245 std::map<std::size_t, long>& edge_flows_by_id
246) {
247 for (const auto& edge : original_edges) {
248 const long flow_value = capacity[edge.edge_desc] - residual[edge.edge_desc];
249 endpoint_flows[{edge.source_id, edge.target_id}] += flow_value;
250 edge_flows_by_id[edge.edge_id] = flow_value;
251 }
252}
253
254template <typename NodeID>
256 NodeID source_id;
257 NodeID target_id;
258 long value = 0;
259 long cost = 0;
260};
261
262template <typename GraphWrapper>
263auto& min_cost_flow_cache() {
264 using NodeID = typename GraphWrapper::NodeType;
265 static std::map<const void*, std::unique_ptr<MinCostFlowState<NodeID>>> cache;
266 return cache;
267}
268
269template <typename GraphWrapper>
270auto& invalidated_min_cost_flow_graphs() {
271 static std::set<const void*> invalidated;
272 return invalidated;
273}
274
275template <typename GraphWrapper>
276auto& min_cost_flow_cache_mutex() {
277 static std::mutex mutex;
278 return mutex;
279}
280
281} // namespace detail
282
283template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
284struct detail::MinCostFlowCacheHooks<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>> {
285 static void invalidate(const void* graph_ptr) {
286 std::lock_guard lock(detail::min_cost_flow_cache_mutex<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>());
287 auto& cache = detail::min_cost_flow_cache<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>();
288 if (cache.erase(graph_ptr) > 0) {
289 detail::invalidated_min_cost_flow_graphs<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>().insert(graph_ptr);
290 }
291 }
292
293 static void clear(const void* graph_ptr) {
294 std::lock_guard lock(detail::min_cost_flow_cache_mutex<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>());
295 auto& cache = detail::min_cost_flow_cache<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>();
296 auto& invalidated = detail::invalidated_min_cost_flow_graphs<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>();
297 cache.erase(graph_ptr);
298 invalidated.erase(graph_ptr);
299 }
300};
301
323template <typename NodeID>
325 long value = 0;
326 std::vector<NodeID> reachable;
327 std::vector<NodeID> non_reachable;
328 std::vector<std::pair<NodeID, NodeID>> cut_edges;
329 std::vector<std::size_t> cut_edge_ids;
330};
331
332template <typename GraphWrapper>
343[[deprecated("Use G.edmonds_karp_maximum_flow(source, target, capacity_attr) instead.")]]
344auto edmonds_karp_maximum_flow(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& capacity_attr = "capacity") {
345 return G.edmonds_karp_maximum_flow(source_id, target_id, capacity_attr);
346}
347
348template <typename GraphWrapper>
359[[deprecated("Use G.push_relabel_maximum_flow_result(source, target, capacity_attr) instead.")]]
360auto push_relabel_maximum_flow_result(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& capacity_attr = "capacity") {
361 return G.push_relabel_maximum_flow_result(source_id, target_id, capacity_attr);
362}
363
364template <typename GraphWrapper>
375[[deprecated("Use G.maximum_flow(source, target, capacity_attr) instead.")]]
376auto maximum_flow(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& capacity_attr = "capacity") {
377 return G.maximum_flow(source_id, target_id, capacity_attr);
378}
379
380template <typename GraphWrapper>
391[[deprecated("Use G.minimum_cut(source, target, capacity_attr) instead.")]]
392auto minimum_cut(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& capacity_attr = "capacity") {
393 return G.minimum_cut(source_id, target_id, capacity_attr);
394}
395
396template <typename GraphWrapper>
408[[deprecated("Use G.max_flow_min_cost_cycle_canceling(source, target, capacity_attr, weight_attr) instead.")]]
409auto max_flow_min_cost_cycle_canceling(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& capacity_attr = "capacity", const std::string& weight_attr = "weight") {
410 return G.max_flow_min_cost_cycle_canceling(source_id, target_id, capacity_attr, weight_attr);
411}
412
413template <typename GraphWrapper>
425[[deprecated("Use G.push_relabel_maximum_flow(source, target, capacity_attr, weight_attr) instead.")]]
426long push_relabel_maximum_flow(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& capacity_attr = "capacity", const std::string& weight_attr = "weight") {
427 return G.push_relabel_maximum_flow(source_id, target_id, capacity_attr, weight_attr);
428}
429
430template <typename GraphWrapper>
438[[deprecated("Use G.cycle_canceling(weight_attr) instead.")]]
439auto cycle_canceling(const GraphWrapper& G, const std::string& weight_attr = "weight") {
440 return G.cycle_canceling(weight_attr);
441}
442
443template <typename GraphWrapper>
455[[deprecated("Use G.successive_shortest_path_nonnegative_weights(source, target, capacity_attr, weight_attr) instead.")]]
456auto successive_shortest_path_nonnegative_weights(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& capacity_attr = "capacity", const std::string& weight_attr = "weight") {
457 return G.successive_shortest_path_nonnegative_weights(source_id, target_id, capacity_attr, weight_attr);
458}
459
460template <typename GraphWrapper>
472[[deprecated("Use G.max_flow_min_cost_successive_shortest_path(source, target, capacity_attr, weight_attr) instead.")]]
473auto max_flow_min_cost_successive_shortest_path(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& capacity_attr = "capacity", const std::string& weight_attr = "weight") {
474 return G.max_flow_min_cost_successive_shortest_path(source_id, target_id, capacity_attr, weight_attr);
475}
476template <typename GraphWrapper>
488[[deprecated("Use G.max_flow_min_cost(source, target, capacity_attr, weight_attr) instead.")]]
489auto max_flow_min_cost(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& capacity_attr = "capacity", const std::string& weight_attr = "weight") {
490 return G.max_flow_min_cost(source_id, target_id, capacity_attr, weight_attr);
491}
492
493template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
494auto Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::edmonds_karp_maximum_flow(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr) const {
495 if (!has_node(source_id) || !has_node(target_id)) throw std::runtime_error("Flow setup failed: source or target node not found.");
497 detail::add_flow_vertices(state, get_bgl_to_id_map().size());
498 detail::add_capacity_flow_edges(
499 *this,
500 state,
501 [this](EdgeDesc edge) { return get_edge_id(edge); },
502 [this, &capacity_attr](std::size_t edge_id) { return get_edge_flow_capacity(edge_id, capacity_attr); }
503 );
504
505 const long flow_value = boost::edmonds_karp_max_flow(
506 state.flow_graph,
507 get_vertex_index(get_id_to_bgl_map().at(source_id)),
508 get_vertex_index(get_id_to_bgl_map().at(target_id))
509 );
511 result.value = flow_value;
512 detail::fill_flow_views(state.original_edges, state.capacity, state.residual, result.flow, result.edge_flows_by_id);
513 return result;
514}
515
516template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
517auto Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::push_relabel_maximum_flow_result(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr) const {
518 if (!has_node(source_id) || !has_node(target_id)) throw std::runtime_error("Flow setup failed: source or target node not found.");
520 detail::add_flow_vertices(state, get_bgl_to_id_map().size());
521 detail::add_capacity_flow_edges(
522 *this,
523 state,
524 [this](EdgeDesc edge) { return get_edge_id(edge); },
525 [this, &capacity_attr](std::size_t edge_id) { return get_edge_flow_capacity(edge_id, capacity_attr); }
526 );
527
528 const long flow_value = boost::push_relabel_max_flow(
529 state.flow_graph,
530 get_vertex_index(get_id_to_bgl_map().at(source_id)),
531 get_vertex_index(get_id_to_bgl_map().at(target_id))
532 );
534 result.value = flow_value;
535 detail::fill_flow_views(state.original_edges, state.capacity, state.residual, result.flow, result.edge_flows_by_id);
536 return result;
537}
538
539template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
540auto Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::maximum_flow(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr) const { return edmonds_karp_maximum_flow(source_id, target_id, capacity_attr); }
541
542template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
543auto Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::minimum_cut(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr) const {
544 if (!has_node(source_id) || !has_node(target_id)) throw std::runtime_error("Flow setup failed: source or target node not found.");
546 const auto& index_to_node = get_bgl_to_id_map();
547 detail::add_flow_vertices(state, index_to_node.size());
548 detail::add_capacity_flow_edges(
549 *this,
550 state,
551 [this](EdgeDesc edge) { return get_edge_id(edge); },
552 [this, &capacity_attr](std::size_t edge_id) { return get_edge_flow_capacity(edge_id, capacity_attr); }
553 );
554 const auto source_index = get_vertex_index(get_id_to_bgl_map().at(source_id));
555 const auto target_index = get_vertex_index(get_id_to_bgl_map().at(target_id));
556 const long cut_value = boost::edmonds_karp_max_flow(state.flow_graph, source_index, target_index);
557 std::vector<bool> visited(index_to_node.size(), false);
558 std::queue<std::size_t> q;
559 visited[source_index] = true;
560 q.push(source_index);
561 while (!q.empty()) {
562 const auto current = q.front();
563 q.pop();
564 for (auto [eit, eend] = boost::out_edges(current, state.flow_graph); eit != eend; ++eit) {
565 const auto next = boost::target(*eit, state.flow_graph);
566 if (!visited[next] && state.residual[*eit] > 0) { visited[next] = true; q.push(next); }
567 }
568 }
570 result.value = cut_value;
571 for (std::size_t i = 0; i < index_to_node.size(); ++i) {
572 if (visited[i]) result.reachable.push_back(index_to_node[i]);
573 else result.non_reachable.push_back(index_to_node[i]);
574 }
575 for (auto [eit, eend] = boost::edges(g); eit != eend; ++eit) {
576 const auto source = boost::source(*eit, g);
577 const auto target = boost::target(*eit, g);
578 const auto source_index_for_cut = get_vertex_index(source);
579 const auto target_index_for_cut = get_vertex_index(target);
580 if (visited[source_index_for_cut] && !visited[target_index_for_cut]) {
581 result.cut_edges.emplace_back(get_node_id(source), get_node_id(target));
582 result.cut_edge_ids.push_back(get_edge_id(*eit));
583 }
584 }
585 return result;
586}
587
588template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
589auto Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::max_flow_min_cost_cycle_canceling(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr, const std::string& weight_attr) const {
590 if (!has_node(source_id) || !has_node(target_id)) throw std::runtime_error("Flow setup failed: source or target node not found.");
592 detail::add_flow_vertices(state, get_bgl_to_id_map().size());
593 detail::add_weighted_flow_edges(
594 *this,
595 state,
596 [this](EdgeDesc edge) { return get_edge_id(edge); },
597 [this, &capacity_attr](std::size_t edge_id) { return get_edge_flow_capacity(edge_id, capacity_attr); },
598 [this, &weight_attr](std::size_t edge_id) { return static_cast<long>(get_edge_numeric_attr(edge_id, weight_attr)); }
599 );
600 boost::push_relabel_max_flow(
601 state.flow_graph,
602 get_vertex_index(get_id_to_bgl_map().at(source_id)),
603 get_vertex_index(get_id_to_bgl_map().at(target_id))
604 );
605 boost::cycle_canceling(state.flow_graph);
606 const long cost = boost::find_flow_cost(state.flow_graph);
608 result.cost = cost;
609 detail::fill_flow_views(state.original_edges, state.capacity, state.residual, result.edge_flows, result.edge_flows_by_id);
610 for (const auto& edge : state.original_edges) {
611 if (edge.source_id == source_id) {
612 result.flow += result.edge_flows_by_id[edge.edge_id];
613 }
614 }
615 return result;
616}
617
618template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
619long Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::push_relabel_maximum_flow(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr, const std::string& weight_attr) const {
620 if (!has_node(source_id) || !has_node(target_id)) throw std::runtime_error("Flow setup failed: source or target node not found.");
621 auto state = std::make_unique<detail::MinCostFlowState<NodeID>>();
622 state->source_id = source_id;
623 state->target_id = target_id;
624 detail::add_flow_vertices(*state, get_bgl_to_id_map().size());
625 detail::add_weighted_flow_edges(
626 *this,
627 *state,
628 [this](EdgeDesc edge) { return get_edge_id(edge); },
629 [this, &capacity_attr](std::size_t edge_id) { return get_edge_flow_capacity(edge_id, capacity_attr); },
630 [this, &weight_attr](std::size_t edge_id) { return static_cast<long>(get_edge_numeric_attr(edge_id, weight_attr)); }
631 );
632 state->value = boost::push_relabel_max_flow(
633 state->flow_graph,
634 get_vertex_index(get_id_to_bgl_map().at(source_id)),
635 get_vertex_index(get_id_to_bgl_map().at(target_id))
636 );
637 const long flow_value = state->value;
638 {
639 std::lock_guard lock(detail::min_cost_flow_cache_mutex<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>());
640 auto& cache = detail::min_cost_flow_cache<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>();
641 auto& invalidated = detail::invalidated_min_cost_flow_graphs<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>();
642 cache[static_cast<const void*>(this)] = std::move(state);
643 invalidated.erase(static_cast<const void*>(this));
644 }
645 return flow_value;
646}
647
648template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
650 std::lock_guard lock(detail::min_cost_flow_cache_mutex<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>());
651 auto& cache = detail::min_cost_flow_cache<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>();
652 auto& invalidated = detail::invalidated_min_cost_flow_graphs<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>();
653 auto it = cache.find(static_cast<const void*>(this));
654 if (it == cache.end()) {
655 if (invalidated.contains(static_cast<const void*>(this))) {
656 throw std::runtime_error("Min-cost-flow state invalidated by graph mutation: rerun push_relabel_maximum_flow(...) before cycle_canceling().");
657 }
658 throw std::runtime_error("Min-cost-flow state unavailable: run push_relabel_maximum_flow(...) first.");
659 }
660 auto& state = *it->second;
661 for (const auto& edge : state.original_edges) {
662 state.weight[edge.edge_desc] = static_cast<long>(get_edge_numeric_attr(edge.edge_id, weight_attr));
663 const auto rev = state.reverse[edge.edge_desc];
664 state.weight[rev] = -state.weight[edge.edge_desc];
665 }
666 boost::cycle_canceling(state.flow_graph);
667 state.cost = boost::find_flow_cost(state.flow_graph);
668 return state.cost;
669}
670
671template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
672auto Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::successive_shortest_path_nonnegative_weights(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr, const std::string& weight_attr) const {
673 if (!has_node(source_id) || !has_node(target_id)) throw std::runtime_error("Flow setup failed: source or target node not found.");
675 detail::add_flow_vertices(state, get_bgl_to_id_map().size());
676 detail::add_weighted_flow_edges(
677 *this,
678 state,
679 [this](EdgeDesc edge) { return get_edge_id(edge); },
680 [this, &capacity_attr](std::size_t edge_id) { return get_edge_flow_capacity(edge_id, capacity_attr); },
681 [this, &weight_attr](std::size_t edge_id) { return static_cast<long>(get_edge_numeric_attr(edge_id, weight_attr)); }
682 );
683 const auto source_index = get_vertex_index(get_id_to_bgl_map().at(source_id));
684 const auto target_index = get_vertex_index(get_id_to_bgl_map().at(target_id));
685 boost::successive_shortest_path_nonnegative_weights(state.flow_graph, source_index, target_index);
686 long flow_value = 0;
687 for (auto [eit, eend] = boost::out_edges(source_index, state.flow_graph); eit != eend; ++eit) {
688 flow_value += state.capacity[*eit] - state.residual[*eit];
689 }
690 const long cost = boost::find_flow_cost(state.flow_graph);
692 result.flow = flow_value;
693 result.cost = cost;
694 detail::fill_flow_views(state.original_edges, state.capacity, state.residual, result.edge_flows, result.edge_flows_by_id);
695 return result;
696}
697
698template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
699auto Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::max_flow_min_cost_successive_shortest_path(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr, const std::string& weight_attr) const {
700 return successive_shortest_path_nonnegative_weights(source_id, target_id, capacity_attr, weight_attr);
701}
702
703template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
704auto Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::max_flow_min_cost(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr, const std::string& weight_attr) const {
705 return max_flow_min_cost_cycle_canceling(source_id, target_id, capacity_attr, weight_attr);
706}
707
708} // namespace nxpp
Attribute-bearing edge insertion helpers and attribute-oriented graph method definitions.
Graph wrapper around Boost Graph Library with Python-inspired helpers.
Definition graph.hpp:288
auto edmonds_karp_maximum_flow(const NodeID &source_id, const NodeID &target_id, const std::string &capacity_attr="capacity") const
Computes a maximum flow using the Edmonds-Karp algorithm.
Definition flow.hpp:494
auto successive_shortest_path_nonnegative_weights(const NodeID &source_id, const NodeID &target_id, const std::string &capacity_attr="capacity", const std::string &weight_attr="weight") const
Computes a max-flow min-cost result using successive shortest path.
Definition flow.hpp:672
auto max_flow_min_cost(const NodeID &source_id, const NodeID &target_id, const std::string &capacity_attr="capacity", const std::string &weight_attr="weight") const
Convenience alias for the default max-flow min-cost wrapper.
Definition flow.hpp:704
auto max_flow_min_cost_successive_shortest_path(const NodeID &source_id, const NodeID &target_id, const std::string &capacity_attr="capacity", const std::string &weight_attr="weight") const
Convenience alias for successive_shortest_path_nonnegative_weights().
Definition flow.hpp:699
auto minimum_cut(const NodeID &source_id, const NodeID &target_id, const std::string &capacity_attr="capacity") const
Computes a minimum cut between source and target using the named capacity attribute.
Definition flow.hpp:543
auto maximum_flow(const NodeID &source_id, const NodeID &target_id, const std::string &capacity_attr="capacity") const
Computes the maximum flow value and edge assignments using the named capacity attribute.
Definition flow.hpp:540
long push_relabel_maximum_flow(const NodeID &source_id, const NodeID &target_id, const std::string &capacity_attr="capacity", const std::string &weight_attr="weight") const
Runs push-relabel and stages residual state for a later cycle_canceling() call. Any later graph mutat...
Definition flow.hpp:619
auto cycle_canceling(const std::string &weight_attr="weight") const
Runs cycle canceling on the previously cached residual network.
Definition flow.hpp:649
auto max_flow_min_cost_cycle_canceling(const NodeID &source_id, const NodeID &target_id, const std::string &capacity_attr="capacity", const std::string &weight_attr="weight") const
Computes a max-flow min-cost result using cycle canceling.
Definition flow.hpp:589
auto push_relabel_maximum_flow_result(const NodeID &source_id, const NodeID &target_id, const std::string &capacity_attr="capacity") const
Computes a maximum flow using push-relabel and returns edge assignments.
Definition flow.hpp:517
auto max_flow_min_cost(const GraphWrapper &G, const typename GraphWrapper::NodeType &source_id, const typename GraphWrapper::NodeType &target_id, const std::string &capacity_attr="capacity", const std::string &weight_attr="weight")
Deprecated free-function alias for G.max_flow_min_cost(...).
Definition flow.hpp:489
auto max_flow_min_cost_successive_shortest_path(const GraphWrapper &G, const typename GraphWrapper::NodeType &source_id, const typename GraphWrapper::NodeType &target_id, const std::string &capacity_attr="capacity", const std::string &weight_attr="weight")
Deprecated free-function alias for G.max_flow_min_cost_successive_shortest_path(.....
Definition flow.hpp:473
auto cycle_canceling(const GraphWrapper &G, const std::string &weight_attr="weight")
Deprecated free-function alias for G.cycle_canceling(weight_attr).
Definition flow.hpp:439
auto maximum_flow(const GraphWrapper &G, const typename GraphWrapper::NodeType &source_id, const typename GraphWrapper::NodeType &target_id, const std::string &capacity_attr="capacity")
Deprecated free-function alias for G.maximum_flow(...).
Definition flow.hpp:376
auto edmonds_karp_maximum_flow(const GraphWrapper &G, const typename GraphWrapper::NodeType &source_id, const typename GraphWrapper::NodeType &target_id, const std::string &capacity_attr="capacity")
Deprecated free-function alias for G.edmonds_karp_maximum_flow(...).
Definition flow.hpp:344
auto push_relabel_maximum_flow_result(const GraphWrapper &G, const typename GraphWrapper::NodeType &source_id, const typename GraphWrapper::NodeType &target_id, const std::string &capacity_attr="capacity")
Deprecated free-function alias for G.push_relabel_maximum_flow_result(...).
Definition flow.hpp:360
auto minimum_cut(const GraphWrapper &G, const typename GraphWrapper::NodeType &source_id, const typename GraphWrapper::NodeType &target_id, const std::string &capacity_attr="capacity")
Deprecated free-function alias for G.minimum_cut(...).
Definition flow.hpp:392
long push_relabel_maximum_flow(const GraphWrapper &G, const typename GraphWrapper::NodeType &source_id, const typename GraphWrapper::NodeType &target_id, const std::string &capacity_attr="capacity", const std::string &weight_attr="weight")
Deprecated free-function alias for G.push_relabel_maximum_flow(...).
Definition flow.hpp:426
auto max_flow_min_cost_cycle_canceling(const GraphWrapper &G, const typename GraphWrapper::NodeType &source_id, const typename GraphWrapper::NodeType &target_id, const std::string &capacity_attr="capacity", const std::string &weight_attr="weight")
Deprecated free-function alias for G.max_flow_min_cost_cycle_canceling(...).
Definition flow.hpp:409
auto successive_shortest_path_nonnegative_weights(const GraphWrapper &G, const typename GraphWrapper::NodeType &source_id, const typename GraphWrapper::NodeType &target_id, const std::string &capacity_attr="capacity", const std::string &weight_attr="weight")
Deprecated free-function alias for G.successive_shortest_path_nonnegative_weights(....
Definition flow.hpp:456
Precise edge_id-based multigraph operations and edge-aware attribute access.
Result of a maximum-flow computation.
Definition flow.hpp:49
Result of a min-cost max-flow computation.
Definition flow.hpp:77
Result of a minimum-cut computation.
Definition flow.hpp:324
Definition flow.hpp:87
Definition flow.hpp:255