PaGMO  1.1.5
tsp_ds.cpp
1 /*****************************************************************************
2  * Copyright (C) 2004-2014 The PaGMO development team, *
3  * Advanced Concepts Team (ACT), European Space Agency (ESA) *
4  * *
5  * https://github.com/esa/pagmo *
6  * *
7  * act@esa.int *
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  * This program is distributed in the hope that it will be useful, *
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17  * GNU General Public License for more details. *
18  * *
19  * You should have received a copy of the GNU General Public License *
20  * along with this program; if not, write to the *
21  * Free Software Foundation, Inc., *
22  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
23  *****************************************************************************/
24 
25 #include <numeric>
26 #include <keplerian_toolbox/astro_constants.h>
27 #include <keplerian_toolbox/lambert_problem.h>
28 #include <keplerian_toolbox/core_functions/array3D_operations.h>
29 #include <keplerian_toolbox/core_functions/ic2par.h>
30 #include <keplerian_toolbox/epoch.h>
31 
32 #include "tsp_ds.h"
33 #include "../exceptions.h"
34 #include "../population.h"
35 
36 
37 
38 namespace pagmo { namespace problem {
39 
41 
51  const std::vector<kep_toolbox::planet::planet_ptr>& planets,
52  const std::vector<double>& values,
53  const double max_DV,
54  const std::vector<double>& epochs,
55  const base_tsp::encoding_type & encoding
56  ) : base_tsp(
57  planets.size(),
58  compute_dimensions(planets.size(), encoding)[0],
59  compute_dimensions(planets.size(), encoding)[1],
60  encoding
61  ), m_planets(planets), m_values(values), m_max_DV(max_DV), m_epochs(epochs), m_mu(planets[0]->get_mu_central_body()), m_DV(values.size() - 1)
62  {
63  if (planets.size() != values.size()){
64  pagmo_throw(value_error, "Planet list must have the same size as values list");
65  }
66  if (planets.size() != epochs.size()){
67  pagmo_throw(value_error, "Planet list must have the same size as times list");
68  }
69  if (planets.size() < 3 ){
70  pagmo_throw(value_error, "Planet list must contain at least 3 elements");
71  }
72 
73  for (auto pl_ptr : m_planets)
74  {
75  if (pl_ptr->get_mu_central_body() != m_mu)
76  {
77  pagmo_throw(value_error, "All planets in planet list must have the same gravity constant");
78  }
79  }
80  precompute_ephemerides();
81  }
82 
85  {
86  return base_ptr(new tsp_ds(*this));
87  }
88 
89  void tsp_ds::objfun_impl(fitness_vector &f, const decision_vector& x) const
90  {
91  f[0]=0;
92  decision_vector tour;
93  decision_vector::size_type n_cities = get_n_cities();
94  double cum_p, ham_path_len;
95  decision_vector::size_type dumb1, dumb2;
96 
97  switch( get_encoding() ) {
98  case FULL:
99  {
100  tour = full2cities(x);
101  find_subsequence(tour, cum_p, ham_path_len, dumb1, dumb2);
102  break;
103  }
104  case RANDOMKEYS:
105  {
106  tour = randomkeys2cities(x);
107  find_subsequence(tour, cum_p, ham_path_len, dumb1, dumb2);
108  break;
109  }
110  case CITIES:
111  {
112  find_subsequence(x, cum_p, ham_path_len, dumb1, dumb2);
113  }
114  }
115  ham_path_len = std::accumulate(m_DV.begin(), m_DV.end(), 0.0);
116  f[0] = -(cum_p + 1 - ham_path_len / (n_cities * m_max_DV * 10));
117  return;
118  }
119 
121 
134  void tsp_ds::find_subsequence(const decision_vector& tour, double& retval_p, double& retval_l, decision_vector::size_type& retval_it_l, decision_vector::size_type& retval_it_r, const bool static_computations) const
135  {
136  if (tour.size() != get_n_cities())
137  {
138  pagmo_throw(value_error, "tour dimension must be equal to the number of planets");
139  }
140 
141  // We declare the necessary variable
142  decision_vector::size_type n_cities = get_n_cities();
143  decision_vector::size_type it_l = 0, it_r = 0;
144  bool cond_r = true, cond_l = true;
145  double cum_p = m_values[tour[0]];
146  double saved_length = m_max_DV;
147 
148  // We initialize the starting values
149  retval_p = cum_p;
150  retval_l = saved_length;
151  retval_it_l = it_l;
152  retval_it_r = it_r;
153 
154  // We precompute all DVs (stored in m_DV)
155  compute_DVs(tour, static_computations);
156 
157  // Main body of the double loop
158  while(cond_l)
159  {
160  while(cond_r)
161  {
162  // We increment the right "pointer" updating the value and length of the path
163  saved_length -= m_DV[it_r];
164  cum_p += m_values[tour[(it_r + 1)]];
165  it_r += 1;
166 
167  // We update the various retvals only if the new subpath is valid
168  if (saved_length < 0 || (it_l == it_r))
169  {
170  cond_r = false;
171  }
172  else if (cum_p > retval_p)
173  {
174  retval_p = cum_p;
175  retval_l = saved_length;
176  retval_it_l = it_l;
177  retval_it_r = it_r;
178  }
179  else if (cum_p == retval_p)
180  {
181  if (saved_length > retval_l)
182  {
183  retval_p = cum_p;
184  retval_l = saved_length;
185  retval_it_l = it_l;
186  retval_it_r = it_r;
187  }
188  }
189 
190  if (it_r==n_cities-1)
191  {
192  goto EndOfLoop;
193  }
194  }
195  // We get out if all cities are included in the current path
196  if (it_l == it_r)
197  {
198  cond_l = false;
199  }
200  else
201  {
202  // We increment the left "pointer" updating the value and length of the path
203  saved_length += m_DV[it_l];
204  cum_p -= m_values[tour[it_l]];
205  it_l += 1;
206  // We update the various retvals only if the new subpath is valid
207  if (saved_length > 0)
208  {
209  cond_r = true;
210  if (cum_p > retval_p)
211  {
212  retval_p = cum_p;
213  retval_l = saved_length;
214  retval_it_l = it_l;
215  retval_it_r = it_r;
216  }
217  else if (cum_p == retval_p)
218  {
219  if (saved_length > retval_l)
220  {
221  retval_p = cum_p;
222  retval_l = saved_length;
223  retval_it_l = it_l;
224  retval_it_r = it_r;
225  }
226  }
227  }
228  if (it_l == n_cities)
229  {
230  cond_l = false;
231  }
232  }
233  }
234 EndOfLoop:
235  return;
236  }
237 
238  size_t tsp_ds::compute_idx(const size_t i, const size_t j, const size_t n) const
239  {
240  pagmo_assert( i!=j && i<n && j<n );
241  return i*(n-1) + j - (j>i? 1:0);
242  }
243 
244  void tsp_ds::compute_constraints_impl(constraint_vector &c, const decision_vector& x) const
245  {
246  decision_vector::size_type n_cities = get_n_cities();
247 
248  switch( get_encoding() )
249  {
250  case FULL:
251  {
252  // 1 - We set the equality constraints
253  for (size_t i = 0; i < n_cities; i++) {
254  c[i] = 0;
255  c[i+n_cities] = 0;
256  for (size_t j = 0; j < n_cities; j++) {
257  if(i==j) continue; // ignoring main diagonal
258  decision_vector::size_type rows = compute_idx(i, j, n_cities);
259  decision_vector::size_type cols = compute_idx(j, i, n_cities);
260  c[i] += x[rows];
261  c[i+n_cities] += x[cols];
262  }
263  c[i] = c[i]-1;
264  c[i+n_cities] = c[i+n_cities]-1;
265  }
266 
267  //2 - We set the inequality constraints
268  //2.1 - First we compute the uj (see http://en.wikipedia.org/wiki/Travelling_salesman_problem#Integer_linear_programming_formulation)
269  // we start always out tour from the first city, without loosing generality
270  size_t next_city = 0,current_city = 0;
271  std::vector<int> u(n_cities);
272  for (size_t i = 0; i < n_cities; i++) {
273  u[current_city] = i+1;
274  for (size_t j = 0; j < n_cities; j++)
275  {
276  if (current_city==j) continue;
277  if (x[compute_idx(current_city, j, n_cities)] == 1)
278  {
279  next_city = j;
280  break;
281  }
282  }
283  current_city = next_city;
284  }
285  int count=0;
286  for (size_t i = 1; i < n_cities; i++) {
287  for (size_t j = 1; j < n_cities; j++)
288  {
289  if (i==j) continue;
290  c[2*n_cities+count] = u[i]-u[j] + (n_cities+1) * x[compute_idx(i, j, n_cities)] - n_cities;
291  count++;
292  }
293  }
294  break;
295  }
296  case RANDOMKEYS:
297  break;
298  case CITIES:
299  {
300  std::vector<population::size_type> range(n_cities);
301  for (std::vector<population::size_type>::size_type i=0; i<range.size(); ++i)
302  {
303  range[i]=i;
304  }
305  c[0] = !std::is_permutation(x.begin(),x.end(),range.begin());
306  break;
307  }
308  }
309  return;
310  }
311 
313 
318  double tsp_ds::distance(decision_vector::size_type i, decision_vector::size_type j) const
319  {
320  // TODO: Edelbaum here instead? (see paper from Gatto-Casalino)
321  using namespace std;
322  kep_toolbox::array6D elements1 = m_planets[i]->compute_elements();
323  kep_toolbox::array6D elements2 = m_planets[j]->compute_elements();
324 
325  double a1 = elements1[0];
326  double i1 = elements1[2];
327  double W1 = elements1[3];
328  double e1 = elements1[1];
329 
330  double a2 = elements2[0];
331  double i2 = elements2[2];
332  double W2 = elements2[3];
333  double e2 = elements2[1];
334  return three_impulses(a1,i1,W1,e1,a2,i2,W2,e2);
335  }
336 
337  boost::array<int, 2> tsp_ds::compute_dimensions(decision_vector::size_type n_cities, base_tsp::encoding_type encoding)
338  {
339  boost::array<int,2> retval;
340  switch( encoding ) {
341  case FULL:
342  retval[0] = n_cities*(n_cities-1)+2;
343  retval[1] = (n_cities-1)*(n_cities-2);
344  break;
345  case RANDOMKEYS:
346  retval[0] = 0;
347  retval[1] = 0;
348  break;
349  case CITIES:
350  retval[0] = 1;
351  retval[1] = 0;
352  break;
353  }
354  return retval;
355  }
356 
357  void tsp_ds::precompute_ephemerides() const
358  {
359  kep_toolbox::array3D dumb0;
360  std::vector<kep_toolbox::array3D> dumb1(m_planets.size(),dumb0);
361  std::vector<std::vector<kep_toolbox::array3D> >dumb2(m_epochs.size(),dumb1);
362  m_eph_r = dumb2;
363  m_eph_v = dumb2;
364 
365  kep_toolbox::array6D dumb3;
366  std::vector<kep_toolbox::array6D> dumb4(m_planets.size(),dumb3);
367  std::vector<std::vector<kep_toolbox::array6D> >dumb5(m_epochs.size(),dumb4);
368  m_eph_el = dumb5;
369  // Precompute all ephemerides
370  for (auto i = 0u; i < m_epochs.size(); ++i)
371  {
372  for (auto j = 0u; j < m_planets.size(); ++j) {
373  try
374  {
375  m_planets[j]->eph(kep_toolbox::epoch(m_epochs[i], kep_toolbox::epoch::MJD2000), m_eph_r[i][j], m_eph_v[i][j]);
376  kep_toolbox::ic2par(m_eph_r[i][j], m_eph_v[i][j], m_mu, m_eph_el[i][j]);
377  }
378  catch( ... )
379  {
380  std::cout << *m_planets[j] << std::endl;
381  std::cout << "At epoch: " << m_epochs[i] << std::endl;
382  std::cout << "planet idx: " << j << std::endl;
383  pagmo_throw(value_error, "Ephemerides computations caused an error (planet above)");
384  }
385  }
386  }
387 
388  }
389 
390  void tsp_ds::compute_DVs(const decision_vector& tour, bool static_computations) const
391  {
392  if (!static_computations)
393  {
394  for (auto i = 0u; i < get_n_cities() - 1; ++i)
395  {
396  m_DV[i] = distance_3_imp(tour[i], tour[(i + 1)], i);
397  }
398  } else {
399  for (auto i = 0u; i < get_n_cities() - 1; ++i)
400  {
401  m_DV[i] = distance(tour[i], tour[(i + 1)]);
402  }
403  }
404  }
405 
406  double tsp_ds::three_impulses(double a1, double i1, double W1, double e1, double a2, double i2, double W2, double e2) const
407  {
408  using namespace std;
409  double Vi,Vf, DV1,DV2;
410 
411  // radius of apocenter starting orbit (km)
412  double ra1 = a1 * (1. + e1);
413  // radius of apocenter target orbit(km)
414  double ra2 = a2 * (1. + e2);
415  // relative inclination between orbits
416  double cosiREL = cos(i1) * cos(i2) + sin(i1) * sin(i2) * cos(W1) * cos(W2) + sin(i1) * sin(i2) * sin(W1) * sin(W2);
417 
418  // radius of pericenter target orbit(km)
419  double rp2 = a2 * (1. - e2);
420  // radius of pericenter starting orbit (km)
421  double rp1 = a1 * (1. - e1);
422 
423  if (ra1 > ra2) { // Strategy is Apocenter-Pericenter
424  Vi = sqrt(m_mu * (2. / ra1 - 1. / a1));
425  Vf = sqrt(m_mu * (2. / ra1 - 2. / (rp2 + ra1)));
426  // Change Inclination + pericenter change
427  DV1 = sqrt(Vi * Vi + Vf * Vf - 2. * Vi * Vf * cosiREL);
428  // Apocenter Change
429  DV2 = sqrt(m_mu) * abs(sqrt(2. / rp2 - 2. / (rp2 + ra1)) - sqrt(2. / rp2 - 2. / (rp2 + ra2)));
430  }
431  else { // (ra1<ra2) Strategy is Pericenter-Apocenter
432  // Apocenter Raise
433  DV1 = sqrt(m_mu) * abs(sqrt(2. / rp1 - 2. / (rp1 + ra1)) - sqrt(2. / rp1 - 2. / (rp1 + ra2)));
434  Vi = sqrt(m_mu * (2. / ra2 - 2. / (rp1 + ra2)));
435  Vf = sqrt(m_mu * (2. / ra2 - 1. / a2));
436  // Change Inclination + apocenter change
437  DV2 = sqrt(abs(Vi * Vi + Vf * Vf - 2 * Vi * Vf * cosiREL));
438  }
439  return DV1 + DV2;
440  }
441 
442 
443 
445 
452  double tsp_ds::distance_3_imp(const decision_vector::size_type dep, const decision_vector::size_type arr, const size_t ep_idx) const
453  {
454  // Computing the orbital parameters
455  return three_impulses(m_eph_el[ep_idx][dep][0], m_eph_el[ep_idx][dep][2], m_eph_el[ep_idx][dep][3], m_eph_el[ep_idx][dep][1], m_eph_el[ep_idx][arr][0], m_eph_el[ep_idx][arr][2], m_eph_el[ep_idx][arr][3], m_eph_el[ep_idx][arr][1]);
456  }
457 
458 
460 
463  const std::vector<kep_toolbox::planet::planet_ptr>& tsp_ds::get_planets() const
464  {
465  return m_planets;
466  }
467 
469 
472  double tsp_ds::get_max_DV() const
473  {
474  return m_max_DV;
475  }
476 
477 
479 
482  const std::vector<double>& tsp_ds::get_values() const
483  {
484  return m_values;
485  }
486 
488 
492  {
493  return m_epochs;
494  }
495 
497 
500  //const std::vector<std::vector<kep_toolbox::array6D>>& tsp_ds::get_eph_el() const
501  //{
502  // return m_eph_el;
503  //}
504 
506  std::string tsp_ds::get_name() const
507  {
508  return "Debris Selection TSP (TSP-DS)";
509  }
510 
512 
515  std::string tsp_ds::human_readable_extra() const
516  {
517  std::ostringstream oss;
518  oss << "\n\tNumber of planets: " << get_n_cities() << '\n';
519  oss << "\tEncoding: ";
520  switch( get_encoding() ) {
521  case FULL:
522  oss << "FULL" << '\n';
523  break;
524  case RANDOMKEYS:
525  oss << "RANDOMKEYS" << '\n';
526  break;
527  case CITIES:
528  oss << "CITIES" << '\n';
529  break;
530  }
531  oss << "\tObjects Names: " << std::endl << "\t\t[";
532  for (auto i = 0u; i < m_planets.size(); ++i)
533  {
534  oss << m_planets[i]->get_name() << ", ";
535  if (i == 4) break;
536  }
537 
538  oss << "]" << std::endl << "\tEpochs: " << m_epochs << "\n";
539  oss << "\ttObjects Values: " << m_values << std::endl;
540  oss << "\tSpacecraft DV: " << m_max_DV << " [m/s]\n";
541 
542  return oss.str();
543  }
544 
545 
546 }} //namespaces
547 
548 BOOST_CLASS_EXPORT_IMPLEMENT(pagmo::problem::tsp_ds)
Root PaGMO namespace.
boost::shared_ptr< base > base_ptr
Alias for shared pointer to base problem.
Definition: problem/base.h:62
std::vector< double > decision_vector
Decision vector type.
Definition: types.h:40
std::string human_readable_extra() const
Extra human readable info for the problem.
Definition: tsp_ds.cpp:515
STL namespace.
const decision_vector & get_epochs() const
Getter for m_times.
Definition: tsp_ds.cpp:491
void find_subsequence(const decision_vector &, double &, double &, decision_vector::size_type &, decision_vector::size_type &, const bool=false) const
Given an hamiltonian path finds the best subtour.
Definition: tsp_ds.cpp:134
const std::vector< double > & get_values() const
Getter for m_values.
Definition: tsp_ds.cpp:482
pagmo::decision_vector full2cities(const pagmo::decision_vector &) const
From FULL to CITIES encoding.
Definition: base_tsp.cpp:74
As a vector of doubles in [0,1].
Definition: base_tsp.h:69
decision_vector::size_type get_n_cities() const
Getter for m_n_cities.
Definition: base_tsp.cpp:193
double get_max_DV() const
Getter for m_max_DV.
Definition: tsp_ds.cpp:472
encoding_type
Mechanism used to encode the sequence of vertices to be visited.
Definition: base_tsp.h:68
std::vector< double > fitness_vector
Fitness vector type.
Definition: types.h:42
std::vector< double > constraint_vector
Constraint vector type.
Definition: types.h:44
std::string get_name() const
Getter for m_eph_el.
Definition: tsp_ds.cpp:506
const std::vector< kep_toolbox::planet::planet_ptr > & get_planets() const
Getter for m_planets.
Definition: tsp_ds.cpp:463
tsp_ds(const std::vector< kep_toolbox::planet::planet_ptr > &planets={kep_toolbox::planet::jpl_lp("venus").clone(), kep_toolbox::planet::jpl_lp("earth").clone(), kep_toolbox::planet::jpl_lp("mars").clone()}, const std::vector< double > &values={1., 1., 1.}, const double max_DV=30000, const std::vector< double > &epochs={1200, 1550, 1940}, const base_tsp::encoding_type &encoding=CITIES)
Constructor.
Definition: tsp_ds.cpp:50
As a matrix with ones and zeros.
Definition: base_tsp.h:70
Base TSP (Travelling Salesman Problem).
Definition: base_tsp.h:64
The TSP - Debris Selection Problem.
Definition: tsp_ds.h:57
As a sequence of cities ids.
Definition: base_tsp.h:71
pagmo::decision_vector randomkeys2cities(const pagmo::decision_vector &) const
From RANDOMKEYS to CITIES encoding.
Definition: base_tsp.cpp:127
encoding_type get_encoding() const
Getter for m_encoding.
Definition: base_tsp.cpp:184
double distance(decision_vector::size_type, decision_vector::size_type) const
Computation of the phaseless distance between two planets.
Definition: tsp_ds.cpp:318
base_ptr clone() const
Copy constructor for polymorphic objects.
Definition: tsp_ds.cpp:84