26#ifndef OR_TOOLS_GRAPH_CHRISTOFIDES_H_
27#define OR_TOOLS_GRAPH_CHRISTOFIDES_H_
31#include "absl/status/status.h"
32#include "absl/status/statusor.h"
45using ::util::CompleteGraph;
47template <
typename CostType,
typename ArcIndex = int64_t,
54#if defined(USE_CBC) || defined(USE_SCIP)
90 int64_t SafeAdd(int64_t
a, int64_t
b) {
98 CompleteGraph<NodeIndex, ArcIndex> graph_;
101 const CostFunction costs_;
107 std::vector<NodeIndex> tsp_path_;
114template <
typename WeightFunctionType,
typename GraphType>
115absl::StatusOr<std::vector<
116 std::pair<typename GraphType::NodeIndex, typename GraphType::NodeIndex>>>
118 const WeightFunctionType&
weight) {
133 return absl::InvalidArgumentError(
"Perfect matching failed");
135 std::vector<std::pair<NodeIndex, NodeIndex>> match;
145#if defined(USE_CBC) || defined(USE_SCIP)
150template <
typename WeightFunctionType,
typename GraphType>
151absl::StatusOr<std::vector<
152 std::pair<typename GraphType::NodeIndex, typename GraphType::NodeIndex>>>
154 const WeightFunctionType&
weight) {
158 model.set_maximize(
false);
163 std::vector<int> variable_indices(graph.num_arcs(), -1);
164 for (
NodeIndex node : graph.AllNodes()) {
166 for (
const ArcIndex arc : graph.OutgoingArcs(node)) {
169 variable_indices[arc] =
model.variable_size();
183 for (
NodeIndex node : graph.AllNodes()) {
184 for (
const ArcIndex arc : graph.OutgoingArcs(node)) {
187 const int arc_var = variable_indices[arc];
192 one_of_ct =
model.mutable_constraint(
head);
199 MPSolver mp_solver(
"MatchingWithSCIP",
201#elif defined(USE_CBC)
202 MPSolver mp_solver(
"MatchingWithCBC",
209 return absl::InvalidArgumentError(
"MIP-based matching failed");
213 std::vector<std::pair<NodeIndex, NodeIndex>> matching;
214 for (
ArcIndex arc = 0; arc < variable_indices.size(); ++arc) {
215 const int arc_var = variable_indices[arc];
216 if (arc_var >= 0 &&
response.variable_value(arc_var) > .9) {
218 matching.emplace_back(graph.Tail(arc), graph.Head(arc));
226 typename CostFunction>
231 costs_(
std::move(costs)),
236 typename CostFunction>
238 CostFunction>::TravelingSalesmanCost() {
240 bool const ok =
Solve();
247 typename CostFunction>
251 const bool ok =
Solve();
258 typename CostFunction>
261 const NodeIndex num_nodes = graph_.num_nodes();
264 if (num_nodes == 1) {
267 if (num_nodes <= 1) {
271 const std::vector<ArcIndex> mst =
273 return costs_(graph_.Tail(arc), graph_.Head(arc));
276 std::vector<NodeIndex> degrees(num_nodes, 0);
278 degrees[graph_.Tail(arc)]++;
279 degrees[graph_.Head(arc)]++;
281 std::vector<NodeIndex> odd_degree_nodes;
282 for (
int i = 0; i < degrees.size(); ++i) {
283 if (degrees[i] % 2 != 0) {
284 odd_degree_nodes.push_back(i);
289 const NodeIndex reduced_size = odd_degree_nodes.size();
291 CompleteGraph<NodeIndex, ArcIndex> reduced_graph(reduced_size);
292 std::vector<std::pair<NodeIndex, NodeIndex>> closure_arcs;
294 case MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING: {
296 reduced_graph, [
this, &reduced_graph,
298 return costs_(odd_degree_nodes[reduced_graph.Tail(arc)],
299 odd_degree_nodes[reduced_graph.Head(arc)]);
304 result->swap(closure_arcs);
307#if defined(USE_CBC) || defined(USE_SCIP)
308 case MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING_WITH_MIP: {
310 reduced_graph, [
this, &reduced_graph,
312 return costs_(odd_degree_nodes[reduced_graph.Tail(arc)],
313 odd_degree_nodes[reduced_graph.Head(arc)]);
318 result->swap(closure_arcs);
322 case MatchingAlgorithm::MINIMAL_WEIGHT_MATCHING: {
325 std::vector<ArcIndex> ordered_arcs(reduced_graph.num_arcs());
326 std::vector<CostType> ordered_arc_costs(reduced_graph.num_arcs(), 0);
327 for (
const ArcIndex arc : reduced_graph.AllForwardArcs()) {
328 ordered_arcs[arc] = arc;
329 ordered_arc_costs[arc] =
330 costs_(odd_degree_nodes[reduced_graph.Tail(arc)],
331 odd_degree_nodes[reduced_graph.Head(arc)]);
333 std::sort(ordered_arcs.begin(), ordered_arcs.end(),
335 return ordered_arc_costs[arc_a] < ordered_arc_costs[arc_b];
337 std::vector<bool> touched_nodes(reduced_size,
false);
338 for (
ArcIndex arc_index = 0; closure_arcs.size() * 2 < reduced_size;
340 const ArcIndex arc = ordered_arcs[arc_index];
344 touched_nodes[
tail] =
true;
345 touched_nodes[
head] =
true;
346 closure_arcs.emplace_back(
tail,
head);
356 num_nodes, closure_arcs.size() + mst.size());
358 egraph.
AddArc(graph_.Tail(arc), graph_.Head(arc));
360 for (
const auto arc : closure_arcs) {
361 egraph.
AddArc(odd_degree_nodes[arc.first], odd_degree_nodes[arc.second]);
363 std::vector<bool> touched(num_nodes,
false);
366 if (touched[node])
continue;
367 touched[node] =
true;
368 tsp_cost_ = SafeAdd(tsp_cost_,
369 tsp_path_.empty() ? 0 : costs_(tsp_path_.back(), node));
370 tsp_path_.push_back(node);
373 SafeAdd(tsp_cost_, tsp_path_.empty() ? 0 : costs_(tsp_path_.back(), 0));
374 tsp_path_.push_back(0);
#define DCHECK_NE(val1, val2)
#define DCHECK_GE(val1, val2)
#define DCHECK(condition)
@ MINIMUM_WEIGHT_MATCHING_WITH_MIP
@ MINIMAL_WEIGHT_MATCHING
@ MINIMUM_WEIGHT_MATCHING
std::vector< NodeIndex > TravelingSalesmanPath()
ChristofidesPathSolver(NodeIndex num_nodes, CostFunction costs)
CostType TravelingSalesmanCost()
void SetMatchingAlgorithm(MatchingAlgorithm matching)
void set_lower_bound(double value)
void add_var_index(::PROTOBUF_NAMESPACE_ID::int32 value)
void add_coefficient(double value)
void set_upper_bound(double value)
This mathematical programming (MP) solver class is the main class though which users build and solve ...
void FillSolutionResponseProto(MPSolutionResponse *response) const
Encodes the current solution in a solution response protocol buffer.
ResultStatus
The status of solving the problem.
@ SCIP_MIXED_INTEGER_PROGRAMMING
@ CBC_MIXED_INTEGER_PROGRAMMING
MPSolverResponseStatus LoadModelFromProto(const MPModelProto &input_model, std::string *error_message)
Loads model from protocol buffer.
ResultStatus Solve()
Solves the problem using the default parameter values.
void set_is_integer(bool value)
void set_objective_coefficient(double value)
void set_lower_bound(double value)
void set_upper_bound(double value)
ABSL_MUST_USE_RESULT Status Solve()
void AddEdgeWithCost(int tail, int head, int64_t cost)
int Match(int node) const
ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head)
SharedResponseManager * response
A C++ wrapper that provides a simple and unified interface to several linear programming and mixed in...
CpSolverResponse Solve(const CpModelProto &model_proto)
Solves the given CpModelProto and returns an instance of CpSolverResponse.
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
absl::StatusOr< std::vector< std::pair< typename GraphType::NodeIndex, typename GraphType::NodeIndex > > > ComputeMinimumWeightMatchingWithMIP(const GraphType &graph, const WeightFunctionType &weight)
std::vector< NodeIndex > BuildEulerianTourFromNode(const Graph &graph, NodeIndex root)
std::vector< typename Graph::ArcIndex > BuildPrimMinimumSpanningTree(const Graph &graph, const ArcValue &arc_value)
bool IsEulerianGraph(const Graph &graph)
absl::StatusOr< std::vector< std::pair< typename GraphType::NodeIndex, typename GraphType::NodeIndex > > > ComputeMinimumWeightMatching(const GraphType &graph, const WeightFunctionType &weight)