121#ifndef OR_TOOLS_GRAPH_ONE_TREE_LOWER_BOUND_H_
122#define OR_TOOLS_GRAPH_ONE_TREE_LOWER_BOUND_H_
131#include "absl/types/span.h"
156template <
typename CostType>
157class VolgenantJonkerEvaluator {
160 : step1_initialized_(false),
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) /
175 ((max_iterations_ - 1) * (max_iterations_ - 2))) *
180 absl::Span<const int>
degrees) {
181 if (!step1_initialized_) {
182 step1_initialized_ =
true;
183 UpdateStep(one_tree_cost);
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_);
200 bool step1_initialized_;
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(
221 number_of_nodes, cost);
222 upper_bound_ = solver.TravelingSalesmanCost();
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) {
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_;
255 CostType upper_bound_;
265template <
typename CostFunction>
266std::set<std::pair<int, int>> NearestNeighbors(
int number_of_nodes,
267 int number_of_neighbors,
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,
298 const CostFunction& cost,
299 std::set<std::pair<int, int>>* arcs) {
301 const std::vector<int> mst =
305 for (
int arc : mst) {
313template <
typename CostFunction,
typename GraphType,
typename AcceptFunction>
314int GetNodeMinimizingEdgeCostToSource(
const GraphType&
graph,
int source,
315 const CostFunction& cost,
316 AcceptFunction accept) {
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,
336 const CostFunction& cost,
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];
344 std::vector<int> mst;
345 if (!sorted_arcs.empty()) {
356 for (
int arc : mst) {
363 const int extra_node =
graph.num_nodes();
364 const auto update_one_tree = [extra_node, one_tree_cost, &
degrees,
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; });
373 update_one_tree(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,
383 int nearest_neighbors,
384 const CostFunction& cost,
385 Algorithm* algorithm) {
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);
395 for (
const auto&
arc : nearest) {
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();
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);
409 for (
int j = 0; j < number_of_nodes; ++j) {
414 best_weights = weights;
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);
426 CostType one_tree_cost = 0;
430 const std::vector<int>
degrees =
431 ComputeOneTree(complete_graph, cost, best_weights, {}, &one_tree_cost);
433 for (
int j = 0; j < number_of_nodes; ++j) {
434 w += best_weights[j] * (
degrees[j] - 2);
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));
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(
470 number_of_nodes, cost);
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,
In SWIG mode, we don't want anything besides these top-level includes.
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)
trees with all degrees equal w the current value of w
void OnNewWMax(CostType one_tree_cost)
trees with all degrees equal to
trees with all degrees equal w the current value of int max_iterations
trees with all degrees equal w the current value of degrees