graph cleaning

This commit is contained in:
Laurent Perron
2025-01-14 15:59:35 +01:00
parent a3a60a3ec9
commit 0d32cb42ca
16 changed files with 85 additions and 2654 deletions

View File

@@ -103,10 +103,7 @@ CostValue BuildAndSolveHungarianInstance(
template <typename GraphType>
void DisplayAssignment(const LinearSumAssignment<GraphType>& assignment) {
for (typename LinearSumAssignment<GraphType>::BipartiteLeftNodeIterator
node_it(assignment);
node_it.Ok(); node_it.Next()) {
const NodeIndex left_node = node_it.Index();
for (const auto left_node : assignment.BipartiteLeftNodes()) {
const ArcIndex matching_arc = assignment.GetAssignmentArc(left_node);
const NodeIndex right_node = assignment.Head(matching_arc);
VLOG(5) << "assigned (" << left_node << ", " << right_node

View File

@@ -56,7 +56,7 @@ void MinCostFlowOn4x4Matrix() {
min_cost_flow.SetNodeSupply(kNumSources + target, -1);
}
CHECK(min_cost_flow.Solve());
CHECK_EQ(MinCostFlow::OPTIMAL, min_cost_flow.status());
CHECK_EQ(GenericMinCostFlow<Graph>::OPTIMAL, min_cost_flow.status());
CostValue total_flow_cost = min_cost_flow.GetOptimalCost();
CHECK_EQ(kExpectedCost, total_flow_cost);
}

View File

@@ -22,15 +22,16 @@
namespace operations_research {
struct Arc {
std::pair<NodeIndex, NodeIndex> nodes;
FlowQuantity capacity;
FlowQuantity unit_cost;
std::pair<SimpleMinCostFlow::NodeIndex, SimpleMinCostFlow::NodeIndex> nodes;
SimpleMinCostFlow::FlowQuantity capacity;
SimpleMinCostFlow::FlowQuantity unit_cost;
};
void SolveMinCostFlow() {
// Define supply of each node.
const std::vector<std::pair<NodeIndex, FlowQuantity> > supplies = {
{0, 20}, {1, 0}, {2, 0}, {3, -5}, {4, -15}};
const std::vector<
std::pair<SimpleMinCostFlow::NodeIndex, SimpleMinCostFlow::FlowQuantity> >
supplies = {{0, 20}, {1, 0}, {2, 0}, {3, -5}, {4, -15}};
// Define each arc
// Can't use std::tuple<NodeIndex, NodeIndex, FlowQuantity>
@@ -58,7 +59,7 @@ void SolveMinCostFlow() {
if (status != SimpleMinCostFlow::OPTIMAL) {
LOG(FATAL) << "Solving the max flow is not optimal!";
}
FlowQuantity total_flow_cost = min_cost_flow.OptimalCost();
SimpleMinCostFlow::FlowQuantity total_flow_cost = min_cost_flow.OptimalCost();
LOG(INFO) << "Minimum cost flow: " << total_flow_cost;
LOG(INFO) << "";
LOG(INFO) << "Arc : Flow / Capacity / Cost";

View File

@@ -46,10 +46,9 @@ void PrintDimacsAssignmentProblem(
absl::StrFormat("p asn %d %d\n", graph.num_nodes(), graph.num_arcs());
CHECK_OK(file::WriteString(output, output_line, file::Defaults()));
for (typename LinearSumAssignment<GraphType>::BipartiteLeftNodeIterator
node_it(assignment);
node_it.Ok(); node_it.Next()) {
output_line = absl::StrFormat("n %d\n", node_it.Index() + 1);
for (const typename GraphType::NodeIndex left_node :
assignment.BipartiteLeftNodes()) {
output_line = absl::StrFormat("n %d\n", left_node + 1);
CHECK_OK(file::WriteString(output, output_line, file::Defaults()));
}

View File

@@ -32,16 +32,6 @@ config_setting(
constraint_values = ["@platforms//os:windows"],
)
# Main Target
cc_library(
name = "graphs",
hdrs = ["graphs.h"],
deps = [
":ebert_graph",
":graph",
],
)
cc_library(
name = "graph",
hdrs = ["graph.h"],
@@ -350,28 +340,6 @@ cc_test(
cc_library(
name = "ebert_graph",
hdrs = ["ebert_graph.h"],
deps = [
":iterators",
"//ortools/base",
"//ortools/util:permutation",
"//ortools/util:zvector",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/strings",
],
)
cc_test(
name = "ebert_graph_test",
size = "small",
srcs = ["ebert_graph_test.cc"],
deps = [
":ebert_graph",
"//ortools/base:gmock_main",
"//ortools/util:permutation",
"@com_google_absl//absl/random:distributions",
"@com_google_absl//absl/strings",
"@com_google_benchmark//:benchmark",
],
)
cc_library(
@@ -485,7 +453,6 @@ cc_library(
deps = [
":ebert_graph",
":flow_problem_cc_proto",
":graphs",
"//ortools/base",
"//ortools/util:stats",
"//ortools/util:zvector",
@@ -502,7 +469,6 @@ cc_test(
":ebert_graph",
":generic_max_flow",
":graph",
":graphs",
"//ortools/base",
"//ortools/base:gmock_main",
"//ortools/linear_solver",
@@ -526,10 +492,8 @@ cc_library(
"//conditions:default": [],
}),
deps = [
":ebert_graph",
":generic_max_flow",
":graph",
":graphs",
"//ortools/base:mathutil",
"//ortools/util:saturated_arithmetic",
"//ortools/util:stats",
@@ -549,7 +513,6 @@ cc_test(
srcs = ["min_cost_flow_test.cc"],
deps = [
":ebert_graph",
":graphs",
":min_cost_flow",
"//ortools/base:gmock_main",
"@com_google_absl//absl/log",
@@ -661,6 +624,7 @@ cc_library(
hdrs = ["linear_assignment.h"],
deps = [
":ebert_graph",
":iterators",
"//ortools/base",
"//ortools/util:permutation",
"//ortools/util:zvector",
@@ -674,11 +638,11 @@ cc_test(
size = "small",
srcs = ["linear_assignment_test.cc"],
deps = [
":ebert_graph",
":graph",
":linear_assignment",
"//ortools/base",
"//ortools/base:gmock_main",
"@com_google_absl//absl/flags:flag",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/random:distributions",
"@com_google_absl//absl/types:span",
"@com_google_benchmark//:benchmark",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -267,14 +267,13 @@ bool SimpleFloatingPointMinCostFlow::ScaleSupplyAndCapacity() {
// When we have no flow on any node, we can simply scale with 2^0 = 1.
log2_scale_ = 0;
} else {
// Note that if max_nodes_in_or_out_flow is a denormal number (< 2^-970)
// Note that if max_nodes_in_or_out_flow is a very small number (< 2^-960)
// then the following division can overflow. If this is the case we simply
// replace the scale by the max possible value.
double scale_upper_bound = static_cast<double>(kMaxFlowQuantity) /
max_nodes_in_or_out_flow.value();
if (!std::isfinite(scale_upper_bound)) {
scale_upper_bound = kMaxFPFlow;
}
const double scale_upper_bound =
std::min(std::numeric_limits<double>::max(),
static_cast<double>(kMaxFlowQuantity) /
max_nodes_in_or_out_flow.value());
const double f = std::frexp(scale_upper_bound, &log2_scale_);
// The result of std::frexp() is such that:
// 2^(p-1) <= scale_upper_bound < 2^p

View File

@@ -130,12 +130,10 @@
#include <utility>
#include <vector>
#include "absl/memory/memory.h"
#include "absl/strings/string_view.h"
#include "ortools/base/logging.h"
#include "ortools/graph/ebert_graph.h"
#include "ortools/graph/flow_problem.pb.h"
#include "ortools/graph/graphs.h"
#include "ortools/util/stats.h"
#include "ortools/util/zvector.h"
@@ -573,7 +571,7 @@ GenericMaxFlow<Graph>::GenericMaxFlow(const Graph* graph, NodeIndex source,
SCOPED_TIME_STAT(&stats_);
DCHECK(graph->IsNodeValid(source));
DCHECK(graph->IsNodeValid(sink));
const NodeIndex max_num_nodes = Graphs<Graph>::NodeReservation(*graph_);
const NodeIndex max_num_nodes = graph_->node_capacity();
if (max_num_nodes > 0) {
// We will initialize them in InitializePreflow(), so no need for memset.
//
@@ -584,7 +582,7 @@ GenericMaxFlow<Graph>::GenericMaxFlow(const Graph* graph, NodeIndex source,
first_admissible_arc_ = std::make_unique<ArcIndex[]>(max_num_nodes);
bfs_queue_.reserve(max_num_nodes);
}
const ArcIndex max_num_arcs = Graphs<Graph>::ArcReservation(*graph_);
const ArcIndex max_num_arcs = graph_->arc_capacity();
if (max_num_arcs > 0) {
if constexpr (Graph::kHasNegativeReverseArcs) {
residual_arc_capacity_.Reserve(-max_num_arcs, max_num_arcs - 1);
@@ -767,7 +765,7 @@ void GenericMaxFlow<Graph>::InitializePreflow() {
// TODO(user): Ebert graph has an issue with nodes with no arcs, so we
// use max_num_nodes here to resize vectors.
const NodeIndex num_nodes = graph_->num_nodes();
const NodeIndex max_num_nodes = Graphs<Graph>::NodeReservation(*graph_);
const NodeIndex max_num_nodes = graph_->node_capacity();
// InitializePreflow() clears the whole flow that could have been computed
// by a previous Solve(). This is not optimal in terms of complexity.
@@ -1165,9 +1163,7 @@ template <typename Graph>
void GenericMaxFlow<Graph>::RefineWithGlobalUpdate() {
SCOPED_TIME_STAT(&stats_);
// TODO(user): This should be graph_->num_nodes(), but ebert graph does not
// have a correct size if the highest index nodes have no arcs.
const NodeIndex num_nodes = Graphs<Graph>::NodeReservation(*graph_);
const NodeIndex num_nodes = graph_->num_nodes();
std::vector<int> skip_active_node;
// Usually SaturateOutgoingArcsFromSource() will saturate all the arcs from
@@ -1304,7 +1300,7 @@ void GenericMaxFlow<Graph>::Relabel(NodeIndex node) {
template <typename Graph>
typename Graph::ArcIndex GenericMaxFlow<Graph>::Opposite(ArcIndex arc) const {
return Graphs<Graph>::OppositeArc(*graph_, arc);
return graph_->OppositeArc(arc);
}
template <typename Graph>
@@ -1314,7 +1310,7 @@ bool GenericMaxFlow<Graph>::IsArcDirect(ArcIndex arc) const {
template <typename Graph>
bool GenericMaxFlow<Graph>::IsArcValid(ArcIndex arc) const {
return Graphs<Graph>::IsArcValid(*graph_, arc);
return graph_->IsArcValid(arc);
}
template <typename Graph>

View File

@@ -33,7 +33,6 @@
#include "ortools/base/logging.h"
#include "ortools/graph/ebert_graph.h"
#include "ortools/graph/graph.h"
#include "ortools/graph/graphs.h"
#include "ortools/linear_solver/linear_solver.h"
namespace operations_research {
@@ -58,7 +57,7 @@ typename GenericMaxFlow<Graph>::Status MaxFlowTester(
graph.AddArc(tail[i], head[i]);
}
std::vector<typename Graph::ArcIndex> permutation;
Graphs<Graph>::Build(&graph, &permutation);
graph.Build(&permutation);
GenericMaxFlow<Graph> max_flow(&graph, 0, num_nodes - 1);
for (typename Graph::ArcIndex arc = 0; arc < num_arcs; ++arc) {
@@ -69,7 +68,8 @@ typename GenericMaxFlow<Graph>::Status MaxFlowTester(
}
EXPECT_TRUE(max_flow.Solve());
if (max_flow.status() == GenericMaxFlow<Graph>::OPTIMAL) {
const FlowQuantity total_flow = max_flow.GetOptimalFlow();
const typename GenericMaxFlow<Graph>::FlowQuantityT total_flow =
max_flow.GetOptimalFlow();
EXPECT_EQ(expected_total_flow, total_flow);
for (int arc = 0; arc < num_arcs; ++arc) {
const int image = arc < permutation.size() ? permutation[arc] : arc;
@@ -80,13 +80,13 @@ typename GenericMaxFlow<Graph>::Status MaxFlowTester(
// Tests the min-cut functions.
if (expected_source_min_cut != nullptr) {
std::vector<NodeIndex> cut;
std::vector<typename Graph::NodeIndex> cut;
max_flow.GetSourceSideMinCut(&cut);
std::sort(cut.begin(), cut.end());
EXPECT_THAT(*expected_source_min_cut, WhenSorted(ContainerEq(cut)));
}
if (expected_sink_min_cut != nullptr) {
std::vector<NodeIndex> cut;
std::vector<typename Graph::NodeIndex> cut;
max_flow.GetSinkSideMinCut(&cut);
std::sort(cut.begin(), cut.end());
EXPECT_THAT(*expected_sink_min_cut, WhenSorted(ContainerEq(cut)));
@@ -98,7 +98,7 @@ typename GenericMaxFlow<Graph>::Status MaxFlowTester(
template <typename Graph>
class GenericMaxFlowTest : public ::testing::Test {};
typedef ::testing::Types<StarGraph, util::ReverseArcListGraph<>,
typedef ::testing::Types<util::ReverseArcListGraph<>,
util::ReverseArcStaticGraph<>,
util::ReverseArcMixedGraph<>>
GraphTypes;
@@ -176,6 +176,7 @@ TYPED_TEST(GenericMaxFlowTest, HugeCapacity) {
}
TYPED_TEST(GenericMaxFlowTest, FlowQuantityOverflowLimitCase) {
using FlowQuantity = typename GenericMaxFlow<TypeParam>::FlowQuantityT;
const FlowQuantity kCapacityMax = std::numeric_limits<FlowQuantity>::max();
const FlowQuantity kHalfLow = kCapacityMax / 2;
const FlowQuantity kHalfHigh = kCapacityMax - kHalfLow;
@@ -197,6 +198,7 @@ TYPED_TEST(GenericMaxFlowTest, FlowQuantityOverflowLimitCase) {
}
TYPED_TEST(GenericMaxFlowTest, FlowQuantityOverflow) {
using FlowQuantity = typename GenericMaxFlow<TypeParam>::FlowQuantityT;
const FlowQuantity kCapacityMax = std::numeric_limits<FlowQuantity>::max();
const int kNumNodes = 4;
const int kNumArcs = 4;
@@ -394,7 +396,7 @@ void FullAssignment(std::optional<FlowQuantity> unused,
typename Graph::NodeIndex num_heads) {
Graph graph;
GenerateCompleteGraph(num_tails, num_heads, &graph);
Graphs<Graph>::Build(&graph);
graph.Build();
std::vector<int64_t> arc_capacity(graph.num_arcs(), 1);
std::unique_ptr<GenericMaxFlow<Graph>> max_flow(new GenericMaxFlow<Graph>(
&graph, graph.num_nodes() - 2, graph.num_nodes() - 1));
@@ -470,7 +472,7 @@ void PartialRandomFlow(std::optional<FlowQuantity> expected_flow,
GenerateRandomArcValuations(random, graph, kCapacityRange, &arc_capacity);
std::vector<typename Graph::ArcIndex> permutation;
Graphs<Graph>::Build(&graph, &permutation);
graph.Build(&permutation);
arc_capacity.resize(graph.num_arcs(), 0); // In case Build() adds more arcs.
util::Permute(permutation, &arc_capacity);
@@ -518,7 +520,7 @@ void FullRandomFlow(std::optional<FlowQuantity> expected_flow,
GenerateRandomArcValuations(random, graph, kCapacityRange, &arc_capacity);
std::vector<typename Graph::ArcIndex> permutation;
Graphs<Graph>::Build(&graph, &permutation);
graph.Build(&permutation);
arc_capacity.resize(graph.num_arcs(), 0); // In case Build() adds more arcs.
util::Permute(permutation, &arc_capacity);
@@ -554,11 +556,7 @@ void FullRandomFlow(std::optional<FlowQuantity> expected_flow,
TEST(MaxFlowListGraphTest, test_name##size) { \
test_name<util::ReverseArcListGraph<>>(std::nullopt, SolveMaxFlow, size, \
size); \
} \
TEST(MaxFlowStarGraphTest, test_name##size) { \
test_name<StarGraph>(std::nullopt, SolveMaxFlow, size, size); \
}
// These are absl::BitGen random test, so they will always work on different
// graphs.
LP_AND_FLOW_TEST(FullAssignment, 300);
@@ -604,22 +602,18 @@ static void BM_FullRandomFlow(benchmark::State& state) {
}
// Note that these benchmark include the graph creation and generation...
BENCHMARK_TEMPLATE(BM_FullRandomAssignment, StarGraph);
BENCHMARK_TEMPLATE(BM_FullRandomAssignment, util::ReverseArcListGraph<>);
BENCHMARK_TEMPLATE(BM_FullRandomAssignment, util::ReverseArcStaticGraph<>);
BENCHMARK_TEMPLATE(BM_FullRandomAssignment, util::ReverseArcMixedGraph<>);
BENCHMARK_TEMPLATE(BM_PartialRandomFlow, StarGraph);
BENCHMARK_TEMPLATE(BM_PartialRandomFlow, util::ReverseArcListGraph<>);
BENCHMARK_TEMPLATE(BM_PartialRandomFlow, util::ReverseArcStaticGraph<>);
BENCHMARK_TEMPLATE(BM_PartialRandomFlow, util::ReverseArcMixedGraph<>);
BENCHMARK_TEMPLATE(BM_FullRandomFlow, StarGraph);
BENCHMARK_TEMPLATE(BM_FullRandomFlow, util::ReverseArcListGraph<>);
BENCHMARK_TEMPLATE(BM_FullRandomFlow, util::ReverseArcStaticGraph<>);
BENCHMARK_TEMPLATE(BM_FullRandomFlow, util::ReverseArcMixedGraph<>);
BENCHMARK_TEMPLATE(BM_PartialRandomAssignment, StarGraph);
BENCHMARK_TEMPLATE(BM_PartialRandomAssignment, util::ReverseArcListGraph<>);
BENCHMARK_TEMPLATE(BM_PartialRandomAssignment, util::ReverseArcStaticGraph<>);
BENCHMARK_TEMPLATE(BM_PartialRandomAssignment, util::ReverseArcMixedGraph<>);

View File

@@ -1,80 +0,0 @@
// Copyright 2010-2025 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Temporary utility class needed as long as we have two slightly
// different graph interface: The one in ebert_graph.h and the one in graph.h
#ifndef OR_TOOLS_GRAPH_GRAPHS_H_
#define OR_TOOLS_GRAPH_GRAPHS_H_
#include <vector>
#include "ortools/graph/ebert_graph.h"
namespace operations_research {
// Since StarGraph does not have exactly the same interface as the other
// graphs, we define a correspondence there.
template <typename Graph>
struct Graphs {
typedef typename Graph::ArcIndex ArcIndex;
typedef typename Graph::NodeIndex NodeIndex;
static ArcIndex OppositeArc(const Graph& graph, ArcIndex arc) {
return graph.OppositeArc(arc);
}
static bool IsArcValid(const Graph& graph, ArcIndex arc) {
return graph.IsArcValid(arc);
}
static NodeIndex NodeReservation(const Graph& graph) {
return graph.node_capacity();
}
static ArcIndex ArcReservation(const Graph& graph) {
return graph.arc_capacity();
}
static void Build(Graph* graph) { graph->Build(); }
static void Build(Graph* graph, std::vector<ArcIndex>* permutation) {
graph->Build(permutation);
}
};
template <>
struct Graphs<operations_research::StarGraph> {
typedef operations_research::StarGraph Graph;
#if defined(_MSC_VER)
typedef Graph::ArcIndex ArcIndex;
typedef Graph::NodeIndex NodeIndex;
#else
typedef typename Graph::ArcIndex ArcIndex;
typedef typename Graph::NodeIndex NodeIndex;
#endif
static ArcIndex OppositeArc(const Graph& graph, ArcIndex arc) {
return graph.Opposite(arc);
}
static bool IsArcValid(const Graph& graph, ArcIndex arc) {
return graph.CheckArcValidity(arc);
}
static NodeIndex NodeReservation(const Graph& graph) {
return graph.max_num_nodes();
}
static ArcIndex ArcReservation(const Graph& graph) {
return graph.max_num_arcs();
}
static void Build(Graph* graph) {}
static void Build(Graph* graph, std::vector<ArcIndex>* permutation) {
permutation->clear();
}
};
} // namespace operations_research
#endif // OR_TOOLS_GRAPH_GRAPHS_H_

View File

@@ -209,6 +209,7 @@
#include "absl/strings/str_format.h"
#include "ortools/base/logging.h"
#include "ortools/graph/ebert_graph.h"
#include "ortools/graph/iterators.h"
#include "ortools/util/permutation.h"
#include "ortools/util/zvector.h"
@@ -230,8 +231,7 @@ class LinearSumAssignment {
// Constructor for the case in which we will build the graph
// incrementally as we discover arc costs, as might be done with any
// of the dynamic graph representations such as `ReverseArcListGraph` or
// `ForwardStarGraph`.
// of the dynamic graph representations such as `ReverseArcListGraph`.
LinearSumAssignment(const GraphType& graph, NodeIndex num_left_nodes);
// Constructor for the case in which the underlying graph cannot be built
@@ -346,24 +346,10 @@ class LinearSumAssignment {
std::string StatsString() const { return total_stats_.StatsString(); }
class BipartiteLeftNodeIterator {
public:
BipartiteLeftNodeIterator(const GraphType& graph, NodeIndex num_left_nodes)
: num_left_nodes_(num_left_nodes), node_iterator_(0) {}
explicit BipartiteLeftNodeIterator(const LinearSumAssignment& assignment)
: num_left_nodes_(assignment.NumLeftNodes()), node_iterator_(0) {}
NodeIndex Index() const { return node_iterator_; }
bool Ok() const { return node_iterator_ < num_left_nodes_; }
void Next() { ++node_iterator_; }
private:
const NodeIndex num_left_nodes_;
typename GraphType::NodeIndex node_iterator_;
};
// Returns the range of valid left node indices.
::util::IntegerRange<NodeIndex> BipartiteLeftNodes() const {
return ::util::IntegerRange<NodeIndex>(0, num_left_nodes_);
}
// Returns true if and only if the current pseudoflow is
// epsilon-optimal. To be used in a DCHECK.
@@ -1119,9 +1105,7 @@ template <typename GraphType, typename CostValue>
void LinearSumAssignment<GraphType,
CostValue>::InitializeActiveNodeContainer() {
DCHECK(active_nodes_->Empty());
for (BipartiteLeftNodeIterator node_it(*graph_, num_left_nodes_);
node_it.Ok(); node_it.Next()) {
const NodeIndex node = node_it.Index();
for (const NodeIndex node : BipartiteLeftNodes()) {
if (IsActive(node)) {
active_nodes_->Add(node);
}
@@ -1141,9 +1125,7 @@ void LinearSumAssignment<GraphType,
template <typename GraphType, typename CostValue>
void LinearSumAssignment<GraphType, CostValue>::SaturateNegativeArcs() {
total_excess_ = 0;
for (BipartiteLeftNodeIterator node_it(*graph_, num_left_nodes_);
node_it.Ok(); node_it.Next()) {
const NodeIndex node = node_it.Index();
for (const NodeIndex node : BipartiteLeftNodes()) {
if (IsActive(node)) {
// This can happen in the first iteration when nothing is
// matched yet.
@@ -1328,9 +1310,7 @@ bool LinearSumAssignment<GraphType, CostValue>::AllMatched() const {
// Only for debugging.
template <typename GraphType, typename CostValue>
bool LinearSumAssignment<GraphType, CostValue>::EpsilonOptimal() const {
for (BipartiteLeftNodeIterator node_it(*graph_, num_left_nodes_);
node_it.Ok(); node_it.Next()) {
const NodeIndex left_node = node_it.Index();
for (const NodeIndex left_node : BipartiteLeftNodes()) {
// Get the implicit price of left_node and make sure the reduced
// costs of left_node's incident arcs are in bounds.
CostValue left_node_price = ImplicitPrice(left_node);
@@ -1452,8 +1432,8 @@ CostValue LinearSumAssignment<GraphType, CostValue>::GetCost() const {
// an optimum assignment.
DCHECK(success_);
CostValue cost = 0;
for (BipartiteLeftNodeIterator node_it(*this); node_it.Ok(); node_it.Next()) {
cost += GetAssignmentCost(node_it.Index());
for (const NodeIndex node : BipartiteLeftNodes()) {
cost += GetAssignmentCost(node);
}
return cost;
}

View File

@@ -228,10 +228,7 @@ template <typename GraphType>
static void VerifyAssignment(
const LinearSumAssignment<GraphType>& a,
const typename GraphType::NodeIndex expected_right_side[]) {
for (typename LinearSumAssignment<GraphType>::BipartiteLeftNodeIterator
node_it(a);
node_it.Ok(); node_it.Next()) {
const typename GraphType::NodeIndex left_node = node_it.Index();
for (const typename GraphType::NodeIndex left_node : a.BipartiteLeftNodes()) {
const typename GraphType::NodeIndex right_node = a.GetMate(left_node);
EXPECT_EQ(expected_right_side[left_node], right_node);
}
@@ -445,10 +442,7 @@ TEST_P(MacholWien, SolveHardProblem) {
}
}
EXPECT_TRUE(assignment.ComputeAssignment());
for (LinearSumAssignment<Graph>::BipartiteLeftNodeIterator node_it(
assignment);
node_it.Ok(); node_it.Next()) {
const Graph::NodeIndex left_node = node_it.Index();
for (const Graph::NodeIndex left_node : assignment.BipartiteLeftNodes()) {
const Graph::NodeIndex right_node = assignment.GetMate(left_node);
EXPECT_EQ(2 * n - 1, left_node + right_node);
}

View File

@@ -30,7 +30,6 @@
#include "ortools/base/mathutil.h"
#include "ortools/graph/generic_max_flow.h"
#include "ortools/graph/graph.h"
#include "ortools/graph/graphs.h"
#include "ortools/util/saturated_arithmetic.h"
#include "ortools/util/stats.h"
@@ -54,14 +53,14 @@ GenericMinCostFlow<Graph, ArcFlowType, ArcScaledCostType>::GenericMinCostFlow(
alpha_(absl::GetFlag(FLAGS_min_cost_flow_alpha)),
stats_("MinCostFlow"),
check_feasibility_(absl::GetFlag(FLAGS_min_cost_flow_check_feasibility)) {
const NodeIndex max_num_nodes = Graphs<Graph>::NodeReservation(*graph_);
const NodeIndex max_num_nodes = graph_->node_capacity();
if (max_num_nodes > 0) {
first_admissible_arc_.assign(max_num_nodes, Graph::kNilArc);
node_potential_.assign(max_num_nodes, 0);
node_excess_.assign(max_num_nodes, 0);
initial_node_excess_.assign(max_num_nodes, 0);
}
const ArcIndex max_num_arcs = Graphs<Graph>::ArcReservation(*graph_);
const ArcIndex max_num_arcs = graph_->arc_capacity();
if (max_num_arcs > 0) {
residual_arc_capacity_.Reserve(-max_num_arcs, max_num_arcs - 1);
residual_arc_capacity_.SetAll(0);
@@ -309,7 +308,6 @@ bool GenericMinCostFlow<Graph, ArcFlowType,
// There are no supplies or demands or costs in the graph, as we will run
// max-flow.
// TODO(user): make it possible to share a graph by MaxFlow and MinCostFlow.
// For this it is necessary to make StarGraph resizable.
feasibility_checked_ = false;
ArcIndex num_extra_arcs = 0;
for (NodeIndex node = 0; node < graph_->num_nodes(); ++node) {
@@ -976,13 +974,13 @@ template <typename Graph, typename ArcFlowType, typename ArcScaledCostType>
typename Graph::ArcIndex
GenericMinCostFlow<Graph, ArcFlowType, ArcScaledCostType>::Opposite(
ArcIndex arc) const {
return Graphs<Graph>::OppositeArc(*graph_, arc);
return graph_->OppositeArc(arc);
}
template <typename Graph, typename ArcFlowType, typename ArcScaledCostType>
bool GenericMinCostFlow<Graph, ArcFlowType, ArcScaledCostType>::IsArcValid(
ArcIndex arc) const {
return Graphs<Graph>::IsArcValid(*graph_, arc);
return graph_->IsArcValid(arc);
}
template <typename Graph, typename ArcFlowType, typename ArcScaledCostType>
@@ -996,7 +994,6 @@ bool GenericMinCostFlow<Graph, ArcFlowType, ArcScaledCostType>::IsArcDirect(
//
// TODO(user): Move this code out of a .cc file and include it at the end of
// the header so it can work with any graph implementation?
template class GenericMinCostFlow<StarGraph>;
template class GenericMinCostFlow<::util::ReverseArcListGraph<>>;
template class GenericMinCostFlow<::util::ReverseArcStaticGraph<>>;
template class GenericMinCostFlow<::util::ReverseArcMixedGraph<>>;

View File

@@ -174,7 +174,6 @@
#include <vector>
#include "absl/strings/string_view.h"
#include "ortools/graph/ebert_graph.h"
#include "ortools/graph/graph.h"
#include "ortools/util/stats.h"
#include "ortools/util/zvector.h"
@@ -383,9 +382,9 @@ class SimpleMinCostFlow : public MinCostFlowBase {
bool scale_prices_ = true;
};
// Generic MinCostFlow that works with StarGraph and all the graphs handling
// reverse arcs from graph.h, see the end of min_cost_flow.cc for the exact
// types this class is compiled for.
// Generic MinCostFlow that works with all the graphs handling reverse arcs from
// graph.h, see the end of min_cost_flow.cc for the exact types this class is
// compiled for.
//
// One can greatly decrease memory usage by using appropriately small integer
// types:
@@ -675,7 +674,6 @@ class GenericMinCostFlow : public MinCostFlowBase {
// Note: SWIG does not seem to understand explicit template specialization and
// instantiation declarations.
extern template class GenericMinCostFlow<StarGraph>;
extern template class GenericMinCostFlow<::util::ReverseArcListGraph<>>;
extern template class GenericMinCostFlow<::util::ReverseArcStaticGraph<>>;
extern template class GenericMinCostFlow<::util::ReverseArcMixedGraph<>>;
@@ -692,9 +690,10 @@ extern template class GenericMinCostFlow<
// a grace period.
struct MinCostFlow : public MinCostFlowBase {
template <typename = void>
MinCostFlow() {
LOG(ERROR) << "MinCostFlow is deprecated. Use `SimpleMinCostFlow` or "
"`GenericMinCostFlow` with a specific graph type instead.";
MinCostFlow() {
static_assert(false,
"MinCostFlow is deprecated. Use `SimpleMinCostFlow` or "
"`GenericMinCostFlow` with a specific graph type instead.");
}
};

View File

@@ -30,7 +30,6 @@
#include "ortools/base/logging.h"
#include "ortools/graph/ebert_graph.h"
#include "ortools/graph/graph.h"
#include "ortools/graph/graphs.h"
#include "ortools/linear_solver/linear_solver.h"
namespace operations_research {
@@ -116,7 +115,7 @@ void GenericMinCostFlowTester(
graph.AddArc(tail[arc], head[arc]);
}
std::vector<typename Graph::ArcIndex> permutation;
Graphs<Graph>::Build(&graph, &permutation);
graph.Build(&permutation);
EXPECT_TRUE(permutation.empty());
GenericMinCostFlow<Graph> min_cost_flow(&graph);
@@ -151,7 +150,7 @@ void GenericMinCostFlowTester(
template <typename Graph>
class GenericMinCostFlowTest : public ::testing::Test {};
typedef ::testing::Types<StarGraph, util::ReverseArcListGraph<>,
typedef ::testing::Types<util::ReverseArcListGraph<>,
util::ReverseArcStaticGraph<>,
util::ReverseArcMixedGraph<>>
GraphTypes;
@@ -248,7 +247,7 @@ TYPED_TEST(GenericMinCostFlowTest, Small4x4Matrix) {
}
}
std::vector<ArcIndex> permutation;
Graphs<TypeParam>::Build(&graph, &permutation);
graph.Build(&permutation);
EXPECT_TRUE(permutation.empty());
GenericMinCostFlow<TypeParam> min_cost_flow(&graph);
@@ -642,7 +641,7 @@ void FullRandomAssignment(typename MinCostFlowSolver<Graph>::Solver f,
Graph graph;
GenerateCompleteGraph(num_sources, num_targets, &graph);
std::vector<typename Graph::ArcIndex> permutation;
Graphs<Graph>::Build(&graph, &permutation);
graph.Build(&permutation);
std::vector<int64_t> supply;
GenerateAssignmentSupply(num_sources, num_targets, &supply);
@@ -668,7 +667,7 @@ void PartialRandomAssignment(typename MinCostFlowSolver<Graph>::Solver f,
Graph graph;
GeneratePartialRandomGraph(num_sources, num_targets, kDegree, &graph);
std::vector<typename Graph::ArcIndex> permutation;
Graphs<Graph>::Build(&graph, &permutation);
graph.Build(&permutation);
std::vector<int64_t> supply;
GenerateAssignmentSupply(num_sources, num_targets, &supply);
@@ -718,7 +717,7 @@ void PartialRandomFlow(typename MinCostFlowSolver<Graph>::Solver f,
Graph graph;
GeneratePartialRandomGraph(num_sources, num_targets, kDegree, &graph);
std::vector<typename Graph::ArcIndex> permutation;
Graphs<Graph>::Build(&graph, &permutation);
graph.Build(&permutation);
std::vector<int64_t> supply;
GenerateRandomSupply(num_sources, num_targets, kSupplyGens, kSupplyRange,
@@ -756,7 +755,7 @@ void FullRandomFlow(typename MinCostFlowSolver<Graph>::Solver f,
Graph graph;
GenerateCompleteGraph(num_sources, num_targets, &graph);
std::vector<typename Graph::ArcIndex> permutation;
Graphs<Graph>::Build(&graph, &permutation);
graph.Build(&permutation);
std::vector<int64_t> supply;
GenerateRandomSupply(num_sources, num_targets, kSupplyGens, kSupplyRange,
@@ -787,16 +786,16 @@ void FullRandomFlow(typename MinCostFlowSolver<Graph>::Solver f,
FLOW_ONLY_TEST(test_name, size, expected_cost1, expected_cost2) \
FLOW_ONLY_TEST_SG(test_name, size, expected_cost1, expected_cost2)
#define LP_ONLY_TEST(test_name, size, expected_cost1, expected_cost2) \
TEST(LPMinCostFlowTest, test_name##size) { \
test_name<StarGraph>(SolveMinCostFlowWithLP, size, size, expected_cost1, \
expected_cost2); \
#define LP_ONLY_TEST(test_name, size, expected_cost1, expected_cost2) \
TEST(LPMinCostFlowTest, test_name##size) { \
test_name<util::ReverseArcListGraph<>>(SolveMinCostFlowWithLP, size, size, \
expected_cost1, expected_cost2); \
}
#define FLOW_ONLY_TEST(test_name, size, expected_cost1, expected_cost2) \
TEST(MinCostFlowTest, test_name##size) { \
test_name<StarGraph>(SolveMinCostFlow, size, size, expected_cost1, \
expected_cost2); \
#define FLOW_ONLY_TEST(test_name, size, expected_cost1, expected_cost2) \
TEST(MinCostFlowTest, test_name##size) { \
test_name<util::ReverseArcListGraph<>>(SolveMinCostFlow, size, size, \
expected_cost1, expected_cost2); \
}
#define FLOW_ONLY_TEST_SG(test_name, size, expected_cost1, expected_cost2) \
@@ -930,7 +929,7 @@ void BM_MinCostFlowOnMultiMatchingProblem(benchmark::State& state) {
graph.AddArc(/*tail=*/kNumChannels + 1 + j, /*head=*/0);
}
std::vector<typename Graph::ArcIndex> permutation;
Graphs<Graph>::Build(&graph, &permutation);
graph.Build(&permutation);
// To spare memory, we added arcs in the right order, so that no permutation
// is needed. See graph.h.
CHECK(permutation.empty());
@@ -974,14 +973,11 @@ void BM_MinCostFlowOnMultiMatchingProblem(benchmark::State& state) {
BENCHMARK(BM_MinCostFlowOnMultiMatchingProblem<
util::ReverseArcStaticGraph<uint16_t, int32_t>, int16_t, int32_t,
/*kNumChannels=*/20000, /*kNumUsers=*/20000>);
// We also benchmark with default parameter types and StarGraph for reference.
// We also benchmark with default parameter types for reference.
// We use fewer channels and users to avoid running out of memory.
BENCHMARK(BM_MinCostFlowOnMultiMatchingProblem<
::util::ReverseArcListGraph<>, int64_t, int64_t,
/*kNumChannels=*/5000, /*kNumUsers=*/5000>);
BENCHMARK(BM_MinCostFlowOnMultiMatchingProblem<StarGraph, int64_t, int64_t,
/*kNumChannels=*/5000,
/*kNumUsers=*/5000>);
} // namespace
} // namespace operations_research