#pragma once
#include <cmath>
#include <functional>
#include <random>
#include <utility>
#include <vector>
#include <wpi/array.h>
#include "frc/EigenCore.h"
#include "frc/geometry/Pose2d.h"
#include "frc/optimization/SimulatedAnnealing.h"
namespace frc {
class TravelingSalesman {
public:
constexpr TravelingSalesman() = default;
explicit TravelingSalesman(std::function<double(Pose2d, Pose2d)> cost)
: m_cost{std::move(cost)} {}
template <size_t Poses>
wpi::array<Pose2d, Poses> Solve(const wpi::array<Pose2d, Poses>& poses,
int iterations) {
SimulatedAnnealing<Vectord<Poses>> solver{
1, &Neighbor<Poses>, [&](const Vectord<Poses>& state) {
double sum = 0.0;
for (int i = 0; i < state.rows(); ++i) {
sum +=
m_cost(poses[static_cast<int>(state(i))],
poses[static_cast<int>(state((i + 1) % poses.size()))]);
}
return sum;
}};
Eigen::Vector<double, Poses> initial;
for (int i = 0; i < initial.rows(); ++i) {
initial(i) = i;
}
auto indices = solver.Solve(initial, iterations);
wpi::array<Pose2d, Poses> solution{wpi::empty_array};
for (size_t i = 0; i < poses.size(); ++i) {
solution[i] = poses[static_cast<int>(indices[i])];
}
std::rotate(solution.begin(),
std::find(solution.begin(), solution.end(), poses[0]),
solution.end());
return solution;
}
std::vector<Pose2d> Solve(std::span<const Pose2d> poses, int iterations) {
SimulatedAnnealing<Eigen::VectorXd> solver{
1.0, &Neighbor<Eigen::Dynamic>, [&](const Eigen::VectorXd& state) {
double sum = 0.0;
for (int i = 0; i < state.rows(); ++i) {
sum +=
m_cost(poses[static_cast<int>(state(i))],
poses[static_cast<int>(state((i + 1) % poses.size()))]);
}
return sum;
}};
Eigen::VectorXd initial{poses.size()};
for (int i = 0; i < initial.rows(); ++i) {
initial(i) = i;
}
auto indices = solver.Solve(initial, iterations);
std::vector<Pose2d> solution;
for (size_t i = 0; i < poses.size(); ++i) {
solution.emplace_back(poses[static_cast<int>(indices[i])]);
}
std::rotate(solution.begin(),
std::find(solution.begin(), solution.end(), poses[0]),
solution.end());
return solution;
}
private:
std::function<double(const Pose2d&, const Pose2d&)> m_cost =
[](const Pose2d& a, const Pose2d& b) -> double {
return units::math::hypot(a.X() - b.X(), a.Y() - b.Y()).value();
};
template <int Poses>
static Eigen::Vector<double, Poses> Neighbor(
const Eigen::Vector<double, Poses>& state) {
Eigen::Vector<double, Poses> proposedState = state;
std::random_device rd;
std::mt19937 gen{rd()};
std::uniform_int_distribution<> distr{0,
static_cast<int>(state.rows()) - 1};
int rangeStart = distr(gen);
int rangeEnd = distr(gen);
if (rangeEnd < rangeStart) {
std::swap(rangeStart, rangeEnd);
}
for (int i = rangeStart; i <= (rangeStart + rangeEnd) / 2; ++i) {
double temp = proposedState(i, 0);
proposedState(i, 0) = state(rangeEnd - (i - rangeStart), 0);
proposedState(rangeEnd - (i - rangeStart), 0) = temp;
}
return proposedState;
}
};
}