57#include "absl/flags/flag.h"
58#include "absl/random/random.h"
59#include "absl/random/uniform_real_distribution.h"
60#include "absl/strings/str_cat.h"
61#include "absl/strings/str_join.h"
70ABSL_FLAG(
int, num_cities, 50,
"Number of cities in random TSP.");
72 "Write a svg of the solution here, or to standard out if empty.");
74 "Solve the test TSP instead of a random instance.");
76 "How many threads to solve with, or solver default if <= 0.");
81using Cycle = std::vector<int>;
93 for (
int i = 0; i < n; ++i) {
94 variables_[i].reserve(i);
95 for (
int j = 0; j < i; ++j) {
96 variables_[i].push_back(
97 model.AddBinaryVariable(absl::StrCat(
"e_", i,
"_", j)));
104 return i > j ? variables_[i][j] : variables_[j][i];
107 int num_cities()
const {
return variables_.size(); }
110 std::vector<std::vector<math_opt::Variable>> variables_;
115std::vector<std::pair<double, double>> RandomCities(
int num_cities) {
117 std::vector<std::pair<double, double>> cities;
118 for (
int i = 0; i < num_cities; ++i) {
119 cities.push_back({absl::Uniform<double>(rand, 0.0, 1.0),
120 absl::Uniform<double>(rand, 0.0, 1.0)});
125std::vector<std::pair<double, double>> TestCities() {
126 return {{0, 0}, {0, 0.1}, {0.1, 0}, {0.1, 0.1},
127 {1, 0}, {1, 0.1}, {0.9, 0}, {0.9, 0.1}};
132std::vector<std::vector<double>> DistanceMatrix(
133 const std::vector<std::pair<double, double>>& cities) {
134 const int num_cities = cities.size();
135 std::vector<std::vector<double>> distance_matrix(
136 num_cities, std::vector<double>(num_cities, 0.0));
137 for (
int i = 0; i < num_cities; ++i) {
138 for (
int j = 0; j < num_cities; ++j) {
140 const double dx = cities[i].first - cities[j].first;
141 const double dy = cities[i].second - cities[j].second;
142 distance_matrix[i][j] = std::sqrt(dx * dx + dy * dy);
146 return distance_matrix;
153std::vector<std::vector<bool>> EdgeValues(
154 const EdgeVariables& edge_vars,
156 const int n = edge_vars.num_cities();
157 std::vector<std::vector<bool>> edge_values(n, std::vector<bool>(n,
false));
158 for (
int i = 0; i < n; ++i) {
159 for (
int j = 0; j < n; ++j) {
161 edge_values[i][j] = var_values.
at(edge_vars.get(i, j)) > 0.5;
172std::vector<Cycle> FindCycles(
173 const std::vector<std::vector<bool>>& edge_values) {
185 const int n = edge_values.size();
186 std::vector<Cycle> result;
187 std::vector<bool> visited(n,
false);
188 for (
int i = 0; i < n; ++i) {
192 std::vector<int> cycle;
193 std::optional<int>
next = i;
194 while (
next.has_value()) {
195 cycle.push_back(*
next);
196 visited[*
next] =
true;
201 for (
int j = i + 1; j < n; ++j) {
202 if (!visited[j] && edge_values[current][j]) {
208 result.push_back(cycle);
216 const Cycle& cycle,
const EdgeVariables& edge_vars) {
217 const int n = edge_vars.num_cities();
218 const absl::flat_hash_set<int> cycle_as_set(cycle.begin(), cycle.end());
219 std::vector<int> not_in_cycle;
220 for (
int i = 0; i < n; ++i) {
221 if (!cycle_as_set.contains(i)) {
222 not_in_cycle.push_back(i);
226 for (
const int in_cycle : cycle) {
227 for (
const int out_of_cycle : not_in_cycle) {
228 cutset_edges += edge_vars.get(in_cycle, out_of_cycle);
231 return cutset_edges >= 2;
236absl::StatusOr<Cycle> SolveTsp(
237 const std::vector<std::pair<double, double>>& cities) {
238 const int n = cities.size();
239 const std::vector<std::vector<double>> distance_matrix =
240 DistanceMatrix(cities);
243 const EdgeVariables edge_vars(
model, n);
245 for (
int i = 0; i < n; ++i) {
246 for (
int j = i + 1; j < n; ++j) {
247 edge_cost += edge_vars.get(i, j) * distance_matrix[i][j];
250 model.Minimize(edge_cost);
253 for (
int i = 0; i < n; ++i) {
255 for (
int j = 0; j < n; ++j) {
257 neighbors += edge_vars.get(i, j);
260 model.AddLinearConstraint(neighbors == 2, absl::StrCat(
"n_", i));
263 const int threads = absl::GetFlag(FLAGS_threads);
268 math_opt::CallbackEvent::kMipSolution);
272 CHECK(cb_data.solution.has_value());
273 const std::vector<Cycle> cycles =
274 FindCycles(EdgeValues(edge_vars, *cb_data.solution));
276 if (cycles.size() > 1) {
277 for (
const Cycle& cycle : cycles) {
287 <<
"Expected TSP solve terminate with reason optimal, found: "
290 std::cout <<
"Route length: " << result.
objective_value() << std::endl;
291 const std::vector<Cycle> cycles =
299std::string RouteSvg(
const std::vector<std::pair<double, double>>& cities,
300 const Cycle& cycle) {
301 constexpr int image_px = 1000;
303 constexpr int image_plus_border = image_px + 2 * r;
304 std::vector<std::string> svg_lines;
305 svg_lines.push_back(absl::StrCat(
"<svg width=\"", image_plus_border,
306 "\" height=\"", image_plus_border,
"\">"));
307 std::vector<std::string> polygon_coords;
308 for (
const int city : cycle) {
310 static_cast<int>(std::round(cities[city].first * image_px)) + r;
312 static_cast<int>(std::round(cities[city].second * image_px)) + r;
313 svg_lines.push_back(absl::StrCat(
"<circle cx=\"", x,
"\" cy=\"", y,
314 "\" r=\"", r,
"\" fill=\"blue\" />"));
315 polygon_coords.push_back(absl::StrCat(x,
",", y));
317 std::string polygon_coords_string = absl::StrJoin(polygon_coords,
" ");
319 absl::StrCat(
"<polygon fill=\"none\" stroke=\"blue\" points=\"",
320 polygon_coords_string,
"\" />"));
321 svg_lines.push_back(
"</svg>");
322 return absl::StrJoin(svg_lines,
"\n");
326 std::vector<std::pair<double, double>> cities;
327 if (absl::GetFlag(FLAGS_test_instance)) {
328 cities = TestCities();
330 cities = RandomCities(absl::GetFlag(FLAGS_num_cities));
332 absl::StatusOr<Cycle> solution = SolveTsp(cities);
333 if (!solution.ok()) {
334 LOG(QFATAL) << solution.status();
336 const std::string svg = RouteSvg(cities, *solution);
337 if (absl::GetFlag(FLAGS_output).empty()) {
338 std::cout << svg << std::endl;
347int main(
int argc,
char** argv) {
#define CHECK_EQ(val1, val2)
#define CHECK_GE(val1, val2)
#define CHECK_NE(val1, val2)
#define ASSIGN_OR_RETURN(lhs, rexpr)
const V & at(const K &k) const
void InitGoogle(const char *usage, int *argc, char ***argv, bool deprecated)
absl::Status SetContents(const absl::string_view &filename, const absl::string_view &contents, int flags)
absl::StatusOr< SolveResult > Solve(const Model &model, const SolverType solver_type, const SolveArguments &solve_args, const SolverInitArguments &init_args)
StatusBuilder InternalErrorBuilder()
bool add_lazy_constraints
absl::flat_hash_set< CallbackEvent > events
void AddLazyConstraint(BoundedLinearExpression linear_constraint)
CallbackRegistration callback_registration
SolveParameters parameters
std::optional< int32_t > threads
double objective_value() const
const VariableMap< double > & variable_values() const
int main(int argc, char **argv)
ABSL_FLAG(int, num_cities, 50, "Number of cities in random TSP.")