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) 84 int64_t SafeAdd(int64_t
a, int64_t
b) {
return CapAdd(
a,
b); }
90 CompleteGraph<NodeIndex, ArcIndex> graph_;
93 const CostFunction costs_;
99 std::vector<NodeIndex> tsp_path_;
106 template <
typename WeightFunctionType,
typename GraphType>
107 absl::StatusOr<std::vector<
108 std::pair<typename GraphType::NodeIndex, typename GraphType::NodeIndex>>>
110 const WeightFunctionType&
weight) {
125 return absl::InvalidArgumentError(
"Perfect matching failed");
127 std::vector<std::pair<NodeIndex, NodeIndex>> match;
137 #if defined(USE_CBC) || defined(USE_SCIP) 142 template <
typename WeightFunctionType,
typename GraphType>
143 absl::StatusOr<std::vector<
144 std::pair<typename GraphType::NodeIndex, typename GraphType::NodeIndex>>>
146 const WeightFunctionType&
weight) {
150 model.set_maximize(
false);
155 std::vector<int> variable_indices(graph.num_arcs(), -1);
156 for (
NodeIndex node : graph.AllNodes()) {
158 for (
const ArcIndex arc : graph.OutgoingArcs(node)) {
161 variable_indices[arc] =
model.variable_size();
175 for (
NodeIndex node : graph.AllNodes()) {
176 for (
const ArcIndex arc : graph.OutgoingArcs(node)) {
179 const int arc_var = variable_indices[arc];
184 one_of_ct =
model.mutable_constraint(
head);
190 #if defined(USE_SCIP) 191 MPSolver mp_solver(
"MatchingWithSCIP",
193 #elif defined(USE_CBC) 194 MPSolver mp_solver(
"MatchingWithCBC",
201 return absl::InvalidArgumentError(
"MIP-based matching failed");
205 std::vector<std::pair<NodeIndex, NodeIndex>> matching;
206 for (
ArcIndex arc = 0; arc < variable_indices.size(); ++arc) {
207 const int arc_var = variable_indices[arc];
208 if (arc_var >= 0 &&
response.variable_value(arc_var) > .9) {
210 matching.emplace_back(graph.Tail(arc), graph.Head(arc));
215 #endif // defined(USE_CBC) || defined(USE_SCIP) 218 typename CostFunction>
223 costs_(std::move(costs)),
228 typename CostFunction>
230 CostFunction>::TravelingSalesmanCost() {
232 bool const ok =
Solve();
239 typename CostFunction>
243 const bool ok =
Solve();
250 typename CostFunction>
253 const NodeIndex num_nodes = graph_.num_nodes();
256 if (num_nodes == 1) {
259 if (num_nodes <= 1) {
263 const std::vector<ArcIndex> mst =
265 return costs_(graph_.Tail(arc), graph_.Head(arc));
268 std::vector<NodeIndex> degrees(num_nodes, 0);
270 degrees[graph_.Tail(arc)]++;
271 degrees[graph_.Head(arc)]++;
273 std::vector<NodeIndex> odd_degree_nodes;
274 for (
int i = 0; i < degrees.size(); ++i) {
275 if (degrees[i] % 2 != 0) {
276 odd_degree_nodes.push_back(i);
281 const NodeIndex reduced_size = odd_degree_nodes.size();
283 CompleteGraph<NodeIndex, ArcIndex> reduced_graph(reduced_size);
284 std::vector<std::pair<NodeIndex, NodeIndex>> closure_arcs;
286 case MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING: {
288 reduced_graph, [
this, &reduced_graph,
290 return costs_(odd_degree_nodes[reduced_graph.Tail(arc)],
291 odd_degree_nodes[reduced_graph.Head(arc)]);
296 result->swap(closure_arcs);
299 #if defined(USE_CBC) || defined(USE_SCIP) 300 case MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING_WITH_MIP: {
302 reduced_graph, [
this, &reduced_graph,
304 return costs_(odd_degree_nodes[reduced_graph.Tail(arc)],
305 odd_degree_nodes[reduced_graph.Head(arc)]);
310 result->swap(closure_arcs);
313 #endif // defined(USE_CBC) || defined(USE_SCIP) 314 case MatchingAlgorithm::MINIMAL_WEIGHT_MATCHING: {
317 std::vector<ArcIndex> ordered_arcs(reduced_graph.num_arcs());
318 std::vector<CostType> ordered_arc_costs(reduced_graph.num_arcs(), 0);
319 for (
const ArcIndex arc : reduced_graph.AllForwardArcs()) {
320 ordered_arcs[arc] = arc;
321 ordered_arc_costs[arc] =
322 costs_(odd_degree_nodes[reduced_graph.Tail(arc)],
323 odd_degree_nodes[reduced_graph.Head(arc)]);
325 std::sort(ordered_arcs.begin(), ordered_arcs.end(),
327 return ordered_arc_costs[arc_a] < ordered_arc_costs[arc_b];
329 std::vector<bool> touched_nodes(reduced_size,
false);
330 for (
ArcIndex arc_index = 0; closure_arcs.size() * 2 < reduced_size;
332 const ArcIndex arc = ordered_arcs[arc_index];
336 touched_nodes[
tail] =
true;
337 touched_nodes[
head] =
true;
338 closure_arcs.emplace_back(
tail,
head);
348 num_nodes, closure_arcs.size() + mst.size());
350 egraph.
AddArc(graph_.Tail(arc), graph_.Head(arc));
352 for (
const auto arc : closure_arcs) {
353 egraph.AddArc(odd_degree_nodes[arc.first], odd_degree_nodes[arc.second]);
355 std::vector<bool> touched(num_nodes,
false);
358 if (touched[node])
continue;
359 touched[node] =
true;
360 tsp_cost_ = SafeAdd(tsp_cost_,
361 tsp_path_.empty() ? 0 : costs_(tsp_path_.back(), node));
362 tsp_path_.push_back(node);
365 SafeAdd(tsp_cost_, tsp_path_.empty() ? 0 : costs_(tsp_path_.back(), 0));
366 tsp_path_.push_back(0);
372 #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 add_var_index(int32_t value)
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)
ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head)