This repository has been archived by the owner on Jul 16, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 87
Add basic unit tests for problem::normalized. #120
Open
neduard
wants to merge
16
commits into
esa:master
Choose a base branch
from
neduard:unit_test_normalize
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
e4a61ce
Add basic unit test for problem::normalized.
neduard 902d0cf
Minor changes.
neduard 480262d
Add extra information of why a test failed.
neduard 60b252b
Add test for normalize invariant.
neduard 29f9506
Add problems with extreme bounds.
neduard cd54ffb
Formatting.
neduard 31632d8
Comments.
neduard 4ee738d
bugfix: Add missing brackets and correct lower bound.
neduard 56d0ec9
Add check for compute_constraints() invariant.
neduard 30f5160
Comments.
neduard 544ffb4
Add test to ensure denormalize() performs sanity checking on input.
neduard 6979c07
Strings, formatting.
neduard 8032c7f
Merge branch 'master' into unit_test_normalize
neduard fd4d21e
Merge branch 'master' into unit_test_normalize
neduard 9c7f07a
Do not redefine helper functions: include "test.h" instead.
neduard 445d66c
Output test failures.
neduard File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,313 @@ | ||
/***************************************************************************** | ||
* Copyright (C) 2004-2013 The PaGMO development team, * | ||
* Advanced Concepts Team (ACT), European Space Agency (ESA) * | ||
* http://apps.sourceforge.net/mediawiki/pagmo * | ||
* http://apps.sourceforge.net/mediawiki/pagmo/index.php?title=Developers * | ||
* http://apps.sourceforge.net/mediawiki/pagmo/index.php?title=Credits * | ||
* [email protected] * | ||
* * | ||
* This program is free software; you can redistribute it and/or modify * | ||
* it under the terms of the GNU General Public License as published by * | ||
* the Free Software Foundation; either version 2 of the License, or * | ||
* (at your option) any later version. * | ||
* * | ||
* This program is distributed in the hope that it will be useful, * | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of * | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | ||
* GNU General Public License for more details. * | ||
* * | ||
* You should have received a copy of the GNU General Public License * | ||
* along with this program; if not, write to the * | ||
* Free Software Foundation, Inc., * | ||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * | ||
*****************************************************************************/ | ||
|
||
// Test code for the normalized meta-problem | ||
|
||
#include <iostream> | ||
#include <algorithm> | ||
#include <cmath> | ||
#include <vector> | ||
#include <cassert> | ||
#include "../src/pagmo.h" | ||
#include "../src/rng.h" | ||
#include "test.h" | ||
#include <boost/random/uniform_real.hpp> | ||
|
||
using namespace pagmo; | ||
|
||
|
||
// TODO: currently this test is not included as it fails: denormalize does not check | ||
// that the input vector is valid. | ||
/// Test that denormalize accepts ONLY decision_vectors with values in [-1, 1]. | ||
/** | ||
* This test is composed of several checks on the behaviour of denormalize: | ||
* MUST NOT throw if input has all values in [-1, 1] | ||
* MUST throw if at least one value is -1.0 - EPS | ||
* MUST NOT throw if one value is -1.0 + EPS | ||
* MUST throw if at least one value is +1.0 + EPS | ||
* MUST NOT throw if one value is +1.0 - EPS | ||
*/ | ||
int test_normalized_denormalize_outside_bounds() | ||
{ | ||
const double EPS = 10e-9; | ||
size_t dim = 20; | ||
|
||
// Create problem and decision vector. | ||
problem::ackley prob((int)dim); | ||
problem::normalized norm(prob); | ||
decision_vector p_test(dim); | ||
|
||
rng_double drng = rng_generator::get<rng_double>(); | ||
|
||
std::cout << "Testing denormalize with invalid bounded input: "; | ||
|
||
// Initialise p_test with random values in [-1, 1]. | ||
for (size_t k = 0; k < dim; ++k) { | ||
p_test[k] = boost::uniform_real<double>(-1.0,1.0)(drng); | ||
} | ||
|
||
// Check that we indeed all values are accepted. | ||
try { | ||
norm.denormalize(p_test); | ||
} catch (...) { | ||
std::cout << "Random [-1, 1] denormalize failed." << std::endl; | ||
} | ||
|
||
// Begin test procedure: | ||
for (size_t k = 0; k < dim; ++k) { | ||
double original_value = p_test[k]; | ||
p_test[k] = -1.0 - EPS; | ||
try { | ||
// Should throw. | ||
norm.denormalize(p_test); | ||
std::cout << "(-1.0 - EPS) value did not cause exception." << std::endl; | ||
return 1; | ||
} catch (const value_error& ex) { | ||
// Expected exception. Do nothing. | ||
} catch (...) { | ||
std::cout << "(-1.0 - EPS) value caused unexpected exception." << std::endl; | ||
return 1; | ||
} | ||
|
||
p_test[k] = -1.0 + EPS; | ||
try { | ||
norm.denormalize(p_test); | ||
} catch (...) { | ||
std::cout << "(-1.0 + EPS) value caused exception." << std::endl; | ||
return 1; | ||
} | ||
|
||
p_test[k] = 1.0 + EPS; | ||
try { | ||
// Should throw. | ||
norm.denormalize(p_test); | ||
std::cout << "(1.0 + EPS) value did not cause exception." << std::endl; | ||
return 1; | ||
} catch (const value_error& ex) { | ||
// Expected exception. Do nothing. | ||
} catch (...) { | ||
std::cout << "(1.0 + EPS) value caused unexpected exception." << std::endl; | ||
return 1; | ||
} | ||
|
||
p_test[k] = 1.0 - EPS; | ||
try { | ||
norm.denormalize(p_test); | ||
} catch (...) { | ||
std::cout << "1.0 - EPS value caused exception." << std::endl; | ||
return 1; | ||
} | ||
|
||
// Restore original value. | ||
p_test[k] = original_value; | ||
} | ||
std::cout << "pass." << std::endl; | ||
return 0; | ||
} | ||
|
||
/// Test various invariants for denormalize. | ||
/** | ||
* The invariants are as follows: | ||
* | ||
* for any k: | ||
* normalized(prob).get_lb[k] == -1 | ||
* normalized(prob).get_ub[k] == 1 | ||
* | ||
* for any decision_vector dv: | ||
* is_eq_vector(normalized(prob).objfun(dv), prob.objfun(normalized(prob).denormalize(dv))) | ||
* is_eq_vector(prob.compute_constraints(dv), | ||
* normalized(prob).compute_constraints(normalized(prob).denormalize(dv))) | ||
*/ | ||
int test_normalized_invariant( | ||
const std::vector<problem::base_ptr> &probs) | ||
{ | ||
rng_double drng = rng_generator::get<rng_double>(); | ||
|
||
std::cout << "Start batch testing of normalization invariant" << std::endl; | ||
|
||
for (size_t i = 0; i < probs.size(); ++i) { | ||
problem::base_ptr cur_prob = probs[i]->clone(); | ||
size_t dim = cur_prob->get_dimension(); | ||
decision_vector p_test(dim); | ||
|
||
std::cout<< std::setw(40) << cur_prob->get_name(); | ||
|
||
// Normalize problem. | ||
pagmo::problem::normalized norm(*(cur_prob)); | ||
|
||
if (dim != norm.get_lb().size()) { | ||
std::cout << " Unexpected bounds vector size" << std::endl; | ||
std::cout << " Expected " << dim << " got " << norm.get_lb().size(); | ||
return 1; | ||
} | ||
|
||
// Test that we have indeed normalized: lb[i] = -1 and ub[i] = 1. | ||
for (size_t k = 0; k < dim; ++k) { | ||
if (!is_eq(norm.get_lb()[k], -1.0) || !is_eq(norm.get_ub()[k], 1.0)) { | ||
std::cout << " bounds check failed!" << std::endl; | ||
PRINT_VEC(norm.get_lb()); | ||
PRINT_VEC(norm.get_ub()); | ||
return 1; | ||
} | ||
} | ||
|
||
std::cout << " bounds check pass, "; | ||
|
||
// Generate random (normalized) decision vector: -1 <= p_test[i] <= 1 | ||
for (size_t k = 0; k < dim; ++k) { | ||
p_test[k] = boost::uniform_real<double>(-1.0,1.0)(drng); | ||
} | ||
|
||
// Check objfun invariant. | ||
decision_vector p_denormalized = norm.denormalize(p_test); | ||
fitness_vector f_expected = cur_prob->objfun(p_denormalized); | ||
fitness_vector f_actual = norm.objfun(p_test); | ||
if (!is_eq_vector(f_expected, f_actual)) { | ||
std::cout << " objfun check failed!" << std::endl; | ||
PRINT_VEC(f_expected); | ||
PRINT_VEC(f_actual); | ||
return 1; | ||
} | ||
|
||
std::cout << " objfun invariant pass, "; | ||
|
||
|
||
// Check compute_constraints invariant. | ||
constraint_vector c_expected(cur_prob->get_c_dimension()); | ||
constraint_vector c_actual(norm.get_c_dimension()); | ||
cur_prob->compute_constraints(c_expected, p_denormalized); | ||
norm.compute_constraints(c_actual, p_test); | ||
if (!is_eq_vector(c_expected, c_actual)) { | ||
std::cout << " compute_constraints check failed!" << std::endl; | ||
PRINT_VEC(c_expected); | ||
PRINT_VEC(c_actual); | ||
return 1; | ||
} | ||
|
||
std::cout << " compute_constraints invariant pass." << std::endl; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
/// Ensure that when bounds are (-1, 1), normalization doesn't have any effect. | ||
int test_normalized_11(const std::vector<problem::base_ptr> &probs) | ||
{ | ||
rng_double drng = rng_generator::get<rng_double>(); | ||
|
||
std::cout << "Start batch testing with bounds (-1, 1)" << std::endl; | ||
|
||
for (size_t i = 0; i < probs.size(); ++i) { | ||
problem::base_ptr cur_prob = probs[i]->clone(); | ||
size_t dim = cur_prob->get_dimension(); | ||
decision_vector p_normalized(dim); | ||
|
||
std::cout<< std::setw(40) << cur_prob->get_name(); | ||
|
||
// Generate a random decision vector with values in (-1, 1). | ||
for (size_t k = 0; k < dim; ++k) { | ||
p_normalized[k] = boost::uniform_real<double>(-1.0,1.0)(drng); | ||
} | ||
|
||
cur_prob->set_bounds(-1.0, 1.0); | ||
|
||
pagmo::problem::normalized prob_norm(*(cur_prob)); | ||
|
||
if (dim != prob_norm.get_lb().size()) { | ||
std::cout << " Unexpected bounds vector size" << std::endl; | ||
std::cout << " Expected " << dim << " got " << prob_norm.get_lb().size(); | ||
return 1; | ||
} | ||
|
||
// Ensure normalization did not change the bounds. | ||
if (!is_eq_vector(prob_norm.get_lb(), cur_prob->get_lb()) || | ||
!is_eq_vector(prob_norm.get_ub(), cur_prob->get_ub())) { | ||
std::cout << " bounds check failed!" << std::endl; | ||
PRINT_VEC(prob_norm.get_lb()); | ||
PRINT_VEC(cur_prob->get_lb()); | ||
PRINT_VEC(prob_norm.get_ub()); | ||
PRINT_VEC(cur_prob->get_ub()); | ||
return 1; | ||
} | ||
|
||
std::cout << " bounds check pass, "; | ||
|
||
// Ensure denormalize() is essentially a no-op. | ||
decision_vector p_denormalized = prob_norm.denormalize(p_normalized); | ||
if (!is_eq_vector(p_denormalized, p_normalized)) { | ||
std::cout << " denormalize failed!" << std::endl; | ||
PRINT_VEC(p_denormalized); | ||
PRINT_VEC(p_normalized); | ||
return 1; | ||
} | ||
|
||
std::cout << " denormalize pass." << std::endl; | ||
} | ||
return 0; | ||
} | ||
|
||
|
||
int main() | ||
{ | ||
int dimension = 40; | ||
std::vector<problem::base_ptr> probs; | ||
|
||
// ZDT and DTLZ are Box-Constrained Continuous Multi-Objective | ||
for (int i = 1; i <= 6; ++i) { | ||
probs.push_back(problem::zdt(i, dimension).clone()); | ||
} | ||
for (int i = 1; i <= 7; ++i) { | ||
probs.push_back(problem::dtlz(i, dimension).clone()); | ||
} | ||
|
||
// Box-constrained Continuous Single-Objective | ||
probs.push_back(problem::ackley(dimension).clone()); | ||
probs.push_back(problem::rastrigin(dimension).clone()); | ||
|
||
// CEC2006 are Constrained Continuous Single-Objective | ||
for (int i = 1; i <= 24; ++i) { | ||
probs.push_back(problem::cec2006(i).clone()); | ||
} | ||
|
||
// CEC2009 problems 1-10 are Constrained Continuous Multi-Objective | ||
for (int i = 1; i <= 10; ++i) { | ||
probs.push_back(problem::cec2009(i, dimension, true).clone()); | ||
} | ||
|
||
// Add a few problems with extreme bounds. | ||
problem::ackley ak(10); | ||
ak.set_bounds(DBL_MIN, DBL_MAX); | ||
probs.push_back(ak.clone()); | ||
ak.set_bounds((double)INT_MIN, (double)INT_MAX); | ||
probs.push_back(ak.clone()); | ||
ak.set_bounds((double)INT_MIN, (double)(INT_MIN + 1)); | ||
probs.push_back(ak.clone()); | ||
ak.set_bounds(1.0 - DBL_EPSILON, 1.0 + DBL_EPSILON); | ||
probs.push_back(ak.clone()); | ||
ak.set_bounds(0.0 - DBL_EPSILON, 0.0 + DBL_EPSILON); | ||
probs.push_back(ak.clone()); | ||
|
||
return test_normalized_11(probs) || | ||
test_normalized_invariant(probs); | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Merge the lines here 63 and 64 (I know I didn't give you a clear guidelines on character limit ;) )
Method/Functions commenting style is either: https://github.com/esa/pagmo/blob/master/src/problem/normalized.cpp#L35-L41
or triple slash like https://github.com/esa/pagmo/blob/master/src/problem/normalized.cpp#L58
Latter is fine for unit testing.