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