Google OR-Tools: ortools/graph/one_tree_lower_bound.h Source File
121#ifndef ORTOOLS_GRAPH_ONE_TREE_LOWER_BOUND_H_
122#define ORTOOLS_GRAPH_ONE_TREE_LOWER_BOUND_H_
132#include "absl/types/span.h"
156template <typename CostType>
157class VolgenantJonkerEvaluator {
159 VolgenantJonkerEvaluator(int number_of_nodes, int max_iterations)
160 : step1_initialized_(false),
161 step1_(0),
162 iteration_(0),
164 : MaxIterations(number_of_nodes)),
165 number_of_nodes_(number_of_nodes) {}
167 bool Next() { return iteration_++ < max_iterations_; }
170 return (1.0 * (iteration_ - 1) * (2 * max_iterations_ - 5) /
171 (2 * (max_iterations_ - 1))) *
173 (iteration_ - 2) * step1_ +
174 (0.5 * (iteration_ - 1) * (iteration_ - 2) /
180 absl::Span<const int> degrees) {
181 if (!step1_initialized_) {
182 step1_initialized_ = true;
187 void OnNewWMax(CostType one_tree_cost) { UpdateStep(one_tree_cost); }
192 static int MaxIterations(int number_of_nodes) {
193 return static_cast<int>(28 * std::pow(number_of_nodes, 0.62));
196 void UpdateStep(CostType one_tree_cost) {
197 step1_ = one_tree_cost / (2 * number_of_nodes_);
203 const int max_iterations_;
204 const int number_of_nodes_;
209template <typename CostType, typename CostFunction>
210class HeldWolfeCrowderEvaluator {
212 HeldWolfeCrowderEvaluator(int number_of_nodes, const CostFunction& cost)
214 number_of_iterations_(2 * number_of_nodes),
220 ChristofidesPathSolver<CostType, int64_t, int, CostFunction> solver(
222 upper_bound_ = solver.TravelingSalesmanCost().value();
225 bool Next() {
226 const int min_iterations = 2;
227 if (iteration_ >= number_of_iterations_) {
228 number_of_iterations_ /= 2;
229 if (number_of_iterations_ < min_iterations) return false;
238 double GetStep() const { return step_; }
240 void OnOneTree(CostType one_tree_cost, double w,
241 absl::Span<const int> degrees) {
243 for (int degree : degrees) {
244 const double delta = degree - 2;
247 step_ = lambda_ * (upper_bound_ - w) / norm;
250 void OnNewWMax(CostType one_tree_cost) {}
254 int number_of_iterations_;
265template <typename CostFunction>
266std::set<std::pair<int, int>> NearestNeighbors(int number_of_nodes,
268 const CostFunction& cost) {
269 using CostType = decltype(cost(0, 0));
270 std::set<std::pair<int, int>> nearest;
271 for (int i = 0; i < number_of_nodes; ++i) {
272 std::vector<std::pair<CostType, int>> neighbors;
273 neighbors.reserve(number_of_nodes - 1);
274 for (int j = 0; j < number_of_nodes; ++j) {
276 neighbors.emplace_back(cost(i, j), j);
279 int size = neighbors.size();
280 if (number_of_neighbors < size) {
281 std::nth_element(neighbors.begin(),
282 neighbors.begin() + number_of_neighbors - 1,
284 size = number_of_neighbors;
286 for (int j = 0; j < size; ++j) {
287 nearest.insert({i, neighbors[j].second});
288 nearest.insert({neighbors[j].second, i});
296template <typename CostFunction>
297void AddArcsFromMinimumSpanningTree(int number_of_nodes,
299 std::set<std::pair<int, int>>* arcs) {
300 util::CompleteGraph<int, int> graph(number_of_nodes);
301 const std::vector<int> mst =
303 return cost(graph.Tail(arc), graph.Head(arc));
306 arcs->insert({graph.Tail(arc), graph.Head(arc)});
307 arcs->insert({graph.Head(arc), graph.Tail(arc)});
313template <typename CostFunction, typename GraphType, typename AcceptFunction>
314int GetNodeMinimizingEdgeCostToSource(const GraphType& graph, int source,
318 double best_edge_cost = 0;
319 for (const auto node : graph.AllNodes()) {
321 const double edge_cost = cost(node, source);
322 if (best_node == -1 || edge_cost < best_edge_cost) {
324 best_edge_cost = edge_cost;
334template <typename CostFunction, typename GraphType, typename CostType>
335std::vector<int> ComputeOneTree(const GraphType& graph,
337 absl::Span<const double> weights,
338 absl::Span<const int> sorted_arcs,
339 CostType* one_tree_cost) {
340 const auto weighed_cost = [&cost, weights](int from, int to) {
341 return cost(from, to) + weights[from] + weights[to];
345 if (!sorted_arcs.empty()) {
350 graph, [&weighed_cost, &graph](int arc) {
351 return weighed_cost(graph.Tail(arc), graph.Head(arc));
354 std::vector<int> degrees(graph.num_nodes() + 1, 0);
357 degrees[graph.Head(arc)]++;
358 degrees[graph.Tail(arc)]++;
359 *one_tree_cost += cost(graph.Tail(arc), graph.Head(arc));
363 const int extra_node = graph.num_nodes();
364 const auto update_one_tree = [extra_node, one_tree_cost, °rees,
366 *one_tree_cost += cost(node, extra_node);
370 const int node = GetNodeMinimizingEdgeCostToSource(
371 graph, extra_node, weighed_cost,
372 [extra_node](int n) { return n != extra_node; });
374 update_one_tree(GetNodeMinimizingEdgeCostToSource(
375 graph, extra_node, weighed_cost,
376 [extra_node, node](int n) { return n != extra_node && n != node; }));
381template <typename CostFunction, typename Algorithm>
382double ComputeOneTreeLowerBoundWithAlgorithm(int number_of_nodes,
386 if (number_of_nodes < 2) return 0;
387 if (number_of_nodes == 2) return cost(0, 1) + cost(1, 0);
388 using CostType = decltype(cost(0, 0));
389 auto nearest = NearestNeighbors(number_of_nodes - 1, nearest_neighbors, cost);
393 AddArcsFromMinimumSpanningTree(number_of_nodes - 1, cost, &nearest);
394 util::ListGraph<int, int> graph(number_of_nodes - 1, nearest.size());
395 for (const auto& arc : nearest) {
396 graph.AddArc(arc.first, arc.second);
398 std::vector<double> weights(number_of_nodes, 0);
399 std::vector<double> best_weights(number_of_nodes, 0);
400 double max_w = -std::numeric_limits<double>::infinity();
401 double w = 0;
403 while (algorithm->Next()) {
404 CostType one_tree_cost = 0;
405 const std::vector<int> degrees =
406 ComputeOneTree(graph, cost, weights, {}, &one_tree_cost);
407 algorithm->OnOneTree(one_tree_cost, w, degrees);
408 w = one_tree_cost;
409 for (int j = 0; j < number_of_nodes; ++j) {
410 w += weights[j] * (degrees[j] - 2);
412 if (w > max_w) {
413 max_w = w;
415 algorithm->OnNewWMax(one_tree_cost);
417 const double step = algorithm->GetStep();
418 for (int j = 0; j < number_of_nodes; ++j) {
419 weights[j] += step * (degrees[j] - 2);
425 util::CompleteGraph<int, int> complete_graph(number_of_nodes - 1);
426 CostType one_tree_cost = 0;
430 const std::vector<int> degrees =
431 ComputeOneTree(complete_graph, cost, best_weights, {}, &one_tree_cost);
432 w = one_tree_cost;
433 for (int j = 0; j < number_of_nodes; ++j) {
434 w += best_weights[j] * (degrees[j] - 2);
436 return w;
440struct TravelingSalesmanLowerBoundParameters {
446 Algorithm algorithm = VolgenantJonker;
449 int volgenant_jonker_iterations = 0;
451 int nearest_neighbors = 40;
455template <typename CostFunction>
456double ComputeOneTreeLowerBoundWithParameters(
457 int number_of_nodes, const CostFunction& cost,
458 const TravelingSalesmanLowerBoundParameters& parameters) {
459 using CostType = decltype(cost(0, 0));
460 switch (parameters.algorithm) {
461 case TravelingSalesmanLowerBoundParameters::VolgenantJonker: {
462 VolgenantJonkerEvaluator<CostType> algorithm(
463 number_of_nodes, parameters.volgenant_jonker_iterations);
464 return ComputeOneTreeLowerBoundWithAlgorithm(
465 number_of_nodes, parameters.nearest_neighbors, cost, &algorithm);
468 case TravelingSalesmanLowerBoundParameters::HeldWolfeCrowder: {
469 HeldWolfeCrowderEvaluator<CostType, CostFunction> algorithm(
471 return ComputeOneTreeLowerBoundWithAlgorithm(
472 number_of_nodes, parameters.nearest_neighbors, cost, &algorithm);
475 LOG(ERROR) << "Unsupported algorithm: " << parameters.algorithm;
483template <typename CostFunction>
484double ComputeOneTreeLowerBound(int number_of_nodes, const CostFunction& cost) {
485 TravelingSalesmanLowerBoundParameters parameters;
486 return ComputeOneTreeLowerBoundWithParameters(number_of_nodes, cost,
std::vector< typename Graph::ArcIndex > BuildPrimMinimumSpanningTree(const Graph &graph, const ArcValue &arc_value)
std::vector< typename Graph::ArcIndex > BuildKruskalMinimumSpanningTreeFromSortedArcs(const Graph &graph, absl::Span< const typename Graph::ArcIndex > sorted_arcs)
void OnOneTree(CostType one_tree_cost, double w, absl::Span< const int > degrees)
Definition one_tree_lower_bound.h:179
trees with all degrees equal w the current value of w
Definition one_tree_lower_bound.h:148
void OnNewWMax(CostType one_tree_cost)
Definition one_tree_lower_bound.h:187
bool Next()
Definition one_tree_lower_bound.h:167
double GetStep() const
Definition one_tree_lower_bound.h:169
trees with all degrees equal to
Definition one_tree_lower_bound.h:34
trees with all degrees equal w the current value of int max_iterations
Definition one_tree_lower_bound.h:160
trees with all degrees equal w the current value of degrees
Definition one_tree_lower_bound.h:159