graph cleaning
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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<>);
|
||||
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<>>;
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user