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"
70 ABSL_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.");
78 "Have the solver print logs to standard out.");
83 using Cycle = std::vector<int>;
95 for (
int i = 0; i < n; ++i) {
96 variables_[i].reserve(i);
97 for (
int j = 0; j < i; ++j) {
98 variables_[i].push_back(
99 model.AddBinaryVariable(absl::StrCat(
"e_", i,
"_", j)));
106 return i > j ? variables_[i][j] : variables_[j][i];
109 int num_cities()
const {
return variables_.size(); }
112 std::vector<std::vector<math_opt::Variable>> variables_;
117 std::vector<std::pair<double, double>> RandomCities(
int num_cities) {
119 std::vector<std::pair<double, double>> cities;
120 for (
int i = 0; i < num_cities; ++i) {
121 cities.push_back({absl::Uniform<double>(rand, 0.0, 1.0),
122 absl::Uniform<double>(rand, 0.0, 1.0)});
127 std::vector<std::pair<double, double>> TestCities() {
128 return {{0, 0}, {0, 0.1}, {0.1, 0}, {0.1, 0.1},
129 {1, 0}, {1, 0.1}, {0.9, 0}, {0.9, 0.1}};
134 std::vector<std::vector<double>> DistanceMatrix(
135 const std::vector<std::pair<double, double>>& cities) {
136 const int num_cities = cities.size();
137 std::vector<std::vector<double>> distance_matrix(
138 num_cities, std::vector<double>(num_cities, 0.0));
139 for (
int i = 0; i < num_cities; ++i) {
140 for (
int j = 0; j < num_cities; ++j) {
142 const double dx = cities[i].first - cities[j].first;
143 const double dy = cities[i].second - cities[j].second;
144 distance_matrix[i][j] = std::sqrt(dx * dx + dy * dy);
148 return distance_matrix;
155 std::vector<std::vector<bool>> EdgeValues(
156 const EdgeVariables& edge_vars,
158 const int n = edge_vars.num_cities();
159 std::vector<std::vector<bool>> edge_values(n, std::vector<bool>(n,
false));
160 for (
int i = 0; i < n; ++i) {
161 for (
int j = 0; j < n; ++j) {
163 edge_values[i][j] = var_values.
at(edge_vars.get(i, j)) > 0.5;
174 std::vector<Cycle> FindCycles(
175 const std::vector<std::vector<bool>>& edge_values) {
187 const int n = edge_values.size();
188 std::vector<Cycle> result;
189 std::vector<bool> visited(n,
false);
190 for (
int i = 0; i < n; ++i) {
194 std::vector<int> cycle;
195 std::optional<int>
next = i;
196 while (
next.has_value()) {
197 cycle.push_back(*
next);
198 visited[*
next] =
true;
203 for (
int j = i + 1; j < n; ++j) {
204 if (!visited[j] && edge_values[current][j]) {
210 result.push_back(cycle);
218 const Cycle& cycle,
const EdgeVariables& edge_vars) {
219 const int n = edge_vars.num_cities();
220 const absl::flat_hash_set<int> cycle_as_set(cycle.begin(), cycle.end());
221 std::vector<int> not_in_cycle;
222 for (
int i = 0; i < n; ++i) {
223 if (!cycle_as_set.contains(i)) {
224 not_in_cycle.push_back(i);
228 for (
const int in_cycle : cycle) {
229 for (
const int out_of_cycle : not_in_cycle) {
230 cutset_edges += edge_vars.get(in_cycle, out_of_cycle);
233 return cutset_edges >= 2;
238 absl::StatusOr<Cycle> SolveTsp(
239 const std::vector<std::pair<double, double>>& cities) {
240 const int n = cities.size();
241 const std::vector<std::vector<double>> distance_matrix =
242 DistanceMatrix(cities);
245 const EdgeVariables edge_vars(
model, n);
247 for (
int i = 0; i < n; ++i) {
248 for (
int j = i + 1; j < n; ++j) {
249 edge_cost += edge_vars.get(i, j) * distance_matrix[i][j];
252 model.Minimize(edge_cost);
255 for (
int i = 0; i < n; ++i) {
257 for (
int j = 0; j < n; ++j) {
259 neighbors += edge_vars.get(i, j);
262 model.AddLinearConstraint(neighbors == 2, absl::StrCat(
"n_", i));
266 const int threads = absl::GetFlag(FLAGS_threads);
271 math_opt::CallbackEvent::kMipSolution);
275 CHECK(cb_data.solution.has_value());
276 const std::vector<Cycle> cycles =
277 FindCycles(EdgeValues(edge_vars, *cb_data.solution));
279 if (cycles.size() > 1) {
280 for (
const Cycle& cycle : cycles) {
290 <<
"Expected TSP solve terminate with reason optimal, found: "
293 std::cout <<
"Route length: " << result.
objective_value() << std::endl;
294 const std::vector<Cycle> cycles =
296 CHECK_EQ(cycles.size(), 1);
297 CHECK_EQ(cycles[0].size(), n);
302 std::string RouteSvg(
const std::vector<std::pair<double, double>>& cities,
303 const Cycle& cycle) {
304 constexpr
int image_px = 1000;
306 constexpr
int image_plus_border = image_px + 2 * r;
307 std::vector<std::string> svg_lines;
308 svg_lines.push_back(absl::StrCat(
"<svg width=\"", image_plus_border,
309 "\" height=\"", image_plus_border,
"\">"));
310 std::vector<std::string> polygon_coords;
311 for (
const int city : cycle) {
313 static_cast<int>(std::round(cities[city].first * image_px)) + r;
315 static_cast<int>(std::round(cities[city].second * image_px)) + r;
316 svg_lines.push_back(absl::StrCat(
"<circle cx=\"", x,
"\" cy=\"", y,
317 "\" r=\"", r,
"\" fill=\"blue\" />"));
318 polygon_coords.push_back(absl::StrCat(x,
",", y));
320 std::string polygon_coords_string = absl::StrJoin(polygon_coords,
" ");
322 absl::StrCat(
"<polygon fill=\"none\" stroke=\"blue\" points=\"",
323 polygon_coords_string,
"\" />"));
324 svg_lines.push_back(
"</svg>");
325 return absl::StrJoin(svg_lines,
"\n");
329 std::vector<std::pair<double, double>> cities;
330 if (absl::GetFlag(FLAGS_test_instance)) {
331 cities = TestCities();
333 cities = RandomCities(absl::GetFlag(FLAGS_num_cities));
335 absl::StatusOr<Cycle> solution = SolveTsp(cities);
336 if (!solution.ok()) {
337 LOG(QFATAL) << solution.status();
339 const std::string svg = RouteSvg(cities, *solution);
340 if (absl::GetFlag(FLAGS_output).empty()) {
341 std::cout << svg << std::endl;
350 int main(
int argc,
char** argv) {
#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.")