516 template <
typename T,
520 struct SaturatedArithmetic {
521 static T Add(T
a, T
b) {
return a +
b; }
522 static T Sub(T
a, T
b) {
return a -
b; }
524 template <
bool Dummy>
525 struct SaturatedArithmetic<int64_t, Dummy> {
526 static int64_t Add(int64_t
a, int64_t
b) {
return CapAdd(
a,
b); }
527 static int64_t Sub(int64_t
a, int64_t
b) {
return CapSub(
a,
b); }
530 template <
bool Dummy>
531 struct SaturatedArithmetic<int32_t, Dummy> {
532 static int32_t Add(int32_t
a, int32_t
b) {
533 const int64_t a64 =
a;
534 const int64_t b64 =
b;
535 const int64_t min_int32 = std::numeric_limits<int32_t>::min();
536 const int64_t max_int32 = std::numeric_limits<int32_t>::max();
537 return static_cast<int32_t
>(
538 std::max(min_int32, std::min(max_int32, a64 + b64)));
540 static int32_t Sub(int32_t
a, int32_t
b) {
541 const int64_t a64 =
a;
542 const int64_t b64 =
b;
543 const int64_t min_int32 = std::numeric_limits<int32_t>::min();
544 const int64_t max_int32 = std::numeric_limits<int32_t>::max();
545 return static_cast<int32_t
>(
546 std::max(min_int32, std::min(max_int32, a64 - b64)));
550 template <
typename T>
551 using Saturated = SaturatedArithmetic<T>;
554 CostType
Cost(
int i,
int j) {
return cost_(i, j); }
560 std::vector<int> ComputePath(CostType cost,
NodeSet set,
int end);
563 bool PathIsValid(absl::Span<const int> path, CostType cost);
566 MatrixOrFunction<CostType, CostFunction, true> cost_;
575 std::vector<CostType> hamiltonian_costs_;
578 bool triangle_inequality_ok_;
579 bool robustness_checked_;
580 bool triangle_inequality_checked_;
582 std::vector<int> tsp_path_;
586 std::vector<std::vector<int>> hamiltonian_paths_;
591 int best_hamiltonian_path_end_node_;
593 LatticeMemoryManager<NodeSet, CostType> mem_;
597template <
typename CostType,
typename CostFunction>
599 int num_nodes, CostFunction cost) {
604template <
typename CostType,
typename CostFunction>
609template <
typename CostType,
typename CostFunction>
611 int num_nodes, CostFunction cost)
612 : cost_(
std::move(cost)),
613 num_nodes_(num_nodes),
615 hamiltonian_costs_(0),
617 triangle_inequality_ok_(true),
618 robustness_checked_(false),
619 triangle_inequality_checked_(false),
622 CHECK(cost_.
Check());
625template <
typename CostType,
typename CostFunction>
628 ChangeCostMatrix(cost.size(), cost);
631template <
typename CostType,
typename CostFunction>
633 int num_nodes, CostFunction cost) {
634 robustness_checked_ =
false;
635 triangle_inequality_checked_ =
false;
638 num_nodes_ = num_nodes;
639 CHECK_GE(NodeSet::MaxCardinality, num_nodes_);
640 CHECK(cost_.Check());
643template <
typename CostType,
typename CostFunction>
646 if (num_nodes_ == 0) {
649 hamiltonian_paths_.resize(1);
650 hamiltonian_costs_.resize(1);
651 best_hamiltonian_path_end_node_ = 0;
652 hamiltonian_costs_[0] = 0;
653 hamiltonian_paths_[0] = {0};
656 mem_.Init(num_nodes_);
659 for (
int dest = 0; dest < num_nodes_; ++dest) {
660 DCHECK_EQ(dest, mem_.BaseOffset(1, NodeSet::Singleton(dest)));
661 mem_.SetValueAtOffset(dest,
Cost(0, dest));
666 for (
int card = 2; card <= num_nodes_; ++card) {
669 SetRangeWithCardinality<Set<uint32_t>>(card, num_nodes_)) {
672 const uint64_t set_offset = mem_.BaseOffset(card, set);
676 uint64_t subset_offset =
677 mem_.BaseOffset(card - 1, set.RemoveSmallestElement());
678 int prev_dest = set.SmallestElement();
680 for (
int dest : set) {
681 CostType min_cost = std::numeric_limits<CostType>::max();
682 const NodeSet subset = set.RemoveElement(dest);
686 subset_offset += mem_.OffsetDelta(card - 1, prev_dest, dest, dest_rank);
688 for (
int src : subset) {
690 min_cost, Saturated<CostType>::Add(
692 mem_.ValueAtOffset(subset_offset + src_rank)));
696 mem_.SetValueAtOffset(set_offset + dest_rank, min_cost);
702 const NodeSet full_set = NodeSet::FullSet(num_nodes_);
706 tsp_cost_ = mem_.Value(full_set, 0);
707 tsp_path_ = ComputePath(tsp_cost_, full_set, 0);
709 hamiltonian_paths_.resize(num_nodes_);
710 hamiltonian_costs_.resize(num_nodes_);
714 CostType min_hamiltonian_cost = std::numeric_limits<CostType>::max();
715 const NodeSet hamiltonian_set = full_set.RemoveElement(0);
716 for (
int end_node : hamiltonian_set) {
717 const CostType cost = mem_.Value(hamiltonian_set, end_node);
718 hamiltonian_costs_[end_node] = cost;
719 if (cost <= min_hamiltonian_cost) {
720 min_hamiltonian_cost = cost;
721 best_hamiltonian_path_end_node_ = end_node;
723 DCHECK_LE(tsp_cost_, Saturated<CostType>::Add(cost,
Cost(end_node, 0)));
725 hamiltonian_paths_[end_node] =
726 ComputePath(hamiltonian_costs_[end_node], hamiltonian_set, end_node);
732template <
typename CostType,
typename CostFunction>
733std::vector<int> HamiltonianPathSolver<CostType, CostFunction>::ComputePath(
734 CostType cost, NodeSet set,
int end_node) {
735 DCHECK(set.Contains(end_node));
736 const int path_size = set.Cardinality() + 1;
737 std::vector<int> path(path_size, 0);
738 NodeSet subset = set.RemoveElement(end_node);
739 path[path_size - 1] = end_node;
741 CostType current_cost = cost;
742 for (
int rank = path_size - 2; rank >= 0; --rank) {
743 for (
int src : subset) {
744 const CostType partial_cost = mem_.Value(subset, src);
745 const CostType incumbent_cost =
746 Saturated<CostType>::Add(partial_cost,
Cost(src, dest));
749 if (std::abs(Saturated<CostType>::Sub(current_cost, incumbent_cost)) <=
750 std::numeric_limits<CostType>::epsilon() * current_cost) {
751 subset = subset.RemoveElement(src);
752 current_cost = partial_cost;
759 DCHECK_EQ(0, subset.value());
760 DCHECK(PathIsValid(path, cost));
764template <
typename CostType,
typename CostFunction>
765bool HamiltonianPathSolver<CostType, CostFunction>::PathIsValid(
766 absl::Span<const int> path, CostType cost) {
768 for (
int node : path) {
769 coverage = coverage.AddElement(node);
771 DCHECK_EQ(NodeSet::FullSet(num_nodes_).
value(), coverage.value());
772 CostType check_cost = 0;
773 for (
int i = 0;
i < path.size() - 1; ++
i) {
775 Saturated<CostType>::Add(check_cost,
Cost(path[i], path[i + 1]));
777 DCHECK_LE(std::abs(Saturated<CostType>::Sub(cost, check_cost)),
778 std::numeric_limits<CostType>::epsilon() * cost)
779 <<
"cost = " << cost <<
" check_cost = " << check_cost;
783template <
typename CostType,
typename CostFunction>
785 if (std::numeric_limits<CostType>::is_integer)
return true;
786 if (robustness_checked_)
return robust_;
787 CostType min_cost = std::numeric_limits<CostType>::max();
788 CostType max_cost = std::numeric_limits<CostType>::min();
791 for (
int i = 0; i < num_nodes_; ++i) {
792 for (
int j = 0; j < num_nodes_; ++j) {
793 if (i == j)
continue;
794 min_cost = std::min(min_cost,
Cost(i, j));
795 max_cost = std::max(max_cost,
Cost(i, j));
801 min_cost >= 0 && min_cost > num_nodes_ * max_cost *
802 std::numeric_limits<CostType>::epsilon();
803 robustness_checked_ =
true;
807template <
typename CostType,
typename CostFunction>
809 CostFunction>::VerifiesTriangleInequality() {
810 if (triangle_inequality_checked_)
return triangle_inequality_ok_;
811 triangle_inequality_ok_ =
true;
812 triangle_inequality_checked_ =
true;
813 for (
int k = 0; k < num_nodes_; ++k) {
814 for (
int i = 0; i < num_nodes_; ++i) {
815 for (
int j = 0; j < num_nodes_; ++j) {
816 const CostType detour_cost =
817 Saturated<CostType>::Add(
Cost(i, k),
Cost(k, j));
818 if (detour_cost <
Cost(i, j)) {
819 triangle_inequality_ok_ =
false;
820 return triangle_inequality_ok_;
825 return triangle_inequality_ok_;