1 /* Copyright 2017-2021 PaGMO development team
2 
3 This file is part of the PaGMO library.
4 
5 The PaGMO library is free software; you can redistribute it and/or modify
6 it under the terms of either:
7 
8   * the GNU Lesser General Public License as published by the Free
9     Software Foundation; either version 3 of the License, or (at your
10     option) any later version.
11 
12 or
13 
14   * the GNU General Public License as published by the Free Software
15     Foundation; either version 3 of the License, or (at your option) any
16     later version.
17 
18 or both in parallel, as here.
19 
20 The PaGMO library is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
23 for more details.
24 
25 You should have received copies of the GNU General Public License and the
26 GNU Lesser General Public License along with the PaGMO library.  If not,
27 see https://www.gnu.org/licenses/. */
28 
29 #include <algorithm>
30 #include <cassert>
31 #include <functional>
32 #include <initializer_list>
33 #include <iterator>
34 #include <sstream>
35 #include <stdexcept>
36 #include <string>
37 #include <utility>
38 #include <vector>
39 
40 #if defined(_MSC_VER)
41 
42 // Disable a warning from MSVC in the TBB code.
43 #pragma warning(push)
44 #pragma warning(disable : 4324)
45 
46 #endif
47 
48 #include <tbb/blocked_range.h>
49 #include <tbb/parallel_for.h>
50 
51 #if defined(_MSC_VER)
52 
53 #pragma warning(pop)
54 
55 #endif
56 
57 #include <pagmo/exceptions.hpp>
58 #include <pagmo/io.hpp>
59 #include <pagmo/problem.hpp>
60 #include <pagmo/problems/translate.hpp>
61 #include <pagmo/s11n.hpp>
62 #include <pagmo/threading.hpp>
63 #include <pagmo/types.hpp>
64 
65 // MINGW-specific warnings.
66 #if defined(__GNUC__) && defined(__MINGW32__)
67 #pragma GCC diagnostic push
68 #pragma GCC diagnostic ignored "-Wsuggest-attribute=pure"
69 #pragma GCC diagnostic ignored "-Wsuggest-attribute=const"
70 #endif
71 
72 namespace pagmo
73 {
74 
75 /// Default constructor.
76 /**
77  * The constructor will initialize a non-translated default-constructed pagmo::problem.
78  */
translate()79 translate::translate() : m_translation({0.}) {}
80 
generic_ctor_impl(const vector_double & translation)81 void translate::generic_ctor_impl(const vector_double &translation)
82 {
83     if (translation.size() != m_problem.get_nx()) {
84         pagmo_throw(std::invalid_argument,
85                     "Length of shift vector is: " + std::to_string(translation.size())
86                         + " while the problem dimension is: " + std::to_string(m_problem.get_nx()));
87     }
88 }
89 
90 /// Fitness.
91 /**
92  * The fitness computation is forwarded to the inner UDP, after the translation of \p x.
93  *
94  * @param x the decision vector.
95  *
96  * @return the fitness of \p x.
97  *
98  * @throws unspecified any exception thrown by memory errors in standard containers,
99  * or by problem::fitness().
100  */
fitness(const vector_double & x) const101 vector_double translate::fitness(const vector_double &x) const
102 {
103     vector_double x_deshifted = translate_back(x);
104     return m_problem.fitness(x_deshifted);
105 }
106 
107 /// Batch fitness.
108 /**
109  * The batch fitness computation is forwarded to the inner UDP, after the translation of \p xs.
110  *
111  * @param xs the input decision vectors.
112  *
113  * @return the fitnesses of \p xs.
114  *
115  * @throws unspecified any exception thrown by memory errors in standard containers,
116  * threading primitives, or by problem::batch_fitness().
117  */
batch_fitness(const vector_double & xs) const118 vector_double translate::batch_fitness(const vector_double &xs) const
119 {
120     const auto nx = m_problem.get_nx();
121     // Assume xs is sane.
122     assert(xs.size() % nx == 0u);
123     const auto n_dvs = xs.size() / nx;
124 
125     // Prepare the deshifted dvs.
126     vector_double xs_deshifted(xs.size());
127 
128     // Do the deshifting in parallel.
129     using range_t = tbb::blocked_range<decltype(xs.size())>;
130     tbb::parallel_for(range_t(0, n_dvs), [&xs, &xs_deshifted, nx, this](const range_t &range) {
131         for (auto i = range.begin(); i != range.end(); ++i) {
132 #if defined(_MSC_VER)
133             std::transform(stdext::make_unchecked_array_iterator(xs.data() + i * nx),
134                            stdext::make_unchecked_array_iterator(xs.data() + (i + 1u) * nx),
135                            stdext::make_unchecked_array_iterator(m_translation.data()),
136                            stdext::make_unchecked_array_iterator(xs_deshifted.data() + i * nx), std::minus<double>{});
137 #else
138                 std::transform(xs.data() + i * nx, xs.data() + (i + 1u) * nx, m_translation.data(),
139                                xs_deshifted.data() + i * nx, std::minus<double>{});
140 #endif
141         }
142     });
143 
144     // Invoke batch_fitness() from m_problem.
145     // NOTE: in non-debug mode, use the helper that avoids calling the checks in m_problem.batch_fitness().
146     // The translate metaproblem does not change the dimensionality of the problem
147     // or of the fitness, thus all the checks run by m_problem.batch_fitness()
148     // are redundant.
149 #if defined(NDEBUG)
150     return detail::prob_invoke_mem_batch_fitness(m_problem, xs_deshifted, true);
151 #else
152     return m_problem.batch_fitness(xs_deshifted);
153 #endif
154 }
155 
156 /// Check if the inner problem can compute fitnesses in batch mode.
157 /**
158  * @return the output of the <tt>has_batch_fitness()</tt> member function invoked
159  * by the inner problem.
160  */
has_batch_fitness() const161 bool translate::has_batch_fitness() const
162 {
163     return m_problem.has_batch_fitness();
164 }
165 
166 /// Box-bounds.
167 /**
168  * The box-bounds returned by this method are the translated box-bounds of the inner UDP.
169  *
170  * @return the lower and upper bounds for each of the decision vector components.
171  *
172  * @throws unspecified any exception thrown by memory errors in standard containers,
173  * or by problem::get_bounds().
174  */
get_bounds() const175 std::pair<vector_double, vector_double> translate::get_bounds() const
176 {
177     auto b_sh = m_problem.get_bounds();
178     // NOTE: this should be safe as the translation vector has been checked against the
179     // bounds size upon construction (via get_nx()).
180     return {apply_translation(b_sh.first), apply_translation(b_sh.second)};
181 }
182 
183 /// Number of objectives.
184 /**
185  * @return the number of objectives of the inner problem.
186  */
get_nobj() const187 vector_double::size_type translate::get_nobj() const
188 {
189     return m_problem.get_nobj();
190 }
191 
192 /// Equality constraint dimension.
193 /**
194  * Returns the number of equality constraints of the inner problem.
195  *
196  * @return the number of equality constraints of the inner problem.
197  */
get_nec() const198 vector_double::size_type translate::get_nec() const
199 {
200     return m_problem.get_nec();
201 }
202 
203 /// Inequality constraint dimension.
204 /**
205  * Returns the number of inequality constraints of the inner problem.
206  *
207  * @return the number of inequality constraints of the inner problem.
208  */
get_nic() const209 vector_double::size_type translate::get_nic() const
210 {
211     return m_problem.get_nic();
212 }
213 
214 /// Integer dimension
215 /**
216  * @return the integer dimension of the inner problem.
217  */
get_nix() const218 vector_double::size_type translate::get_nix() const
219 {
220     return m_problem.get_nix();
221 }
222 
223 /// Checks if the inner problem has gradients.
224 /**
225  * The <tt>has_gradient()</tt> computation is forwarded to the inner problem.
226  *
227  * @return a flag signalling the availability of the gradient in the inner problem.
228  */
has_gradient() const229 bool translate::has_gradient() const
230 {
231     return m_problem.has_gradient();
232 }
233 
234 /// Gradients.
235 /**
236  * The gradients computation is forwarded to the inner problem, after the translation of \p x.
237  *
238  * @param x the decision vector.
239  *
240  * @return the gradient of the fitness function.
241  *
242  * @throws unspecified any exception thrown by memory errors in standard containers,
243  * or by <tt>problem::gradient()</tt>.
244  */
gradient(const vector_double & x) const245 vector_double translate::gradient(const vector_double &x) const
246 {
247     vector_double x_deshifted = translate_back(x);
248     return m_problem.gradient(x_deshifted);
249 }
250 
251 /// Checks if the inner problem has gradient sparisty implemented.
252 /**
253  * The <tt>has_gradient_sparsity()</tt> computation is forwarded to the inner problem.
254  *
255  * @return a flag signalling the availability of the gradient sparisty in the inner problem.
256  */
has_gradient_sparsity() const257 bool translate::has_gradient_sparsity() const
258 {
259     return m_problem.has_gradient_sparsity();
260 }
261 
262 /// Gradient sparsity.
263 /**
264  * The <tt>gradient_sparsity</tt> computation is forwarded to the inner problem.
265  *
266  * @return the gradient sparsity of the inner problem.
267  */
gradient_sparsity() const268 sparsity_pattern translate::gradient_sparsity() const
269 {
270     return m_problem.gradient_sparsity();
271 }
272 
273 /// Checks if the inner problem has hessians.
274 /**
275  * The <tt>has_hessians()</tt> computation is forwarded to the inner problem.
276  *
277  * @return a flag signalling the availability of the hessians in the inner problem.
278  */
has_hessians() const279 bool translate::has_hessians() const
280 {
281     return m_problem.has_hessians();
282 }
283 
284 /// Hessians.
285 /**
286  * The <tt>hessians()</tt> computation is forwarded to the inner problem, after the translation of \p x.
287  *
288  * @param x the decision vector.
289  *
290  * @return the hessians of the fitness function computed at \p x.
291  *
292  * @throws unspecified any exception thrown by memory errors in standard containers,
293  * or by problem::hessians().
294  */
hessians(const vector_double & x) const295 std::vector<vector_double> translate::hessians(const vector_double &x) const
296 {
297     vector_double x_deshifted = translate_back(x);
298     return m_problem.hessians(x_deshifted);
299 }
300 
301 /// Checks if the inner problem has hessians sparisty implemented.
302 /**
303  * The <tt>has_hessians_sparsity()</tt> computation is forwarded to the inner problem.
304  *
305  * @return a flag signalling the availability of the hessians sparisty in the inner problem.
306  */
has_hessians_sparsity() const307 bool translate::has_hessians_sparsity() const
308 {
309     return m_problem.has_hessians_sparsity();
310 }
311 
312 /// Hessians sparsity.
313 /**
314  * The <tt>hessians_sparsity()</tt> computation is forwarded to the inner problem.
315  *
316  * @return the hessians sparsity of the inner problem.
317  */
hessians_sparsity() const318 std::vector<sparsity_pattern> translate::hessians_sparsity() const
319 {
320     return m_problem.hessians_sparsity();
321 }
322 
323 /// Calls <tt>has_set_seed()</tt> of the inner problem.
324 /**
325  * Calls the method <tt>has_set_seed()</tt> of the inner problem.
326  *
327  * @return a flag signalling wether the inner problem is stochastic.
328  */
has_set_seed() const329 bool translate::has_set_seed() const
330 {
331     return m_problem.has_set_seed();
332 }
333 
334 /// Calls <tt>set_seed()</tt> of the inner problem.
335 /**
336  * Calls the method <tt>set_seed()</tt> of the inner problem.
337  *
338  * @param seed seed to be set.
339  *
340  * @throws unspecified any exception thrown by the method <tt>set_seed()</tt> of the inner problem.
341  */
set_seed(unsigned seed)342 void translate::set_seed(unsigned seed)
343 {
344     return m_problem.set_seed(seed);
345 }
346 
347 /// Problem name
348 /**
349  * This method will add <tt>[translated]</tt> to the name provided by the inner problem.
350  *
351  * @return a string containing the problem name.
352  *
353  * @throws unspecified any exception thrown by <tt>problem::get_name()</tt> or memory errors in standard classes.
354  */
get_name() const355 std::string translate::get_name() const
356 {
357     return m_problem.get_name() + " [translated]";
358 }
359 
360 /// Extra info
361 /**
362  * This method will append a description of the translation vector to the extra info provided
363  * by the inner problem.
364  *
365  * @return a string containing extra info on the problem.
366  *
367  * @throws unspecified any exception thrown by problem::get_extra_info(), the public interface of
368  * \p std::ostringstream or memory errors in standard classes.
369  */
get_extra_info() const370 std::string translate::get_extra_info() const
371 {
372     std::ostringstream oss;
373     stream(oss, m_translation);
374     return m_problem.get_extra_info() + "\n\tTranslation Vector: " + oss.str();
375 }
376 
377 /// Get the translation vector
378 /**
379  * @return a reference to the translation vector.
380  */
get_translation() const381 const vector_double &translate::get_translation() const
382 {
383     return m_translation;
384 }
385 
386 /// Problem's thread safety level.
387 /**
388  * The thread safety of a meta-problem is defined by the thread safety of the inner pagmo::problem.
389  *
390  * @return the thread safety level of the inner pagmo::problem.
391  */
get_thread_safety() const392 thread_safety translate::get_thread_safety() const
393 {
394     return m_problem.get_thread_safety();
395 }
396 
get_inner_problem() const397 const problem &translate::get_inner_problem() const
398 {
399     return m_problem;
400 }
401 
get_inner_problem()402 problem &translate::get_inner_problem()
403 {
404     return m_problem;
405 }
406 
407 // Object serialization
408 template <typename Archive>
serialize(Archive & ar,unsigned)409 void translate::serialize(Archive &ar, unsigned)
410 {
411     detail::archive(ar, m_problem, m_translation);
412 }
413 
translate_back(const vector_double & x) const414 vector_double translate::translate_back(const vector_double &x) const
415 {
416     // NOTE: here we use assert instead of throwing because the general idea is that we don't
417     // protect UDPs from misuses, and we have checks in problem. In Python, UDP methods that could cause
418     // troubles are not exposed.
419     assert(x.size() == m_translation.size());
420     vector_double x_sh(x.size());
421     std::transform(x.begin(), x.end(), m_translation.begin(), x_sh.begin(), std::minus<double>());
422     return x_sh;
423 }
424 
apply_translation(const vector_double & x) const425 vector_double translate::apply_translation(const vector_double &x) const
426 {
427     assert(x.size() == m_translation.size());
428     vector_double x_sh(x.size());
429     std::transform(x.begin(), x.end(), m_translation.begin(), x_sh.begin(), std::plus<double>());
430     return x_sh;
431 }
432 
433 } // namespace pagmo
434 
435 PAGMO_S11N_PROBLEM_IMPLEMENT(pagmo::translate)
436