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 #ifndef PAGMO_PROBLEM_HPP
30 #define PAGMO_PROBLEM_HPP
31 
32 #include <atomic>
33 #include <cassert>
34 #include <exception>
35 #include <iostream>
36 #include <memory>
37 #include <string>
38 #include <type_traits>
39 #include <typeindex>
40 #include <typeinfo>
41 #include <utility>
42 #include <vector>
43 
44 #include <boost/type_traits/integral_constant.hpp>
45 
46 #include <pagmo/config.hpp>
47 #include <pagmo/detail/support_xeus_cling.hpp>
48 #include <pagmo/detail/type_name.hpp>
49 #include <pagmo/detail/typeid_name_extract.hpp>
50 #include <pagmo/detail/visibility.hpp>
51 #include <pagmo/exceptions.hpp>
52 #include <pagmo/s11n.hpp>
53 #include <pagmo/threading.hpp>
54 #include <pagmo/type_traits.hpp>
55 #include <pagmo/types.hpp>
56 
57 // NOTE: we disable address tracking for all user-defined classes. The reason is that even if the final
58 // classes (e.g., problem) use value semantics, the internal implementation details use old-style
59 // OO construct (i.e., base classes, pointers, etc.). By default, Boost serialization wants to track
60 // the addresses of these internal implementation-detail classes, and this has some undesirable consequences
61 // (for instance, when deserializing a problem object in a variable and then moving it into another
62 // one, which is a pattern we sometimes use in order to provide increased exception safety).
63 //
64 // See also:
65 // https://www.boost.org/doc/libs/1_70_0/libs/serialization/doc/special.html#objecttracking
66 // https://www.boost.org/doc/libs/1_70_0/libs/serialization/doc/traits.html#level
67 #define PAGMO_S11N_PROBLEM_EXPORT_KEY(prob)                                                                            \
68     BOOST_CLASS_EXPORT_KEY2(pagmo::detail::prob_inner<prob>, "udp " #prob)                                             \
69     BOOST_CLASS_TRACKING(pagmo::detail::prob_inner<prob>, boost::serialization::track_never)
70 
71 #define PAGMO_S11N_PROBLEM_IMPLEMENT(prob) BOOST_CLASS_EXPORT_IMPLEMENT(pagmo::detail::prob_inner<prob>)
72 
73 #define PAGMO_S11N_PROBLEM_EXPORT(prob)                                                                                \
74     PAGMO_S11N_PROBLEM_EXPORT_KEY(prob)                                                                                \
75     PAGMO_S11N_PROBLEM_IMPLEMENT(prob)
76 
77 namespace pagmo
78 {
79 
80 /// Detect \p fitness() method.
81 /**
82  * This type trait will be \p true if \p T provides a method with
83  * the following signature:
84  * @code{.unparsed}
85  * vector_double fitness(const vector_double &) const;
86  * @endcode
87  * The \p fitness() method is part of the interface for the definition of a problem
88  * (see pagmo::problem).
89  */
90 template <typename T>
91 class has_fitness
92 {
93     template <typename U>
94     using fitness_t = decltype(std::declval<const U &>().fitness(std::declval<const vector_double &>()));
95     static const bool implementation_defined = std::is_same<detected_t<fitness_t, T>, vector_double>::value;
96 
97 public:
98     /// Value of the type trait.
99     static constexpr bool value = implementation_defined;
100 };
101 
102 /// Detect \p get_nobj() method.
103 /**
104  * This type trait will be \p true if \p T provides a method with
105  * the following signature:
106  * @code{.unparsed}
107  * vector_double::size_type get_nobj() const;
108  * @endcode
109  * The \p get_nobj() method is part of the interface for the definition of a problem
110  * (see pagmo::problem).
111  */
112 template <typename T>
113 class has_get_nobj
114 {
115     template <typename U>
116     using get_nobj_t = decltype(std::declval<const U &>().get_nobj());
117     static const bool implementation_defined = std::is_same<detected_t<get_nobj_t, T>, vector_double::size_type>::value;
118 
119 public:
120     /// Value of the type trait.
121     static constexpr bool value = implementation_defined;
122 };
123 
124 /// Detect \p get_bounds() method.
125 /**
126  * This type trait will be \p true if \p T provides a method with
127  * the following signature:
128  * @code{.unparsed}
129  * std::pair<vector_double, vector_double> get_bounds() const;
130  * @endcode
131  * The \p get_bounds() method is part of the interface for the definition of a problem
132  * (see pagmo::problem).
133  */
134 template <typename T>
135 class has_bounds
136 {
137     template <typename U>
138     using get_bounds_t = decltype(std::declval<const U &>().get_bounds());
139     static const bool implementation_defined
140         = std::is_same<std::pair<vector_double, vector_double>, detected_t<get_bounds_t, T>>::value;
141 
142 public:
143     /// Value of the type trait.
144     static constexpr bool value = implementation_defined;
145 };
146 
147 /// Detect \p get_nec() method.
148 /**
149  * This type trait will be \p true if \p T provides a method with
150  * the following signature:
151  * @code{.unparsed}
152  * vector_double::size_type get_nec() const;
153  * @endcode
154  * The \p get_nec() method is part of the interface for the definition of a problem
155  * (see pagmo::problem).
156  */
157 template <typename T>
158 class has_e_constraints
159 {
160     template <typename U>
161     using get_nec_t = decltype(std::declval<const U &>().get_nec());
162     static const bool implementation_defined = std::is_same<vector_double::size_type, detected_t<get_nec_t, T>>::value;
163 
164 public:
165     /// Value of the type trait.
166     static constexpr bool value = implementation_defined;
167 };
168 
169 /// Detect \p get_nic() method.
170 /**
171  * This type trait will be \p true if \p T provides a method with
172  * the following signature:
173  * @code{.unparsed}
174  * vector_double::size_type get_nic() const;
175  * @endcode
176  * The \p get_nic() method is part of the interface for the definition of a problem
177  * (see pagmo::problem).
178  */
179 template <typename T>
180 class has_i_constraints
181 {
182     template <typename U>
183     using get_nic_t = decltype(std::declval<const U &>().get_nic());
184     static const bool implementation_defined = std::is_same<vector_double::size_type, detected_t<get_nic_t, T>>::value;
185 
186 public:
187     /// Value of the type trait.
188     static constexpr bool value = implementation_defined;
189 };
190 
191 /// Detect \p get_nix() method.
192 /**
193  * This type trait will be \p true if \p T provides a method with
194  * the following signature:
195  * @code{.unparsed}
196  * vector_double::size_type get_nix() const;
197  * @endcode
198  * The \p get_nix() method is part of the interface for the definition of a problem
199  * (see pagmo::problem).
200  */
201 template <typename T>
202 class has_integer_part
203 {
204     template <typename U>
205     using get_nix_t = decltype(std::declval<const U &>().get_nix());
206     static const bool implementation_defined = std::is_same<vector_double::size_type, detected_t<get_nix_t, T>>::value;
207 
208 public:
209     /// Value of the type trait.
210     static constexpr bool value = implementation_defined;
211 };
212 
213 /// Detect \p gradient() method.
214 /**
215  * This type trait will be \p true if \p T provides a method with
216  * the following signature:
217  * @code{.unparsed}
218  * vector_double gradient(const vector_double &) const;
219  * @endcode
220  * The \p gradient() method is part of the interface for the definition of a problem
221  * (see pagmo::problem).
222  */
223 template <typename T>
224 class has_gradient
225 {
226     template <typename U>
227     using gradient_t = decltype(std::declval<const U &>().gradient(std::declval<const vector_double &>()));
228     static const bool implementation_defined = std::is_same<vector_double, detected_t<gradient_t, T>>::value;
229 
230 public:
231     /// Value of the type trait.
232     static constexpr bool value = implementation_defined;
233 };
234 
235 /// Detect \p has_gradient() method.
236 /**
237  * This type trait will be \p true if \p T provides a method with
238  * the following signature:
239  * @code{.unparsed}
240  * bool has_gradient() const;
241  * @endcode
242  * The \p has_gradient() method is part of the interface for the definition of a problem
243  * (see pagmo::problem).
244  */
245 template <typename T>
246 class override_has_gradient
247 {
248     template <typename U>
249     using has_gradient_t = decltype(std::declval<const U &>().has_gradient());
250     static const bool implementation_defined = std::is_same<bool, detected_t<has_gradient_t, T>>::value;
251 
252 public:
253     /// Value of the type trait.
254     static constexpr bool value = implementation_defined;
255 };
256 
257 /// Detect \p gradient_sparsity() method.
258 /**
259  * This type trait will be \p true if \p T provides a method with
260  * the following signature:
261  * @code{.unparsed}
262  * sparsity_pattern gradient_sparsity() const;
263  * @endcode
264  * The \p gradient_sparsity() method is part of the interface for the definition of a problem
265  * (see pagmo::problem).
266  */
267 template <typename T>
268 class has_gradient_sparsity
269 {
270     template <typename U>
271     using gradient_sparsity_t = decltype(std::declval<const U &>().gradient_sparsity());
272     static const bool implementation_defined
273         = std::is_same<sparsity_pattern, detected_t<gradient_sparsity_t, T>>::value;
274 
275 public:
276     /// Value of the type trait.
277     static constexpr bool value = implementation_defined;
278 };
279 
280 /// Detect \p hessians() method.
281 /**
282  * This type trait will be \p true if \p T provides a method with
283  * the following signature:
284  * @code{.unparsed}
285  * std::vector<vector_double> hessians(const vector_double &) const;
286  * @endcode
287  * The \p hessians() method is part of the interface for the definition of a problem
288  * (see pagmo::problem).
289  */
290 template <typename T>
291 class has_hessians
292 {
293     template <typename U>
294     using hessians_t = decltype(std::declval<const U &>().hessians(std::declval<const vector_double &>()));
295     static const bool implementation_defined
296         = std::is_same<std::vector<vector_double>, detected_t<hessians_t, T>>::value;
297 
298 public:
299     /// Value of the type trait.
300     static constexpr bool value = implementation_defined;
301 };
302 
303 /// Detect \p has_hessians() method.
304 /**
305  * This type trait will be \p true if \p T provides a method with
306  * the following signature:
307  * @code{.unparsed}
308  * bool has_hessians() const;
309  * @endcode
310  * The \p has_hessians() method is part of the interface for the definition of a problem
311  * (see pagmo::problem).
312  */
313 template <typename T>
314 class override_has_hessians
315 {
316     template <typename U>
317     using has_hessians_t = decltype(std::declval<const U &>().has_hessians());
318     static const bool implementation_defined = std::is_same<bool, detected_t<has_hessians_t, T>>::value;
319 
320 public:
321     /// Value of the type trait.
322     static constexpr bool value = implementation_defined;
323 };
324 
325 /// Detect \p hessians_sparsity() method.
326 /**
327  * This type trait will be \p true if \p T provides a method with
328  * the following signature:
329  * @code{.unparsed}
330  * std::vector<sparsity_pattern> hessians_sparsity() const;
331  * @endcode
332  * The \p hessians_sparsity() method is part of the interface for the definition of a problem
333  * (see pagmo::problem).
334  */
335 template <typename T>
336 class has_hessians_sparsity
337 {
338     template <typename U>
339     using hessians_sparsity_t = decltype(std::declval<const U &>().hessians_sparsity());
340     static const bool implementation_defined
341         = std::is_same<std::vector<sparsity_pattern>, detected_t<hessians_sparsity_t, T>>::value;
342 
343 public:
344     /// Value of the type trait.
345     static constexpr bool value = implementation_defined;
346 };
347 
348 /// Detect \p has_gradient_sparsity() method.
349 /**
350  * This type trait will be \p true if \p T provides a method with
351  * the following signature:
352  * @code{.unparsed}
353  * bool has_gradient_sparsity() const;
354  * @endcode
355  * The \p has_gradient_sparsity() method is part of the interface for the definition of a problem
356  * (see pagmo::problem).
357  */
358 template <typename T>
359 class override_has_gradient_sparsity
360 {
361     template <typename U>
362     using has_gradient_sparsity_t = decltype(std::declval<const U &>().has_gradient_sparsity());
363     static const bool implementation_defined = std::is_same<bool, detected_t<has_gradient_sparsity_t, T>>::value;
364 
365 public:
366     /// Value of the type trait.
367     static constexpr bool value = implementation_defined;
368 };
369 
370 /// Detect \p has_hessians_sparsity() method.
371 /**
372  * This type trait will be \p true if \p T provides a method with
373  * the following signature:
374  * @code{.unparsed}
375  * bool has_hessians_sparsity() const;
376  * @endcode
377  * The \p has_hessians_sparsity() method is part of the interface for the definition of a problem
378  * (see pagmo::problem).
379  */
380 template <typename T>
381 class override_has_hessians_sparsity
382 {
383     template <typename U>
384     using has_hessians_sparsity_t = decltype(std::declval<const U &>().has_hessians_sparsity());
385     static const bool implementation_defined = std::is_same<bool, detected_t<has_hessians_sparsity_t, T>>::value;
386 
387 public:
388     /// Value of the type trait.
389     static constexpr bool value = implementation_defined;
390 };
391 
392 // Detect the batch_fitness() member function.
393 template <typename T>
394 class has_batch_fitness
395 {
396     template <typename U>
397     using batch_fitness_t = decltype(std::declval<const U &>().batch_fitness(std::declval<const vector_double &>()));
398     static const bool implementation_defined = std::is_same<vector_double, detected_t<batch_fitness_t, T>>::value;
399 
400 public:
401     static constexpr bool value = implementation_defined;
402 };
403 
404 // Detect the has_batch_fitness() member function.
405 template <typename T>
406 class override_has_batch_fitness
407 {
408     template <typename U>
409     using has_batch_fitness_t = decltype(std::declval<const U &>().has_batch_fitness());
410     static const bool implementation_defined = std::is_same<bool, detected_t<has_batch_fitness_t, T>>::value;
411 
412 public:
413     static constexpr bool value = implementation_defined;
414 };
415 
416 namespace detail
417 {
418 
419 // Specialise this to true in order to disable all the UDP checks and mark a type
420 // as a UDP regardless of the features provided by it.
421 // NOTE: this is needed when implementing the machinery for Python problems.
422 // NOTE: leave this as an implementation detail for now.
423 template <typename>
424 struct disable_udp_checks : std::false_type {
425 };
426 
427 } // namespace detail
428 
429 /// Detect user-defined problems (UDP).
430 /**
431  * This type trait will be \p true if \p T is not cv/reference qualified, it is destructible, default, copy and move
432  * constructible, and if it satisfies the pagmo::has_fitness and pagmo::has_bounds type traits.
433  *
434  * Types satisfying this type trait can be used as user-defined problems (UDP) in pagmo::problem.
435  */
436 template <typename T>
437 class is_udp
438 {
439     static const bool implementation_defined
440         = detail::disjunction<detail::conjunction<std::is_same<T, uncvref_t<T>>, std::is_default_constructible<T>,
441                                                   std::is_copy_constructible<T>, std::is_move_constructible<T>,
442                                                   std::is_destructible<T>, has_fitness<T>, has_bounds<T>>,
443                               detail::disable_udp_checks<T>>::value;
444 
445 public:
446     /// Value of the type trait.
447     static constexpr bool value = implementation_defined;
448 };
449 
450 namespace detail
451 {
452 
453 // Helper to check that the problem bounds are valid. This will throw if the bounds
454 // are invalid because of:
455 // - the bounds size is zero,
456 // - inconsistent lengths of the vectors,
457 // - nans in the bounds,
458 // - lower bounds greater than upper bounds.
459 // - integer part larger than bounds size
460 // - integer bounds not integers
461 PAGMO_DLL_PUBLIC void check_problem_bounds(const std::pair<vector_double, vector_double> &bounds,
462                                            vector_double::size_type nix = 0u);
463 
464 PAGMO_DLL_PUBLIC sparsity_pattern dense_hessian(vector_double::size_type);
465 
466 PAGMO_DLL_PUBLIC std::vector<sparsity_pattern> dense_hessians(vector_double::size_type, vector_double::size_type);
467 
468 PAGMO_DLL_PUBLIC sparsity_pattern dense_gradient(vector_double::size_type, vector_double::size_type);
469 
470 struct PAGMO_DLL_PUBLIC_INLINE_CLASS prob_inner_base {
~prob_inner_basepagmo::detail::prob_inner_base471     virtual ~prob_inner_base() {}
472     virtual std::unique_ptr<prob_inner_base> clone() const = 0;
473     virtual vector_double fitness(const vector_double &) const = 0;
474     virtual vector_double batch_fitness(const vector_double &) const = 0;
475     virtual bool has_batch_fitness() const = 0;
476     virtual vector_double gradient(const vector_double &) const = 0;
477     virtual bool has_gradient() const = 0;
478     virtual sparsity_pattern gradient_sparsity() const = 0;
479     virtual bool has_gradient_sparsity() const = 0;
480     virtual std::vector<vector_double> hessians(const vector_double &) const = 0;
481     virtual bool has_hessians() const = 0;
482     virtual std::vector<sparsity_pattern> hessians_sparsity() const = 0;
483     virtual bool has_hessians_sparsity() const = 0;
484     virtual vector_double::size_type get_nobj() const = 0;
485     virtual std::pair<vector_double, vector_double> get_bounds() const = 0;
486     virtual vector_double::size_type get_nec() const = 0;
487     virtual vector_double::size_type get_nic() const = 0;
488     virtual vector_double::size_type get_nix() const = 0;
489     virtual void set_seed(unsigned) = 0;
490     virtual bool has_set_seed() const = 0;
491     virtual std::string get_name() const = 0;
492     virtual std::string get_extra_info() const = 0;
493     virtual thread_safety get_thread_safety() const = 0;
494     virtual std::type_index get_type_index() const = 0;
495     virtual const void *get_ptr() const = 0;
496     virtual void *get_ptr() = 0;
497 
498 private:
499     friend class boost::serialization::access;
500     template <typename Archive>
serializepagmo::detail::prob_inner_base501     void serialize(Archive &, unsigned)
502     {
503     }
504 };
505 
506 template <typename T>
507 struct PAGMO_DLL_PUBLIC_INLINE_CLASS prob_inner final : prob_inner_base {
508     // We just need the def ctor, delete everything else.
509     prob_inner() = default;
510     prob_inner(const prob_inner &) = delete;
511     prob_inner(prob_inner &&) = delete;
512     prob_inner &operator=(const prob_inner &) = delete;
513     prob_inner &operator=(prob_inner &&) = delete;
514     // Constructors from T (copy and move variants).
prob_innerpagmo::detail::prob_inner515     explicit prob_inner(const T &x) : m_value(x) {}
prob_innerpagmo::detail::prob_inner516     explicit prob_inner(T &&x) : m_value(std::move(x)) {}
517     // The clone method, used in the copy constructor of problem.
clonepagmo::detail::prob_inner518     std::unique_ptr<prob_inner_base> clone() const final
519     {
520         return std::make_unique<prob_inner>(m_value);
521     }
522     // Mandatory methods.
fitnesspagmo::detail::prob_inner523     vector_double fitness(const vector_double &dv) const final
524     {
525         return m_value.fitness(dv);
526     }
get_boundspagmo::detail::prob_inner527     std::pair<vector_double, vector_double> get_bounds() const final
528     {
529         return m_value.get_bounds();
530     }
531     // optional methods
batch_fitnesspagmo::detail::prob_inner532     vector_double batch_fitness([[maybe_unused]] const vector_double &dv) const final
533     {
534         if constexpr (pagmo::has_batch_fitness<T>::value) {
535             return m_value.batch_fitness(dv);
536         } else {
537             pagmo_throw(not_implemented_error,
538                         "The batch_fitness() method has been invoked, but it is not implemented in a UDP of type '"
539                             + get_name_impl(m_value) + "'");
540         }
541     }
has_batch_fitnesspagmo::detail::prob_inner542     bool has_batch_fitness() const final
543     {
544         if constexpr (detail::conjunction<pagmo::has_batch_fitness<T>, pagmo::override_has_batch_fitness<T>>::value) {
545             return m_value.has_batch_fitness();
546         } else {
547             // This covers the following cases:
548             // - has batch fitness, no override (returns true),
549             // - no batch fitness, no override (returns false),
550             // - no batch fitness, override (returns false).
551             return pagmo::has_batch_fitness<T>::value;
552         }
553     }
get_nobjpagmo::detail::prob_inner554     vector_double::size_type get_nobj() const final
555     {
556         return get_nobj_impl(m_value);
557     }
gradientpagmo::detail::prob_inner558     vector_double gradient(const vector_double &dv) const final
559     {
560         return gradient_impl(m_value, dv);
561     }
has_gradientpagmo::detail::prob_inner562     bool has_gradient() const final
563     {
564         return has_gradient_impl(m_value);
565     }
gradient_sparsitypagmo::detail::prob_inner566     sparsity_pattern gradient_sparsity() const final
567     {
568         return gradient_sparsity_impl(m_value);
569     }
has_gradient_sparsitypagmo::detail::prob_inner570     bool has_gradient_sparsity() const final
571     {
572         return has_gradient_sparsity_impl(m_value);
573     }
hessianspagmo::detail::prob_inner574     std::vector<vector_double> hessians(const vector_double &dv) const final
575     {
576         return hessians_impl(m_value, dv);
577     }
has_hessianspagmo::detail::prob_inner578     bool has_hessians() const final
579     {
580         return has_hessians_impl(m_value);
581     }
hessians_sparsitypagmo::detail::prob_inner582     std::vector<sparsity_pattern> hessians_sparsity() const final
583     {
584         return hessians_sparsity_impl(m_value);
585     }
has_hessians_sparsitypagmo::detail::prob_inner586     bool has_hessians_sparsity() const final
587     {
588         return has_hessians_sparsity_impl(m_value);
589     }
get_necpagmo::detail::prob_inner590     vector_double::size_type get_nec() const final
591     {
592         return get_nec_impl(m_value);
593     }
get_nicpagmo::detail::prob_inner594     vector_double::size_type get_nic() const final
595     {
596         return get_nic_impl(m_value);
597     }
get_nixpagmo::detail::prob_inner598     vector_double::size_type get_nix() const final
599     {
600         return get_nix_impl(m_value);
601     }
set_seedpagmo::detail::prob_inner602     void set_seed(unsigned seed) final
603     {
604         set_seed_impl(m_value, seed);
605     }
has_set_seedpagmo::detail::prob_inner606     bool has_set_seed() const final
607     {
608         return has_set_seed_impl(m_value);
609     }
get_namepagmo::detail::prob_inner610     std::string get_name() const final
611     {
612         return get_name_impl(m_value);
613     }
get_extra_infopagmo::detail::prob_inner614     std::string get_extra_info() const final
615     {
616         return get_extra_info_impl(m_value);
617     }
get_thread_safetypagmo::detail::prob_inner618     thread_safety get_thread_safety() const final
619     {
620         return get_thread_safety_impl(m_value);
621     }
622     // Implementation of the optional methods.
623     template <typename U, enable_if_t<has_get_nobj<U>::value, int> = 0>
get_nobj_implpagmo::detail::prob_inner624     static vector_double::size_type get_nobj_impl(const U &value)
625     {
626         return value.get_nobj();
627     }
628     template <typename U, enable_if_t<!has_get_nobj<U>::value, int> = 0>
get_nobj_implpagmo::detail::prob_inner629     static vector_double::size_type get_nobj_impl(const U &)
630     {
631         return 1u;
632     }
633     template <typename U, enable_if_t<pagmo::has_gradient<U>::value, int> = 0>
gradient_implpagmo::detail::prob_inner634     static vector_double gradient_impl(const U &value, const vector_double &dv)
635     {
636         return value.gradient(dv);
637     }
638     template <typename U, enable_if_t<!pagmo::has_gradient<U>::value, int> = 0>
gradient_implpagmo::detail::prob_inner639     [[noreturn]] static vector_double gradient_impl(const U &value, const vector_double &)
640     {
641         pagmo_throw(not_implemented_error,
642                     "The gradient has been requested, but it is not implemented in a UDP of type '"
643                         + get_name_impl(value) + "'");
644     }
645     template <typename U,
646               enable_if_t<detail::conjunction<pagmo::has_gradient<U>, pagmo::override_has_gradient<U>>::value, int> = 0>
has_gradient_implpagmo::detail::prob_inner647     static bool has_gradient_impl(const U &p)
648     {
649         return p.has_gradient();
650     }
651     template <typename U, enable_if_t<detail::conjunction<pagmo::has_gradient<U>,
652                                                           detail::negation<pagmo::override_has_gradient<U>>>::value,
653                                       int> = 0>
has_gradient_implpagmo::detail::prob_inner654     static bool has_gradient_impl(const U &)
655     {
656         return true;
657     }
658     template <typename U, enable_if_t<!pagmo::has_gradient<U>::value, int> = 0>
has_gradient_implpagmo::detail::prob_inner659     static bool has_gradient_impl(const U &)
660     {
661         return false;
662     }
663     template <typename U, enable_if_t<pagmo::has_gradient_sparsity<U>::value, int> = 0>
gradient_sparsity_implpagmo::detail::prob_inner664     static sparsity_pattern gradient_sparsity_impl(const U &p)
665     {
666         return p.gradient_sparsity();
667     }
668     template <typename U, enable_if_t<!pagmo::has_gradient_sparsity<U>::value, int> = 0>
gradient_sparsity_implpagmo::detail::prob_inner669     [[noreturn]] static sparsity_pattern gradient_sparsity_impl(const U &) // LCOV_EXCL_LINE
670     {
671         // NOTE: we should never end up here. gradient_sparsity() is called only if m_has_gradient_sparsity
672         // in the problem is set to true, and m_has_gradient_sparsity is unconditionally false if the UDP
673         // does not implement gradient_sparsity() (see implementation of the three overloads below).
674         assert(false); // LCOV_EXCL_LINE
675         std::terminate();
676     }
677     template <typename U, enable_if_t<detail::conjunction<pagmo::has_gradient_sparsity<U>,
678                                                           pagmo::override_has_gradient_sparsity<U>>::value,
679                                       int> = 0>
has_gradient_sparsity_implpagmo::detail::prob_inner680     static bool has_gradient_sparsity_impl(const U &p)
681     {
682         return p.has_gradient_sparsity();
683     }
684     template <typename U,
685               enable_if_t<detail::conjunction<pagmo::has_gradient_sparsity<U>,
686                                               detail::negation<pagmo::override_has_gradient_sparsity<U>>>::value,
687                           int> = 0>
has_gradient_sparsity_implpagmo::detail::prob_inner688     static bool has_gradient_sparsity_impl(const U &)
689     {
690         return true;
691     }
692     template <typename U, enable_if_t<!pagmo::has_gradient_sparsity<U>::value, int> = 0>
has_gradient_sparsity_implpagmo::detail::prob_inner693     static bool has_gradient_sparsity_impl(const U &)
694     {
695         return false;
696     }
697     template <typename U, enable_if_t<pagmo::has_hessians<U>::value, int> = 0>
hessians_implpagmo::detail::prob_inner698     static std::vector<vector_double> hessians_impl(const U &value, const vector_double &dv)
699     {
700         return value.hessians(dv);
701     }
702     template <typename U, enable_if_t<!pagmo::has_hessians<U>::value, int> = 0>
hessians_implpagmo::detail::prob_inner703     [[noreturn]] static std::vector<vector_double> hessians_impl(const U &value, const vector_double &)
704     {
705         pagmo_throw(not_implemented_error,
706                     "The hessians have been requested, but they are not implemented in a UDP of type '"
707                         + get_name_impl(value) + "'");
708     }
709     template <typename U,
710               enable_if_t<detail::conjunction<pagmo::has_hessians<U>, pagmo::override_has_hessians<U>>::value, int> = 0>
has_hessians_implpagmo::detail::prob_inner711     static bool has_hessians_impl(const U &p)
712     {
713         return p.has_hessians();
714     }
715     template <typename U, enable_if_t<detail::conjunction<pagmo::has_hessians<U>,
716                                                           detail::negation<pagmo::override_has_hessians<U>>>::value,
717                                       int> = 0>
has_hessians_implpagmo::detail::prob_inner718     static bool has_hessians_impl(const U &)
719     {
720         return true;
721     }
722     template <typename U, enable_if_t<!pagmo::has_hessians<U>::value, int> = 0>
has_hessians_implpagmo::detail::prob_inner723     static bool has_hessians_impl(const U &)
724     {
725         return false;
726     }
727     template <typename U, enable_if_t<pagmo::has_hessians_sparsity<U>::value, int> = 0>
hessians_sparsity_implpagmo::detail::prob_inner728     static std::vector<sparsity_pattern> hessians_sparsity_impl(const U &value)
729     {
730         return value.hessians_sparsity();
731     }
732     template <typename U, enable_if_t<!pagmo::has_hessians_sparsity<U>::value, int> = 0>
hessians_sparsity_implpagmo::detail::prob_inner733     [[noreturn]] static std::vector<sparsity_pattern> hessians_sparsity_impl(const U &) // LCOV_EXCL_LINE
734     {
735         // NOTE: we should never end up here. hessians_sparsity() is called only if m_has_hessians_sparsity
736         // in the problem is set to true, and m_has_hessians_sparsity is unconditionally false if the UDP
737         // does not implement hessians_sparsity() (see implementation of the three overloads below).
738         assert(false); // LCOV_EXCL_LINE
739         std::terminate();
740     }
741     template <typename U, enable_if_t<detail::conjunction<pagmo::has_hessians_sparsity<U>,
742                                                           pagmo::override_has_hessians_sparsity<U>>::value,
743                                       int> = 0>
has_hessians_sparsity_implpagmo::detail::prob_inner744     static bool has_hessians_sparsity_impl(const U &p)
745     {
746         return p.has_hessians_sparsity();
747     }
748     template <typename U,
749               enable_if_t<detail::conjunction<pagmo::has_hessians_sparsity<U>,
750                                               detail::negation<pagmo::override_has_hessians_sparsity<U>>>::value,
751                           int> = 0>
has_hessians_sparsity_implpagmo::detail::prob_inner752     static bool has_hessians_sparsity_impl(const U &)
753     {
754         return true;
755     }
756     template <typename U, enable_if_t<!pagmo::has_hessians_sparsity<U>::value, int> = 0>
has_hessians_sparsity_implpagmo::detail::prob_inner757     static bool has_hessians_sparsity_impl(const U &)
758     {
759         return false;
760     }
761     template <typename U, enable_if_t<has_e_constraints<U>::value, int> = 0>
get_nec_implpagmo::detail::prob_inner762     static vector_double::size_type get_nec_impl(const U &value)
763     {
764         return value.get_nec();
765     }
766     template <typename U, enable_if_t<!has_e_constraints<U>::value, int> = 0>
get_nec_implpagmo::detail::prob_inner767     static vector_double::size_type get_nec_impl(const U &)
768     {
769         return 0u;
770     }
771     template <typename U, enable_if_t<has_i_constraints<U>::value, int> = 0>
get_nic_implpagmo::detail::prob_inner772     static vector_double::size_type get_nic_impl(const U &value)
773     {
774         return value.get_nic();
775     }
776     template <typename U, enable_if_t<!has_i_constraints<U>::value, int> = 0>
get_nic_implpagmo::detail::prob_inner777     static vector_double::size_type get_nic_impl(const U &)
778     {
779         return 0u;
780     }
781     template <typename U, enable_if_t<has_integer_part<U>::value, int> = 0>
get_nix_implpagmo::detail::prob_inner782     static vector_double::size_type get_nix_impl(const U &value)
783     {
784         return value.get_nix();
785     }
786     template <typename U, enable_if_t<!has_integer_part<U>::value, int> = 0>
get_nix_implpagmo::detail::prob_inner787     static vector_double::size_type get_nix_impl(const U &)
788     {
789         return 0u;
790     }
791     template <typename U, enable_if_t<pagmo::has_set_seed<U>::value, int> = 0>
set_seed_implpagmo::detail::prob_inner792     static void set_seed_impl(U &value, unsigned seed)
793     {
794         value.set_seed(seed);
795     }
796     template <typename U, enable_if_t<!pagmo::has_set_seed<U>::value, int> = 0>
set_seed_implpagmo::detail::prob_inner797     [[noreturn]] static void set_seed_impl(U &value, unsigned)
798     {
799         pagmo_throw(not_implemented_error,
800                     "The set_seed() method has been invoked, but it is not implemented in a UDP of type '"
801                         + get_name_impl(value) + "'");
802     }
803     template <typename U,
804               enable_if_t<detail::conjunction<pagmo::has_set_seed<U>, override_has_set_seed<U>>::value, int> = 0>
has_set_seed_implpagmo::detail::prob_inner805     static bool has_set_seed_impl(const U &p)
806     {
807         return p.has_set_seed();
808     }
809     template <
810         typename U,
811         enable_if_t<detail::conjunction<pagmo::has_set_seed<U>, detail::negation<override_has_set_seed<U>>>::value,
812                     int> = 0>
has_set_seed_implpagmo::detail::prob_inner813     static bool has_set_seed_impl(const U &)
814     {
815         return true;
816     }
817     template <typename U, enable_if_t<!pagmo::has_set_seed<U>::value, int> = 0>
has_set_seed_implpagmo::detail::prob_inner818     static bool has_set_seed_impl(const U &)
819     {
820         return false;
821     }
822     template <typename U, enable_if_t<has_name<U>::value, int> = 0>
get_name_implpagmo::detail::prob_inner823     static std::string get_name_impl(const U &value)
824     {
825         return value.get_name();
826     }
827     template <typename U, enable_if_t<!has_name<U>::value, int> = 0>
get_name_implpagmo::detail::prob_inner828     static std::string get_name_impl(const U &)
829     {
830         return detail::type_name<U>();
831     }
832     template <typename U, enable_if_t<has_extra_info<U>::value, int> = 0>
get_extra_info_implpagmo::detail::prob_inner833     static std::string get_extra_info_impl(const U &value)
834     {
835         return value.get_extra_info();
836     }
837     template <typename U, enable_if_t<!has_extra_info<U>::value, int> = 0>
get_extra_info_implpagmo::detail::prob_inner838     static std::string get_extra_info_impl(const U &)
839     {
840         return "";
841     }
842     template <typename U, enable_if_t<has_get_thread_safety<U>::value, int> = 0>
get_thread_safety_implpagmo::detail::prob_inner843     static thread_safety get_thread_safety_impl(const U &value)
844     {
845         return value.get_thread_safety();
846     }
847     template <typename U, enable_if_t<!has_get_thread_safety<U>::value, int> = 0>
get_thread_safety_implpagmo::detail::prob_inner848     static thread_safety get_thread_safety_impl(const U &)
849     {
850         return thread_safety::basic;
851     }
852     // Get the type at runtime.
get_type_indexpagmo::detail::prob_inner853     std::type_index get_type_index() const final
854     {
855         return std::type_index(typeid(T));
856     }
857     // Raw getters for the internal instance.
get_ptrpagmo::detail::prob_inner858     const void *get_ptr() const final
859     {
860         return &m_value;
861     }
get_ptrpagmo::detail::prob_inner862     void *get_ptr() final
863     {
864         return &m_value;
865     }
866 
867 private:
868     // Serialization.
869     friend class boost::serialization::access;
870     template <typename Archive>
serializepagmo::detail::prob_inner871     void serialize(Archive &ar, unsigned)
872     {
873         detail::archive(ar, boost::serialization::base_object<prob_inner_base>(*this), m_value);
874     }
875 
876 public:
877     T m_value;
878 };
879 
880 } // namespace detail
881 
882 } // namespace pagmo
883 
884 // Disable Boost.Serialization tracking for the implementation
885 // details of problem.
886 BOOST_CLASS_TRACKING(pagmo::detail::prob_inner_base, boost::serialization::track_never)
887 
888 namespace pagmo
889 {
890 
891 // Fwd declare for the declarations below.
892 class PAGMO_DLL_PUBLIC problem;
893 
894 // Streaming operator
895 PAGMO_DLL_PUBLIC std::ostream &operator<<(std::ostream &, const problem &);
896 
897 namespace detail
898 {
899 
900 // These are internal private helpers which are used both in problem
901 // and elsewhere. Hence, decouple them from the problem class and provide
902 // them as free functions.
903 PAGMO_DLL_PUBLIC void prob_check_dv(const problem &, const double *, vector_double::size_type);
904 PAGMO_DLL_PUBLIC void prob_check_fv(const problem &, const double *, vector_double::size_type);
905 PAGMO_DLL_PUBLIC vector_double prob_invoke_mem_batch_fitness(const problem &, const vector_double &, bool);
906 
907 } // namespace detail
908 
909 /// Problem class.
910 /**
911  * \image html prob_no_text.png
912  *
913  * This class represents a generic *mathematical programming* or *evolutionary optimization* problem in the form:
914  * \f[
915  * \begin{array}{rl}
916  * \mbox{find:}      & \mathbf {lb} \le \mathbf x \le \mathbf{ub}\\
917  * \mbox{to minimize: } & \mathbf f(\mathbf x, s) \in \mathbb R^{n_{obj}}\\
918  * \mbox{subject to:} & \mathbf {c}_e(\mathbf x, s) = 0 \\
919  *                    & \mathbf {c}_i(\mathbf x, s) \le 0
920  * \end{array}
921  * \f]
922  *
923  * where \f$\mathbf x \in \mathbb R^{n_{cx}} \times  \mathbb Z^{n_{ix}}\f$ is called *decision vector* or
924  * *chromosome*, and is made of \f$n_{cx}\f$ real numbers and \f$n_{ix}\f$ integers (all represented as doubles). The
925  * total problem dimension is then indicated with \f$n_x = n_{cx} + n_{ix}\f$. \f$\mathbf{lb}, \mathbf{ub} \in
926  * \mathbb R^{n_{cx}} \times  \mathbb Z^{n_{ix}}\f$ are the *box-bounds*, \f$ \mathbf f: \mathbb R^{n_{cx}} \times
927  * \mathbb Z^{n_{ix}} \rightarrow \mathbb R^{n_{obj}}\f$ define the *objectives*, \f$ \mathbf c_e:  \mathbb R^{n_{cx}}
928  * \times  \mathbb Z^{n_{ix}} \rightarrow \mathbb R^{n_{ec}}\f$ are non linear *equality constraints*, and \f$ \mathbf
929  * c_i:  \mathbb R^{n_{cx}} \times  \mathbb Z^{n_{ix}} \rightarrow \mathbb R^{n_{ic}}\f$ are non linear *inequality
930  * constraints*. Note that the objectives and constraints may also depend from an added value \f$s\f$ seeding the
931  * values of any number of stochastic variables. This allows also for stochastic programming tasks to be represented by
932  * this class. A tolerance is also considered for all constraints and set, by default, to zero. It can be modified
933  * via the problem::set_c_tol() method.
934  *
935  * In order to define an optimizaztion problem in pagmo, the user must first define a class
936  * (or a struct) whose methods describe the properties of the problem and allow to compute
937  * the objective function, the gradient, the constraints, etc. In pagmo, we refer to such
938  * a class as a **user-defined problem**, or UDP for short. Once defined and instantiated,
939  * a UDP can then be used to construct an instance of this class, pagmo::problem, which
940  * provides a generic interface to optimization problems.
941  *
942  * Every UDP must implement at least the following two methods:
943  * @code{.unparsed}
944  * vector_double fitness(const vector_double &) const;
945  * std::pair<vector_double, vector_double> get_bounds() const;
946  * @endcode
947  *
948  * The <tt>%fitness()</tt> method is expected to return the fitness of the input decision vector (
949  * concatenating the objectives, the equality and the inequality constraints), while
950  * <tt>%get_bounds()</tt> is expected to return the box bounds of the problem,
951  * \f$(\mathbf{lb}, \mathbf{ub})\f$, which also implicitly define the dimension of the problem.
952  * The <tt>%fitness()</tt> and <tt>%get_bounds()</tt> methods of the UDP are accessible from the corresponding
953  * problem::fitness() and problem::get_bounds() methods (see their documentation for details).
954  * In addition to providing the above methods, a UDP must also be default, copy and move constructible.
955  *
956  * The two mandatory methods above allow to define a continuous, single objective, deterministic, derivative-free,
957  * unconstrained optimization problem. In order to consider more complex cases, the UDP may implement one or more of the
958  * following methods:
959  * @code{.unparsed}
960  * vector_double::size_type get_nobj() const;
961  * vector_double::size_type get_nec() const;
962  * vector_double::size_type get_nic() const;
963  * vector_double::size_type get_nix() const;
964  * vector_double batch_fitness(const vector_double &) const;
965  * bool has_batch_fitness() const;
966  * bool has_gradient() const;
967  * vector_double gradient(const vector_double &) const;
968  * bool has_gradient_sparsity() const;
969  * sparsity_pattern gradient_sparsity() const;
970  * bool has_hessians() const;
971  * std::vector<vector_double> hessians(const vector_double &) const;
972  * bool has_hessians_sparsity() const;
973  * std::vector<sparsity_pattern> hessians_sparsity() const;
974  * bool has_set_seed() const;
975  * void set_seed(unsigned);
976  * std::string get_name() const;
977  * std::string get_extra_info() const;
978  * thread_safety get_thread_safety() const;
979  * @endcode
980  *
981  * See the documentation of the corresponding methods in this class for details on how the optional
982  * methods in the UDP are used by pagmo::problem.
983  *
984  * \verbatim embed:rst:leading-asterisk
985  * .. warning::
986  *
987  *    The only operations allowed on a moved-from :cpp:class:`pagmo::problem` are destruction,
988  *    assignment, and the invocation of the :cpp:func:`~pagmo::problem::is_valid()` member function.
989  *    Any other operation will result in undefined behaviour.
990  *
991  * \endverbatim
992  */
993 class PAGMO_DLL_PUBLIC problem
994 {
995     // Make friends with the streaming operator, which needs access
996     // to the internals.
997     friend PAGMO_DLL_PUBLIC std::ostream &operator<<(std::ostream &, const problem &);
998 
999     // Enable the generic ctor only if T is not a problem (after removing
1000     // const/reference qualifiers), and if T is a udp.
1001     template <typename T>
1002     using generic_ctor_enabler = enable_if_t<
1003         detail::conjunction<detail::negation<std::is_same<problem, uncvref_t<T>>>, is_udp<uncvref_t<T>>>::value, int>;
1004 
1005 public:
1006     // Default constructor.
1007     problem();
1008 
1009 private:
1010     void generic_ctor_impl();
1011 
1012 public:
1013     /// Constructor from a user-defined problem of type \p T
1014     /**
1015      * \verbatim embed:rst:leading-asterisk
1016      * .. note::
1017      *
1018      *    This constructor is not enabled if, after the removal of cv and reference qualifiers,
1019      *    ``T`` is of type :cpp:class:`pagmo::problem` (that is, this constructor does not compete with the copy/move
1020      *    constructors of :cpp:class:`pagmo::problem`), or if ``T`` does not satisfy :cpp:class:`pagmo::is_udp`.
1021      *
1022      * \endverbatim
1023      *
1024      * This constructor will construct a pagmo::problem from the UDP (user-defined problem) \p x of type \p T. In order
1025      * for the construction to be successful, the UDP must implement a minimal set of methods,
1026      * as described in the documentation of pagmo::problem. The constructor will examine the properties of \p x and
1027      * store them as data members of \p this.
1028      *
1029      * @param x the UDP.
1030      *
1031      * @throws std::invalid_argument in the following cases:
1032      * - the number of objectives of the UDP is zero,
1033      * - the number of objectives, equality or inequality constraints is larger than an implementation-defined value,
1034      * - the problem bounds are invalid (e.g., they contain NaNs, the dimensionality of the lower bounds is
1035      *   different from the dimensionality of the upper bounds, the bounds relative to the integer part are not
1036      *   integers, etc. - note that infinite bounds are allowed),
1037      * - the <tt>%gradient_sparsity()</tt> and <tt>%hessians_sparsity()</tt> methods of the UDP fail basic sanity checks
1038      *   (e.g., they return vectors with repeated indices, they contain indices exceeding the problem's dimensions,
1039      *   etc.).
1040      * - the integer part of the problem is larger than the problem size.
1041      * @throws unspecified any exception thrown by methods of the UDP invoked during construction or by memory errors
1042      * in strings and standard containers.
1043      */
1044     template <typename T, generic_ctor_enabler<T> = 0>
problem(T && x)1045     explicit problem(T &&x)
1046         : m_ptr(std::make_unique<detail::prob_inner<uncvref_t<T>>>(std::forward<T>(x))), m_fevals(0u), m_gevals(0u),
1047           m_hevals(0u)
1048     {
1049         generic_ctor_impl();
1050     }
1051 
1052     // Copy constructor.
1053     problem(const problem &);
1054     // Move constructor.
1055     problem(problem &&) noexcept;
1056     // Move assignment operator
1057     problem &operator=(problem &&) noexcept;
1058     // Copy assignment operator
1059     problem &operator=(const problem &);
1060     /// Assignment from a user-defined problem of type \p T
1061     /**
1062      * \verbatim embed:rst:leading-asterisk
1063      * .. note::
1064      *
1065      *    This operator is not enabled if, after the removal of cv and reference qualifiers,
1066      *    ``T`` is of type :cpp:class:`pagmo::problem` (that is, this operator does not compete with the copy/move
1067      *    assignment operators of :cpp:class:`pagmo::problem`), or if ``T`` does not satisfy :cpp:class:`pagmo::is_udp`.
1068      *
1069      * \endverbatim
1070      *
1071      * This operator will set the internal UDP to ``x`` by constructing a pagmo::problem from ``x``, and then
1072      * move-assigning the result to ``this``.
1073      *
1074      * @param x the UDP.
1075      *
1076      * @return a reference to ``this``.
1077      *
1078      * @throws unspecified any exception thrown by the constructor from UDP.
1079      */
1080     template <typename T, generic_ctor_enabler<T> = 0>
operator =(T && x)1081     problem &operator=(T &&x)
1082     {
1083         return (*this) = problem(std::forward<T>(x));
1084     }
1085 
1086     /// Extract a const pointer to the UDP used for construction.
1087     /**
1088      * This method will extract a const pointer to the internal instance of the UDP. If \p T is not the same type
1089      * as the UDP used during construction (after removal of cv and reference qualifiers), this method will
1090      * return \p nullptr.
1091      *
1092      * \verbatim embed:rst:leading-asterisk
1093      * .. note::
1094      *
1095      *    The returned value is a raw non-owning pointer: the lifetime of the pointee is tied to the lifetime
1096      *    of ``this``, and ``delete`` must never be called on the pointer.
1097      *
1098      * \endverbatim
1099      *
1100      * @return a const pointer to the internal UDP, or \p nullptr
1101      * if \p T does not correspond exactly to the original UDP type used
1102      * in the constructor.
1103      */
1104     template <typename T>
extract() const1105     const T *extract() const noexcept
1106     {
1107 #if defined(PAGMO_PREFER_TYPEID_NAME_EXTRACT)
1108         return detail::typeid_name_extract<T>(*this);
1109 #else
1110         auto p = dynamic_cast<const detail::prob_inner<T> *>(ptr());
1111         return p == nullptr ? nullptr : &(p->m_value);
1112 #endif
1113     }
1114 
1115     /// Extract a pointer to the UDP used for construction.
1116     /**
1117      * This method will extract a pointer to the internal instance of the UDP. If \p T is not the same type
1118      * as the UDP used during construction (after removal of cv and reference qualifiers), this method will
1119      * return \p nullptr.
1120      *
1121      * \verbatim embed:rst:leading-asterisk
1122      * .. note::
1123      *
1124      *    The returned value is a raw non-owning pointer: the lifetime of the pointee is tied to the lifetime
1125      *    of ``this``, and ``delete`` must never be called on the pointer.
1126      *
1127      * .. note::
1128      *
1129      *    The ability to extract a mutable pointer is provided only in order to allow to call non-const
1130      *    methods on the internal UDP instance. Assigning a new UDP via this pointer is undefined behaviour.
1131      *
1132      * \endverbatim
1133      *
1134      * @return a pointer to the internal UDP, or \p nullptr
1135      * if \p T does not correspond exactly to the original UDP type used
1136      * in the constructor.
1137      */
1138     template <typename T>
extract()1139     T *extract() noexcept
1140     {
1141 #if defined(PAGMO_PREFER_TYPEID_NAME_EXTRACT)
1142         return detail::typeid_name_extract<T>(*this);
1143 #else
1144         auto p = dynamic_cast<detail::prob_inner<T> *>(ptr());
1145         return p == nullptr ? nullptr : &(p->m_value);
1146 #endif
1147     }
1148 
1149     /// Check if the UDP used for construction is of type \p T.
1150     /**
1151      * @return \p true if the UDP used for construction is of type \p T, \p false otherwise.
1152      */
1153     template <typename T>
is() const1154     bool is() const noexcept
1155     {
1156         return extract<T>() != nullptr;
1157     }
1158 
1159     // Fitness.
1160     vector_double fitness(const vector_double &) const;
1161 
1162 private:
1163 #if !defined(PAGMO_DOXYGEN_INVOKED)
1164     // Make friends with the batch_fitness() invocation helper.
1165     friend PAGMO_DLL_PUBLIC vector_double detail::prob_invoke_mem_batch_fitness(const problem &, const vector_double &,
1166                                                                                 bool);
1167 #endif
1168 
1169 public:
1170     // Batch fitness.
1171     vector_double batch_fitness(const vector_double &) const;
1172 
1173     /// Check if the UDP is capable of fitness evaluation in batch mode.
1174     /**
1175      * This method will return \p true if the UDP is capable of fitness evaluation in batch mode, \p false otherwise.
1176      *
1177      * \verbatim embed:rst:leading-asterisk
1178      * The batch fitness evaluation capability of the UDP is determined as follows:
1179      *
1180      * * if the UDP does not satisfy :cpp:class:`pagmo::has_batch_fitness`, then this method will always return
1181      *   ``false``;
1182      * * if the UDP satisfies :cpp:class:`pagmo::has_batch_fitness` but it does not satisfy
1183      *   :cpp:class:`pagmo::override_has_batch_fitness`, then this method will always return ``true``;
1184      * * if the UDP satisfies both :cpp:class:`pagmo::has_batch_fitness` and
1185      *   :cpp:class:`pagmo::override_has_batch_fitness`, then this method will return the output of the
1186      *   ``has_batch_fitness()`` method of the UDP.
1187      *
1188      * \endverbatim
1189      *
1190      * @return a flag signalling the availability of fitness evaluation in batch mode in the UDP.
1191      */
has_batch_fitness() const1192     bool has_batch_fitness() const
1193     {
1194         return m_has_batch_fitness;
1195     }
1196 
1197     // Gradient.
1198     vector_double gradient(const vector_double &) const;
1199 
1200     /// Check if the gradient is available in the UDP.
1201     /**
1202      * This method will return \p true if the gradient is available in the UDP, \p false otherwise.
1203      *
1204      * The availability of the gradient is determined as follows:
1205      * - if the UDP does not satisfy pagmo::has_gradient, then this method will always return \p false;
1206      * - if the UDP satisfies pagmo::has_gradient but it does not satisfy pagmo::override_has_gradient,
1207      *   then this method will always return \p true;
1208      * - if the UDP satisfies both pagmo::has_gradient and pagmo::override_has_gradient,
1209      *   then this method will return the output of the <tt>%has_gradient()</tt> method of the UDP.
1210      *
1211      * @return a flag signalling the availability of the gradient in the UDP.
1212      */
has_gradient() const1213     bool has_gradient() const
1214     {
1215         return m_has_gradient;
1216     }
1217 
1218     // Gradient sparsity pattern.
1219     sparsity_pattern gradient_sparsity() const;
1220 
1221     /// Check if the gradient sparsity is available in the UDP.
1222     /**
1223      * This method will return \p true if the gradient sparsity is available in the UDP, \p false otherwise.
1224      *
1225      * The availability of the gradient sparsity is determined as follows:
1226      * - if the UDP does not satisfy pagmo::has_gradient_sparsity, then this method will always return \p false;
1227      * - if the UDP satisfies pagmo::has_gradient_sparsity but it does not satisfy
1228      *   pagmo::override_has_gradient_sparsity, then this method will always return \p true;
1229      * - if the UDP satisfies both pagmo::has_gradient_sparsity and pagmo::override_has_gradient_sparsity,
1230      *   then this method will return the output of the <tt>%has_gradient_sparsity()</tt> method of the UDP.
1231      *
1232      * \verbatim embed:rst:leading-asterisk
1233      * .. note::
1234      *
1235      *    Regardless of what this method returns, the :cpp:func:`problem::gradient_sparsity()` method will always return
1236      *    a sparsity pattern: if the UDP does not provide the gradient sparsity, PaGMO will assume that the sparsity
1237      *    pattern of the gradient is dense. See :cpp:func:`problem::gradient_sparsity()` for more details.
1238      *
1239      * \endverbatim
1240      *
1241      * @return a flag signalling the availability of the gradient sparsity in the UDP.
1242      */
has_gradient_sparsity() const1243     bool has_gradient_sparsity() const
1244     {
1245         return m_has_gradient_sparsity;
1246     }
1247 
1248     // Hessians.
1249     std::vector<vector_double> hessians(const vector_double &) const;
1250 
1251     /// Check if the hessians are available in the UDP.
1252     /**
1253      * This method will return \p true if the hessians are available in the UDP, \p false otherwise.
1254      *
1255      * The availability of the hessians is determined as follows:
1256      * - if the UDP does not satisfy pagmo::has_hessians, then this method will always return \p false;
1257      * - if the UDP satisfies pagmo::has_hessians but it does not satisfy pagmo::override_has_hessians,
1258      *   then this method will always return \p true;
1259      * - if the UDP satisfies both pagmo::has_hessians and pagmo::override_has_hessians,
1260      *   then this method will return the output of the <tt>%has_hessians()</tt> method of the UDP.
1261      *
1262      * @return a flag signalling the availability of the hessians in the UDP.
1263      */
has_hessians() const1264     bool has_hessians() const
1265     {
1266         return m_has_hessians;
1267     }
1268 
1269     // Hessians sparsity pattern.
1270     std::vector<sparsity_pattern> hessians_sparsity() const;
1271 
1272     /// Check if the hessians sparsity is available in the UDP.
1273     /**
1274      * This method will return \p true if the hessians sparsity is available in the UDP, \p false otherwise.
1275      *
1276      * The availability of the hessians sparsity is determined as follows:
1277      * - if the UDP does not satisfy pagmo::has_hessians_sparsity, then this method will always return \p false;
1278      * - if the UDP satisfies pagmo::has_hessians_sparsity but it does not satisfy
1279      *   pagmo::override_has_hessians_sparsity, then this method will always return \p true;
1280      * - if the UDP satisfies both pagmo::has_hessians_sparsity and pagmo::override_has_hessians_sparsity,
1281      *   then this method will return the output of the <tt>%has_hessians_sparsity()</tt> method of the UDP.
1282      *
1283      * \verbatim embed:rst:leading-asterisk
1284      * .. note::
1285      *
1286      *    Regardless of what this method returns, the :cpp:func:`problem::hessians_sparsity()` method will always return
1287      *    a vector of sparsity patterns: if the UDP does not provide the hessians sparsity, PaGMO will assume that the
1288      *    sparsity pattern of the hessians is dense. See :cpp:func:`problem::hessians_sparsity()` for more details.
1289      *
1290      * \endverbatim
1291      *
1292      * @return a flag signalling the availability of the hessians sparsity in the UDP.
1293      */
has_hessians_sparsity() const1294     bool has_hessians_sparsity() const
1295     {
1296         return m_has_hessians_sparsity;
1297     }
1298 
1299     /// Number of objectives.
1300     /**
1301      * This method will return \f$ n_{obj}\f$, the number of objectives of the optimization
1302      * problem. If the UDP satisfies the pagmo::has_get_nobj type traits, then the output of
1303      * its <tt>%get_nobj()</tt> method will be returned. Otherwise, this method will return 1.
1304      *
1305      * @return the number of objectives of the problem.
1306      */
get_nobj() const1307     vector_double::size_type get_nobj() const
1308     {
1309         return m_nobj;
1310     }
1311 
1312     /// Dimension.
1313     /**
1314      * @return \f$ n_{x}\f$, the dimension of the problem as established
1315      * by the length of the bounds returned by problem::get_bounds().
1316      */
get_nx() const1317     vector_double::size_type get_nx() const
1318     {
1319         return m_lb.size();
1320     }
1321 
1322     /// Integer Dimension.
1323     /**
1324      * This method will return \f$ n_{ix} \f$, the dimension of the integer part of the problem.
1325      * If the UDP satisfies pagmo::has_integer_part, then the output of
1326      * its <tt>%get_nix()</tt> method will be returned. Otherwise, this method will return 0.
1327      *
1328      * @return \f$ n_{ix}\f$, the integer dimension of the problem.
1329      */
get_nix() const1330     vector_double::size_type get_nix() const
1331     {
1332         return m_nix;
1333     }
1334 
1335     /// Continuous Dimension.
1336     /**
1337      * @return \f$ n_{cx}\f$, the continuous dimension of the problem as established
1338      * by the relation \f$n_{cx} = n_{x} - n_{ix} \f$.
1339      *
1340      * @return \f$ n_{cx}\f$, the continuous dimension of the problem.
1341      */
get_ncx() const1342     vector_double::size_type get_ncx() const
1343     {
1344         return get_nx() - m_nix;
1345     }
1346 
1347     /// Fitness dimension.
1348     /**
1349      * @return \f$ n_{f}\f$, the dimension of the fitness, which is the
1350      * sum of \f$n_{obj}\f$, \f$n_{ec}\f$ and \f$n_{ic}\f$
1351      */
get_nf() const1352     vector_double::size_type get_nf() const
1353     {
1354         return m_nobj + m_nic + m_nec;
1355     }
1356 
1357     // Box-bounds.
1358     std::pair<vector_double, vector_double> get_bounds() const;
1359 
1360     /// Lower bounds.
1361     /**
1362      * @return a const reference to the vector of lower box bounds for this problem.
1363      */
get_lb() const1364     const vector_double &get_lb() const
1365     {
1366         return m_lb;
1367     }
1368 
1369     /// Upper bounds.
1370     /**
1371      * @return a const reference to the vector of upper box bounds for this problem.
1372      */
get_ub() const1373     const vector_double &get_ub() const
1374     {
1375         return m_ub;
1376     }
1377 
1378     /// Number of equality constraints.
1379     /**
1380      * This method will return \f$ n_{ec} \f$, the number of equality constraints of the problem.
1381      * If the UDP satisfies pagmo::has_e_constraints, then the output of
1382      * its <tt>%get_nec()</tt> method will be returned. Otherwise, this method will return 0.
1383      *
1384      * @return the number of equality constraints of the problem.
1385      */
get_nec() const1386     vector_double::size_type get_nec() const
1387     {
1388         return m_nec;
1389     }
1390 
1391     /// Number of inequality constraints.
1392     /**
1393      * This method will return \f$ n_{ic} \f$, the number of inequality constraints of the problem.
1394      * If the UDP satisfies pagmo::has_i_constraints, then the output of
1395      * its <tt>%get_nic()</tt> method will be returned. Otherwise, this method will return 0.
1396      *
1397      * @return the number of inequality constraints of the problem.
1398      */
get_nic() const1399     vector_double::size_type get_nic() const
1400     {
1401         return m_nic;
1402     }
1403 
1404     // Set the constraint tolerance (from a vector of doubles).
1405     void set_c_tol(const vector_double &);
1406     // Set the constraint tolerance (from a single double value).
1407     void set_c_tol(double);
1408     /// Get the constraint tolerance.
1409     /**
1410      * This method will return a vector of dimension \f$n_{ec} + n_{ic}\f$ containing tolerances to
1411      * be used when checking constraint feasibility. The constraint tolerance is zero-filled upon problem
1412      * construction, and it can be set via problem::set_c_tol().
1413      *
1414      * @return a pagmo::vector_double containing the tolerances to use when
1415      * checking for constraint feasibility.
1416      */
get_c_tol() const1417     vector_double get_c_tol() const
1418     {
1419         return m_c_tol;
1420     }
1421 
1422     /// Total number of constraints
1423     /**
1424      * @return the sum of the output of get_nic() and get_nec() (i.e., the total number of constraints).
1425      */
get_nc() const1426     vector_double::size_type get_nc() const
1427     {
1428         return m_nec + m_nic;
1429     }
1430 
1431     /// Number of fitness evaluations.
1432     /**
1433      * Each time a call to problem::fitness() successfully completes, an internal counter is increased by one.
1434      * The counter is initialised to zero upon problem construction and it is never reset. Copy and move operations
1435      * copy the counter as well.
1436      *
1437      * @return the number of times problem::fitness() was successfully called.
1438      */
get_fevals() const1439     unsigned long long get_fevals() const
1440     {
1441         return m_fevals.load(std::memory_order_relaxed);
1442     }
1443 
1444     /// Increment the number of fitness evaluations.
1445     /**
1446      * This method will increase the internal counter of fitness evaluations by \p n.
1447      *
1448      * @param n the amount by which the internal counter of fitness evaluations will be increased.
1449      */
increment_fevals(unsigned long long n) const1450     void increment_fevals(unsigned long long n) const
1451     {
1452         m_fevals.fetch_add(n, std::memory_order_relaxed);
1453     }
1454 
1455     /// Number of gradient evaluations.
1456     /**
1457      * Each time a call to problem::gradient() successfully completes, an internal counter is increased by one.
1458      * The counter is initialised to zero upon problem construction and it is never reset. Copy and move operations
1459      * copy the counter as well.
1460      *
1461      * @return the number of times problem::gradient() was successfully called.
1462      */
get_gevals() const1463     unsigned long long get_gevals() const
1464     {
1465         return m_gevals.load(std::memory_order_relaxed);
1466     }
1467 
1468     /// Number of hessians evaluations.
1469     /**
1470      * Each time a call to problem::hessians() successfully completes, an internal counter is increased by one.
1471      * The counter is initialised to zero upon problem construction and it is never reset. Copy and move operations
1472      * copy the counter as well.
1473      *
1474      * @return the number of times problem::hessians() was successfully called.
1475      */
get_hevals() const1476     unsigned long long get_hevals() const
1477     {
1478         return m_hevals.load(std::memory_order_relaxed);
1479     }
1480 
1481     // Set the seed for the stochastic variables.
1482     void set_seed(unsigned);
1483 
1484     // Feasibility of a decision vector.
1485     bool feasibility_x(const vector_double &) const;
1486     // Feasibility of a fitness vector.
1487     bool feasibility_f(const vector_double &) const;
1488 
1489     /// Check if a <tt>%set_seed()</tt> method is available in the UDP.
1490     /**
1491      * This method will return \p true if a <tt>%set_seed()</tt> method is available in the UDP, \p false otherwise.
1492      *
1493      * The availability of the a <tt>%set_seed()</tt> method is determined as follows:
1494      * - if the UDP does not satisfy pagmo::has_set_seed, then this method will always return \p false;
1495      * - if the UDP satisfies pagmo::has_set_seed but it does not satisfy pagmo::override_has_set_seed,
1496      *   then this method will always return \p true;
1497      * - if the UDP satisfies both pagmo::has_set_seed and pagmo::override_has_set_seed,
1498      *   then this method will return the output of the <tt>%has_set_seed()</tt> method of the UDP.
1499      *
1500      * @return a flag signalling the availability of the <tt>%set_seed()</tt> method in the UDP.
1501      */
has_set_seed() const1502     bool has_set_seed() const
1503     {
1504         return m_has_set_seed;
1505     }
1506 
1507     /// Alias for problem::has_set_seed().
1508     /**
1509      * @return the output of problem::has_set_seed().
1510      */
is_stochastic() const1511     bool is_stochastic() const
1512     {
1513         return has_set_seed();
1514     }
1515 
1516     /// Problem's name.
1517     /**
1518      * If the UDP satisfies pagmo::has_name, then this method will return the output of its <tt>%get_name()</tt> method.
1519      * Otherwise, an implementation-defined name based on the type of the UDP will be returned.
1520      *
1521      * @return the problem's name.
1522      *
1523      * @throws unspecified any exception thrown by copying an \p std::string object.
1524      */
get_name() const1525     std::string get_name() const
1526     {
1527         return m_name;
1528     }
1529 
1530     // Problem's extra info.
1531     std::string get_extra_info() const;
1532 
1533     /// Problem's thread safety level.
1534     /**
1535      * If the UDP satisfies pagmo::has_get_thread_safety, then this method will return the output of its
1536      * <tt>%get_thread_safety()</tt> method. Otherwise, thread_safety::basic will be returned.
1537      * That is, pagmo assumes by default that is it safe to operate concurrently on distinct UDP instances.
1538      *
1539      * @return the thread safety level of the UDP.
1540      */
get_thread_safety() const1541     thread_safety get_thread_safety() const
1542     {
1543         return m_thread_safety;
1544     }
1545 
1546     // Check if the problem is in a valid state.
1547     bool is_valid() const;
1548 
1549     // Get the type at runtime.
1550     std::type_index get_type_index() const;
1551 
1552     /// Get a const pointer to the UDP.
1553     /**
1554      * \verbatim embed:rst:leading-asterisk
1555      * .. versionadded:: 2.15
1556      *
1557      * This function will return a raw const pointer
1558      * to the internal UDP instance. Differently from
1559      * :cpp:func:`~pagmo::problem::extract()`, this function
1560      * does not require to pass the correct type
1561      * in input. It is however the user's responsibility
1562      * to cast the returned void pointer to the correct type.
1563      *
1564      * .. note::
1565      *
1566      *    The returned value is a raw non-owning pointer: the lifetime of the pointee is tied to the lifetime
1567      *    of ``this``, and ``delete`` must never be called on the pointer.
1568      * \endverbatim
1569      *
1570      * @return a pointer to the internal UDP.
1571      */
1572     const void *get_ptr() const;
1573 
1574     /// Get a mutable pointer to the UDP.
1575     /**
1576      * \verbatim embed:rst:leading-asterisk
1577      * .. versionadded:: 2.15
1578      *
1579      * This function will return a raw pointer
1580      * to the internal UDP instance. Differently from
1581      * :cpp:func:`~pagmo::problem::extract()`, this function
1582      * does not require to pass the correct type
1583      * in input. It is however the user's responsibility
1584      * to cast the returned void pointer to the correct type.
1585      *
1586      * .. note::
1587      *
1588      *    The returned value is a raw non-owning pointer: the lifetime of the pointee is tied to the lifetime
1589      *    of ``this``, and ``delete`` must never be called on the pointer.
1590      *
1591      * .. note::
1592      *
1593      *    The ability to extract a mutable pointer is provided only in order to allow to call non-const
1594      *    methods on the internal UDP instance. Assigning a new UDP via this pointer is undefined behaviour.
1595      * \endverbatim
1596      *
1597      * @return a pointer to the internal UDP.
1598      */
1599     void *get_ptr();
1600 
1601 private:
1602     friend class boost::serialization::access;
1603     template <typename Archive>
save(Archive & ar,unsigned) const1604     void save(Archive &ar, unsigned) const
1605     {
1606         detail::to_archive(ar, m_ptr, m_fevals.load(std::memory_order_relaxed),
1607                            m_gevals.load(std::memory_order_relaxed), m_hevals.load(std::memory_order_relaxed), m_lb,
1608                            m_ub, m_nobj, m_nec, m_nic, m_nix, m_c_tol, m_has_batch_fitness, m_has_gradient,
1609                            m_has_gradient_sparsity, m_has_hessians, m_has_hessians_sparsity, m_has_set_seed, m_name,
1610                            m_gs_dim, m_hs_dim, m_thread_safety);
1611     }
1612 
1613     template <typename Archive>
load(Archive & ar,unsigned)1614     void load(Archive &ar, unsigned)
1615     {
1616         try {
1617             unsigned long long fevals, gevals, hevals;
1618             detail::from_archive(ar, m_ptr, fevals, gevals, hevals, m_lb, m_ub, m_nobj, m_nec, m_nic, m_nix, m_c_tol,
1619                                  m_has_batch_fitness, m_has_gradient, m_has_gradient_sparsity, m_has_hessians,
1620                                  m_has_hessians_sparsity, m_has_set_seed, m_name, m_gs_dim, m_hs_dim, m_thread_safety);
1621             m_fevals.store(fevals, std::memory_order_relaxed);
1622             m_gevals.store(gevals, std::memory_order_relaxed);
1623             m_hevals.store(hevals, std::memory_order_relaxed);
1624         } catch (...) {
1625             *this = problem{};
1626             throw;
1627         }
1628     }
BOOST_SERIALIZATION_SPLIT_MEMBER()1629     BOOST_SERIALIZATION_SPLIT_MEMBER()
1630 
1631     // Just two small helpers to make sure that whenever we require
1632     // access to the pointer it actually points to something.
1633     detail::prob_inner_base const *ptr() const
1634     {
1635         assert(m_ptr.get() != nullptr);
1636         return m_ptr.get();
1637     }
ptr()1638     detail::prob_inner_base *ptr()
1639     {
1640         assert(m_ptr.get() != nullptr);
1641         return m_ptr.get();
1642     }
1643 
1644     void check_gradient_sparsity(const sparsity_pattern &) const;
1645     void check_hessians_sparsity(const std::vector<sparsity_pattern> &) const;
1646     void check_hessian_sparsity(const sparsity_pattern &) const;
1647     void check_gradient_vector(const vector_double &) const;
1648     void check_hessians_vector(const std::vector<vector_double> &) const;
1649 
1650     // Pointer to the inner base problem
1651     std::unique_ptr<detail::prob_inner_base> m_ptr;
1652     // Counter for calls to the fitness
1653     mutable std::atomic<unsigned long long> m_fevals;
1654     // Counter for calls to the gradient
1655     mutable std::atomic<unsigned long long> m_gevals;
1656     // Counter for calls to the hessians
1657     mutable std::atomic<unsigned long long> m_hevals;
1658     // Various problem properties determined at construction time
1659     // from the concrete problem. These will be constant for the lifetime
1660     // of problem, but we cannot mark them as such because we want to be
1661     // able to assign and deserialise problems.
1662     vector_double m_lb;
1663     vector_double m_ub;
1664     vector_double::size_type m_nobj;
1665     vector_double::size_type m_nec;
1666     vector_double::size_type m_nic;
1667     vector_double::size_type m_nix;
1668     vector_double m_c_tol;
1669     bool m_has_batch_fitness;
1670     bool m_has_gradient;
1671     bool m_has_gradient_sparsity;
1672     bool m_has_hessians;
1673     bool m_has_hessians_sparsity;
1674     bool m_has_set_seed;
1675     std::string m_name;
1676     // These are the dimensions of the sparsity objects, cached
1677     // here upon construction in order to provide fast checking
1678     // on the returned gradient and hessians.
1679     vector_double::size_type m_gs_dim;
1680     std::vector<vector_double::size_type> m_hs_dim;
1681     // Thread safety.
1682     thread_safety m_thread_safety;
1683 };
1684 
1685 } // namespace pagmo
1686 
1687 // Add some repr support for CLING
1688 PAGMO_IMPLEMENT_XEUS_CLING_REPR(problem)
1689 
1690 #endif
1691