20 #ifndef OR_TOOLS_GRAPH_CHRISTOFIDES_H_ 21 #define OR_TOOLS_GRAPH_CHRISTOFIDES_H_ 23 #include "absl/container/flat_hash_map.h" 24 #include "ortools/base/integral_types.h" 25 #include "ortools/base/logging.h" 29 #include "ortools/linear_solver/linear_solver.h" 30 #include "ortools/linear_solver/linear_solver.pb.h" 31 #include "ortools/util/saturated_arithmetic.h" 35 using ::util::CompleteGraph;
37 template <
typename CostType,
typename ArcIndex = int64,
43 #if defined(USE_CBC) || defined(USE_SCIP) 45 #endif // defined(USE_CBC) || defined(USE_SCIP) 78 int64 SafeAdd(int64 a, int64 b) {
86 CompleteGraph<NodeIndex, ArcIndex> graph_;
89 const CostFunction costs_;
95 std::vector<NodeIndex> tsp_path_;
101 #if defined(USE_CBC) || defined(USE_SCIP) 106 template <
typename WeightFunctionType,
typename GraphType>
108 const GraphType& graph,
const WeightFunctionType& weight) {
112 model.set_maximize(
false);
117 absl::flat_hash_map<ArcIndex, ArcIndex> variable_indices;
118 for (
NodeIndex node : graph.AllNodes()) {
120 for (
const ArcIndex arc : graph.OutgoingArcs(node)) {
123 variable_indices[arc] = model.variable_size();
124 MPVariableProto*
const arc_var = model.add_variable();
125 arc_var->set_lower_bound(0);
126 arc_var->set_upper_bound(1);
127 arc_var->set_is_integer(
true);
128 arc_var->set_objective_coefficient(weight(arc));
133 MPConstraintProto*
const one_of_ct = model.add_constraint();
134 one_of_ct->set_lower_bound(1);
135 one_of_ct->set_upper_bound(1);
137 for (
NodeIndex node : graph.AllNodes()) {
138 for (
const ArcIndex arc : graph.OutgoingArcs(node)) {
141 MPConstraintProto* one_of_ct = model.mutable_constraint(node);
142 one_of_ct->add_var_index(variable_indices[arc]);
143 one_of_ct->add_coefficient(1);
144 one_of_ct = model.mutable_constraint(head);
145 one_of_ct->add_var_index(variable_indices[arc]);
146 one_of_ct->add_coefficient(1);
150 #if defined(USE_SCIP) 151 MPSolver mp_solver(
"MatchingWithSCIP",
152 MPSolver::SCIP_MIXED_INTEGER_PROGRAMMING);
153 #elif defined(USE_CBC) 154 MPSolver mp_solver(
"MatchingWithCBC",
155 MPSolver::CBC_MIXED_INTEGER_PROGRAMMING);
158 mp_solver.LoadModelFromProto(model, &error);
159 MPSolver::ResultStatus status = mp_solver.Solve();
160 CHECK_EQ(status, MPSolver::OPTIMAL);
161 MPSolutionResponse response;
162 mp_solver.FillSolutionResponseProto(&response);
163 std::vector<ArcIndex> matching;
164 for (
auto arc : variable_indices) {
165 if (response.variable_value(arc.second) > .9) {
166 DCHECK_GE(response.variable_value(arc.second), 1.0 - 1e-4);
167 matching.push_back(arc.first);
172 #endif // defined(USE_CBC) || defined(USE_SCIP) 175 typename CostFunction>
180 costs_(std::move(costs)),
185 typename CostFunction>
187 CostFunction>::TravelingSalesmanCost() {
195 typename CostFunction>
205 typename CostFunction>
207 CostFunction>::Solve() {
208 const NodeIndex num_nodes = graph_.num_nodes();
211 if (num_nodes == 1) {
214 if (num_nodes <= 1) {
218 const std::vector<ArcIndex> mst =
220 return costs_(graph_.Tail(arc), graph_.Head(arc));
223 std::vector<NodeIndex> degrees(num_nodes, 0);
225 degrees[graph_.Tail(arc)]++;
226 degrees[graph_.Head(arc)]++;
228 std::vector<NodeIndex> odd_degree_nodes;
229 for (
int i = 0; i < degrees.size(); ++i) {
230 if (degrees[i] % 2 != 0) {
231 odd_degree_nodes.push_back(i);
236 const NodeIndex reduced_size = odd_degree_nodes.size();
237 DCHECK_NE(0, reduced_size);
238 CompleteGraph<NodeIndex, ArcIndex> reduced_graph(reduced_size);
239 std::vector<ArcIndex> closure_arcs;
241 #if defined(USE_CBC) || defined(USE_SCIP) 242 case MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING: {
244 reduced_graph, [
this, &reduced_graph,
246 return costs_(odd_degree_nodes[reduced_graph.Tail(arc)],
247 odd_degree_nodes[reduced_graph.Head(arc)]);
251 #endif // defined(USE_CBC) || defined(USE_SCIP) 252 case MatchingAlgorithm::MINIMAL_WEIGHT_MATCHING: {
255 std::vector<ArcIndex> ordered_arcs(reduced_graph.num_arcs());
256 std::vector<CostType> ordered_arc_costs(reduced_graph.num_arcs(), 0);
257 for (
const ArcIndex arc : reduced_graph.AllForwardArcs()) {
258 ordered_arcs[arc] = arc;
259 ordered_arc_costs[arc] =
260 costs_(odd_degree_nodes[reduced_graph.Tail(arc)],
261 odd_degree_nodes[reduced_graph.Head(arc)]);
263 std::sort(ordered_arcs.begin(), ordered_arcs.end(),
265 return ordered_arc_costs[arc_a] < ordered_arc_costs[arc_b];
267 std::vector<bool> touched_nodes(reduced_size,
false);
268 for (
ArcIndex arc_index = 0; closure_arcs.size() * 2 < reduced_size;
270 const ArcIndex arc = ordered_arcs[arc_index];
271 const NodeIndex tail = reduced_graph.Tail(arc);
272 const NodeIndex head = reduced_graph.Head(arc);
273 if (head != tail && !touched_nodes[tail] && !touched_nodes[head]) {
274 touched_nodes[tail] =
true;
275 touched_nodes[head] =
true;
276 closure_arcs.push_back(arc);
286 num_nodes, closure_arcs.size() + mst.size());
290 egraph.AddArc(tail, head);
293 const NodeIndex tail = odd_degree_nodes[reduced_graph.Tail(arc)];
294 const NodeIndex head = odd_degree_nodes[reduced_graph.Head(arc)];
295 egraph.AddArc(tail, head);
297 std::vector<bool> touched(num_nodes,
false);
300 if (touched[node])
continue;
301 touched[node] =
true;
302 tsp_cost_ = SafeAdd(tsp_cost_,
303 tsp_path_.empty() ? 0 : costs_(tsp_path_.back(), node));
304 tsp_path_.push_back(node);
307 SafeAdd(tsp_cost_, tsp_path_.empty() ? 0 : costs_(tsp_path_.back(), 0));
308 tsp_path_.push_back(0);
313 #endif // OR_TOOLS_GRAPH_CHRISTOFIDES_H_
std::vector< typename GraphType::ArcIndex > ComputeMinimumWeightMatchingWithMIP(const GraphType &graph, const WeightFunctionType &weight)
ChristofidesPathSolver(NodeIndex num_nodes, CostFunction costs)
std::vector< NodeIndex > BuildEulerianTourFromNode(const Graph &graph, NodeIndex root)
void SetMatchingAlgorithm(MatchingAlgorithm matching)
std::vector< NodeIndex > TravelingSalesmanPath()
bool IsEulerianGraph(const Graph &graph)
std::vector< typename Graph::ArcIndex > BuildPrimMinimumSpanningTree(const Graph &graph, const ArcValue &arc_value)
CostType TravelingSalesmanCost()