Files
ortools-clone/examples/cpp/dimacs_assignment.cc

204 lines
8.0 KiB
C++
Raw Normal View History

2021-04-02 10:08:51 +02:00
// Copyright 2010-2021 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.
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <string>
#include <vector>
2020-09-23 11:45:03 +02:00
#include "absl/container/flat_hash_map.h"
dotnet: Remove reference to dotnet release command - Currently not implemented... Add abseil patch - Add patches/absl-config.cmake Makefile: Add abseil-cpp on unix - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake Makefile: Add abseil-cpp on windows - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake CMake: Add abseil-cpp - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake port to absl: C++ Part - Fix warning with the use of ABSL_MUST_USE_RESULT > The macro must appear as the very first part of a function declaration or definition: ... Note: past advice was to place the macro after the argument list. src: dependencies/sources/abseil-cpp-master/absl/base/attributes.h:418 - Rename enum after windows clash - Remove non compact table constraints - Change index type from int64 to int in routing library - Fix file_nonport compilation on windows - Fix another naming conflict with windows (NO_ERROR is a macro) - Cleanup hash containers; work on sat internals - Add optional_boolean sub-proto Sync cpp examples with internal code - reenable issue173 after reducing number of loops port to absl: Python Part - Add back cp_model.INT32_MIN|MAX for examples Update Python examples - Add random_tsp.py - Run words_square example - Run magic_square in python tests port to absl: Java Part - Fix compilation of the new routing parameters in java - Protect some code from SWIG parsing Update Java Examples port to absl: .Net Part Update .Net examples work on sat internals; Add C++ CP-SAT CpModelBuilder API; update sample code and recipes to use the new API; sync with internal code Remove VS 2015 in Appveyor-CI - abseil-cpp does not support VS 2015... improve tables upgrade C++ sat examples to use the new API; work on sat internals update license dates rewrite jobshop_ft06_distance.py to use the CP-SAT solver rename last example revert last commit more work on SAT internals fix
2018-10-31 16:18:18 +01:00
#include "absl/strings/str_format.h"
2018-06-08 16:40:43 +02:00
#include "examples/cpp/parse_dimacs_assignment.h"
#include "examples/cpp/print_dimacs_assignment.h"
#include "ortools/algorithms/hungarian.h"
#include "ortools/base/commandlineflags.h"
2022-02-25 09:47:52 +01:00
#include "ortools/base/init_google.h"
#include "ortools/base/logging.h"
#include "ortools/base/timer.h"
#include "ortools/graph/ebert_graph.h"
#include "ortools/graph/linear_assignment.h"
2020-10-23 11:50:14 +02:00
ABSL_FLAG(bool, assignment_compare_hungarian, false,
"Compare result and speed against Hungarian method.");
ABSL_FLAG(std::string, assignment_problem_output_file, "",
"Print the problem to this file in DIMACS format (after layout "
"is optimized, if applicable).");
ABSL_FLAG(bool, assignment_reverse_arcs, false,
"Ignored if --assignment_static_graph=true. Use StarGraph "
"if true, ForwardStarGraph if false.");
ABSL_FLAG(bool, assignment_static_graph, true,
"Use the ForwardStarStaticGraph representation, "
"otherwise ForwardStarGraph or StarGraph according "
"to --assignment_reverse_arcs.");
namespace operations_research {
typedef ForwardStarStaticGraph GraphType;
2014-01-08 12:01:58 +00:00
template <typename GraphType>
CostValue BuildAndSolveHungarianInstance(
2020-10-29 14:25:39 +01:00
const LinearSumAssignment<GraphType>& assignment) {
const GraphType& graph = assignment.Graph();
typedef std::vector<double> HungarianRow;
typedef std::vector<HungarianRow> HungarianProblem;
HungarianProblem hungarian_cost;
hungarian_cost.resize(assignment.NumLeftNodes());
// First we have to find the biggest cost magnitude so we can
// initialize the arc costs that aren't really there.
CostValue largest_cost_magnitude = 0;
2014-01-08 12:01:58 +00:00
for (typename GraphType::ArcIterator arc_it(graph); arc_it.Ok();
arc_it.Next()) {
ArcIndex arc = arc_it.Index();
CostValue cost_magnitude = std::abs(assignment.ArcCost(arc));
largest_cost_magnitude = std::max(largest_cost_magnitude, cost_magnitude);
}
2014-01-08 12:01:58 +00:00
double missing_arc_cost = static_cast<double>(
(assignment.NumLeftNodes() * largest_cost_magnitude) + 1);
for (HungarianProblem::iterator row = hungarian_cost.begin();
2014-01-08 12:01:58 +00:00
row != hungarian_cost.end(); ++row) {
row->resize(assignment.NumNodes() - assignment.NumLeftNodes(),
missing_arc_cost);
}
// We're using a graph representation without forward arcs, so in
// order to use the generic GraphType::ArcIterator we would
// need to increase our memory footprint by building the array of
// arc tails (since we need tails to build the input to the
// hungarian algorithm). We opt for the alternative of iterating
// over hte arcs via adjacency lists, which gives us the arc tails
// implicitly.
2014-01-08 12:01:58 +00:00
for (typename GraphType::NodeIterator node_it(graph); node_it.Ok();
node_it.Next()) {
NodeIndex node = node_it.Index();
NodeIndex tail = (node - GraphType::kFirstNode);
for (typename GraphType::OutgoingArcIterator arc_it(graph, node);
2014-01-08 12:01:58 +00:00
arc_it.Ok(); arc_it.Next()) {
ArcIndex arc = arc_it.Index();
2014-01-08 12:01:58 +00:00
NodeIndex head =
(graph.Head(arc) - assignment.NumLeftNodes() - GraphType::kFirstNode);
double cost = static_cast<double>(assignment.ArcCost(arc));
hungarian_cost[tail][head] = cost;
}
}
dotnet: Remove reference to dotnet release command - Currently not implemented... Add abseil patch - Add patches/absl-config.cmake Makefile: Add abseil-cpp on unix - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake Makefile: Add abseil-cpp on windows - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake CMake: Add abseil-cpp - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake port to absl: C++ Part - Fix warning with the use of ABSL_MUST_USE_RESULT > The macro must appear as the very first part of a function declaration or definition: ... Note: past advice was to place the macro after the argument list. src: dependencies/sources/abseil-cpp-master/absl/base/attributes.h:418 - Rename enum after windows clash - Remove non compact table constraints - Change index type from int64 to int in routing library - Fix file_nonport compilation on windows - Fix another naming conflict with windows (NO_ERROR is a macro) - Cleanup hash containers; work on sat internals - Add optional_boolean sub-proto Sync cpp examples with internal code - reenable issue173 after reducing number of loops port to absl: Python Part - Add back cp_model.INT32_MIN|MAX for examples Update Python examples - Add random_tsp.py - Run words_square example - Run magic_square in python tests port to absl: Java Part - Fix compilation of the new routing parameters in java - Protect some code from SWIG parsing Update Java Examples port to absl: .Net Part Update .Net examples work on sat internals; Add C++ CP-SAT CpModelBuilder API; update sample code and recipes to use the new API; sync with internal code Remove VS 2015 in Appveyor-CI - abseil-cpp does not support VS 2015... improve tables upgrade C++ sat examples to use the new API; work on sat internals update license dates rewrite jobshop_ft06_distance.py to use the CP-SAT solver rename last example revert last commit more work on SAT internals fix
2018-10-31 16:18:18 +01:00
absl::flat_hash_map<int, int> result;
absl::flat_hash_map<int, int> wish_this_could_be_null;
WallTimer timer;
VLOG(1) << "Beginning Hungarian method.";
timer.Start();
MinimizeLinearAssignment(hungarian_cost, &result, &wish_this_could_be_null);
double elapsed = timer.GetInMs() / 1000.0;
LOG(INFO) << "Hungarian result computed in " << elapsed << " seconds.";
double result_cost = 0.0;
for (int i = 0; i < assignment.NumLeftNodes(); ++i) {
int mate = result[i];
result_cost += hungarian_cost[i][mate];
}
return static_cast<CostValue>(result_cost);
}
2014-01-08 12:01:58 +00:00
template <typename GraphType>
2020-10-29 14:25:39 +01:00
void DisplayAssignment(const LinearSumAssignment<GraphType>& assignment) {
for (typename LinearSumAssignment<GraphType>::BipartiteLeftNodeIterator
node_it(assignment);
2014-01-08 12:01:58 +00:00
node_it.Ok(); node_it.Next()) {
const NodeIndex left_node = node_it.Index();
const ArcIndex matching_arc = assignment.GetAssignmentArc(left_node);
const NodeIndex right_node = assignment.Head(matching_arc);
2014-01-08 12:01:58 +00:00
VLOG(5) << "assigned (" << left_node << ", " << right_node
<< "): " << assignment.ArcCost(matching_arc);
}
}
2014-01-08 12:01:58 +00:00
template <typename GraphType>
2020-10-29 14:25:39 +01:00
int SolveDimacsAssignment(int argc, char* argv[]) {
std::string error_message;
// Handle on the graph we will need to delete because the
// LinearSumAssignment object does not take ownership of it.
2020-10-29 14:25:39 +01:00
GraphType* graph = nullptr;
DimacsAssignmentParser<GraphType> parser(argv[1]);
2020-10-29 14:25:39 +01:00
LinearSumAssignment<GraphType>* assignment =
parser.Parse(&error_message, &graph);
if (assignment == nullptr) {
LOG(FATAL) << error_message;
}
if (!absl::GetFlag(FLAGS_assignment_problem_output_file).empty()) {
2012-01-16 10:30:41 +00:00
// The following tail array management stuff is done in a generic
// way so we can plug in different types of graphs for which the
// TailArrayManager template can be instantiated, even though we
// know the type of the graph explicitly. In this way, the type of
// the graph can be switched just by changing the graph type in
// this file and making no other changes to the code.
TailArrayManager<GraphType> tail_array_manager(graph);
PrintDimacsAssignmentProblem<GraphType>(
*assignment, tail_array_manager,
absl::GetFlag(FLAGS_assignment_problem_output_file));
2012-01-16 10:30:41 +00:00
tail_array_manager.ReleaseTailArrayIfForwardGraph();
}
CostValue hungarian_cost = 0.0;
bool hungarian_solved = false;
if (absl::GetFlag(FLAGS_assignment_compare_hungarian)) {
hungarian_cost = BuildAndSolveHungarianInstance(*assignment);
hungarian_solved = true;
}
WallTimer timer;
timer.Start();
bool success = assignment->ComputeAssignment();
double elapsed = timer.GetInMs() / 1000.0;
if (success) {
CostValue cost = assignment->GetCost();
DisplayAssignment(*assignment);
LOG(INFO) << "Cost of optimum assignment: " << cost;
LOG(INFO) << "Computed in " << elapsed << " seconds.";
LOG(INFO) << assignment->StatsString();
if (hungarian_solved && (cost != hungarian_cost)) {
LOG(ERROR) << "Optimum cost mismatch: " << cost << " vs. "
<< hungarian_cost << ".";
}
} else {
LOG(WARNING) << "Given problem is infeasible.";
}
delete assignment;
delete graph;
2018-11-07 09:52:37 +01:00
return EXIT_SUCCESS;
}
2020-10-22 23:36:58 +02:00
} // namespace operations_research
2020-10-29 14:25:39 +01:00
static const char* const kUsageTemplate = "usage: %s <filename>";
using ::operations_research::ForwardStarGraph;
2018-06-08 16:40:43 +02:00
using ::operations_research::ForwardStarStaticGraph;
using ::operations_research::SolveDimacsAssignment;
using ::operations_research::StarGraph;
2020-10-29 14:25:39 +01:00
int main(int argc, char* argv[]) {
std::string usage;
if (argc < 1) {
dotnet: Remove reference to dotnet release command - Currently not implemented... Add abseil patch - Add patches/absl-config.cmake Makefile: Add abseil-cpp on unix - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake Makefile: Add abseil-cpp on windows - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake CMake: Add abseil-cpp - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake port to absl: C++ Part - Fix warning with the use of ABSL_MUST_USE_RESULT > The macro must appear as the very first part of a function declaration or definition: ... Note: past advice was to place the macro after the argument list. src: dependencies/sources/abseil-cpp-master/absl/base/attributes.h:418 - Rename enum after windows clash - Remove non compact table constraints - Change index type from int64 to int in routing library - Fix file_nonport compilation on windows - Fix another naming conflict with windows (NO_ERROR is a macro) - Cleanup hash containers; work on sat internals - Add optional_boolean sub-proto Sync cpp examples with internal code - reenable issue173 after reducing number of loops port to absl: Python Part - Add back cp_model.INT32_MIN|MAX for examples Update Python examples - Add random_tsp.py - Run words_square example - Run magic_square in python tests port to absl: Java Part - Fix compilation of the new routing parameters in java - Protect some code from SWIG parsing Update Java Examples port to absl: .Net Part Update .Net examples work on sat internals; Add C++ CP-SAT CpModelBuilder API; update sample code and recipes to use the new API; sync with internal code Remove VS 2015 in Appveyor-CI - abseil-cpp does not support VS 2015... improve tables upgrade C++ sat examples to use the new API; work on sat internals update license dates rewrite jobshop_ft06_distance.py to use the CP-SAT solver rename last example revert last commit more work on SAT internals fix
2018-10-31 16:18:18 +01:00
usage = absl::StrFormat(kUsageTemplate, "solve_dimacs_assignment");
} else {
dotnet: Remove reference to dotnet release command - Currently not implemented... Add abseil patch - Add patches/absl-config.cmake Makefile: Add abseil-cpp on unix - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake Makefile: Add abseil-cpp on windows - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake CMake: Add abseil-cpp - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake port to absl: C++ Part - Fix warning with the use of ABSL_MUST_USE_RESULT > The macro must appear as the very first part of a function declaration or definition: ... Note: past advice was to place the macro after the argument list. src: dependencies/sources/abseil-cpp-master/absl/base/attributes.h:418 - Rename enum after windows clash - Remove non compact table constraints - Change index type from int64 to int in routing library - Fix file_nonport compilation on windows - Fix another naming conflict with windows (NO_ERROR is a macro) - Cleanup hash containers; work on sat internals - Add optional_boolean sub-proto Sync cpp examples with internal code - reenable issue173 after reducing number of loops port to absl: Python Part - Add back cp_model.INT32_MIN|MAX for examples Update Python examples - Add random_tsp.py - Run words_square example - Run magic_square in python tests port to absl: Java Part - Fix compilation of the new routing parameters in java - Protect some code from SWIG parsing Update Java Examples port to absl: .Net Part Update .Net examples work on sat internals; Add C++ CP-SAT CpModelBuilder API; update sample code and recipes to use the new API; sync with internal code Remove VS 2015 in Appveyor-CI - abseil-cpp does not support VS 2015... improve tables upgrade C++ sat examples to use the new API; work on sat internals update license dates rewrite jobshop_ft06_distance.py to use the CP-SAT solver rename last example revert last commit more work on SAT internals fix
2018-10-31 16:18:18 +01:00
usage = absl::StrFormat(kUsageTemplate, argv[0]);
}
2022-02-25 09:47:52 +01:00
InitGoogle(usage.c_str(), &argc, &argv, true);
if (argc < 2) {
LOG(FATAL) << usage;
}
if (absl::GetFlag(FLAGS_assignment_static_graph)) {
return SolveDimacsAssignment<ForwardStarStaticGraph>(argc, argv);
} else if (absl::GetFlag(FLAGS_assignment_reverse_arcs)) {
return SolveDimacsAssignment<StarGraph>(argc, argv);
} else {
return SolveDimacsAssignment<ForwardStarGraph>(argc, argv);
}
}