Google OR-Tools: ortools/graph/christofides.h Source File
26#ifndef ORTOOLS_GRAPH_CHRISTOFIDES_H_
27#define ORTOOLS_GRAPH_CHRISTOFIDES_H_
36#include "absl/status/status.h"
37#include "absl/status/statusor.h"
38#include "absl/strings/str_cat.h"
50using ::util::CompleteGraph;
53template <typename CostType, typename ArcIndex = int64_t,
55 typename CostFunction = std::function<CostType(NodeIndex, NodeIndex)>>
86 absl::Status Solve();
93 if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>,
105 CompleteGraph<NodeIndex, ArcIndex> graph_;
108 const CostFunction costs_;
121template <typename CostType, typename WeightFunctionType, typename GraphType>
122absl::StatusOr<std::vector<
123 std::pair<typename GraphType::NodeIndex, typename GraphType::NodeIndex>>>
125 const WeightFunctionType& weight) {
126 using ArcIndex = typename GraphType::ArcIndex;
127 using NodeIndex = typename GraphType::NodeIndex;
128 if constexpr (!std::is_integral_v<CostType>) {
129 DLOG(WARNING) << "Weights are being cast to int64_t. This might result "
130 "in loss of precision or overflows.";
133 for (NodeIndex tail : graph.AllNodes()) {
134 for (const ArcIndex arc : graph.OutgoingArcs(tail)) {
135 const NodeIndex head = graph.Head(arc);
145 return absl::InvalidArgumentError(
146 absl::StrCat("Perfect matching failed: ", status));
148 std::vector<std::pair<NodeIndex, NodeIndex>> match;
149 for (NodeIndex tail : graph.AllNodes()) {
158#if defined(USE_CBC) || defined(USE_SCIP)
163template <typename WeightFunctionType, typename GraphType>
164absl::StatusOr<std::vector<
165 std::pair<typename GraphType::NodeIndex, typename GraphType::NodeIndex>>>
167 const WeightFunctionType& weight) {
168 using ArcIndex = typename GraphType::ArcIndex;
169 using NodeIndex = typename GraphType::NodeIndex;
176 std::vector<int> variable_indices(graph.num_arcs(), -1);
177 for (NodeIndex node : graph.AllNodes()) {
179 for (const ArcIndex arc : graph.OutgoingArcs(node)) {
180 const NodeIndex head = graph.Head(arc);
196 for (NodeIndex node : graph.AllNodes()) {
197 for (const ArcIndex arc : graph.OutgoingArcs(node)) {
198 const NodeIndex head = graph.Head(arc);
200 const int arc_var = variable_indices[arc];
212 MPSolver mp_solver("MatchingWithSCIP",
215 MPSolver mp_solver("MatchingWithCBC",
222 return absl::InvalidArgumentError("MIP-based matching failed");
226 std::vector<std::pair<NodeIndex, NodeIndex>> matching;
227 for (ArcIndex arc = 0; arc < variable_indices.size(); ++arc) {
228 const int arc_var = variable_indices[arc];
229 if (arc_var >= 0 && response.variable_value(arc_var) > .9) {
230 DCHECK_GE(response.variable_value(arc_var), 1.0 - 1e-4);
231 matching.emplace_back(graph.Tail(arc), graph.Head(arc));
238template <typename CostType, typename ArcIndex, typename NodeIndex,
248template <typename CostType, typename ArcIndex, typename NodeIndex,
253 const absl::Status status = Solve();
259template <typename CostType, typename ArcIndex, typename NodeIndex,
264 const absl::Status status = Solve();
270template <typename CostType, typename ArcIndex, typename NodeIndex,
274 const NodeIndex num_nodes = graph_.num_nodes();
284 const std::vector<ArcIndex> mst =
286 return costs_(graph_.Tail(arc), graph_.Head(arc));
289 std::vector<NodeIndex> degrees(num_nodes, 0);
290 for (ArcIndex arc : mst) {
291 degrees[graph_.Tail(arc)]++;
292 degrees[graph_.Head(arc)]++;
294 std::vector<NodeIndex> odd_degree_nodes;
295 for (int i = 0; i < degrees.size(); ++i) {
296 if (degrees[i] % 2 != 0) {
297 odd_degree_nodes.push_back(i);
301 const NodeIndex reduced_size = odd_degree_nodes.size();
302 DCHECK_NE(0, reduced_size);
304 std::vector<std::pair<NodeIndex, NodeIndex>> closure_arcs;
308 reduced_graph, [this, &reduced_graph,
310 return costs_(odd_degree_nodes[reduced_graph.Tail(arc)],
311 odd_degree_nodes[reduced_graph.Head(arc)]);
316 result->swap(closure_arcs);
319#if defined(USE_CBC) || defined(USE_SCIP)
322 reduced_graph, [this, &reduced_graph,
324 return costs_(odd_degree_nodes[reduced_graph.Tail(arc)],
325 odd_degree_nodes[reduced_graph.Head(arc)]);
330 result->swap(closure_arcs);
337 std::vector<ArcIndex> ordered_arcs(reduced_graph.num_arcs());
338 std::vector<CostType> ordered_arc_costs(reduced_graph.num_arcs(), 0);
339 for (const ArcIndex arc : reduced_graph.AllForwardArcs()) {
342 costs_(odd_degree_nodes[reduced_graph.Tail(arc)],
343 odd_degree_nodes[reduced_graph.Head(arc)]);
345 absl::c_sort(ordered_arcs,
346 [&ordered_arc_costs](ArcIndex arc_a, ArcIndex arc_b) {
347 return ordered_arc_costs[arc_a] < ordered_arc_costs[arc_b];
349 std::vector<bool> touched_nodes(reduced_size, false);
350 for (ArcIndex arc_index = 0; closure_arcs.size() * 2 < reduced_size;
352 const ArcIndex arc = ordered_arcs[arc_index];
355 if (head != tail && !touched_nodes[tail] && !touched_nodes[head]) {
356 touched_nodes[tail] = true;
357 touched_nodes[head] = true;
358 closure_arcs.emplace_back(tail, head);
368 num_nodes, closure_arcs.size() + mst.size());
369 for (ArcIndex arc : mst) {
370 egraph.AddArc(graph_.Tail(arc), graph_.Head(arc));
372 for (const auto arc : closure_arcs) {
373 egraph.AddArc(odd_degree_nodes[arc.first], odd_degree_nodes[arc.second]);
375 std::vector<bool> touched(num_nodes, false);
378 if (touched[node]) continue;
380 tsp_cost_ = SafeAdd(tsp_cost_,
381 tsp_path_.empty() ? 0 : costs_(tsp_path_.back(), node));
382 tsp_path_.push_back(node);
385 SafeAdd(tsp_cost_, tsp_path_.empty() ? 0 : costs_(tsp_path_.back(), 0));
Definition christofides.h:56
absl::Status Solve()
Definition christofides.h:273
ChristofidesPathSolver(NodeIndex num_nodes, CostFunction costs)
Definition christofides.h:241
void SetMatchingAlgorithm(MatchingAlgorithm matching)
Definition christofides.h:73
absl::StatusOr< CostType > TravelingSalesmanCost()
Definition christofides.h:251
absl::StatusOr< const std::vector< NodeIndex > & > TravelingSalesmanPath()
Definition christofides.h:262
MatchingAlgorithm
Definition christofides.h:58
@ MINIMUM_WEIGHT_MATCHING_WITH_MIP
Definition christofides.h:61
@ MINIMAL_WEIGHT_MATCHING
Definition christofides.h:63
@ MINIMUM_WEIGHT_MATCHING
Definition christofides.h:59
void set_lower_bound(double value)
void add_coefficient(double value)
void set_upper_bound(double value)
void add_var_index(::int32_t value)
int variable_size() const
void set_maximize(bool value)
::operations_research::MPConstraintProto *PROTOBUF_NONNULL add_constraint()
::operations_research::MPConstraintProto *PROTOBUF_NONNULL mutable_constraint(int index)
::operations_research::MPVariableProto *PROTOBUF_NONNULL add_variable()
double variable_value(int index) const
ResultStatus Solve()
Solves the problem using the default parameter values.
MPSolverResponseStatus LoadModelFromProto(const MPModelProto &input_model, std::string *error_message, bool clear_names=true)
@ SCIP_MIXED_INTEGER_PROGRAMMING
@ CBC_MIXED_INTEGER_PROGRAMMING
void FillSolutionResponseProto(MPSolutionResponse *response) const
Encodes the current solution in a solution response protocol buffer.
void set_upper_bound(double value)
void set_is_integer(bool value)
void set_objective_coefficient(double value)
void set_lower_bound(double value)
int Match(int node) const
void AddEdgeWithCost(int tail, int head, int64_t cost)
ABSL_MUST_USE_RESULT Status Solve()
IntegerRange< ArcIndex > AllForwardArcs() const
ArcIndexType num_arcs() const
NodeIndexType Tail(ArcIndexType arc) const
NodeIndexType Head(ArcIndexType arc) const
ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head)
bool IsEulerianGraph(const Graph &graph, bool assume_connectivity=true)
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)
Definition christofides.h:166
std::vector< NodeIndex > BuildEulerianTourFromNode(const Graph &graph, NodeIndex root, bool assume_connectivity=true)
BlossomGraph::NodeIndex NodeIndex
std::vector< typename Graph::ArcIndex > BuildPrimMinimumSpanningTree(const Graph &graph, const ArcValue &arc_value)
absl::StatusOr< std::vector< std::pair< typename GraphType::NodeIndex, typename GraphType::NodeIndex > > > ComputeMinimumWeightMatching(const GraphType &graph, const WeightFunctionType &weight)
Definition christofides.h:124
trees with all degrees equal w the current value of degrees