From a74af5db6b2002d6232e7cb0c6bfdf56b57f725b Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Fri, 6 Sep 2019 11:02:16 +0200 Subject: [PATCH 1/8] Fix island printing when MO/stochastic problems are involved. Don't ask for champion data in that case. --- pygmo/_island_test.py | 19 +++++++++++++++++++ src/island.cpp | 17 +++++++++++++---- tests/island.cpp | 18 ++++++++++++++++++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/pygmo/_island_test.py b/pygmo/_island_test.py index 742f200d0..ba3823fb1 100644 --- a/pygmo/_island_test.py +++ b/pygmo/_island_test.py @@ -111,6 +111,7 @@ def runTest(self): self.run_io_tests() self.run_status_tests() self.run_stateful_algo_tests() + self.run_mo_sto_repr_bug() def run_basic_tests(self): from .core import island, thread_island, null_algorithm, null_problem, de, rosenbrock, r_policy, s_policy, fair_replace, select_best, population @@ -399,6 +400,24 @@ def run_evolve(self, algo, pop): self.assertTrue(isl.extract(object) is None) self.assertTrue(not isl.extract(thread_island) is None) + def run_mo_sto_repr_bug(self): + # Old bug: printing islands containing MO/sto + # problems would throw due to an error being raised + # when accessing the champion. + from .core import island, de, rosenbrock, zdt, inventory + + isl = island(algo=de(), prob=rosenbrock(), size=25) + self.assertTrue("Champion decision vector" in repr(isl)) + self.assertTrue("Champion fitness" in repr(isl)) + + isl = island(algo=de(), prob=zdt(), size=25) + self.assertFalse("Champion decision vector" in repr(isl)) + self.assertFalse("Champion fitness" in repr(isl)) + + isl = island(algo=de(), prob=inventory(), size=25) + self.assertFalse("Champion decision vector" in repr(isl)) + self.assertFalse("Champion fitness" in repr(isl)) + class mp_island_test_case(_ut.TestCase): """Test case for the :class:`~pygmo.mp_island` class. diff --git a/src/island.cpp b/src/island.cpp index 29ea9f544..118302abf 100644 --- a/src/island.cpp +++ b/src/island.cpp @@ -874,13 +874,22 @@ std::ostream &operator<<(std::ostream &os, const island &isl) if (!extra_str.empty()) { stream(os, "Extra info:\n", extra_str, "\n\n"); } + + // Cache out a copy of the population for use below. + const auto pop = isl.get_population(); + stream(os, "Algorithm: " + isl.get_algorithm().get_name(), "\n\n"); - stream(os, "Problem: " + isl.get_population().get_problem().get_name(), "\n\n"); + stream(os, "Problem: " + pop.get_problem().get_name(), "\n\n"); stream(os, "Replacement policy: " + isl.m_ptr->r_pol.get_name(), "\n\n"); stream(os, "Selection policy: " + isl.m_ptr->s_pol.get_name(), "\n\n"); - stream(os, "Population size: ", isl.get_population().size(), "\n"); - stream(os, "\tChampion decision vector: ", isl.get_population().champion_x(), "\n"); - stream(os, "\tChampion fitness: ", isl.get_population().champion_f(), "\n"); + stream(os, "Population size: ", pop.size(), "\n"); + + // NOTE: don't print champion info for MO or stochastic problems. + if (pop.get_problem().get_nobj() == 1u && !pop.get_problem().is_stochastic()) { + stream(os, "\tChampion decision vector: ", isl.get_population().champion_x(), "\n"); + stream(os, "\tChampion fitness: ", isl.get_population().champion_f(), "\n"); + } + return os; } diff --git a/tests/island.cpp b/tests/island.cpp index 13780bd99..cce78ee41 100644 --- a/tests/island.cpp +++ b/tests/island.cpp @@ -54,8 +54,10 @@ see https://www.gnu.org/licenses/. */ #include #include #include +#include #include #include +#include #include #include #include @@ -392,7 +394,23 @@ BOOST_AUTO_TEST_CASE(island_name_info_stream) BOOST_CHECK(isl.get_extra_info() == "extra bits"); BOOST_CHECK(boost::contains(oss.str(), "Replacement policy: Fair replace")); BOOST_CHECK(boost::contains(oss.str(), "Selection policy: Select best")); + // Make sure champion info is printed. + BOOST_CHECK(boost::contains(oss.str(), "Champion decision vector")); + BOOST_CHECK(boost::contains(oss.str(), "Champion fitness")); std::cout << isl << '\n'; + + // Make sure champion info is skipped for MO/sto problems. + isl = island{udi_01{}, de{}, population{zdt{}, 25}}; + oss.str(""); + oss << isl; + BOOST_CHECK(!boost::contains(oss.str(), "Champion decision vector")); + BOOST_CHECK(!boost::contains(oss.str(), "Champion fitness")); + + isl = island{udi_01{}, de{}, population{inventory{}, 25}}; + oss.str(""); + oss << isl; + BOOST_CHECK(!boost::contains(oss.str(), "Champion decision vector")); + BOOST_CHECK(!boost::contains(oss.str(), "Champion fitness")); } BOOST_AUTO_TEST_CASE(island_serialization) From 879047a0dce88665a5ae653317151682549f0017 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Fri, 6 Sep 2019 11:31:45 +0200 Subject: [PATCH 2/8] Improve the python implementation of archipelago.push_back(). --- pygmo/__init__.py | 35 +++++++++++++++++++++++++++++++---- pygmo/test.py | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/pygmo/__init__.py b/pygmo/__init__.py index 2caf6b68b..7cc0aa7ad 100644 --- a/pygmo/__init__.py +++ b/pygmo/__init__.py @@ -689,7 +689,7 @@ def _archi_init(self, n=0, t=topology(), **kwargs): setattr(archipelago, "__init__", _archi_init) -def _archi_push_back(self, **kwargs): +def _archi_push_back(self, *args, **kwargs): """Add an island. This method will construct an island from the supplied arguments and add it to the archipelago. @@ -699,15 +699,42 @@ def _archi_push_back(self, **kwargs): the addition of a new island to the archipelago is mirrored by the addition of a new vertex to the topology. - The keyword arguments accept the same format as explained in the constructor of - :class:`~pygmo.island`. + This method accepts either a single positional argument, or a set of + keyword arguments: + + * if no positional arguments are provided, then the keyword arguments will + be used to construct a :class:`~pygmo.island` which will then be added + to the archipelago; otherwise, + * if a single positional argument is provided and no keyword arguments are + provided, then the positional argument is interpreted as an :class:`~pygmo.island` + object to be added to the archipelago. + + Any other combination of positional/keyword arguments will result in an error. Raises: + ValueError: if, when using positional arguments, there are more than 1 positional arguments, + or if keyword arguments are also used at the same time + TypeError: if, when using a single positional argument, the type of that argument + is not :class:`~pygmo.island` unspecified: any exception thrown by the constructor of :class:`~pygmo.island`, :func:`pygmo.topology.push_back()` or by the underlying C++ method """ - self._push_back(island(**kwargs)) + from . import island + + if len(args) == 0: + self._push_back(island(**kwargs)) + else: + if len(args) != 1: + raise ValueError( + "{} positional arguments were provided, but this method accepts only a single positional argument".format(len(args))) + if len(kwargs) != 0: + raise ValueError( + "if a positional argument is passed to this method, then no keyword arguments must be passed, but {} keyword arguments were passed instead".format(len(kwargs))) + if type(args[0]) != island: + raise TypeError( + "the positional argument passed to this method must be an island, but the type of the argument is '{}' instead".format(type(args[0]))) + self._push_back(args[0]) setattr(archipelago, "push_back", _archi_push_back) diff --git a/pygmo/test.py b/pygmo/test.py index 3eeb48c2e..5cc28ce82 100644 --- a/pygmo/test.py +++ b/pygmo/test.py @@ -2093,7 +2093,7 @@ def run_access_tests(self): def run_push_back_tests(self): from . import (archipelago, de, rosenbrock, r_policy, - s_policy, fair_replace, select_best) + s_policy, fair_replace, select_best, island) a = archipelago(5, algo=de(), prob=rosenbrock(), pop_size=10) # Push back while evolving. a.evolve(10) @@ -2147,6 +2147,37 @@ def run_push_back_tests(self): self.assertTrue(all([a[i].get_s_policy().is_(_s_pol) for i in range(7, 9)])) + # push_back() with positional argument. + a = archipelago() + a.push_back(island(algo=de(), prob=rosenbrock(), size=10)) + a.push_back(island(algo=de(), prob=rosenbrock(), size=10)) + a.push_back(island(algo=de(), prob=rosenbrock(), size=10)) + + self.assertEqual(len(a), 3) + for i in range(3): + self.assertTrue(a[i].get_algorithm().is_(de)) + self.assertTrue(a[i].get_population().problem.is_(rosenbrock)) + self.assertEqual(len(a[i].get_population()), 10) + + # Error handling. + with self.assertRaises(ValueError) as cm: + a.push_back(island(algo=de(), prob=rosenbrock(), size=10), a=5, b=6) + err = cm.exception + self.assertTrue( + "if a positional argument is passed to this method, then no keyword arguments must be passed, but 2 keyword arguments were passed instead" in str(err)) + + with self.assertRaises(ValueError) as cm: + a.push_back(island(algo=de(), prob=rosenbrock(), size=10), 1) + err = cm.exception + self.assertTrue( + "2 positional arguments were provided, but this method accepts only a single positional argument" in str(err)) + + with self.assertRaises(TypeError) as cm: + a.push_back(42) + err = cm.exception + self.assertTrue( + "the positional argument passed to this method must be an island, but the type of the argument is '{}' instead".format(type(42)) in str(err)) + def run_io_tests(self): from . import archipelago, de, rosenbrock a = archipelago(5, algo=de(), prob=rosenbrock(), pop_size=10) From 550f6a590654dc1336497bba679aeb9a02aae5f7 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Sun, 8 Sep 2019 23:30:39 +0200 Subject: [PATCH 3/8] Streamline the pygmo helpers to convert vectors to/from numpy arrays. --- pygmo/_problem_test.py | 30 +- pygmo/bfe.cpp | 2 +- pygmo/common_utils.hpp | 471 ++++++++--------------------- pygmo/core.cpp | 203 +++++++------ pygmo/expose_algorithms_0.cpp | 26 +- pygmo/expose_algorithms_1.cpp | 4 +- pygmo/expose_problems_0.cpp | 24 +- pygmo/expose_problems_1.cpp | 10 +- pygmo/problem.cpp | 17 +- pygmo/problem_exposition_suite.hpp | 2 +- pygmo/r_policy.cpp | 6 +- pygmo/s_policy.cpp | 5 +- pygmo/test.py | 10 +- pygmo/topology.cpp | 4 +- 14 files changed, 311 insertions(+), 503 deletions(-) diff --git a/pygmo/_problem_test.py b/pygmo/_problem_test.py index 6c5a7f727..5d13293e6 100644 --- a/pygmo/_problem_test.py +++ b/pygmo/_problem_test.py @@ -1120,7 +1120,7 @@ def fitness(self, a): return [42] def gradient_sparsity(self): - return array([[0, 0], [0, 1]]) + return array([[0, 0], [0, 1]], dtype='uint32') self.assert_(problem(p()).has_gradient_sparsity()) self.assert_(isinstance(problem(p()).gradient_sparsity(), ndarray)) @@ -1137,7 +1137,7 @@ def fitness(self, a): return [42] def gradient_sparsity(self): - return array([[0, 0], [0, 123]]) + return array([[0, 0], [0, 123]], dtype='uint32') self.assertRaises(ValueError, lambda: problem(p())) @@ -1150,7 +1150,7 @@ def fitness(self, a): return [42] def gradient_sparsity(self): - return array([[0, 0, 0], [0, 1, 0]]) + return array([[0, 0, 0], [0, 1, 0]], dtype='uint32') self.assertRaises(ValueError, lambda: problem(p())) @@ -1163,7 +1163,7 @@ def fitness(self, a): return [42] def gradient_sparsity(self): - return array([[[0], [1], [2]]]) + return array([[[0], [1], [2]]], dtype='uint32') self.assertRaises(ValueError, lambda: problem(p())) @@ -1189,9 +1189,9 @@ def fitness(self, a): return [42] def gradient_sparsity(self): - return array([[0, 0], [0, -1]]) + return array([[0, 0], [0, -1]], dtype='int32') - self.assertRaises(OverflowError, lambda: problem(p())) + self.assertRaises(TypeError, lambda: problem(p())) class p(object): @@ -1202,7 +1202,7 @@ def fitness(self, a): return [42] def gradient_sparsity(self): - a = array([[0, 0, 0], [0, 1, 0]]) + a = array([[0, 0, 0], [0, 1, 0]], dtype='uint32') return a[:, :2] self.assert_(problem(p()).has_gradient_sparsity()) @@ -1627,7 +1627,7 @@ def fitness(self, a): return [42] def hessians_sparsity(self): - return [array([[0, 0], [1, 1]])] + return [array([[0, 0], [1, 1]], dtype='uint32')] self.assert_(problem(p()).has_hessians_sparsity()) self.assert_(isinstance(problem(p()).hessians_sparsity(), list)) @@ -1645,7 +1645,7 @@ def fitness(self, a): return [42] def hessians_sparsity(self): - return array([[0, 0], [0, 123]]) + return array([[0, 0], [0, 123]], dtype='uint32') self.assertRaises(ValueError, lambda: problem(p())) @@ -1658,7 +1658,7 @@ def fitness(self, a): return [42] def hessians_sparsity(self): - return (array([[0, 0, 0], [0, 1, 0]]),) + return (array([[0, 0, 0], [0, 1, 0]], dtype='uint32'),) self.assertRaises(ValueError, lambda: problem(p())) @@ -1671,7 +1671,7 @@ def fitness(self, a): return [42] def hessians_sparsity(self): - return [array([[[0], [1], [2]]])] + return [array([[[0], [1], [2]]], dtype='uint32')] self.assertRaises(ValueError, lambda: problem(p())) @@ -1697,9 +1697,9 @@ def fitness(self, a): return [42] def hessians_sparsity(self): - return [array([[0, 0], [0, -1]])] + return [array([[0, 0], [0, -1]], dtype='int32')] - self.assertRaises(OverflowError, lambda: problem(p())) + self.assertRaises(TypeError, lambda: problem(p())) class p(object): @@ -1710,7 +1710,7 @@ def fitness(self, a): return [42] def hessians_sparsity(self): - a = array([[0, 0, 0], [1, 1, 0]]) + a = array([[0, 0, 0], [1, 1, 0]], dtype='uint32') return [a[:, :2]] self.assert_(problem(p()).has_hessians_sparsity()) @@ -1745,7 +1745,7 @@ def get_nobj(self): return 2 def hessians_sparsity(self): - return [array([[0, 0], [1, 1]]), array([[0, 0], [1, 0]])] + return [array([[0, 0], [1, 1]], dtype='uint32'), array([[0, 0], [1, 0]], dtype='uint32')] self.assert_(problem(p()).has_hessians_sparsity()) self.assert_(isinstance(problem(p()).hessians_sparsity(), list)) diff --git a/pygmo/bfe.cpp b/pygmo/bfe.cpp index 09fde0fbf..befe74bc7 100644 --- a/pygmo/bfe.cpp +++ b/pygmo/bfe.cpp @@ -93,7 +93,7 @@ std::unique_ptr bfe_inner::clone() const vector_double bfe_inner::operator()(const problem &p, const vector_double &dvs) const { - return pygmo::to_vd(m_value.attr("__call__")(p, pygmo::v_to_a(dvs))); + return pygmo::obj_to_vector(m_value.attr("__call__")(p, pygmo::vector_to_ndarr(dvs))); } pagmo::thread_safety bfe_inner::get_thread_safety() const diff --git a/pygmo/common_utils.hpp b/pygmo/common_utils.hpp index d953e7137..eaf7693ec 100644 --- a/pygmo/common_utils.hpp +++ b/pygmo/common_utils.hpp @@ -167,16 +167,15 @@ inline bp::object callable_attribute(const bp::object &o, const char *s) return bp::object(); } +// isinstance wrapper. +inline bool isinstance(const bp::object &o, const bp::object &t) +{ + return bp::extract(builtin().attr("isinstance")(o, t)); +} + // Convert a vector of arithmetic types into a 1D numpy array. -// NOTE: provide two overloads, one for unsigned integral types and the -// other for the other arithmetic types. The idea is that we may not want -// to produce numpy arrays of unsigned integrals, as they are more painful -// to deal with in Python than just plain signed integrals. -template , - pagmo::detail::conjunction, std::is_signed>>::value, - int> = 0> -inline bp::object v_to_a(const std::vector &v) +template +inline bp::object vector_to_ndarr(const std::vector &v) { // The dimensions of the array to be created. npy_intp dims[] = {boost::numeric_cast(v.size())}; @@ -191,47 +190,12 @@ inline bp::object v_to_a(const std::vector &v) // Copy over the data. std::copy(v.begin(), v.end(), static_cast(PyArray_DATA(reinterpret_cast(ret)))); } - // Hand over to boost python. - return retval; -} - -template , std::is_unsigned>::value, int> = 0> -inline bp::object v_to_a(const std::vector &v, bool keep_unsigned = false) -{ - // The dimensions of the array to be created. - npy_intp dims[] = {boost::numeric_cast(v.size())}; - // Attempt creating the array. The ndtype will be the signed counterpart of T, - // if keep_unsigned is false. - using int_type = typename std::make_signed::type; - PyObject *ret = PyArray_SimpleNew(1, dims, keep_unsigned ? cpp_npy::value : cpp_npy::value); - if (!ret) { - pygmo_throw(PyExc_RuntimeError, "couldn't create a NumPy array: the 'PyArray_SimpleNew()' function failed"); - } - // Hand over to BP for exception-safe behaviour. - bp::object retval{bp::handle<>(ret)}; - if (v.size()) { - if (keep_unsigned) { - // Copy over the data. - std::copy(v.begin(), v.end(), static_cast(PyArray_DATA(reinterpret_cast(ret)))); - } else { - // Copy over the data, transforming from unsigned to signed on the fly. - std::transform(v.begin(), v.end(), - static_cast(PyArray_DATA(reinterpret_cast(ret))), - [](const T &n) { return boost::numeric_cast(n); }); - } - } - // Hand over to boost python. return retval; } // Convert a vector of vectors of arithmetic types into a 2D numpy array. -// NOTE: same scheme as above. -template , - pagmo::detail::conjunction, std::is_signed>>::value, - int> = 0> -inline bp::object vv_to_a(const std::vector> &v) +template +inline bp::object vvector_to_ndarr(const std::vector> &v) { // The dimensions of the array to be created. const auto nrows = v.size(); @@ -258,364 +222,201 @@ inline bp::object vv_to_a(const std::vector> &v) return retval; } -template , std::is_unsigned>::value, int> = 0> -inline bp::object vv_to_a(const std::vector> &v, bool keep_unsigned = false) -{ - // The dimensions of the array to be created. - const auto nrows = v.size(); - const auto ncols = nrows ? v[0].size() : 0u; - npy_intp dims[] = {boost::numeric_cast(nrows), boost::numeric_cast(ncols)}; - // Attempt creating the array. The ndtype will be the signed counterpart of T, - // if keep_unsigned is false. - using int_type = typename std::make_signed::type; - PyObject *ret = PyArray_SimpleNew(2, dims, keep_unsigned ? cpp_npy::value : cpp_npy::value); - if (!ret) { - pygmo_throw(PyExc_RuntimeError, "couldn't create a NumPy array: the 'PyArray_SimpleNew()' function failed"); - } - // Hand over to BP for exception-safe behaviour. - bp::object retval{bp::handle<>(ret)}; - if (nrows) { - if (keep_unsigned) { - auto data = static_cast(PyArray_DATA(reinterpret_cast(ret))); - for (const auto &i : v) { - if (i.size() != ncols) { - pygmo_throw(PyExc_ValueError, "cannot convert a vector of vectors to a NumPy 2D array " - "if the vector instances don't have all the same size"); - } - std::copy(i.begin(), i.end(), data); - data += ncols; - } - } else { - auto data = static_cast(PyArray_DATA(reinterpret_cast(ret))); - for (const auto &i : v) { - if (i.size() != ncols) { - pygmo_throw(PyExc_ValueError, "cannot convert a vector of vectors to a NumPy 2D array " - "if the vector instances don't have all the same size"); - } - std::transform(i.begin(), i.end(), data, [](const T &n) { return boost::numeric_cast(n); }); - data += ncols; - } - } - return retval; - } -} - -// isinstance wrapper. -inline bool isinstance(const bp::object &o, const bp::object &t) +// Convert a numpy array to a std::vector-like object. +template +inline Vector ndarr_to_vector(PyArrayObject *o) { - return bp::extract(builtin().attr("isinstance")(o, t)); -} + using value_t = typename Vector::value_type; + using size_type = typename Vector::size_type; -// Convert a numpy array of double to a vector_double. -inline pagmo::vector_double ad_to_vd(PyArrayObject *o) -{ - assert(PyArray_TYPE(o) == NPY_DOUBLE); - using size_type = pagmo::vector_double::size_type; + // Checks on the input array. + if (PyArray_TYPE(o) != cpp_npy::value) { + pygmo_throw(PyExc_RuntimeError, "cannot convert a NumPy array to a C++ vector: " + "the source data type does not match the destination data type"); + } if (!PyArray_ISCARRAY_RO(o)) { - pygmo_throw(PyExc_RuntimeError, "cannot convert NumPy array to a vector of doubles: " - "data must be C-style contiguous, aligned, and in machine byte-order"); + pygmo_throw(PyExc_RuntimeError, "cannot convert a NumPy array to a C++ vector: " + "the data must be C-style contiguous, aligned, and in machine byte-order"); } if (PyArray_NDIM(o) != 1) { - pygmo_throw(PyExc_ValueError, ("cannot convert NumPy array to a vector of doubles: " + pygmo_throw(PyExc_ValueError, ("cannot convert a NumPy array to a C++ vector: " "the array must be unidimensional, but the dimension is " + std::to_string(PyArray_NDIM(o)) + " instead") .c_str()); } - if (PyArray_STRIDES(o)[0] != sizeof(double)) { - pygmo_throw(PyExc_RuntimeError, ("cannot convert NumPy array to a vector of doubles: " - "the stride value must be " - + std::to_string(sizeof(double))) - .c_str()); - } - if (PyArray_ITEMSIZE(o) != sizeof(double)) { - pygmo_throw(PyExc_RuntimeError, ("cannot convert NumPy array to a vector of doubles: " + if (PyArray_ITEMSIZE(o) != sizeof(value_t)) { + pygmo_throw(PyExc_RuntimeError, ("cannot convert a NumPy array to a C++ vector: " "the size of the scalar type must be " - + std::to_string(sizeof(double))) + + std::to_string(sizeof(value_t))) .c_str()); } + // NOTE: not sure if this special casing is needed. We make sure // the array contains something in order to avoid messing around // with a potentially null pointer in the array. const auto size = boost::numeric_cast(PyArray_SHAPE(o)[0]); if (size) { - auto data = static_cast(PyArray_DATA(o)); - return pagmo::vector_double(data, data + size); + auto data = static_cast(PyArray_DATA(o)); + return Vector(data, data + size); } - return pagmo::vector_double{}; + return Vector{}; } -// Convert an arbitrary python object to a vector_double. -inline pagmo::vector_double to_vd(const bp::object &o) +// Convert an arbitrary python object to a +// std::vector-like object. +template +inline Vector obj_to_vector(const bp::object &o) { + // Fetch the target scalar type. + using value_t = typename Vector::value_type; + + // Check if o is a numpy array. bp::object a = bp::import("numpy").attr("ndarray"); if (isinstance(o, a)) { // NOTE: the idea here is that we want to be able to convert - // from a NumPy array of types other than double. This is useful - // because one can then create arrays of ints and have them converted - // on the fly (e.g., for the bounds). If the array is already a - // double-precision array, this function should not do any copy. - auto n = PyArray_FROM_OTF(o.ptr(), NPY_DOUBLE, NPY_ARRAY_IN_ARRAY); + // from a numpy array of types other than value_t. This is useful + // because one can then create arrays of other types and have them + // converted on the fly. If the array is already of the correct type, + // this function should not do any copy. + auto n = PyArray_FROM_OTF(o.ptr(), cpp_npy::value, NPY_ARRAY_IN_ARRAY); if (!n) { bp::throw_error_already_set(); } - return ad_to_vd(reinterpret_cast(bp::object(bp::handle<>(n)).ptr())); + return ndarr_to_vector(reinterpret_cast(bp::object(bp::handle<>(n)).ptr())); } - // If o is not a numpy array, just try to iterate over it and extract doubles. - bp::stl_input_iterator begin(o), end; - return pagmo::vector_double(begin, end); + + // If o is not a numpy array, just try to iterate over it and extract value_t. + bp::stl_input_iterator begin(o), end; + return Vector(begin, end); } -// Convert a numpy array to a vector of vector_double. -inline std::vector a_to_vvd(PyArrayObject *o) +// Convert a 2D numpy array to a vector of std::vector-like objects. +template +inline VVector ndarr_to_vvector(PyArrayObject *o) { - using size_type = std::vector::size_type; + using size_type = typename VVector::size_type; + using value_t = typename VVector::value_type::value_type; + + // Checks on the input array. + if (PyArray_TYPE(o) != cpp_npy::value) { + pygmo_throw(PyExc_RuntimeError, "cannot convert a NumPy array to a vector of C++ vectors: " + "the source data type does not match the destination data type"); + } if (!PyArray_ISCARRAY_RO(o)) { - pygmo_throw(PyExc_RuntimeError, "cannot convert NumPy array to a vector of vector_double: " - "data must be C-style contiguous, aligned, and in machine byte-order"); + pygmo_throw(PyExc_RuntimeError, "cannot convert a NumPy array to a vector of C++ vectors: " + "the data must be C-style contiguous, aligned, and in machine byte-order"); } if (PyArray_NDIM(o) != 2) { - pygmo_throw(PyExc_ValueError, "cannot convert NumPy array to a vector of vector_double: " + pygmo_throw(PyExc_ValueError, "cannot convert a NumPy array to a vector of C++ vectors: " "the array must be 2-dimensional"); } - if (PyArray_TYPE(o) != NPY_DOUBLE) { - pygmo_throw(PyExc_TypeError, "cannot convert NumPy array to a vector of vector_double: " - "the scalar type must be 'double'"); - } - if (PyArray_ITEMSIZE(o) != sizeof(double)) { - pygmo_throw(PyExc_RuntimeError, ("cannot convert NumPy array to a vector of vector_double: " + if (PyArray_ITEMSIZE(o) != sizeof(value_t)) { + pygmo_throw(PyExc_RuntimeError, ("cannot convert a 2D NumPy array to a vector of C++ vectors: " "the size of the scalar type must be " - + std::to_string(sizeof(double))) + + std::to_string(sizeof(value_t))) .c_str()); } + const auto size = boost::numeric_cast(PyArray_SHAPE(o)[0]); - std::vector retval; + VVector retval; if (size) { - auto data = static_cast(PyArray_DATA(o)); + auto data = static_cast(PyArray_DATA(o)); const auto ssize = PyArray_SHAPE(o)[1]; - for (size_type i = 0u; i < size; ++i, data += ssize) { - retval.push_back(pagmo::vector_double(data, data + ssize)); + for (size_type i = 0; i < size; ++i, data += ssize) { + retval.push_back(typename VVector::value_type(data, data + ssize)); } } return retval; } -// Convert an arbitrary Python object to a vector of vector_double. -inline std::vector to_vvd(const bp::object &o) +// Convert an arbitrary python object to a +// vector of std::vector-like objects. +template +inline VVector obj_to_vvector(const bp::object &o) { - bp::object l = builtin().attr("list"); + using value_t = typename VVector::value_type::value_type; + bp::object a = bp::import("numpy").attr("ndarray"); - if (isinstance(o, l)) { - bp::stl_input_iterator begin(o), end; - std::vector retval; - for (; begin != end; ++begin) { - retval.push_back(to_vd(*begin)); - } - return retval; - } else if (isinstance(o, a)) { - auto n = PyArray_FROM_OTF(o.ptr(), NPY_DOUBLE, NPY_ARRAY_IN_ARRAY); + if (isinstance(o, a)) { + auto n = PyArray_FROM_OTF(o.ptr(), cpp_npy::value, NPY_ARRAY_IN_ARRAY); if (!n) { bp::throw_error_already_set(); } - return a_to_vvd(reinterpret_cast(bp::object(bp::handle<>(n)).ptr())); + return ndarr_to_vvector(reinterpret_cast(bp::object(bp::handle<>(n)).ptr())); } - pygmo_throw(PyExc_TypeError, ("cannot convert the type '" + str(type(o)) - + "' to a vector of vector_double: only lists of doubles and NumPy arrays of doubles " - "are supported") - .c_str()); -} - -// Convert a 1D numpy array of input integral type SourceInt to an std::vector of -// unsigned integral type UInt. -template -inline std::vector a_to_vuint(PyArrayObject *o) -{ - static_assert(std::is_integral::value && std::is_unsigned::value, - "The UInt type must be an unsigned integral type."); - - static_assert(std::is_integral::value, "The SourceInt type must be an integral type."); - using size_type = typename std::vector::size_type; - - if (!PyArray_ISCARRAY_RO(o)) { - pygmo_throw(PyExc_RuntimeError, "cannot convert a NumPy array to a vector of unsigned integrals: " - "data must be C-style contiguous, aligned, and in machine byte-order"); - } - if (PyArray_NDIM(o) != 1) { - pygmo_throw(PyExc_ValueError, "cannot convert a NumPy array to a vector of unsigned integrals: " - "the array must be unidimensional"); - } - if (PyArray_TYPE(o) != cpp_npy::value) { - pygmo_throw(PyExc_TypeError, "cannot convert a NumPy array to a vector of unsigned integrals: " - "the input scalar type is inconsistent with the expected integral type"); - } - if (PyArray_STRIDES(o)[0] != sizeof(SourceInt)) { - pygmo_throw(PyExc_RuntimeError, ("cannot convert a NumPy array to a vector of unsigned integrals: " - "the stride value must be " - + std::to_string(sizeof(SourceInt))) - .c_str()); - } - if (PyArray_ITEMSIZE(o) != sizeof(SourceInt)) { - pygmo_throw(PyExc_RuntimeError, ("cannot convert a NumPy array to a vector of unsigned integrals: " - "the size of the scalar type must be " - + std::to_string(sizeof(SourceInt))) - .c_str()); - } - - const auto size = boost::numeric_cast(PyArray_SHAPE(o)[0]); - std::vector retval; - retval.resize(boost::numeric_cast(size)); - - if (size) { - auto data = static_cast(PyArray_DATA(o)); - std::transform(data, data + size, retval.data(), [](SourceInt n) { return boost::numeric_cast(n); }); + bp::stl_input_iterator begin(o), end; + VVector retval; + for (; begin != end; ++begin) { + retval.push_back(obj_to_vector(*begin)); } - return retval; } -// Convert an arbitrary python object to a vector of unsigned integrals. -// This function expects in input either a list of something convertible -// to UInt, or a NumPy array of *signed* integrals. The point of expecting -// signed integrals is that, except in rare occasions, we just want to -// deal with signed integral types on the Python side and convert back -// to unsigned only when crossing over to C++. This is much more natural -// for the user, who does not have to deal with the creation of NumPy -// arrays with a non-default dtype, etc. The only exception to this -// rule is when we are dealing with individual IDs, which must be kept -// unsigned. -template -inline std::vector to_vuint(const bp::object &o) -{ - static_assert(std::is_integral::value && std::is_unsigned::value, - "This function must be used only with unsigned integral types."); - - bp::object l = builtin().attr("list"); - bp::object a = bp::import("numpy").attr("ndarray"); - - if (isinstance(o, l)) { - bp::stl_input_iterator begin(o), end; - return std::vector(begin, end); - } else if (isinstance(o, a)) { - // NOTE: the idea here is that we expect in input a numpy array of - // the "natural sized" signed int type for the platform, which we - // heuristically assume to be the signed counterpart of size_t. The idea - // is that we basically want the users to use signed integral arrays - // when talking to pygmo, because they are the easiest to work with - // and I don't see much benefit in making the distinction between signed/unsigned - // in Python. We can rework this approach if it turns out to be too restrictive. - using int_type = std::make_signed::type; - - auto n = PyArray_FROM_OTF(o.ptr(), cpp_npy::value, NPY_ARRAY_IN_ARRAY); - if (!n) { - bp::throw_error_already_set(); - } - - return a_to_vuint(reinterpret_cast(bp::object(bp::handle<>(n)).ptr())); - } - pygmo_throw(PyExc_TypeError, - ("cannot convert the type '" + str(type(o)) - + "' to a vector of unsigned integrals: only lists of ints and NumPy arrays of ints are supported") - .c_str()); -} - // Convert a sparsity pattern into a numpy array. -inline bp::object sp_to_a(const pagmo::sparsity_pattern &s) +inline bp::object sp_to_ndarr(const pagmo::sparsity_pattern &s) { // The unsigned integral type that is used in the sparsity pattern. using size_type = pagmo::vector_double::size_type; - // Its signed counterpart. - using int_type = std::make_signed::type; + + // Create the return value. npy_intp dims[] = {boost::numeric_cast(s.size()), 2}; - PyObject *ret = PyArray_SimpleNew(2, dims, cpp_npy::value); + PyObject *ret = PyArray_SimpleNew(2, dims, cpp_npy::value); if (!ret) { pygmo_throw(PyExc_RuntimeError, "couldn't create a NumPy array: the 'PyArray_SimpleNew()' function failed"); } // Hand over to BP for exception-safe behaviour. bp::object retval{bp::handle<>(ret)}; - auto err_handler = [](const decltype(s[0].first) &n) { - pygmo_throw(PyExc_OverflowError, ("overflow in the conversion of the sparsity index " + std::to_string(n) - + " to the appropriate signed integer type") - .c_str()); - }; + // NOTE: same as above, avoid asking for the data pointer if size is zero. if (s.size()) { - auto data = static_cast(PyArray_DATA(reinterpret_cast(ret))); + auto data = static_cast(PyArray_DATA(reinterpret_cast(ret))); for (decltype(s.size()) i = 0u; i < s.size(); ++i) { - try { - *(data + i + i) = boost::numeric_cast(s[i].first); - } catch (const std::bad_cast &) { - err_handler(s[i].first); - } - try { - *(data + i + i + 1u) = boost::numeric_cast(s[i].second); - } catch (const std::bad_cast &) { - err_handler(s[i].second); - } + *(data + i + i) = s[i].first; + *(data + i + i + 1u) = s[i].second; } } return retval; } -// Convert a numpy array of std::make_signed::type into a sparsity pattern. -inline pagmo::sparsity_pattern a_to_sp(PyArrayObject *o) +// Convert a numpy array into a sparsity pattern. +inline pagmo::sparsity_pattern ndarr_to_sp(PyArrayObject *o) { using size_type = pagmo::vector_double::size_type; - using int_type = std::make_signed::type; + + // Checks on the input array. + if (PyArray_TYPE(o) != cpp_npy::value) { + pygmo_throw(PyExc_RuntimeError, "cannot convert a NumPy array to a sparsity pattern: " + "the source data type does not match the destination data type"); + } if (!PyArray_ISCARRAY_RO(o)) { - pygmo_throw(PyExc_ValueError, "cannot convert NumPy array to a sparsity pattern: " - "data must be C-style contiguous, aligned, and in machine byte-order"); + pygmo_throw(PyExc_ValueError, "cannot convert a NumPy array to a sparsity pattern: " + "the data must be C-style contiguous, aligned, and in machine byte-order"); } if (PyArray_NDIM(o) != 2) { - pygmo_throw(PyExc_ValueError, ("cannot convert NumPy array to a sparsity pattern: " + pygmo_throw(PyExc_ValueError, ("cannot convert a NumPy array to a sparsity pattern: " "the array must be bidimensional, but its dimension is " + std::to_string(PyArray_NDIM(o)) + " instead") .c_str()); } if (PyArray_SHAPE(o)[1] != 2) { - pygmo_throw(PyExc_ValueError, ("cannot convert NumPy array to a sparsity pattern: " + pygmo_throw(PyExc_ValueError, ("cannot convert a NumPy array to a sparsity pattern: " "the second dimension must be 2, but it is instead " + std::to_string(PyArray_SHAPE(o)[1])) .c_str()); } - if (PyArray_TYPE(o) != cpp_npy::value) { - pygmo_throw(PyExc_TypeError, - "cannot convert NumPy array to a sparsity pattern: " - "the scalar type must be the signed counterpart of 'pagmo::vector_double::size_type'"); - } - if (PyArray_STRIDES(o)[0] != sizeof(int_type) * 2u || PyArray_STRIDES(o)[1] != sizeof(int_type)) { - pygmo_throw(PyExc_ValueError, "cannot convert NumPy array to a sparsity pattern: " - "invalid strides detected"); - } - if (PyArray_ITEMSIZE(o) != sizeof(int_type)) { - pygmo_throw(PyExc_ValueError, ("cannot convert NumPy array to a sparsity pattern: " + if (PyArray_ITEMSIZE(o) != sizeof(size_type)) { + pygmo_throw(PyExc_ValueError, ("cannot convert a NumPy array to a sparsity pattern: " "the size of the scalar type must be " - + std::to_string(sizeof(int_type))) + + std::to_string(sizeof(size_type))) .c_str()); } const auto size = boost::numeric_cast(PyArray_SHAPE(o)[0]); - // Error handler for nice Python error messages. - auto err_handler = [](int_type n) { - pygmo_throw(PyExc_OverflowError, ("overflow in the conversion of the sparsity index " + std::to_string(n) - + " to the appropriate unsigned integer type") - .c_str()); - }; if (size) { - auto data = static_cast(PyArray_DATA(o)); + auto data = static_cast(PyArray_DATA(o)); pagmo::sparsity_pattern retval; for (pagmo::sparsity_pattern::size_type i = 0u; i < size; ++i) { - size_type a, b; - try { - a = boost::numeric_cast(*(data + i + i)); - } catch (const std::bad_cast &) { - err_handler(*(data + i + i)); - } - try { - b = boost::numeric_cast(*(data + i + i + 1u)); - } catch (const std::bad_cast &) { - err_handler(*(data + i + i + 1u)); - } - retval.emplace_back(a, b); + retval.emplace_back(*(data + i + i), *(data + i + i + 1u)); } return retval; } @@ -623,18 +424,11 @@ inline pagmo::sparsity_pattern a_to_sp(PyArrayObject *o) } // Try converting a python object to a sparsity pattern. -inline pagmo::sparsity_pattern to_sp(const bp::object &o) +inline pagmo::sparsity_pattern obj_to_sp(const bp::object &o) { using size_type = pagmo::vector_double::size_type; if (isinstance(o, bp::import("numpy").attr("ndarray"))) { - // Input object is a NumPy array of some kind. - // NOTE: the idea here is the following: we try to build a NumPy array of the signed counterpart of - // vector_double::size_type (most likely long or long long) from whatever type of NumPy array was passed as - // input, and then we will convert the elements to the appropriate size_type inside the a_to_sp routine. The - // reason for doing this is that in typical usage Python integers are converted to signed integers when used - // inside NumPy arrays, so we want to work with signed ints here as well in order no to force the user to - // create sparsity patterns like array(...,dtype='ulonglong'). - auto n = PyArray_FROM_OTF(o.ptr(), cpp_npy::type>::value, NPY_ARRAY_IN_ARRAY); + auto n = PyArray_FROM_OTF(o.ptr(), cpp_npy::value, NPY_ARRAY_IN_ARRAY); if (!n) { // NOTE: PyArray_FROM_OTF already sets the exception at the Python level with an appropriate message, // so we just throw the Python exception. @@ -642,7 +436,7 @@ inline pagmo::sparsity_pattern to_sp(const bp::object &o) } // Hand over to BP for nice RAII and exception safety. auto bp_n = bp::object(bp::handle<>(n)); - return a_to_sp(reinterpret_cast(bp_n.ptr())); + return ndarr_to_sp(reinterpret_cast(bp_n.ptr())); } pagmo::sparsity_pattern retval; // We will try to interpret o as a collection of generic python objects, and each element @@ -922,48 +716,19 @@ inline void import_aps(const bp::list &l) inline bp::tuple inds_to_tuple(const pagmo::individuals_group_t &inds) { // Do the IDs. - // NOTE: as usual, keep the IDs as unsigned values. - auto ID_arr = v_to_a(std::get<0>(inds), true); + auto ID_arr = vector_to_ndarr(std::get<0>(inds)); // Decision vectors. - auto dv_arr = vv_to_a(std::get<1>(inds)); + auto dv_arr = vvector_to_ndarr(std::get<1>(inds)); // Fitness vectors. - auto fv_arr = vv_to_a(std::get<2>(inds)); + auto fv_arr = vvector_to_ndarr(std::get<2>(inds)); return bp::make_tuple(ID_arr, dv_arr, fv_arr); } -// Convert a generic Python object into a vector of individual IDs. -// NOTE: this is very similar to to_vuint(), but with the difference -// that we want to keep the unsignedness of the IDs. Perhaps -// in the future we can refactor these functions to have less code -// duplication. -inline std::vector to_ID_vector(const bp::object &o) -{ - bp::object l = builtin().attr("list"); - bp::object a = bp::import("numpy").attr("ndarray"); - - if (isinstance(o, l)) { - bp::stl_input_iterator begin(o), end; - return std::vector(begin, end); - } else if (isinstance(o, a)) { - auto n = PyArray_FROM_OTF(o.ptr(), cpp_npy::value, NPY_ARRAY_IN_ARRAY); - if (!n) { - bp::throw_error_already_set(); - } - - return a_to_vuint( - reinterpret_cast(bp::object(bp::handle<>(n)).ptr())); - } - pygmo_throw(PyExc_TypeError, ("cannot convert the type '" + str(type(o)) - + "' to a vector of individual IDs: only lists of unsigned ints and NumPy arrays of " - "unsigned ints are supported") - .c_str()); -} - // Convert a generic Python object into an individuals_group_t. -inline pagmo::individuals_group_t to_inds(const bp::object &o) +inline pagmo::individuals_group_t obj_to_inds(const bp::object &o) { // We assume we receive in input something we can iterate over. bp::stl_input_iterator begin(o), end; @@ -974,7 +739,7 @@ inline pagmo::individuals_group_t to_inds(const bp::object &o) } // Try fetching the IDs. - auto ID_vec = to_ID_vector(*begin); + auto ID_vec = obj_to_vector>(*begin); if (++begin == end) { // Iteratable with only 1 element. @@ -983,7 +748,7 @@ inline pagmo::individuals_group_t to_inds(const bp::object &o) } // Try fetching the decision vectors. - auto dvs_vec = to_vvd(*begin); + auto dvs_vec = obj_to_vvector>(*begin); if (++begin == end) { // Iteratable with only 2 elements. @@ -992,7 +757,7 @@ inline pagmo::individuals_group_t to_inds(const bp::object &o) } // Try fetching the fitness vectors. - auto fvs_vec = to_vvd(*begin); + auto fvs_vec = obj_to_vvector>(*begin); if (++begin != end) { // Iteratable with too many elements. diff --git a/pygmo/core.cpp b/pygmo/core.cpp index 492d0addb..d817fbaf4 100644 --- a/pygmo/core.cpp +++ b/pygmo/core.cpp @@ -286,7 +286,7 @@ struct archipelago_pickle_suite : bp::pickle_suite { // Helper function to test the to_vd functionality. bool test_to_vd(const bp::object &o, unsigned n) { - auto res = to_vd(o); + auto res = obj_to_vector(o); if (res.size() != n) { return false; } @@ -301,7 +301,7 @@ bool test_to_vd(const bp::object &o, unsigned n) // Helper function to test the to_vvd functionality. bool test_to_vvd(const bp::object &o, unsigned n, unsigned m) { - auto res = to_vvd(o); + auto res = obj_to_vvector>(o); return res.size() == n && std::all_of(res.begin(), res.end(), [m](const vector_double &v) { return v.size() == m; }); } @@ -460,7 +460,7 @@ BOOST_PYTHON_MODULE(core) bp::def("_str", &pygmo::str); bp::def("_callable", &pygmo::callable); bp::def("_deepcopy", &pygmo::deepcopy); - bp::def("_to_sp", &pygmo::to_sp); + bp::def("_to_sp", &pygmo::obj_to_sp); bp::def("_test_object_serialization", &pygmo::detail::test_object_serialization); bp::def("_test_to_vd", &pygmo::detail::test_to_vd); bp::def("_test_to_vvd", &pygmo::detail::test_to_vvd); @@ -567,48 +567,50 @@ BOOST_PYTHON_MODULE(core) .def_pickle(pygmo::detail::population_pickle_suite()) .def("push_back", lcast([](population &pop, const bp::object &x, const bp::object &f) { if (f.is_none()) { - pop.push_back(pygmo::to_vd(x)); + pop.push_back(pygmo::obj_to_vector(x)); } else { - pop.push_back(pygmo::to_vd(x), pygmo::to_vd(f)); + pop.push_back(pygmo::obj_to_vector(x), pygmo::obj_to_vector(f)); } }), pygmo::population_push_back_docstring().c_str(), (bp::arg("x"), bp::arg("f") = bp::object())) .def("random_decision_vector", - lcast([](const population &pop) { return pygmo::v_to_a(pop.random_decision_vector()); }), + lcast([](const population &pop) { return pygmo::vector_to_ndarr(pop.random_decision_vector()); }), pygmo::population_random_decision_vector_docstring().c_str()) - .def("best_idx", - lcast([](const population &pop, const bp::object &tol) { return pop.best_idx(pygmo::to_vd(tol)); }), + .def("best_idx", lcast([](const population &pop, const bp::object &tol) { + return pop.best_idx(pygmo::obj_to_vector(tol)); + }), (bp::arg("tol"))) .def("best_idx", lcast([](const population &pop, double tol) { return pop.best_idx(tol); }), (bp::arg("tol"))) .def("best_idx", lcast([](const population &pop) { return pop.best_idx(); }), pygmo::population_best_idx_docstring().c_str()) - .def("worst_idx", - lcast([](const population &pop, const bp::object &tol) { return pop.worst_idx(pygmo::to_vd(tol)); }), + .def("worst_idx", lcast([](const population &pop, const bp::object &tol) { + return pop.worst_idx(pygmo::obj_to_vector(tol)); + }), (bp::arg("tol"))) .def("worst_idx", lcast([](const population &pop, double tol) { return pop.worst_idx(tol); }), (bp::arg("tol"))) .def("worst_idx", lcast([](const population &pop) { return pop.worst_idx(); }), pygmo::population_worst_idx_docstring().c_str()) .def("__len__", &population::size) .def("set_xf", lcast([](population &pop, population::size_type i, const bp::object &x, const bp::object &f) { - pop.set_xf(i, pygmo::to_vd(x), pygmo::to_vd(f)); + pop.set_xf(i, pygmo::obj_to_vector(x), pygmo::obj_to_vector(f)); }), pygmo::population_set_xf_docstring().c_str()) .def("set_x", lcast([](population &pop, population::size_type i, const bp::object &x) { - pop.set_x(i, pygmo::to_vd(x)); + pop.set_x(i, pygmo::obj_to_vector(x)); }), pygmo::population_set_x_docstring().c_str()) - .def("get_f", lcast([](const population &pop) { return pygmo::vv_to_a(pop.get_f()); }), + .def("get_f", lcast([](const population &pop) { return pygmo::vvector_to_ndarr(pop.get_f()); }), pygmo::population_get_f_docstring().c_str()) - .def("get_x", lcast([](const population &pop) { return pygmo::vv_to_a(pop.get_x()); }), + .def("get_x", lcast([](const population &pop) { return pygmo::vvector_to_ndarr(pop.get_x()); }), pygmo::population_get_x_docstring().c_str()) - .def("get_ID", lcast([](const population &pop) { return pygmo::v_to_a(pop.get_ID(), true); }), + .def("get_ID", lcast([](const population &pop) { return pygmo::vector_to_ndarr(pop.get_ID()); }), pygmo::population_get_ID_docstring().c_str()) .def("get_seed", &population::get_seed, pygmo::population_get_seed_docstring().c_str()); pygmo::add_property(pop_class, "champion_x", - lcast([](const population &pop) { return pygmo::v_to_a(pop.champion_x()); }), + lcast([](const population &pop) { return pygmo::vector_to_ndarr(pop.champion_x()); }), pygmo::population_champion_x_docstring().c_str()); pygmo::add_property(pop_class, "champion_f", - lcast([](const population &pop) { return pygmo::v_to_a(pop.champion_f()); }), + lcast([](const population &pop) { return pygmo::vector_to_ndarr(pop.champion_f()); }), pygmo::population_champion_f_docstring().c_str()); pygmo::add_property(pop_class, "problem", bp::make_function(lcast([](population &pop) -> problem & { return pop.get_problem(); }), @@ -629,37 +631,38 @@ BOOST_PYTHON_MODULE(core) .def("_py_extract", &pygmo::generic_py_extract) // Problem methods. .def("fitness", lcast([](const pagmo::problem &p, const bp::object &dv) { - return pygmo::v_to_a(p.fitness(pygmo::to_vd(dv))); + return pygmo::vector_to_ndarr(p.fitness(pygmo::obj_to_vector(dv))); }), pygmo::problem_fitness_docstring().c_str(), (bp::arg("dv"))) .def("get_bounds", lcast([](const pagmo::problem &p) -> bp::tuple { auto retval = p.get_bounds(); - return bp::make_tuple(pygmo::v_to_a(retval.first), pygmo::v_to_a(retval.second)); + return bp::make_tuple(pygmo::vector_to_ndarr(retval.first), pygmo::vector_to_ndarr(retval.second)); }), pygmo::problem_get_bounds_docstring().c_str()) - .def("get_lb", lcast([](const pagmo::problem &p) { return pygmo::v_to_a(p.get_lb()); }), + .def("get_lb", lcast([](const pagmo::problem &p) { return pygmo::vector_to_ndarr(p.get_lb()); }), pygmo::problem_get_lb_docstring().c_str()) - .def("get_ub", lcast([](const pagmo::problem &p) { return pygmo::v_to_a(p.get_ub()); }), + .def("get_ub", lcast([](const pagmo::problem &p) { return pygmo::vector_to_ndarr(p.get_ub()); }), pygmo::problem_get_ub_docstring().c_str()) .def("batch_fitness", lcast([](const pagmo::problem &p, const bp::object &dvs) { - return pygmo::v_to_a(p.batch_fitness(pygmo::to_vd(dvs))); + return pygmo::vector_to_ndarr(p.batch_fitness(pygmo::obj_to_vector(dvs))); }), pygmo::problem_batch_fitness_docstring().c_str(), (bp::arg("dvs"))) .def("has_batch_fitness", &problem::has_batch_fitness, pygmo::problem_has_batch_fitness_docstring().c_str()) .def("gradient", lcast([](const pagmo::problem &p, const bp::object &dv) { - return pygmo::v_to_a(p.gradient(pygmo::to_vd(dv))); + return pygmo::vector_to_ndarr(p.gradient(pygmo::obj_to_vector(dv))); }), pygmo::problem_gradient_docstring().c_str(), (bp::arg("dv"))) .def("has_gradient", &problem::has_gradient, pygmo::problem_has_gradient_docstring().c_str()) - .def("gradient_sparsity", lcast([](const pagmo::problem &p) { return pygmo::sp_to_a(p.gradient_sparsity()); }), + .def("gradient_sparsity", + lcast([](const pagmo::problem &p) { return pygmo::sp_to_ndarr(p.gradient_sparsity()); }), pygmo::problem_gradient_sparsity_docstring().c_str()) .def("has_gradient_sparsity", &problem::has_gradient_sparsity, pygmo::problem_has_gradient_sparsity_docstring().c_str()) .def("hessians", lcast([](const pagmo::problem &p, const bp::object &dv) -> bp::list { bp::list retval; - const auto h = p.hessians(pygmo::to_vd(dv)); + const auto h = p.hessians(pygmo::obj_to_vector(dv)); for (const auto &v : h) { - retval.append(pygmo::v_to_a(v)); + retval.append(pygmo::vector_to_ndarr(v)); } return retval; }), @@ -669,7 +672,7 @@ BOOST_PYTHON_MODULE(core) bp::list retval; const auto hs = p.hessians_sparsity(); for (const auto &sp : hs) { - retval.append(pygmo::sp_to_a(sp)); + retval.append(pygmo::sp_to_ndarr(sp)); } return retval; }), @@ -691,23 +694,25 @@ BOOST_PYTHON_MODULE(core) .def("has_set_seed", &problem::has_set_seed, pygmo::problem_has_set_seed_docstring().c_str()) .def("is_stochastic", &problem::is_stochastic, "is_stochastic()\n\nAlias for :func:`~pygmo.problem.has_set_seed()`.\n") - .def("feasibility_x", - lcast([](const problem &p, const bp::object &x) { return p.feasibility_x(pygmo::to_vd(x)); }), + .def("feasibility_x", lcast([](const problem &p, const bp::object &x) { + return p.feasibility_x(pygmo::obj_to_vector(x)); + }), pygmo::problem_feasibility_x_docstring().c_str(), (bp::arg("x"))) - .def("feasibility_f", - lcast([](const problem &p, const bp::object &f) { return p.feasibility_f(pygmo::to_vd(f)); }), + .def("feasibility_f", lcast([](const problem &p, const bp::object &f) { + return p.feasibility_f(pygmo::obj_to_vector(f)); + }), pygmo::problem_feasibility_f_docstring().c_str(), (bp::arg("f"))) .def("get_name", &problem::get_name, pygmo::problem_get_name_docstring().c_str()) .def("get_extra_info", &problem::get_extra_info, pygmo::problem_get_extra_info_docstring().c_str()) .def("get_thread_safety", &problem::get_thread_safety, pygmo::problem_get_thread_safety_docstring().c_str()); pygmo::add_property(problem_class, "c_tol", - lcast([](const problem &prob) { return pygmo::v_to_a(prob.get_c_tol()); }), + lcast([](const problem &prob) { return pygmo::vector_to_ndarr(prob.get_c_tol()); }), lcast([](problem &prob, const bp::object &c_tol) { bp::extract c_tol_double(c_tol); if (c_tol_double.check()) { prob.set_c_tol(static_cast(c_tol_double)); } else { - prob.set_c_tol(pygmo::to_vd(c_tol)); + prob.set_c_tol(pygmo::obj_to_vector(c_tol)); } }), pygmo::problem_c_tol_docstring().c_str()); @@ -750,7 +755,7 @@ BOOST_PYTHON_MODULE(core) hv_class .def("__init__", bp::make_constructor(lcast([](const bp::object &points) { - auto vvd_points = pygmo::to_vvd(points); + auto vvd_points = pygmo::obj_to_vvector>(points); return ::new hypervolume(vvd_points, true); }), bp::default_call_policies(), (bp::arg("points"))), @@ -759,53 +764,56 @@ BOOST_PYTHON_MODULE(core) bp::make_constructor(lcast([](const population &pop) { return ::new hypervolume(pop, true); }), bp::default_call_policies(), (bp::arg("pop"))), pygmo::hv_init1_docstring().c_str()) - .def("compute", - lcast([](const hypervolume &hv, const bp::object &r_point) { return hv.compute(pygmo::to_vd(r_point)); }), + .def("compute", lcast([](const hypervolume &hv, const bp::object &r_point) { + return hv.compute(pygmo::obj_to_vector(r_point)); + }), (bp::arg("ref_point"))) .def("compute", lcast([](const hypervolume &hv, const bp::object &r_point, boost::shared_ptr hv_algo) { - return hv.compute(pygmo::to_vd(r_point), *hv_algo); + return hv.compute(pygmo::obj_to_vector(r_point), *hv_algo); }), pygmo::hv_compute_docstring().c_str(), (bp::arg("ref_point"), bp::arg("hv_algo"))) .def("exclusive", lcast([](const hypervolume &hv, unsigned p_idx, const bp::object &r_point) { - return hv.exclusive(p_idx, pygmo::to_vd(r_point)); + return hv.exclusive(p_idx, pygmo::obj_to_vector(r_point)); }), (bp::arg("idx"), bp::arg("ref_point"))) .def("exclusive", lcast([](const hypervolume &hv, unsigned p_idx, const bp::object &r_point, boost::shared_ptr hv_algo) { - return hv.exclusive(p_idx, pygmo::to_vd(r_point), *hv_algo); + return hv.exclusive(p_idx, pygmo::obj_to_vector(r_point), *hv_algo); }), pygmo::hv_exclusive_docstring().c_str(), (bp::arg("idx"), bp::arg("ref_point"), bp::arg("hv_algo"))) .def("least_contributor", lcast([](const hypervolume &hv, const bp::object &r_point) { - return hv.least_contributor(pygmo::to_vd(r_point)); + return hv.least_contributor(pygmo::obj_to_vector(r_point)); }), (bp::arg("ref_point"))) .def("least_contributor", lcast([](const hypervolume &hv, const bp::object &r_point, boost::shared_ptr hv_algo) { - return hv.least_contributor(pygmo::to_vd(r_point), *hv_algo); + return hv.least_contributor(pygmo::obj_to_vector(r_point), *hv_algo); }), pygmo::hv_least_contributor_docstring().c_str(), (bp::arg("ref_point"), bp::arg("hv_algo"))) .def("greatest_contributor", lcast([](const hypervolume &hv, const bp::object &r_point) { - return hv.greatest_contributor(pygmo::to_vd(r_point)); + return hv.greatest_contributor(pygmo::obj_to_vector(r_point)); }), (bp::arg("ref_point"))) .def("greatest_contributor", lcast([](const hypervolume &hv, const bp::object &r_point, boost::shared_ptr hv_algo) { - return hv.greatest_contributor(pygmo::to_vd(r_point), *hv_algo); + return hv.greatest_contributor(pygmo::obj_to_vector(r_point), *hv_algo); }), pygmo::hv_greatest_contributor_docstring().c_str(), (bp::arg("ref_point"), bp::arg("hv_algo"))) .def("contributions", lcast([](const hypervolume &hv, const bp::object &r_point) { - return pygmo::v_to_a(hv.contributions(pygmo::to_vd(r_point))); + return pygmo::vector_to_ndarr(hv.contributions(pygmo::obj_to_vector(r_point))); }), (bp::arg("ref_point"))) .def("contributions", lcast([](const hypervolume &hv, const bp::object &r_point, boost::shared_ptr hv_algo) { - return pygmo::v_to_a(hv.contributions(pygmo::to_vd(r_point), *hv_algo)); + return pygmo::vector_to_ndarr( + hv.contributions(pygmo::obj_to_vector(r_point), *hv_algo)); }), pygmo::hv_contributions_docstring().c_str(), (bp::arg("ref_point"), bp::arg("hv_algo"))) - .def("get_points", lcast([](const hypervolume &hv) { return pygmo::vv_to_a(hv.get_points()); })) - .def("refpoint", lcast([](const hypervolume &hv, double offset) { return pygmo::v_to_a(hv.refpoint(offset)); }), + .def("get_points", lcast([](const hypervolume &hv) { return pygmo::vvector_to_ndarr(hv.get_points()); })) + .def("refpoint", + lcast([](const hypervolume &hv, double offset) { return pygmo::vector_to_ndarr(hv.refpoint(offset)); }), pygmo::hv_refpoint_docstring().c_str(), (bp::arg("offset") = 0)); pygmo::add_property(hv_class, "copy_points", &hypervolume::get_copy_points, &hypervolume::set_copy_points); @@ -831,39 +839,46 @@ BOOST_PYTHON_MODULE(core) // Exposition of stand alone functions // Multi-objective utilities bp::def("fast_non_dominated_sorting", lcast([](const bp::object &x) -> bp::object { - auto fnds = fast_non_dominated_sorting(pygmo::to_vvd(x)); + auto fnds = fast_non_dominated_sorting(pygmo::obj_to_vvector>(x)); // the non-dominated fronts auto ndf = std::get<0>(fnds); bp::list ndf_py; for (const std::vector &front : ndf) { - ndf_py.append(pygmo::v_to_a(front)); + ndf_py.append(pygmo::vector_to_ndarr(front)); } // the domination list auto dl = std::get<1>(fnds); bp::list dl_py; for (const auto &item : dl) { - dl_py.append(pygmo::v_to_a(item)); + dl_py.append(pygmo::vector_to_ndarr(item)); } - return bp::make_tuple(ndf_py, dl_py, pygmo::v_to_a(std::get<2>(fnds)), - pygmo::v_to_a(std::get<3>(fnds))); + return bp::make_tuple(ndf_py, dl_py, pygmo::vector_to_ndarr(std::get<2>(fnds)), + pygmo::vector_to_ndarr(std::get<3>(fnds))); }), pygmo::fast_non_dominated_sorting_docstring().c_str(), boost::python::arg("points")); bp::def("pareto_dominance", lcast([](const bp::object &obj1, const bp::object &obj2) { - return pareto_dominance(pygmo::to_vd(obj1), pygmo::to_vd(obj2)); + return pareto_dominance(pygmo::obj_to_vector(obj1), + pygmo::obj_to_vector(obj2)); }), pygmo::pareto_dominance_docstring().c_str(), (bp::arg("obj1"), bp::arg("obj2"))); bp::def("non_dominated_front_2d", lcast([](const bp::object &points) { - return pygmo::v_to_a(non_dominated_front_2d(pygmo::to_vvd(points))); + return pygmo::vector_to_ndarr( + non_dominated_front_2d(pygmo::obj_to_vvector>(points))); }), pygmo::non_dominated_front_2d_docstring().c_str(), bp::arg("points")); - bp::def("crowding_distance", - lcast([](const bp::object &points) { return pygmo::v_to_a(crowding_distance(pygmo::to_vvd(points))); }), + bp::def("crowding_distance", lcast([](const bp::object &points) { + return pygmo::vector_to_ndarr( + crowding_distance(pygmo::obj_to_vvector>(points))); + }), pygmo::crowding_distance_docstring().c_str(), bp::arg("points")); - bp::def("sort_population_mo", - lcast([](const bp::object &input_f) { return pygmo::v_to_a(sort_population_mo(pygmo::to_vvd(input_f))); }), + bp::def("sort_population_mo", lcast([](const bp::object &input_f) { + return pygmo::vector_to_ndarr( + sort_population_mo(pygmo::obj_to_vvector>(input_f))); + }), pygmo::sort_population_mo_docstring().c_str(), bp::arg("points")); bp::def("select_best_N_mo", lcast([](const bp::object &input_f, unsigned N) { - return pygmo::v_to_a(select_best_N_mo(pygmo::to_vvd(input_f), N)); + return pygmo::vector_to_ndarr( + select_best_N_mo(pygmo::obj_to_vvector>(input_f), N)); }), pygmo::select_best_N_mo_docstring().c_str(), (bp::arg("points"), bp::arg("N"))); bp::def( @@ -871,7 +886,7 @@ BOOST_PYTHON_MODULE(core) lcast([](vector_double::size_type n_f, vector_double::size_type n_w, const std::string &method, unsigned seed) { using reng_t = pagmo::detail::random_engine_type; reng_t tmp_rng(static_cast(seed)); - return pygmo::vv_to_a(decomposition_weights(n_f, n_w, method, tmp_rng)); + return pygmo::vvector_to_ndarr(decomposition_weights(n_f, n_w, method, tmp_rng)); }), pygmo::decomposition_weights_docstring().c_str(), (bp::arg("n_f"), bp::arg("n_w"), bp::arg("method"), bp::arg("seed"))); @@ -879,22 +894,27 @@ BOOST_PYTHON_MODULE(core) bp::def("decompose_objectives", lcast([](const bp::object &objs, const bp::object &weights, const bp::object &ref_point, const std::string &method) { - return pygmo::v_to_a( - decompose_objectives(pygmo::to_vd(objs), pygmo::to_vd(weights), pygmo::to_vd(ref_point), method)); + return pygmo::vector_to_ndarr(decompose_objectives( + pygmo::obj_to_vector(objs), pygmo::obj_to_vector(weights), + pygmo::obj_to_vector(ref_point), method)); }), pygmo::decompose_objectives_docstring().c_str(), (bp::arg("objs"), bp::arg("weights"), bp::arg("ref_point"), bp::arg("method"))); - bp::def("nadir", lcast([](const bp::object &p) { return pygmo::v_to_a(pagmo::nadir(pygmo::to_vvd(p))); }), + bp::def("nadir", lcast([](const bp::object &p) { + return pygmo::vector_to_ndarr(pagmo::nadir(pygmo::obj_to_vvector>(p))); + }), pygmo::nadir_docstring().c_str(), bp::arg("points")); - bp::def("ideal", lcast([](const bp::object &p) { return pygmo::v_to_a(pagmo::ideal(pygmo::to_vvd(p))); }), + bp::def("ideal", lcast([](const bp::object &p) { + return pygmo::vector_to_ndarr(pagmo::ideal(pygmo::obj_to_vvector>(p))); + }), pygmo::ideal_docstring().c_str(), bp::arg("points")); // Generic utilities bp::def("random_decision_vector", lcast([](const pagmo::problem &p) -> bp::object { using reng_t = pagmo::detail::random_engine_type; reng_t tmp_rng(static_cast(pagmo::random_device::next())); auto retval = random_decision_vector(p, tmp_rng); - return pygmo::v_to_a(retval); + return pygmo::vector_to_ndarr(retval); }), pygmo::random_decision_vector_docstring().c_str(), (bp::arg("prob"))); bp::def("batch_random_decision_vector", @@ -902,38 +922,47 @@ BOOST_PYTHON_MODULE(core) using reng_t = pagmo::detail::random_engine_type; reng_t tmp_rng(static_cast(pagmo::random_device::next())); auto retval = batch_random_decision_vector(p, n, tmp_rng); - return pygmo::v_to_a(retval); + return pygmo::vector_to_ndarr(retval); }), pygmo::batch_random_decision_vector_docstring().c_str(), (bp::arg("prob"), bp::arg("n"))); // Gradient and Hessians utilities bp::def("estimate_sparsity", lcast([](const bp::object &func, const bp::object &x, double dx) -> bp::object { - auto f = [&func](const vector_double &x_) { return pygmo::to_vd(func(pygmo::v_to_a(x_))); }; - auto retval = estimate_sparsity(f, pygmo::to_vd(x), dx); - return pygmo::sp_to_a(retval); + auto f = [&func](const vector_double &x_) { + return pygmo::obj_to_vector(func(pygmo::vector_to_ndarr(x_))); + }; + auto retval = estimate_sparsity(f, pygmo::obj_to_vector(x), dx); + return pygmo::sp_to_ndarr(retval); }), pygmo::estimate_sparsity_docstring().c_str(), (bp::arg("callable"), bp::arg("x"), bp::arg("dx") = 1e-8)); bp::def("estimate_gradient", lcast([](const bp::object &func, const bp::object &x, double dx) -> bp::object { - auto f = [&func](const vector_double &x_) { return pygmo::to_vd(func(pygmo::v_to_a(x_))); }; - auto retval = estimate_gradient(f, pygmo::to_vd(x), dx); - return pygmo::v_to_a(retval); + auto f = [&func](const vector_double &x_) { + return pygmo::obj_to_vector(func(pygmo::vector_to_ndarr(x_))); + }; + auto retval = estimate_gradient(f, pygmo::obj_to_vector(x), dx); + return pygmo::vector_to_ndarr(retval); }), pygmo::estimate_gradient_docstring().c_str(), (bp::arg("callable"), bp::arg("x"), bp::arg("dx") = 1e-8)); bp::def("estimate_gradient_h", lcast([](const bp::object &func, const bp::object &x, double dx) -> bp::object { - auto f = [&func](const vector_double &x_) { return pygmo::to_vd(func(pygmo::v_to_a(x_))); }; - auto retval = estimate_gradient_h(f, pygmo::to_vd(x), dx); - return pygmo::v_to_a(retval); + auto f = [&func](const vector_double &x_) { + return pygmo::obj_to_vector(func(pygmo::vector_to_ndarr(x_))); + }; + auto retval = estimate_gradient_h(f, pygmo::obj_to_vector(x), dx); + return pygmo::vector_to_ndarr(retval); }), pygmo::estimate_gradient_h_docstring().c_str(), (bp::arg("callable"), bp::arg("x"), bp::arg("dx") = 1e-2)); // Constrained optimization utilities bp::def("compare_fc", lcast([](const bp::object &f1, const bp::object &f2, vector_double::size_type nec, const bp::object &tol) { - return compare_fc(pygmo::to_vd(f1), pygmo::to_vd(f2), nec, pygmo::to_vd(tol)); + return compare_fc(pygmo::obj_to_vector(f1), pygmo::obj_to_vector(f2), nec, + pygmo::obj_to_vector(tol)); }), pygmo::compare_fc_docstring().c_str(), (bp::arg("f1"), bp::arg("f2"), bp::arg("nec"), bp::arg("tol"))); bp::def("sort_population_con", lcast([](const bp::object &input_f, vector_double::size_type nec, const bp::object &tol) { - return pygmo::v_to_a(sort_population_con(pygmo::to_vvd(input_f), nec, pygmo::to_vd(tol))); + return pygmo::vector_to_ndarr( + sort_population_con(pygmo::obj_to_vvector>(input_f), nec, + pygmo::obj_to_vector(tol))); }), pygmo::sort_population_con_docstring().c_str(), (bp::arg("input_f"), bp::arg("nec"), bp::arg("tol"))); // Global random number generator @@ -994,7 +1023,7 @@ BOOST_PYTHON_MODULE(core) bp::list retval; auto fs = archi.get_champions_f(); for (const auto &f : fs) { - retval.append(pygmo::v_to_a(f)); + retval.append(pygmo::vector_to_ndarr(f)); } return retval; }), @@ -1003,7 +1032,7 @@ BOOST_PYTHON_MODULE(core) bp::list retval; auto xs = archi.get_champions_x(); for (const auto &x : xs) { - retval.append(pygmo::v_to_a(x)); + retval.append(pygmo::vector_to_ndarr(x)); } return retval; }), @@ -1021,8 +1050,9 @@ BOOST_PYTHON_MODULE(core) bp::list retval; const auto tmp = archi.get_migration_log(); for (const auto &le : tmp) { - retval.append(bp::make_tuple(std::get<0>(le), std::get<1>(le), pygmo::v_to_a(std::get<2>(le)), - pygmo::v_to_a(std::get<3>(le)), std::get<4>(le), std::get<5>(le))); + retval.append( + bp::make_tuple(std::get<0>(le), std::get<1>(le), pygmo::vector_to_ndarr(std::get<2>(le)), + pygmo::vector_to_ndarr(std::get<3>(le)), std::get<4>(le), std::get<5>(le))); } return retval; }), @@ -1052,7 +1082,7 @@ BOOST_PYTHON_MODULE(core) .def("_py_extract", &pygmo::generic_py_extract) // Bfe methods. .def("__call__", lcast([](const bfe &b, const problem &prob, const bp::object &dvs) { - return pygmo::v_to_a(b(prob, pygmo::to_vd(dvs))); + return pygmo::vector_to_ndarr(b(prob, pygmo::obj_to_vector(dvs))); }), pygmo::bfe_call_docstring().c_str(), (bp::arg("prob"), bp::arg("dvs"))) .def("get_name", &bfe::get_name, pygmo::bfe_get_name_docstring().c_str()) @@ -1077,7 +1107,7 @@ BOOST_PYTHON_MODULE(core) // Topology methods. .def("get_connections", lcast([](const topology &t, std::size_t n) -> bp::tuple { auto ret = t.get_connections(n); - return bp::make_tuple(pygmo::v_to_a(ret.first), pygmo::v_to_a(ret.second)); + return bp::make_tuple(pygmo::vector_to_ndarr(ret.first), pygmo::vector_to_ndarr(ret.second)); }), pygmo::topology_get_connections_docstring().c_str(), (bp::arg("prob"), bp::arg("dvs"))) .def("push_back", lcast([](topology &t, unsigned n) { t.push_back(n); }), @@ -1106,8 +1136,8 @@ BOOST_PYTHON_MODULE(core) const vector_double::size_type &nix, const vector_double::size_type &nobj, const vector_double::size_type &nec, const vector_double::size_type &nic, const bp::object &tol, const bp::object &mig) -> bp::tuple { - auto ret - = r.replace(pygmo::to_inds(inds), nx, nix, nobj, nec, nic, pygmo::to_vd(tol), pygmo::to_inds(mig)); + auto ret = r.replace(pygmo::obj_to_inds(inds), nx, nix, nobj, nec, nic, + pygmo::obj_to_vector(tol), pygmo::obj_to_inds(mig)); return pygmo::inds_to_tuple(ret); }), pygmo::r_policy_replace_docstring().c_str(), @@ -1137,7 +1167,8 @@ BOOST_PYTHON_MODULE(core) const vector_double::size_type &nix, const vector_double::size_type &nobj, const vector_double::size_type &nec, const vector_double::size_type &nic, const bp::object &tol) -> bp::tuple { - auto ret = s.select(pygmo::to_inds(inds), nx, nix, nobj, nec, nic, pygmo::to_vd(tol)); + auto ret = s.select(pygmo::obj_to_inds(inds), nx, nix, nobj, nec, nic, + pygmo::obj_to_vector(tol)); return pygmo::inds_to_tuple(ret); }), pygmo::s_policy_select_docstring().c_str(), diff --git a/pygmo/expose_algorithms_0.cpp b/pygmo/expose_algorithms_0.cpp index 09983e538..85ddcc976 100644 --- a/pygmo/expose_algorithms_0.cpp +++ b/pygmo/expose_algorithms_0.cpp @@ -48,6 +48,7 @@ see https://www.gnu.org/licenses/. */ #include #include #include +#include #include #include @@ -83,6 +84,7 @@ see https://www.gnu.org/licenses/. */ #include #include #include +#include #include #include @@ -139,21 +141,23 @@ void expose_algorithms_0() { // MBH meta-algo. auto mbh_ = expose_algorithm_pygmo("mbh", mbh_docstring().c_str()); - mbh_.def("__init__", - bp::make_constructor(lcast([](const algorithm &a, unsigned stop, const bp::object &perturb, - unsigned seed) { return ::new pagmo::mbh(a, stop, to_vd(perturb), seed); }), - bp::default_call_policies())); + mbh_.def("__init__", bp::make_constructor( + lcast([](const algorithm &a, unsigned stop, const bp::object &perturb, unsigned seed) { + return ::new pagmo::mbh(a, stop, obj_to_vector(perturb), seed); + }), + bp::default_call_policies())); mbh_.def("__init__", bp::make_constructor(lcast([](const algorithm &a, unsigned stop, const bp::object &perturb) { - return ::new pagmo::mbh(a, stop, to_vd(perturb), + return ::new pagmo::mbh(a, stop, + obj_to_vector(perturb), pagmo::random_device::next()); }), bp::default_call_policies())); mbh_.def("get_seed", &mbh::get_seed, mbh_get_seed_docstring().c_str()); mbh_.def("get_verbosity", &mbh::get_verbosity, mbh_get_verbosity_docstring().c_str()); - mbh_.def("set_perturb", lcast([](mbh &a, const bp::object &o) { a.set_perturb(to_vd(o)); }), + mbh_.def("set_perturb", lcast([](mbh &a, const bp::object &o) { a.set_perturb(obj_to_vector(o)); }), mbh_set_perturb_docstring().c_str(), (bp::arg("perturb"))); expose_algo_log(mbh_, mbh_get_log_docstring().c_str()); - mbh_.def("get_perturb", lcast([](const mbh &a) { return v_to_a(a.get_perturb()); }), + mbh_.def("get_perturb", lcast([](const mbh &a) { return vector_to_ndarr(a.get_perturb()); }), mbh_get_perturb_docstring().c_str()); add_property(mbh_, "inner_algorithm", bp::make_function(lcast([](mbh &uda) -> algorithm & { return uda.get_inner_algorithm(); }), @@ -229,7 +233,7 @@ void expose_algorithms_0() de1220_.def("__init__", bp::make_constructor(lcast([](unsigned gen, const bp::object &allowed_variants, unsigned variant_adptv, double ftol, double xtol, bool memory) -> de1220 * { - auto av = to_vuint(allowed_variants); + auto av = obj_to_vector>(allowed_variants); return ::new de1220(gen, av, variant_adptv, ftol, xtol, memory); }), bp::default_call_policies(), @@ -239,7 +243,7 @@ void expose_algorithms_0() de1220_.def("__init__", bp::make_constructor(lcast([](unsigned gen, const bp::object &allowed_variants, unsigned variant_adptv, double ftol, double xtol, bool memory, unsigned seed) -> de1220 * { - auto av = to_vuint(allowed_variants); + auto av = obj_to_vector>(allowed_variants); return ::new de1220(gen, av, variant_adptv, ftol, xtol, memory, seed); }), bp::default_call_policies(), @@ -292,8 +296,8 @@ void expose_algorithms_0() moead_.def("get_log", lcast([](const moead &a) -> bp::list { bp::list retval; for (const auto &t : a.get_log()) { - retval.append( - bp::make_tuple(std::get<0>(t), std::get<1>(t), std::get<2>(t), v_to_a(std::get<3>(t)))); + retval.append(bp::make_tuple(std::get<0>(t), std::get<1>(t), std::get<2>(t), + vector_to_ndarr(std::get<3>(t)))); } return retval; }), diff --git a/pygmo/expose_algorithms_1.cpp b/pygmo/expose_algorithms_1.cpp index 86ba6e252..b393fbb2a 100644 --- a/pygmo/expose_algorithms_1.cpp +++ b/pygmo/expose_algorithms_1.cpp @@ -135,7 +135,7 @@ void expose_algorithms_1() for (const auto &t : a.get_log()) { retval.append(bp::make_tuple(std::get<0>(t), std::get<1>(t), std::get<2>(t), std::get<3>(t), std::get<4>(t), std::get<5>(t), std::get<6>(t), - v_to_a(std::get<7>(t)))); + vector_to_ndarr(std::get<7>(t)))); } return retval; }), @@ -192,7 +192,7 @@ void expose_algorithms_1() nsga2_.def("get_log", lcast([](const nsga2 &a) -> bp::list { bp::list retval; for (const auto &t : a.get_log()) { - retval.append(bp::make_tuple(std::get<0>(t), std::get<1>(t), v_to_a(std::get<2>(t)))); + retval.append(bp::make_tuple(std::get<0>(t), std::get<1>(t), vector_to_ndarr(std::get<2>(t)))); } return retval; }), diff --git a/pygmo/expose_problems_0.cpp b/pygmo/expose_problems_0.cpp index 2fe99097c..5b7a1cfcd 100644 --- a/pygmo/expose_problems_0.cpp +++ b/pygmo/expose_problems_0.cpp @@ -46,6 +46,7 @@ see https://www.gnu.org/licenses/. */ #include #include +#include #include #include @@ -182,7 +183,8 @@ void expose_problems_0() auto dtlz_p = expose_problem_pygmo("dtlz", dtlz_docstring().c_str()); dtlz_p.def(bp::init( (bp::arg("prob_id") = 1u, bp::arg("dim") = 5u, bp::arg("fdim") = 3u, bp::arg("alpha") = 100u))); - dtlz_p.def("p_distance", lcast([](const dtlz &z, const bp::object &x) { return z.p_distance(to_vd(x)); })); + dtlz_p.def("p_distance", + lcast([](const dtlz &z, const bp::object &x) { return z.p_distance(obj_to_vector(x)); })); dtlz_p.def("p_distance", lcast([](const dtlz &z, const population &pop) { return z.p_distance(pop); }), dtlz_p_distance_docstring().c_str()); // Inventory. @@ -212,16 +214,18 @@ void expose_problems_0() auto decompose_ = expose_problem_pygmo("decompose", decompose_docstring().c_str()); // NOTE: An __init__ wrapper on the Python side will take care of cting a pagmo::problem from the input UDP, // and then invoke this ctor. This way we avoid having to expose a different ctor for every exposed C++ prob. - decompose_.def("__init__", - bp::make_constructor(lcast([](const problem &p, const bp::object &weight, const bp::object &z, - const std::string &method, bool adapt_ideal) { - return ::new decompose(p, to_vd(weight), to_vd(z), method, adapt_ideal); - }), - bp::default_call_policies())); - decompose_.def("original_fitness", - lcast([](const decompose &p, const bp::object &x) { return v_to_a(p.original_fitness(to_vd(x))); }), + decompose_.def("__init__", bp::make_constructor( + lcast([](const problem &p, const bp::object &weight, const bp::object &z, + const std::string &method, bool adapt_ideal) { + return ::new decompose(p, obj_to_vector(weight), + obj_to_vector(z), method, adapt_ideal); + }), + bp::default_call_policies())); + decompose_.def("original_fitness", lcast([](const decompose &p, const bp::object &x) { + return vector_to_ndarr(p.original_fitness(obj_to_vector(x))); + }), decompose_original_fitness_docstring().c_str(), (bp::arg("x"))); - add_property(decompose_, "z", lcast([](const decompose &p) { return v_to_a(p.get_z()); }), + add_property(decompose_, "z", lcast([](const decompose &p) { return vector_to_ndarr(p.get_z()); }), decompose_z_docstring().c_str()); add_property(decompose_, "inner_problem", bp::make_function(lcast([](decompose &udp) -> problem & { return udp.get_inner_problem(); }), diff --git a/pygmo/expose_problems_1.cpp b/pygmo/expose_problems_1.cpp index b77745a75..aaaed9aaa 100644 --- a/pygmo/expose_problems_1.cpp +++ b/pygmo/expose_problems_1.cpp @@ -109,7 +109,8 @@ void expose_problems_1() auto zdt_p = expose_problem_pygmo("zdt", "__init__(prob_id = 1, param = 30)\n\nThe ZDT problem.\n\n" "See :cpp:class:`pagmo::zdt`.\n\n"); zdt_p.def(bp::init((bp::arg("prob_id") = 1u, bp::arg("param") = 30u))); - zdt_p.def("p_distance", lcast([](const zdt &z, const bp::object &x) { return z.p_distance(to_vd(x)); })); + zdt_p.def("p_distance", + lcast([](const zdt &z, const bp::object &x) { return z.p_distance(obj_to_vector(x)); })); zdt_p.def("p_distance", lcast([](const zdt &z, const population &pop) { return z.p_distance(pop); }), zdt_p_distance_docstring().c_str()); @@ -134,10 +135,11 @@ void expose_problems_1() // NOTE: An __init__ wrapper on the Python side will take care of cting a pagmo::problem from the input UDP, // and then invoke this ctor. This way we avoid having to expose a different ctor for every exposed C++ prob. translate_.def("__init__", bp::make_constructor(lcast([](const problem &p, const bp::object &tv) { - return ::new translate(p, to_vd(tv)); + return ::new translate(p, obj_to_vector(tv)); }), bp::default_call_policies())); - add_property(translate_, "translation", lcast([](const translate &t) { return v_to_a(t.get_translation()); }), + add_property(translate_, "translation", + lcast([](const translate &t) { return vector_to_ndarr(t.get_translation()); }), translate_translation_docstring().c_str()); add_property(translate_, "inner_problem", bp::make_function(lcast([](translate &udp) -> problem & { return udp.get_inner_problem(); }), @@ -149,7 +151,7 @@ void expose_problems_1() // and then invoke this ctor. This way we avoid having to expose a different ctor for every exposed C++ prob. unconstrain_.def("__init__", bp::make_constructor( lcast([](const problem &p, const std::string &method, const bp::object &weights) { - return ::new unconstrain(p, method, to_vd(weights)); + return ::new unconstrain(p, method, obj_to_vector(weights)); }), bp::default_call_policies())); add_property(unconstrain_, "inner_problem", diff --git a/pygmo/problem.cpp b/pygmo/problem.cpp index 10cbca07f..58322b9e8 100644 --- a/pygmo/problem.cpp +++ b/pygmo/problem.cpp @@ -101,7 +101,7 @@ std::unique_ptr prob_inner::clone() const vector_double prob_inner::fitness(const vector_double &dv) const { - return pygmo::to_vd(m_value.attr("fitness")(pygmo::v_to_a(dv))); + return pygmo::obj_to_vector(m_value.attr("fitness")(pygmo::vector_to_ndarr(dv))); } std::pair prob_inner::get_bounds() const @@ -115,7 +115,7 @@ std::pair prob_inner::get_bounds() con .c_str()); } // Finally, we build the pair from the tuple elements. - return std::make_pair(pygmo::to_vd(tup[0]), pygmo::to_vd(tup[1])); + return std::make_pair(pygmo::obj_to_vector(tup[0]), pygmo::obj_to_vector(tup[1])); } vector_double prob_inner::batch_fitness(const vector_double &dv) const @@ -129,7 +129,7 @@ vector_double prob_inner::batch_fitness(const vector_double &dv) con + "': the method is either not present or not callable") .c_str()); } - return pygmo::to_vd(bf(pygmo::v_to_a(dv))); + return pygmo::obj_to_vector(bf(pygmo::vector_to_ndarr(dv))); } bool prob_inner::has_batch_fitness() const @@ -206,7 +206,7 @@ vector_double prob_inner::gradient(const vector_double &dv) const + "': the method is either not present or not callable") .c_str()); } - return pygmo::to_vd(g(pygmo::v_to_a(dv))); + return pygmo::obj_to_vector(g(pygmo::vector_to_ndarr(dv))); } bool prob_inner::has_gradient_sparsity() const @@ -245,7 +245,7 @@ sparsity_pattern prob_inner::gradient_sparsity() const "at a later stage") .c_str()); } - return pygmo::to_sp(gs()); + return pygmo::obj_to_sp(gs()); } bool prob_inner::has_hessians() const @@ -276,11 +276,12 @@ std::vector prob_inner::hessians(const vector_double .c_str()); } // Invoke the method, getting out a generic Python object. - bp::object tmp = h(pygmo::v_to_a(dv)); + bp::object tmp = h(pygmo::vector_to_ndarr(dv)); // Let's build the return value. std::vector retval; bp::stl_input_iterator begin(tmp), end; - std::transform(begin, end, std::back_inserter(retval), [](const bp::object &o) { return pygmo::to_vd(o); }); + std::transform(begin, end, std::back_inserter(retval), + [](const bp::object &o) { return pygmo::obj_to_vector(o); }); return retval; } @@ -316,7 +317,7 @@ std::vector prob_inner::hessians_sparsity() const bp::object tmp = hs(); std::vector retval; bp::stl_input_iterator begin(tmp), end; - std::transform(begin, end, std::back_inserter(retval), [](const bp::object &o) { return pygmo::to_sp(o); }); + std::transform(begin, end, std::back_inserter(retval), [](const bp::object &o) { return pygmo::obj_to_sp(o); }); return retval; } diff --git a/pygmo/problem_exposition_suite.hpp b/pygmo/problem_exposition_suite.hpp index 4079f2b79..db356fd40 100644 --- a/pygmo/problem_exposition_suite.hpp +++ b/pygmo/problem_exposition_suite.hpp @@ -77,7 +77,7 @@ inline bp::class_ expose_problem(const char *name, const char *descr) template inline bp::object best_known_wrapper(const Prob &p) { - return v_to_a(p.best_known()); + return vector_to_ndarr(p.best_known()); } } // namespace pygmo diff --git a/pygmo/r_policy.cpp b/pygmo/r_policy.cpp index 02e517058..a99f03707 100644 --- a/pygmo/r_policy.cpp +++ b/pygmo/r_policy.cpp @@ -112,11 +112,11 @@ r_pol_inner::replace(const individuals_group_t &inds, const vector_d try { // Fetch the new individuals in Python form. - bp::object o = m_value.attr("replace")(pygmo::inds_to_tuple(inds), nx, nix, nobj, nec, nic, pygmo::v_to_a(tol), - pygmo::inds_to_tuple(mig)); + bp::object o = m_value.attr("replace")(pygmo::inds_to_tuple(inds), nx, nix, nobj, nec, nic, + pygmo::vector_to_ndarr(tol), pygmo::inds_to_tuple(mig)); // Convert back to C++ form and return. - return pygmo::to_inds(o); + return pygmo::obj_to_inds(o); } catch (const bp::error_already_set &) { pygmo::handle_thread_py_exception("The replace() method of a pythonic replacement policy of type '" + r_pol_name + "' raised an error:\n"); diff --git a/pygmo/s_policy.cpp b/pygmo/s_policy.cpp index 8640ee9c5..75fdf86e8 100644 --- a/pygmo/s_policy.cpp +++ b/pygmo/s_policy.cpp @@ -112,10 +112,11 @@ individuals_group_t s_pol_inner::select(const individuals_group_t &i try { // Fetch the new individuals in Python form. - bp::object o = m_value.attr("select")(pygmo::inds_to_tuple(inds), nx, nix, nobj, nec, nic, pygmo::v_to_a(tol)); + bp::object o + = m_value.attr("select")(pygmo::inds_to_tuple(inds), nx, nix, nobj, nec, nic, pygmo::vector_to_ndarr(tol)); // Convert back to C++ form and return. - return pygmo::to_inds(o); + return pygmo::obj_to_inds(o); } catch (const bp::error_already_set &) { pygmo::handle_thread_py_exception("The select() method of a pythonic selection policy of type '" + s_pol_name + "' raised an error:\n"); diff --git a/pygmo/test.py b/pygmo/test.py index 5cc28ce82..bc89ebac4 100644 --- a/pygmo/test.py +++ b/pygmo/test.py @@ -125,8 +125,7 @@ def runTest(self): self.assert_(_test_to_vd(array([0, 1]), 2)) self.assert_(_test_to_vvd([[1., 2, 3], [4, 5, 6]], 2, 3)) self.assert_(_test_to_vvd(array([[1., 2, 3], [4, 5, 6]]), 2, 3)) - self.assertRaises(TypeError, lambda: _test_to_vvd( - ([1., 2, 3], [4, 5, 6]), 2, 3)) + self.assert_(_test_to_vvd(([1., 2, 3], [4, 5, 6]), 2, 3)) self.assertEqual(type(int), _type(int)) self.assertEqual(str(123), _str(123)) self.assertEqual(callable(1), _callable(1)) @@ -2002,8 +2001,8 @@ def run_mt_mh_tests(self): def run_mo_migration_bug_test(self): from . import dtlz, nsga2, ring, archipelago - udp = dtlz(2, dim = 50) - uda = nsga2(gen = 100) + udp = dtlz(2, dim=50) + uda = nsga2(gen=100) topo = ring() archi = archipelago(n=10, t=topo, prob=udp, algo=uda, pop_size=100) @@ -2161,7 +2160,8 @@ def run_push_back_tests(self): # Error handling. with self.assertRaises(ValueError) as cm: - a.push_back(island(algo=de(), prob=rosenbrock(), size=10), a=5, b=6) + a.push_back( + island(algo=de(), prob=rosenbrock(), size=10), a=5, b=6) err = cm.exception self.assertTrue( "if a positional argument is passed to this method, then no keyword arguments must be passed, but 2 keyword arguments were passed instead" in str(err)) diff --git a/pygmo/topology.cpp b/pygmo/topology.cpp index 058fadb89..e06534a1f 100644 --- a/pygmo/topology.cpp +++ b/pygmo/topology.cpp @@ -128,7 +128,7 @@ std::pair, vector_double> topo_inner::get_c .c_str()); } - retval.first = pygmo::to_vuint(*begin); + retval.first = pygmo::obj_to_vector>(*begin); if (++begin == end) { // Only one element in the iteratable. @@ -137,7 +137,7 @@ std::pair, vector_double> topo_inner::get_c .c_str()); } - retval.second = pygmo::to_vd(*begin); + retval.second = pygmo::obj_to_vector(*begin); if (++begin != end) { // Too many elements. From a2baaac50bf8af0dc6f8b2ef3dc12a82e6fc236b Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Mon, 9 Sep 2019 00:50:21 +0200 Subject: [PATCH 4/8] Docstring fixes. --- pygmo/docstrings.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pygmo/docstrings.cpp b/pygmo/docstrings.cpp index 5c218cbb9..ee4e2a996 100644 --- a/pygmo/docstrings.cpp +++ b/pygmo/docstrings.cpp @@ -3502,7 +3502,7 @@ IEEE Transactions on Evolutionary Computation 7.5 (2003): 503-515. Examples: >>> import pygmo as pg >>> pg.non_dominated_front_2d(points = [[0,5],[1,4],[2,3],[3,2],[4,1],[2,2]]) - array([0, 1, 5, 4]) + array([0, 1, 5, 4], dtype=uint64) )"; } @@ -3865,7 +3865,7 @@ confident the estimate will be correct. array([[0, 0], [0, 3], [1, 2], - [2, 1]]) + [2, 1]], dtype=uint64) )"; } From 2a1be7a3f9070914e9f3db26a9af6105044c74fc Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Mon, 9 Sep 2019 01:04:53 +0200 Subject: [PATCH 5/8] Some additional testing. --- pygmo/_r_policy_test.py | 29 +++++++++++++++++++++++++++++ pygmo/_s_policy_test.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/pygmo/_r_policy_test.py b/pygmo/_r_policy_test.py index 7583489dc..b185ffa70 100644 --- a/pygmo/_r_policy_test.py +++ b/pygmo/_r_policy_test.py @@ -204,6 +204,35 @@ def replace(self, inds, nx, nix, nobj, nec, nic, tol, mig): r_pol.replace(([1, 2], [[.1, .2], [.3, .4]], [ [1.1], [2.2]]), 2, 0, 1, 0, 0, [], ([], [], [])) + class r(object): + + def replace(self, inds, nx, nix, nobj, nec, nic, tol, mig): + return (np.array([1], dtype='float64'), [[1, 2]], [[1]]) + r_pol = r_policy(r()) + with self.assertRaises(RuntimeError) as cm: + r_pol.replace(([1, 2], [[.1, .2], [.3, .4]], [ + [1.1], [2.2]]), 2, 0, 1, 0, 0, [], ([], [], [])) + + # Test construction of array ID from list. + inds = ([1, 2], [[.1, .2], [.3, .4]], [[1.1], [2.2]]) + + class r(object): + + def replace(self, inds, nx, nix, nobj, nec, nic, tol, mig): + return ([1], [[1, 2]], [[1]]) + r_pol = r_policy(r()) + ret = r_pol.replace(inds, 2, 0, 1, 0, 0, [], inds) + self.assertEqual(ret[0][0], 1) + + # Test construction of array ID from list. + class r(object): + + def replace(self, inds, nx, nix, nobj, nec, nic, tol, mig): + return (np.array([1], dtype='ulonglong'), [[1, 2]], [[1]]) + r_pol = r_policy(r()) + ret = r_pol.replace(inds, 2, 0, 1, 0, 0, [], inds) + self.assertEqual(ret[0][0], 1) + # Test that construction from another pygmo.r_policy fails. with self.assertRaises(TypeError) as cm: r_policy(r_pol) diff --git a/pygmo/_s_policy_test.py b/pygmo/_s_policy_test.py index 8f7a41843..303d9964a 100644 --- a/pygmo/_s_policy_test.py +++ b/pygmo/_s_policy_test.py @@ -204,6 +204,36 @@ def select(self, inds, nx, nix, nobj, nec, nic, tol): s_pol.select(([1, 2], [[.1, .2], [.3, .4]], [ [1.1], [2.2]]), 2, 0, 1, 0, 0, []) + # Test wrong array construction of IDs. + class r(object): + + def select(self, inds, nx, nix, nobj, nec, nic, tol): + return (np.array([1], dtype='float64'), [[1, 2]], [[1]]) + s_pol = s_policy(r()) + with self.assertRaises(RuntimeError) as cm: + s_pol.select(([1, 2], [[.1, .2], [.3, .4]], [ + [1.1], [2.2]]), 2, 0, 1, 0, 0, []) + + # Test construction of array ID from list. + class r(object): + + def select(self, inds, nx, nix, nobj, nec, nic, tol): + return ([1], [[1, 2]], [[1]]) + s_pol = s_policy(r()) + ret = s_pol.select(([1, 2], [[.1, .2], [.3, .4]], [ + [1.1], [2.2]]), 2, 0, 1, 0, 0, []) + self.assertEqual(ret[0][0], 1) + + # Test construction of array ID from array. + class r(object): + + def select(self, inds, nx, nix, nobj, nec, nic, tol): + return (np.array([1], dtype='ulonglong'), [[1, 2]], [[1]]) + s_pol = s_policy(r()) + ret = s_pol.select(([1, 2], [[.1, .2], [.3, .4]], [ + [1.1], [2.2]]), 2, 0, 1, 0, 0, []) + self.assertEqual(ret[0][0], 1) + # Test that construction from another pygmo.s_policy fails. with self.assertRaises(TypeError) as cm: s_policy(s_pol) From 4617cf295e44bcf20626fefa5c63e4607b9e013a Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Mon, 9 Sep 2019 01:11:21 +0200 Subject: [PATCH 6/8] Update changelog. --- doc/sphinx/changelog.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/sphinx/changelog.rst b/doc/sphinx/changelog.rst index 44585ea22..5048e7ae4 100644 --- a/doc/sphinx/changelog.rst +++ b/doc/sphinx/changelog.rst @@ -4,9 +4,22 @@ Changelog 2.11.3 (unreleased) ------------------- +New +~~~ + +- :func:`pygmo.archipelago.push_back()` now also accepts :class:`~pygmo.island` + objects as input arguments (`#342 `__). + Fix ~~~ +- Various improvements, fixes and cleanups in the translation of + C++ vectors to/from Python + (`#342 `__). + +- Fix the printing of islands which contain MO problems + (`#342 `__). + - Various doc improvements and fixes (`#340 `__). 2.11.2 (2019-08-21) From 897b0f493c925dd07058197c66d3df895fa5e3c0 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Mon, 9 Sep 2019 01:12:51 +0200 Subject: [PATCH 7/8] Minor. --- pygmo/_r_policy_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmo/_r_policy_test.py b/pygmo/_r_policy_test.py index b185ffa70..a1c9dafd2 100644 --- a/pygmo/_r_policy_test.py +++ b/pygmo/_r_policy_test.py @@ -224,7 +224,7 @@ def replace(self, inds, nx, nix, nobj, nec, nic, tol, mig): ret = r_pol.replace(inds, 2, 0, 1, 0, 0, [], inds) self.assertEqual(ret[0][0], 1) - # Test construction of array ID from list. + # Test construction of array ID from numpy array. class r(object): def replace(self, inds, nx, nix, nobj, nec, nic, tol, mig): From 8f634ecc7bf886d0b9446eaf74a00a70ef60f9af Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Mon, 9 Sep 2019 10:26:11 +0200 Subject: [PATCH 8/8] A couple of small bits. --- doc/sphinx/changelog.rst | 10 ++++++++++ pygmo/common_utils.hpp | 3 +++ 2 files changed, 13 insertions(+) diff --git a/doc/sphinx/changelog.rst b/doc/sphinx/changelog.rst index 5048e7ae4..13fcc1ec8 100644 --- a/doc/sphinx/changelog.rst +++ b/doc/sphinx/changelog.rst @@ -10,6 +10,16 @@ New - :func:`pygmo.archipelago.push_back()` now also accepts :class:`~pygmo.island` objects as input arguments (`#342 `__). +Changes +~~~~~~~ + +- **BREAKING**: the machinery for the translation between C++ and Python + of vectors of unsigned integral types (e.g., sparsity patterns, individual + IDs, etc.) now requires that, on the Python side, NumPy arrays are created + with the appropriate unsigned integral dtype (e.g., ``uint64`` in most + cases). Previously, pagmo would accept also signed integral dtypes + (`#342 `__). + Fix ~~~ diff --git a/pygmo/common_utils.hpp b/pygmo/common_utils.hpp index eaf7693ec..1671b5c58 100644 --- a/pygmo/common_utils.hpp +++ b/pygmo/common_utils.hpp @@ -323,6 +323,9 @@ inline VVector ndarr_to_vvector(PyArrayObject *o) auto data = static_cast(PyArray_DATA(o)); const auto ssize = PyArray_SHAPE(o)[1]; for (size_type i = 0; i < size; ++i, data += ssize) { + // NOTE: here and elsewhere in this header where we + // use push_back()/emplace_back(), we should really reserve() + // beforeheand. retval.push_back(typename VVector::value_type(data, data + ssize)); } }