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" 45 using ::util::CompleteGraph;
47 template <
typename CostType,
typename ArcIndex = int64_t,
54 #if defined(USE_CBC) || defined(USE_SCIP) 56 #endif // 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_;
114 template <
typename WeightFunctionType,
typename GraphType>
115 absl::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) 150 template <
typename WeightFunctionType,
typename GraphType>
151 absl::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);
198 #if defined(USE_SCIP) 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));
223 #endif // defined(USE_CBC) || defined(USE_SCIP) 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);
321 #endif // defined(USE_CBC) || defined(USE_SCIP) 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);
380 #endif // OR_TOOLS_GRAPH_CHRISTOFIDES_H_
void set_lower_bound(double value)
ResultStatus
The status of solving the problem.
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.
ChristofidesPathSolver(NodeIndex num_nodes, CostFunction costs)
std::vector< NodeIndex > TravelingSalesmanPath()
void set_upper_bound(double value)
void add_coefficient(double value)
std::vector< NodeIndex > BuildEulerianTourFromNode(const Graph &graph, NodeIndex root)
void SetMatchingAlgorithm(MatchingAlgorithm matching)
int64_t CapAdd(int64_t x, int64_t y)
#define DCHECK_NE(val1, val2)
void set_is_integer(bool value)
#define DCHECK_GE(val1, val2)
SharedResponseManager * response
#define DCHECK(condition)
void set_lower_bound(double value)
void set_upper_bound(double value)
MPSolverResponseStatus LoadModelFromProto(const MPModelProto &input_model, std::string *error_message)
Loads model from protocol buffer.
void set_objective_coefficient(double value)
ResultStatus Solve()
Solves the problem using the default parameter values.
This mathematical programming (MP) solver class is the main class though which users build and solve ...
Collection of objects used to extend the Constraint Solver library.
bool IsEulerianGraph(const Graph &graph)
CostType TravelingSalesmanCost()
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)
void FillSolutionResponseProto(MPSolutionResponse *response) const
Encodes the current solution in a solution response protocol buffer.
absl::StatusOr< std::vector< std::pair< typename GraphType::NodeIndex, typename GraphType::NodeIndex > > > ComputeMinimumWeightMatchingWithMIP(const GraphType &graph, const WeightFunctionType &weight)
void add_var_index(::PROTOBUF_NAMESPACE_ID::int32 value)
ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head)