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