Skip to content

Commit

Permalink
Merge pull request #334 from bluescarni/pr/fix_mo_migration
Browse files Browse the repository at this point in the history
MO migration fix
  • Loading branch information
bluescarni authored Aug 9, 2019
2 parents a0126d5 + da56a7e commit c57dcc8
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 19 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ if((NOT PAGMO_BUILD_PAGMO) AND (NOT PAGMO_BUILD_PYGMO))
endif()

# Main pagmo/pygmo project version.
set(PAGMO_PROJECT_VERSION 2.11)
set(PAGMO_PROJECT_VERSION 2.11.1)

if(PAGMO_BUILD_PAGMO)
# Initial setup of a pagmo build.
Expand Down
10 changes: 10 additions & 0 deletions doc/sphinx/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
Changelog
=========

2.11.1 (unreleased)
-------------------

Fix
~~~

- Fix a migration issue when multi-objective problems are involved (`#334 <https://github.com/esa/pagmo2/pull/334>`__).

- Various docstring fixes (`#334 <https://github.com/esa/pagmo2/pull/334>`__).

2.11 (2019-08-07)
-----------------

Expand Down
4 changes: 4 additions & 0 deletions doc/sphinx/docs/python/utils/py_mo_utils.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ Multi-objective optimization utilities

-------------------------------------------------------

.. autofunction:: pygmo.crowding_distance

-------------------------------------------------------

.. autofunction:: pygmo.sort_population_mo

-------------------------------------------------------
Expand Down
26 changes: 14 additions & 12 deletions pygmo/docstrings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3436,7 +3436,7 @@ Runs the fast non dominated sorting algorithm on the input *points*
TypeError: if *points* cannot be converted to a vector of vector floats
Returns:
``tuple``: (*ndf*, *dl*, *dc*, *ndr*), where:
tuple: (*ndf*, *dl*, *dc*, *ndr*), where:
* *ndf* (``list`` of 1D NumPy int array): the non dominated fronts
* *dl* (``list`` of 1D NumPy int array): the domination list
Expand Down Expand Up @@ -3469,7 +3469,7 @@ but at least one is different, ``True`` will be returned. Otherwise, ``False`` w
TypeError: if *obj1* or *obj2* cannot be converted to a vector of vector floats
Returns:
``bool``: ``True`` if *obj1* is dominating *obj2*, ``False`` otherwise.
bool: :data:`True` if *obj1* is dominating *obj2*, :data:`False` otherwise.
Examples:
>>> import pygmo as pg
Expand Down Expand Up @@ -3497,7 +3497,7 @@ IEEE Transactions on Evolutionary Computation 7.5 (2003): 503-515.
TypeError: if *points* cannot be converted to a vector of vector floats
Returns:
(``list`` of 1D NumPy int array): the non dominated fronts
1D NumPy int array: the non dominated fronts
Examples:
>>> import pygmo as pg
Expand All @@ -3508,7 +3508,7 @@ IEEE Transactions on Evolutionary Computation 7.5 (2003): 503-515.

std::string crowding_distance_docstring()
{
return R"(non_dominated_front_2d(points)
return R"(crowding_distance(points)
An implementation of the crowding distance. Complexity is :math:`O(M N \log N)` where :math:`M` is the number of
objectives and :math:`N` is the number of individuals. The function assumes *points* contain a non-dominated front.
Expand All @@ -3525,12 +3525,12 @@ optimization: NSGA-II." Parallel problem solving from nature PPSN VI. Springer B
TypeError: if *points* cannot be converted to a vector of vector floats
Returns:
(``list`` of 1D NumPy int array): the non dominated fronts
1D NumPy float array: the crowding distances
Examples:
>>> import pygmo as pg
>>> pg.crowding_distance(points = [[0,5],[1,4],[2,3],[3,2],[4,1]])
array([ inf, 1., 1., 1., inf])
array([inf, 1., 1., 1., inf])
)";
}

Expand All @@ -3555,11 +3555,11 @@ Complexity is :math:`\mathcal{O}(M N^2)` where :math:`M` is the size of the obje
points (2d-array-like object): the input objective vectors
Raises:
unspecified: all exceptions thrown by pagmo::fast_non_dominated_sorting and pagmo::crowding_distance
unspecified: all exceptions thrown by :func:`pygmo.fast_non_dominated_sorting()` and :func:`pygmo.crowding_distance()`
TypeError: if *points* cannot be converted to a vector of vector floats
Returns:
(``list`` of 1D NumPy int array): the indexes of the sorted objectives vectors.
1D NumPy int array: the indexes of the sorted objectives vectors.
Examples:
>>> import pygmo as pg
Expand All @@ -3583,16 +3583,18 @@ While the complexity is the same as that of :func:`~pygmo.sort_population_mo()`,
possible in that it avoids to compute the crowidng distance for all individuals and only computes it for the last
non-dominated front containing individuals included in the best N.
If N is zero, an empty array will be returned.
Args:
points (2d-array-like object): the input objective vectors
N (``int``): The size of the returned list of bests.
N (int): The size of the returned list of bests.
Raises:
unspecified: all exceptions thrown by :cpp:func:`pagmo::fast_non_dominated_sorting()` and :cpp:func:`pagmo::crowding_distance()`
unspecified: all exceptions thrown by :func:`pygmo.fast_non_dominated_sorting()` and :func:`pygmo.crowding_distance()`
TypeError: if *points* cannot be converted to a vector of vector floats
Returns:
(``list`` of 1D NumPy int array): the indexes of the *N* best objectives vectors.
1D NumPy int array: the indexes of the *N* best objectives vectors.
Examples:
>>> import pygmo as pg
Expand Down Expand Up @@ -3645,7 +3647,7 @@ Note that while `ref_point` is required, it does not impact the calculation for
TypeError: if *weights* or *ref_point* or *objs* cannot be converted to a vector of floats.
Returns:
1D NumPy float array: a one dimensional array containing the decomposed objective.
1D NumPy float array: a one dimensional array containing the decomposed objective.
Examples:
>>> import pygmo as pg
Expand Down
16 changes: 16 additions & 0 deletions pygmo/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -983,6 +983,7 @@ def runTest(self):
decomposition_weights(n_f=2, n_w=6, method="low discrepancy", seed=33)
nadir(points=[[1, 1], [-1, 1], [2.2, 3], [0.1, -0.1]])
ideal(points=[[1, 1], [-1, 1], [2.2, 3], [0.1, -0.1]])
self.assertEqual(len(select_best_N_mo(points=pop.get_f(), N=0)), 0)


