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 <set>
18
19#include "attributes.hpp"
20#include "multigraph.hpp"
21
22namespace nxpp {
23
45template <typename NodeID>
47 long value = 0;
48 std::map<std::pair<NodeID, NodeID>, long> flow;
49 std::map<std::size_t, long> edge_flows_by_id;
50};
51
73template <typename NodeID>
75 long flow = 0;
76 long cost = 0;
77 std::map<std::pair<NodeID, NodeID>, long> edge_flows;
78 std::map<std::size_t, long> edge_flows_by_id;
79};
80
81namespace detail {
82
83template <typename NodeID, typename FlowEdgeDesc>
85 std::size_t edge_id;
86 NodeID source_id;
87 NodeID target_id;
88 FlowEdgeDesc edge_desc;
89};
90
91template <typename NodeID, typename FlowEdgeDesc, typename CapacityMap, typename ResidualMap>
92void fill_flow_views(
93 const std::vector<FlowEdgeRecord<NodeID, FlowEdgeDesc>>& original_edges,
94 const CapacityMap& capacity,
95 const ResidualMap& residual,
96 std::map<std::pair<NodeID, NodeID>, long>& endpoint_flows,
97 std::map<std::size_t, long>& edge_flows_by_id
98) {
99 for (const auto& edge : original_edges) {
100 const long flow_value = capacity[edge.edge_desc] - residual[edge.edge_desc];
101 endpoint_flows[{edge.source_id, edge.target_id}] += flow_value;
102 edge_flows_by_id[edge.edge_id] = flow_value;
103 }
104}
105
106template <typename NodeID>
108 using FlowTraits = boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS>;
109 using FlowGraph = boost::adjacency_list<
110 boost::vecS,
111 boost::vecS,
112 boost::directedS,
113 boost::no_property,
114 boost::property<
115 boost::edge_weight_t,
116 long,
117 boost::property<
118 boost::edge_capacity_t,
119 long,
120 boost::property<
121 boost::edge_residual_capacity_t,
122 long,
123 boost::property<boost::edge_reverse_t, typename FlowTraits::edge_descriptor>
124 >
125 >
126 >
127 >;
128 using WeightMap = typename boost::property_map<FlowGraph, boost::edge_weight_t>::type;
129 using CapacityMap = typename boost::property_map<FlowGraph, boost::edge_capacity_t>::type;
130 using ResidualMap = typename boost::property_map<FlowGraph, boost::edge_residual_capacity_t>::type;
131 using ReverseMap = typename boost::property_map<FlowGraph, boost::edge_reverse_t>::type;
132 using FlowEdgeDesc = typename boost::graph_traits<FlowGraph>::edge_descriptor;
133
134 FlowGraph flow_graph;
135 WeightMap weight;
136 CapacityMap capacity;
137 ResidualMap residual;
138 ReverseMap reverse;
139 std::vector<FlowEdgeRecord<NodeID, FlowEdgeDesc>> original_edges;
140 NodeID source_id;
141 NodeID target_id;
142 long value = 0;
143 long cost = 0;
144
146 : flow_graph(),
147 weight(boost::get(boost::edge_weight, flow_graph)),
148 capacity(boost::get(boost::edge_capacity, flow_graph)),
149 residual(boost::get(boost::edge_residual_capacity, flow_graph)),
150 reverse(boost::get(boost::edge_reverse, flow_graph)) {}
151};
152
153template <typename GraphWrapper>
154auto& min_cost_flow_cache() {
155 using NodeID = typename GraphWrapper::NodeType;
156 static std::map<const void*, std::unique_ptr<MinCostFlowState<NodeID>>> cache;
157 return cache;
158}
159
160template <typename GraphWrapper>
161auto& invalidated_min_cost_flow_graphs() {
162 static std::set<const void*> invalidated;
163 return invalidated;
164}
165
166} // namespace detail
167
168template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
169struct detail::MinCostFlowCacheHooks<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>> {
170 static void invalidate(const void* graph_ptr) {
171 auto& cache = detail::min_cost_flow_cache<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>();
172 if (cache.erase(graph_ptr) > 0) {
173 detail::invalidated_min_cost_flow_graphs<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>().insert(graph_ptr);
174 }
175 }
176
177 static void clear(const void* graph_ptr) {
178 auto& cache = detail::min_cost_flow_cache<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>();
179 auto& invalidated = detail::invalidated_min_cost_flow_graphs<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>();
180 cache.erase(graph_ptr);
181 invalidated.erase(graph_ptr);
182 }
183};
184
206template <typename NodeID>
208 long value = 0;
209 std::vector<NodeID> reachable;
210 std::vector<NodeID> non_reachable;
211 std::vector<std::pair<NodeID, NodeID>> cut_edges;
212 std::vector<std::size_t> cut_edge_ids;
213};
214
215template <typename GraphWrapper>
226[[deprecated("Use G.edmonds_karp_maximum_flow(source, target, capacity_attr) instead.")]]
227auto 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") {
228 return G.edmonds_karp_maximum_flow(source_id, target_id, capacity_attr);
229}
230
231template <typename GraphWrapper>
242[[deprecated("Use G.push_relabel_maximum_flow_result(source, target, capacity_attr) instead.")]]
243auto 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") {
244 return G.push_relabel_maximum_flow_result(source_id, target_id, capacity_attr);
245}
246
247template <typename GraphWrapper>
258[[deprecated("Use G.maximum_flow(source, target, capacity_attr) instead.")]]
259auto maximum_flow(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& capacity_attr = "capacity") {
260 return G.maximum_flow(source_id, target_id, capacity_attr);
261}
262
263template <typename GraphWrapper>
274[[deprecated("Use G.minimum_cut(source, target, capacity_attr) instead.")]]
275auto minimum_cut(const GraphWrapper& G, const typename GraphWrapper::NodeType& source_id, const typename GraphWrapper::NodeType& target_id, const std::string& capacity_attr = "capacity") {
276 return G.minimum_cut(source_id, target_id, capacity_attr);
277}
278
279template <typename GraphWrapper>
291[[deprecated("Use G.max_flow_min_cost_cycle_canceling(source, target, capacity_attr, weight_attr) instead.")]]
292auto 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") {
293 return G.max_flow_min_cost_cycle_canceling(source_id, target_id, capacity_attr, weight_attr);
294}
295
296template <typename GraphWrapper>
308[[deprecated("Use G.push_relabel_maximum_flow(source, target, capacity_attr, weight_attr) instead.")]]
309long 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") {
310 return G.push_relabel_maximum_flow(source_id, target_id, capacity_attr, weight_attr);
311}
312
313template <typename GraphWrapper>
321[[deprecated("Use G.cycle_canceling(weight_attr) instead.")]]
322auto cycle_canceling(const GraphWrapper& G, const std::string& weight_attr = "weight") {
323 return G.cycle_canceling(weight_attr);
324}
325
326template <typename GraphWrapper>
338[[deprecated("Use G.successive_shortest_path_nonnegative_weights(source, target, capacity_attr, weight_attr) instead.")]]
339auto 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") {
340 return G.successive_shortest_path_nonnegative_weights(source_id, target_id, capacity_attr, weight_attr);
341}
342
343template <typename GraphWrapper>
355[[deprecated("Use G.max_flow_min_cost_successive_shortest_path(source, target, capacity_attr, weight_attr) instead.")]]
356auto 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") {
357 return G.max_flow_min_cost_successive_shortest_path(source_id, target_id, capacity_attr, weight_attr);
358}
359template <typename GraphWrapper>
371[[deprecated("Use G.max_flow_min_cost(source, target, capacity_attr, weight_attr) instead.")]]
372auto 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") {
373 return G.max_flow_min_cost(source_id, target_id, capacity_attr, weight_attr);
374}
375
376template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
377auto 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 {
378 if (!has_node(source_id) || !has_node(target_id)) throw std::runtime_error("Flow setup failed: source or target node not found.");
379 using FlowTraits = boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS>;
380 using FlowGraph = boost::adjacency_list<
381 boost::vecS, boost::vecS, boost::directedS, boost::no_property,
382 boost::property<boost::edge_capacity_t, long,
383 boost::property<boost::edge_residual_capacity_t, long,
384 boost::property<boost::edge_reverse_t, typename FlowTraits::edge_descriptor>>>>;
385 using CapacityMap = typename boost::property_map<FlowGraph, boost::edge_capacity_t>::type;
386 using ResidualMap = typename boost::property_map<FlowGraph, boost::edge_residual_capacity_t>::type;
387 using ReverseMap = typename boost::property_map<FlowGraph, boost::edge_reverse_t>::type;
388 using FlowEdgeDesc = typename boost::graph_traits<FlowGraph>::edge_descriptor;
389
390 FlowGraph flow_graph;
391 CapacityMap capacity = boost::get(boost::edge_capacity, flow_graph);
392 ReverseMap reverse = boost::get(boost::edge_reverse, flow_graph);
393 const auto& all_nodes = get_bgl_to_id_map();
394 for (std::size_t i = 0; i < all_nodes.size(); ++i) { boost::add_vertex(flow_graph); }
395
396 std::vector<detail::FlowEdgeRecord<NodeID, FlowEdgeDesc>> original_edges;
397 for (auto [eit, eend] = boost::edges(g); eit != eend; ++eit) {
398 const auto source = boost::source(*eit, g);
399 const auto target = boost::target(*eit, g);
400 const auto source_index = get_vertex_index(source);
401 const auto target_index = get_vertex_index(target);
402 const auto& u = get_node_id(source);
403 const auto& v = get_node_id(target);
404 const auto edge_id = get_edge_id(*eit);
405 auto [e, added] = boost::add_edge(source_index, target_index, flow_graph);
406 auto [rev, rev_added] = boost::add_edge(target_index, source_index, flow_graph);
407 (void)added; (void)rev_added;
408 capacity[e] = static_cast<long>(get_edge_numeric_attr(edge_id, capacity_attr));
409 capacity[rev] = 0;
410 reverse[e] = rev;
411 reverse[rev] = e;
412 original_edges.push_back({edge_id, u, v, e});
413 }
414
415 const long flow_value = boost::edmonds_karp_max_flow(
416 flow_graph,
417 get_vertex_index(id_to_bgl.at(source_id)),
418 get_vertex_index(id_to_bgl.at(target_id))
419 );
420 ResidualMap residual = boost::get(boost::edge_residual_capacity, flow_graph);
422 result.value = flow_value;
423 detail::fill_flow_views(original_edges, capacity, residual, result.flow, result.edge_flows_by_id);
424 return result;
425}
426
427template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
428auto 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 {
429 if (!has_node(source_id) || !has_node(target_id)) throw std::runtime_error("Flow setup failed: source or target node not found.");
430 using FlowTraits = boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS>;
431 using FlowGraph = boost::adjacency_list<
432 boost::vecS, boost::vecS, boost::directedS, boost::no_property,
433 boost::property<boost::edge_capacity_t, long,
434 boost::property<boost::edge_residual_capacity_t, long,
435 boost::property<boost::edge_reverse_t, typename FlowTraits::edge_descriptor>>>>;
436 using CapacityMap = typename boost::property_map<FlowGraph, boost::edge_capacity_t>::type;
437 using ResidualMap = typename boost::property_map<FlowGraph, boost::edge_residual_capacity_t>::type;
438 using ReverseMap = typename boost::property_map<FlowGraph, boost::edge_reverse_t>::type;
439 using FlowEdgeDesc = typename boost::graph_traits<FlowGraph>::edge_descriptor;
440
441 FlowGraph flow_graph;
442 CapacityMap capacity = boost::get(boost::edge_capacity, flow_graph);
443 ReverseMap reverse = boost::get(boost::edge_reverse, flow_graph);
444 const auto& all_nodes = get_bgl_to_id_map();
445 for (std::size_t i = 0; i < all_nodes.size(); ++i) { boost::add_vertex(flow_graph); }
446
447 std::vector<detail::FlowEdgeRecord<NodeID, FlowEdgeDesc>> original_edges;
448 for (auto [eit, eend] = boost::edges(g); eit != eend; ++eit) {
449 const auto source = boost::source(*eit, g);
450 const auto target = boost::target(*eit, g);
451 const auto source_index = get_vertex_index(source);
452 const auto target_index = get_vertex_index(target);
453 const auto& u = get_node_id(source);
454 const auto& v = get_node_id(target);
455 const auto edge_id = get_edge_id(*eit);
456 auto [e, added] = boost::add_edge(source_index, target_index, flow_graph);
457 auto [rev, rev_added] = boost::add_edge(target_index, source_index, flow_graph);
458 (void)added; (void)rev_added;
459 capacity[e] = static_cast<long>(get_edge_numeric_attr(edge_id, capacity_attr));
460 capacity[rev] = 0;
461 reverse[e] = rev;
462 reverse[rev] = e;
463 original_edges.push_back({edge_id, u, v, e});
464 }
465
466 const long flow_value = boost::push_relabel_max_flow(
467 flow_graph,
468 get_vertex_index(id_to_bgl.at(source_id)),
469 get_vertex_index(id_to_bgl.at(target_id))
470 );
471 ResidualMap residual = boost::get(boost::edge_residual_capacity, flow_graph);
473 result.value = flow_value;
474 detail::fill_flow_views(original_edges, capacity, residual, result.flow, result.edge_flows_by_id);
475 return result;
476}
477
478template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
479auto 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); }
480
481template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
482auto Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>::minimum_cut(const NodeID& source_id, const NodeID& target_id, const std::string& capacity_attr) const {
483 if (!has_node(source_id) || !has_node(target_id)) throw std::runtime_error("Flow setup failed: source or target node not found.");
484 using FlowTraits = boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS>;
485 using FlowGraph = boost::adjacency_list<
486 boost::vecS, boost::vecS, boost::directedS, boost::no_property,
487 boost::property<boost::edge_capacity_t, long,
488 boost::property<boost::edge_residual_capacity_t, long,
489 boost::property<boost::edge_reverse_t, typename FlowTraits::edge_descriptor>>>>;
490 using CapacityMap = typename boost::property_map<FlowGraph, boost::edge_capacity_t>::type;
491 using ResidualMap = typename boost::property_map<FlowGraph, boost::edge_residual_capacity_t>::type;
492 using ReverseMap = typename boost::property_map<FlowGraph, boost::edge_reverse_t>::type;
493
494 FlowGraph flow_graph;
495 CapacityMap capacity = boost::get(boost::edge_capacity, flow_graph);
496 ReverseMap reverse = boost::get(boost::edge_reverse, flow_graph);
497 const auto& index_to_node = get_bgl_to_id_map();
498 for (std::size_t i = 0; i < index_to_node.size(); ++i) { boost::add_vertex(flow_graph); }
499 for (auto [eit, eend] = boost::edges(g); eit != eend; ++eit) {
500 const auto source = boost::source(*eit, g);
501 const auto target = boost::target(*eit, g);
502 const auto source_index = get_vertex_index(source);
503 const auto target_index = get_vertex_index(target);
504 const auto edge_id = get_edge_id(*eit);
505 auto [e, added] = boost::add_edge(source_index, target_index, flow_graph);
506 auto [rev, rev_added] = boost::add_edge(target_index, source_index, flow_graph);
507 (void)added; (void)rev_added;
508 capacity[e] = static_cast<long>(get_edge_numeric_attr(edge_id, capacity_attr));
509 capacity[rev] = 0;
510 reverse[e] = rev;
511 reverse[rev] = e;
512 }
513 const auto source_index = get_vertex_index(id_to_bgl.at(source_id));
514 const auto target_index = get_vertex_index(id_to_bgl.at(target_id));
515 const long cut_value = boost::edmonds_karp_max_flow(flow_graph, source_index, target_index);
516 ResidualMap residual = boost::get(boost::edge_residual_capacity, flow_graph);
517 std::vector<bool> visited(index_to_node.size(), false);
518 std::queue<std::size_t> q;
519 visited[source_index] = true;
520 q.push(source_index);
521 while (!q.empty()) {
522 const auto current = q.front();
523 q.pop();
524 for (auto [eit, eend] = boost::out_edges(current, flow_graph); eit != eend; ++eit) {
525 const auto next = boost::target(*eit, flow_graph);
526 if (!visited[next] && residual[*eit] > 0) { visited[next] = true; q.push(next); }
527 }
528 }
530 result.value = cut_value;
531 for (std::size_t i = 0; i < index_to_node.size(); ++i) {
532 if (visited[i]) result.reachable.push_back(index_to_node[i]);
533 else result.non_reachable.push_back(index_to_node[i]);
534 }
535 for (auto [eit, eend] = boost::edges(g); eit != eend; ++eit) {
536 const auto source = boost::source(*eit, g);
537 const auto target = boost::target(*eit, g);
538 const auto source_index_for_cut = get_vertex_index(source);
539 const auto target_index_for_cut = get_vertex_index(target);
540 if (visited[source_index_for_cut] && !visited[target_index_for_cut]) {
541 result.cut_edges.emplace_back(get_node_id(source), get_node_id(target));
542 result.cut_edge_ids.push_back(get_edge_id(*eit));
543 }
544 }
545 return result;
546}
547
548template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
549auto 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 {
550 if (!has_node(source_id) || !has_node(target_id)) throw std::runtime_error("Flow setup failed: source or target node not found.");
551 using FlowTraits = boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS>;
552 using FlowGraph = boost::adjacency_list<
553 boost::vecS, boost::vecS, boost::directedS, boost::no_property,
554 boost::property<boost::edge_weight_t, long,
555 boost::property<boost::edge_capacity_t, long,
556 boost::property<boost::edge_residual_capacity_t, long,
557 boost::property<boost::edge_reverse_t, typename FlowTraits::edge_descriptor>>>>>;
558 using WeightMap = typename boost::property_map<FlowGraph, boost::edge_weight_t>::type;
559 using CapacityMap = typename boost::property_map<FlowGraph, boost::edge_capacity_t>::type;
560 using ResidualMap = typename boost::property_map<FlowGraph, boost::edge_residual_capacity_t>::type;
561 using ReverseMap = typename boost::property_map<FlowGraph, boost::edge_reverse_t>::type;
562 using FlowEdgeDesc = typename boost::graph_traits<FlowGraph>::edge_descriptor;
563
564 FlowGraph flow_graph;
565 WeightMap weight = boost::get(boost::edge_weight, flow_graph);
566 CapacityMap capacity = boost::get(boost::edge_capacity, flow_graph);
567 ReverseMap reverse = boost::get(boost::edge_reverse, flow_graph);
568 const auto& all_nodes = get_bgl_to_id_map();
569 for (std::size_t i = 0; i < all_nodes.size(); ++i) { boost::add_vertex(flow_graph); }
570 std::vector<detail::FlowEdgeRecord<NodeID, FlowEdgeDesc>> original_edges;
571 for (auto [eit, eend] = boost::edges(g); eit != eend; ++eit) {
572 const auto source = boost::source(*eit, g);
573 const auto target = boost::target(*eit, g);
574 const auto source_index = get_vertex_index(source);
575 const auto target_index = get_vertex_index(target);
576 const auto& u = get_node_id(source);
577 const auto& v = get_node_id(target);
578 const auto edge_id = get_edge_id(*eit);
579 auto [e, added] = boost::add_edge(source_index, target_index, flow_graph);
580 auto [rev, rev_added] = boost::add_edge(target_index, source_index, flow_graph);
581 (void)added; (void)rev_added;
582 weight[e] = static_cast<long>(get_edge_numeric_attr(edge_id, weight_attr));
583 weight[rev] = -weight[e];
584 capacity[e] = static_cast<long>(get_edge_numeric_attr(edge_id, capacity_attr));
585 capacity[rev] = 0;
586 reverse[e] = rev;
587 reverse[rev] = e;
588 original_edges.push_back({edge_id, u, v, e});
589 }
590 boost::push_relabel_max_flow(
591 flow_graph,
592 get_vertex_index(id_to_bgl.at(source_id)),
593 get_vertex_index(id_to_bgl.at(target_id))
594 );
595 boost::cycle_canceling(flow_graph);
596 const long cost = boost::find_flow_cost(flow_graph);
597 ResidualMap residual = boost::get(boost::edge_residual_capacity, flow_graph);
599 result.cost = cost;
600 detail::fill_flow_views(original_edges, capacity, residual, result.edge_flows, result.edge_flows_by_id);
601 for (const auto& edge : original_edges) {
602 if (edge.source_id == source_id) {
603 result.flow += result.edge_flows_by_id[edge.edge_id];
604 }
605 }
606 return result;
607}
608
609template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
610long 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 {
611 if (!has_node(source_id) || !has_node(target_id)) throw std::runtime_error("Flow setup failed: source or target node not found.");
612 auto state = std::make_unique<detail::MinCostFlowState<NodeID>>();
613 state->source_id = source_id;
614 state->target_id = target_id;
615 const auto& all_nodes = get_bgl_to_id_map();
616 for (std::size_t i = 0; i < all_nodes.size(); ++i) { boost::add_vertex(state->flow_graph); }
617 for (auto [eit, eend] = boost::edges(g); eit != eend; ++eit) {
618 const auto source = boost::source(*eit, g);
619 const auto target = boost::target(*eit, g);
620 const auto source_index = get_vertex_index(source);
621 const auto target_index = get_vertex_index(target);
622 const auto& u = get_node_id(source);
623 const auto& v = get_node_id(target);
624 const auto edge_id = get_edge_id(*eit);
625 auto [e, added] = boost::add_edge(source_index, target_index, state->flow_graph);
626 auto [rev, rev_added] = boost::add_edge(target_index, source_index, state->flow_graph);
627 (void)added; (void)rev_added;
628 state->weight[e] = static_cast<long>(get_edge_numeric_attr(edge_id, weight_attr));
629 state->weight[rev] = -state->weight[e];
630 state->capacity[e] = static_cast<long>(get_edge_numeric_attr(edge_id, capacity_attr));
631 state->capacity[rev] = 0;
632 state->reverse[e] = rev;
633 state->reverse[rev] = e;
634 state->original_edges.push_back({edge_id, u, v, e});
635 }
636 state->value = boost::push_relabel_max_flow(
637 state->flow_graph,
638 get_vertex_index(id_to_bgl.at(source_id)),
639 get_vertex_index(id_to_bgl.at(target_id))
640 );
641 auto& cache = detail::min_cost_flow_cache<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>();
642 auto& invalidated = detail::invalidated_min_cost_flow_graphs<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>();
643 cache[static_cast<const void*>(this)] = std::move(state);
644 invalidated.erase(static_cast<const void*>(this));
645 return cache.at(static_cast<const void*>(this))->value;
646}
647
648template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
650 auto& cache = detail::min_cost_flow_cache<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>();
651 auto& invalidated = detail::invalidated_min_cost_flow_graphs<Graph<NodeID, EdgeWeight, Directed, Multi, Weighted, OutEdgeSelector, VertexSelector>>();
652 auto it = cache.find(static_cast<const void*>(this));
653 if (it == cache.end()) {
654 if (invalidated.contains(static_cast<const void*>(this))) {
655 throw std::runtime_error("Min-cost-flow state invalidated by graph mutation: rerun push_relabel_maximum_flow(...) before cycle_canceling().");
656 }
657 throw std::runtime_error("Min-cost-flow state unavailable: run push_relabel_maximum_flow(...) first.");
658 }
659 auto& state = *it->second;
660 for (const auto& edge : state.original_edges) {
661 state.weight[edge.edge_desc] = static_cast<long>(get_edge_numeric_attr(edge.edge_id, weight_attr));
662 const auto rev = state.reverse[edge.edge_desc];
663 state.weight[rev] = -state.weight[edge.edge_desc];
664 }
665 boost::cycle_canceling(state.flow_graph);
666 state.cost = boost::find_flow_cost(state.flow_graph);
667 return state.cost;
668}
669
670template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
671auto 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 {
672 if (!has_node(source_id) || !has_node(target_id)) throw std::runtime_error("Flow setup failed: source or target node not found.");
673 using FlowTraits = boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS>;
674 using FlowGraph = boost::adjacency_list<
675 boost::vecS, boost::vecS, boost::directedS, boost::no_property,
676 boost::property<boost::edge_weight_t, long,
677 boost::property<boost::edge_capacity_t, long,
678 boost::property<boost::edge_residual_capacity_t, long,
679 boost::property<boost::edge_reverse_t, typename FlowTraits::edge_descriptor>>>>>;
680 using WeightMap = typename boost::property_map<FlowGraph, boost::edge_weight_t>::type;
681 using CapacityMap = typename boost::property_map<FlowGraph, boost::edge_capacity_t>::type;
682 using ResidualMap = typename boost::property_map<FlowGraph, boost::edge_residual_capacity_t>::type;
683 using ReverseMap = typename boost::property_map<FlowGraph, boost::edge_reverse_t>::type;
684 using FlowEdgeDesc = typename boost::graph_traits<FlowGraph>::edge_descriptor;
685
686 FlowGraph flow_graph;
687 WeightMap weight = boost::get(boost::edge_weight, flow_graph);
688 CapacityMap capacity = boost::get(boost::edge_capacity, flow_graph);
689 ReverseMap reverse = boost::get(boost::edge_reverse, flow_graph);
690 const auto& all_nodes = get_bgl_to_id_map();
691 for (std::size_t i = 0; i < all_nodes.size(); ++i) { boost::add_vertex(flow_graph); }
692 std::vector<detail::FlowEdgeRecord<NodeID, FlowEdgeDesc>> original_edges;
693 for (auto [eit, eend] = boost::edges(g); eit != eend; ++eit) {
694 const auto source = boost::source(*eit, g);
695 const auto target = boost::target(*eit, g);
696 const auto source_index = get_vertex_index(source);
697 const auto target_index = get_vertex_index(target);
698 const auto& u = get_node_id(source);
699 const auto& v = get_node_id(target);
700 const auto edge_id = get_edge_id(*eit);
701 auto [e, added] = boost::add_edge(source_index, target_index, flow_graph);
702 auto [rev, rev_added] = boost::add_edge(target_index, source_index, flow_graph);
703 (void)added; (void)rev_added;
704 weight[e] = static_cast<long>(get_edge_numeric_attr(edge_id, weight_attr));
705 weight[rev] = -weight[e];
706 capacity[e] = static_cast<long>(get_edge_numeric_attr(edge_id, capacity_attr));
707 capacity[rev] = 0;
708 reverse[e] = rev;
709 reverse[rev] = e;
710 original_edges.push_back({edge_id, u, v, e});
711 }
712 const auto source_index = get_vertex_index(id_to_bgl.at(source_id));
713 const auto target_index = get_vertex_index(id_to_bgl.at(target_id));
714 boost::successive_shortest_path_nonnegative_weights(flow_graph, source_index, target_index);
715 ResidualMap residual = boost::get(boost::edge_residual_capacity, flow_graph);
716 long flow_value = 0;
717 for (auto [eit, eend] = boost::out_edges(source_index, flow_graph); eit != eend; ++eit) flow_value += capacity[*eit] - residual[*eit];
718 const long cost = boost::find_flow_cost(flow_graph);
720 result.flow = flow_value;
721 result.cost = cost;
722 detail::fill_flow_views(original_edges, capacity, residual, result.edge_flows, result.edge_flows_by_id);
723 return result;
724}
725
726template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
727auto 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 {
728 return successive_shortest_path_nonnegative_weights(source_id, target_id, capacity_attr, weight_attr);
729}
730
731template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
732auto 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 {
733 return max_flow_min_cost_cycle_canceling(source_id, target_id, capacity_attr, weight_attr);
734}
735
736} // 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:272
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:377
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:671
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:732
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:727
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:482
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:479
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:610
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:549
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:428
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:372
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:356
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:322
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:259
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:227
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:243
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:275
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:309
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:292
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:339
Precise edge_id-based multigraph operations and edge-aware attribute access.
Result of a maximum-flow computation.
Definition flow.hpp:46
Result of a min-cost max-flow computation.
Definition flow.hpp:74
Result of a minimum-cut computation.
Definition flow.hpp:207
Definition flow.hpp:84
Definition flow.hpp:107