26 #ifndef OR_TOOLS_GRAPH_CHRISTOFIDES_H_
27 #define OR_TOOLS_GRAPH_CHRISTOFIDES_H_
33 #include "absl/status/status.h"
34 #include "absl/status/statusor.h"
42 #include "ortools/linear_solver/linear_solver.pb.h"
47 using ::util::CompleteGraph;
49 template <
typename CostType,
typename ArcIndex = int64_t,
56 #if defined(USE_CBC) || defined(USE_SCIP)
86 int64_t SafeAdd(int64_t
a, int64_t
b) {
return CapAdd(
a,
b); }
92 CompleteGraph<NodeIndex, ArcIndex> graph_;
95 const CostFunction costs_;
101 std::vector<NodeIndex> tsp_path_;
108 template <
typename WeightFunctionType,
typename GraphType>
109 absl::StatusOr<std::vector<
110 std::pair<typename GraphType::NodeIndex, typename GraphType::NodeIndex>>>
112 const WeightFunctionType&
weight) {
127 return absl::InvalidArgumentError(
"Perfect matching failed");
129 std::vector<std::pair<NodeIndex, NodeIndex>> match;
139 #if defined(USE_CBC) || defined(USE_SCIP)
144 template <
typename WeightFunctionType,
typename GraphType>
145 absl::StatusOr<std::vector<
146 std::pair<typename GraphType::NodeIndex, typename GraphType::NodeIndex>>>
148 const WeightFunctionType&
weight) {
152 model.set_maximize(
false);
157 std::vector<int> variable_indices(graph.num_arcs(), -1);
158 for (
NodeIndex node : graph.AllNodes()) {
160 for (
const ArcIndex arc : graph.OutgoingArcs(node)) {
163 variable_indices[
arc] =
model.variable_size();
164 MPVariableProto*
const arc_var =
model.add_variable();
165 arc_var->set_lower_bound(0);
166 arc_var->set_upper_bound(1);
167 arc_var->set_is_integer(
true);
168 arc_var->set_objective_coefficient(
weight(
arc));
173 MPConstraintProto*
const one_of_ct =
model.add_constraint();
174 one_of_ct->set_lower_bound(1);
175 one_of_ct->set_upper_bound(1);
177 for (
NodeIndex node : graph.AllNodes()) {
178 for (
const ArcIndex arc : graph.OutgoingArcs(node)) {
181 const int arc_var = variable_indices[
arc];
182 DCHECK_GE(arc_var, 0);
183 MPConstraintProto* one_of_ct =
model.mutable_constraint(node);
184 one_of_ct->add_var_index(arc_var);
185 one_of_ct->add_coefficient(1);
186 one_of_ct =
model.mutable_constraint(
head);
187 one_of_ct->add_var_index(arc_var);
188 one_of_ct->add_coefficient(1);
192 #if defined(USE_SCIP)
193 MPSolver mp_solver(
"MatchingWithSCIP",
195 #elif defined(USE_CBC)
196 MPSolver mp_solver(
"MatchingWithCBC",
203 return absl::InvalidArgumentError(
"MIP-based matching failed");
207 std::vector<std::pair<NodeIndex, NodeIndex>> matching;
209 const int arc_var = variable_indices[
arc];
210 if (arc_var >= 0 &&
response.variable_value(arc_var) > .9) {
211 DCHECK_GE(
response.variable_value(arc_var), 1.0 - 1e-4);
212 matching.emplace_back(graph.Tail(
arc), graph.Head(
arc));
220 typename CostFunction>
225 costs_(std::move(costs)),
230 typename CostFunction>
232 CostFunction>::TravelingSalesmanCost() {
234 bool const ok =
Solve();
241 typename CostFunction>
245 const bool ok =
Solve();
252 typename CostFunction>
255 const NodeIndex num_nodes = graph_.num_nodes();
258 if (num_nodes == 1) {
261 if (num_nodes <= 1) {
265 const std::vector<ArcIndex> mst =
267 return costs_(graph_.Tail(
arc), graph_.Head(
arc));
270 std::vector<NodeIndex> degrees(num_nodes, 0);
272 degrees[graph_.Tail(
arc)]++;
273 degrees[graph_.Head(
arc)]++;
275 std::vector<NodeIndex> odd_degree_nodes;
276 for (
int i = 0; i < degrees.size(); ++i) {
277 if (degrees[i] % 2 != 0) {
278 odd_degree_nodes.push_back(i);
283 const NodeIndex reduced_size = odd_degree_nodes.size();
284 DCHECK_NE(0, reduced_size);
285 CompleteGraph<NodeIndex, ArcIndex> reduced_graph(reduced_size);
286 std::vector<std::pair<NodeIndex, NodeIndex>> closure_arcs;
288 case MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING: {
290 reduced_graph, [
this, &reduced_graph,
292 return costs_(odd_degree_nodes[reduced_graph.Tail(
arc)],
293 odd_degree_nodes[reduced_graph.Head(
arc)]);
298 result->swap(closure_arcs);
301 #if defined(USE_CBC) || defined(USE_SCIP)
302 case MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING_WITH_MIP: {
304 reduced_graph, [
this, &reduced_graph,
306 return costs_(odd_degree_nodes[reduced_graph.Tail(
arc)],
307 odd_degree_nodes[reduced_graph.Head(
arc)]);
312 result->swap(closure_arcs);
316 case MatchingAlgorithm::MINIMAL_WEIGHT_MATCHING: {
319 std::vector<ArcIndex> ordered_arcs(reduced_graph.num_arcs());
320 std::vector<CostType> ordered_arc_costs(reduced_graph.num_arcs(), 0);
321 for (
const ArcIndex arc : reduced_graph.AllForwardArcs()) {
323 ordered_arc_costs[
arc] =
324 costs_(odd_degree_nodes[reduced_graph.Tail(
arc)],
325 odd_degree_nodes[reduced_graph.Head(
arc)]);
327 std::sort(ordered_arcs.begin(), ordered_arcs.end(),
329 return ordered_arc_costs[arc_a] < ordered_arc_costs[arc_b];
331 std::vector<bool> touched_nodes(reduced_size,
false);
332 for (
ArcIndex arc_index = 0; closure_arcs.size() * 2 < reduced_size;
338 touched_nodes[
tail] =
true;
339 touched_nodes[
head] =
true;
340 closure_arcs.emplace_back(
tail,
head);
350 num_nodes, closure_arcs.size() + mst.size());
354 for (
const auto arc : closure_arcs) {
355 egraph.
AddArc(odd_degree_nodes[
arc.first], odd_degree_nodes[
arc.second]);
357 std::vector<bool> touched(num_nodes,
false);
360 if (touched[node])
continue;
361 touched[node] =
true;
362 tsp_cost_ = SafeAdd(tsp_cost_,
363 tsp_path_.empty() ? 0 : costs_(tsp_path_.back(), node));
364 tsp_path_.push_back(node);
367 SafeAdd(tsp_cost_, tsp_path_.empty() ? 0 : costs_(tsp_path_.back(), 0));
368 tsp_path_.push_back(0);
@ 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)
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.
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...
absl::StatusOr< SolveResult > Solve(const Model &model, const SolverType solver_type, const SolveArguments &solve_args, const SolverInitArguments &init_args)
Collection of objects used to extend the Constraint Solver library.
absl::StatusOr< std::vector< std::pair< typename GraphType::NodeIndex, typename GraphType::NodeIndex > > > ComputeMinimumWeightMatching(const GraphType &graph, const WeightFunctionType &weight)
bool IsEulerianGraph(const Graph &graph, bool assume_connectivity=true)
int64_t CapAdd(int64_t x, int64_t y)
std::vector< NodeIndex > BuildEulerianTourFromNode(const Graph &graph, NodeIndex root, bool assume_connectivity=true)
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 > > > ComputeMinimumWeightMatchingWithMIP(const GraphType &graph, const WeightFunctionType &weight)