class con_utils_test_case(_ut.TestCase):
Expand Down Expand Up @@ -1843,6 +1844,7 @@ def runTest(self):
# turn it back on.
# self.run_torture_test_1()
self.run_migration_torture_test()
self.run_mo_migration_bug_test()

def run_init_tests(self):
from . import (archipelago, de, rosenbrock, population, null_problem, thread_island,
Expand Down Expand Up @@ -1996,6 +1998,20 @@ def run_mt_mh_tests(self):
self.assertEqual(a.get_migrant_handling(), migrant_handling.evict)
a.wait_check()

def run_mo_migration_bug_test(self):
from . import dtlz, nsga2, ring, archipelago

udp = dtlz(2, dim = 50)
uda = nsga2(gen = 100)
topo = ring()

archi = archipelago(n=10, t=topo, prob=udp, algo=uda, pop_size=100)
archi.evolve(4)
try:
archi.wait_check()
except:
self.fail("The MO migration bug test failed")

def run_evolve_tests(self):
from . import archipelago, de, rosenbrock, mp_island, evolve_status
from copy import deepcopy
Expand Down
10 changes: 5 additions & 5 deletions src/utils/multi_objective.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,9 @@ vector_double crowding_distance(const std::vector<vector_double> &non_dom_front)
* @endcode
*
* but it is faster than the above code: it avoids to compute the crowidng distance for all individuals and only
* computes
* it for the last non-dominated front that contains individuals included in the best N.
* computes it for the last non-dominated front that contains individuals included in the best N.
*
* If N is zero, an empty vector will be returned.
*
* @param input_f Input objectives vectors. Example {{0.25,0.25},{-1,1},{2,-2}};
* @param N Number of best individuals to return
Expand All @@ -342,9 +343,8 @@ vector_double crowding_distance(const std::vector<vector_double> &non_dom_front)
*/
std::vector<pop_size_t> select_best_N_mo(const std::vector<vector_double> &input_f, pop_size_t N)
{
if (N < 1u) {
pagmo_throw(std::invalid_argument,
"The best: " + std::to_string(N) + " individuals were requested, while 1 is the minimum");
if (N == 0u) { // corner case
return {};
}
if (input_f.size() == 0u) { // corner case
return {};
Expand Down
11 changes: 11 additions & 0 deletions tests/archipelago.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,15 @@ see https://www.gnu.org/licenses/. */
#include <boost/lexical_cast.hpp>

#include <pagmo/algorithms/de.hpp>
#include <pagmo/algorithms/nsga2.hpp>
#include <pagmo/algorithms/pso.hpp>
#include <pagmo/archipelago.hpp>
#include <pagmo/batch_evaluators/thread_bfe.hpp>
#include <pagmo/bfe.hpp>
#include <pagmo/island.hpp>
#include <pagmo/islands/thread_island.hpp>
#include <pagmo/population.hpp>
#include <pagmo/problems/dtlz.hpp>
#include <pagmo/problems/rosenbrock.hpp>
#include <pagmo/problems/schwefel.hpp>
#include <pagmo/problems/zdt.hpp>
Expand Down Expand Up @@ -912,3 +914,12 @@ BOOST_AUTO_TEST_CASE(archipelago_bfe_ctors)
}
}
}

// Test case for a bug in multi-objective migration in pagmo 2.11.
BOOST_AUTO_TEST_CASE(archipelago_mo_migration_bug)
{
archipelago a{ring{}, 10u, nsga2{100}, dtlz{2, 50}, 100u};

a.evolve(4);
BOOST_CHECK_NO_THROW(a.wait_check());
}
2 changes: 1 addition & 1 deletion tests/multi_objective.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ BOOST_AUTO_TEST_CASE(select_best_N_mo_test)
example = {{}, {}, {}, {}, {}, {}};
BOOST_CHECK_THROW(select_best_N_mo(example, 2u), std::invalid_argument);
example = {{1, 2}, {3, 4}, {0, 1}, {1, 0}, {2, 2}, {2, 4}};
BOOST_CHECK_THROW(select_best_N_mo(example, 0u), std::invalid_argument);
BOOST_CHECK(select_best_N_mo(example, 0u).empty());
}

BOOST_AUTO_TEST_CASE(ideal_test)
Expand Down

0 comments on commit c57dcc8

Please sign in to comment.