Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Affine bbob #176

Merged
merged 5 commits into from
Sep 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion include/ioh/common/container_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,37 @@ namespace ioh
}
return target;
}

/**
* @brief Makes an array and fills it with a specific value
*
* @tparam T the type of the elements
* @tparam N the number of elements
* @param value the value
* @return std::array<T, N> the filled array
*/
template<typename T, size_t N>
std::array<T, N> fill_array(const T value){
std::array<T, N> array {};
for(auto &e: array)
e = value;
return array;
}
/**
* @brief Convert vector into a std::array
*
* @tparam T the type of the elements
* @tparam N the number of elements
* @param v the vector to convert from
* @return std::array<T, N> the array
*/
template<typename T, size_t N>
std::array<T, N> from_vector(const std::vector<T>& v){
std::array<T, N> array {};
for (size_t i = 0; i < std::min(N, v.size()); i++){
array[i] = v[i];
}
return array;
}

//! Permutation struct
struct Permutation
Expand Down
1 change: 1 addition & 0 deletions include/ioh/problem/bbob.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@
#include "bbob/gallagher21.hpp"
#include "bbob/katsuura.hpp"
#include "bbob/lunacek_bi_rastrigin.hpp"
#include "bbob/many_affine.hpp"
143 changes: 143 additions & 0 deletions include/ioh/problem/bbob/many_affine.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#pragma once

#include "bbob_problem.hpp"

namespace ioh::problem::bbob
{
namespace many_affine {
inline std::array<double, 24> get_weights(const int instance){
auto weights = common::random::bbob2009::uniform(24, 2000 + instance, 0., 1.);

// Make the two largest weights larger than 0.85.
// There shall be at least two functions being combined.
size_t max1_i = 0, max2_i = 0;
double max1 = -1;
double max2 = -2;
for (size_t i = 0; i != weights.size(); ++i) {
if(weights[i] > max1) {
max2 = max1;
max2_i = max1_i;

max1 = weights[i];
max1_i = i;
} else if (weights[i] > max2) {
max2 = weights[i];
max2_i = i;
}
}
weights[max1_i] = weights[max1_i] > 0.85 ? weights[max1_i] : 0.85;
weights[max2_i] = weights[max2_i] > 0.85 ? weights[max2_i] : 0.85;

auto sum = 0.;
for (auto &wi: weights){
if (wi >= 0.85){
sum += wi;
} else {
wi = 0;
}
}
for (auto &wi: weights){
wi /= sum;
}
return common::from_vector<double, 24>(weights);
}
}

class ManyAffine : public RealSingleObjective
{
public:
static inline const std::array<double, 24> default_scales{11., 17.5, 12.3, 12.6, 11.5, 15.3, 12.1, 15.3,
15.2, 17.4, 13.4, 20.4, 12.9, 10.4, 12.3, 10.3,
9.8, 10.6, 10., 14.7, 10.7, 10.8, 9., 12.1};

/**
* @brief Construct a new Many Affine object, requires weights and instances to be specified
* @param xopt the location of the optimum
* @param weights weight vector which specifies the contribution of each of the 24 bbob to the final
* objective value
* @param instances the vector of instances used
* @param n_variables the dimension of the problem
* @param scale_factors the relative scale factors for each problem
*/
ManyAffine(const std::vector<double> &xopt, const std::array<double, 24> &weights,
const std::array<int, 24> &instances, const int n_variables,
const std::array<double, 24> &scale_factors) :
RealSingleObjective(MetaData(0, instances[0], "ManyAffine", n_variables),
Bounds<double>(n_variables, -5, 5)),
weights(weights), instances(instances), scale_factors(scale_factors), problems{}, function_values{}
{
const auto &problem_factory = ioh::problem::ProblemRegistry<ioh::problem::BBOB>::instance();
for (int fid = 1; fid < 25; fid++)
problems[fid - 1] = problem_factory.create(fid, instances[fid - 1], n_variables);

this->optimum_.x = xopt;
this->optimum_.y = evaluate(xopt);
}

/**
* @brief Construct a new Many Affine object with default weights and a single instance for all sub functions.
* A random location of the optimum and a weight vector are generated using the bbob2009 uniform random number
* generator, using the instance as seed offset.
*
* @param instance the instance
* @param n_variables the dimension used
*/
ManyAffine(const int instance, const int n_variables) :
ManyAffine(common::random::bbob2009::uniform(n_variables, 1000 + instance, -4., 4.),
many_affine::get_weights(instance),
common::fill_array<int, 24>(instance), n_variables, ManyAffine::default_scales)
{
}

protected:
double evaluate(const std::vector<double> &x) override
{
auto result = 0.0;
for (int fi = 0; fi < 24; fi++)
{
// compute xopt shifted x
std::vector<double> x0 = x;
for (size_t i = 0; i < x.size(); i++)
{
x0[i] = x[i] + problems[fi]->optimum().x[i] - this->optimum_.x[i];
}
// evaluate and shift by yopt
double f0 = (*problems[fi])(x0)-problems[fi]->optimum().y;
// clamp to finite
f0 = std::min(std::max(f0, 1e-12), 1e20);
// apply scaling
f0 = (std::log10(f0) + 8) / scale_factors[fi];
// apply weights
f0 = f0 * weights[fi];
result += f0;
}
// convert to base 10
result = pow(10, (10 * result - 8));
return result;
}

private:
std::array<double, 24> weights;
std::array<int, 24> instances;
std::array<double, 24> scale_factors;
std::array<std::shared_ptr<ioh::problem::BBOB>, 24> problems;
std::array<double, 24> function_values;

public:
std::array<double, 24> get_weights(){
return weights;
}
std::array<int, 24> get_instances(){
return instances;
}
std::array<double, 24> get_scale_factors(){
return scale_factors;
}
std::array<std::shared_ptr<ioh::problem::BBOB>, 24> get_problems(){
return problems;
}
std::array<double, 24> get_function_values(){
return function_values;
}
};
} // namespace ioh::problem::bbob
27 changes: 27 additions & 0 deletions include/ioh/problem/problem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,33 @@ namespace ioh
constraintset_.remove(c);
log_info_.allocate(optimum_, constraintset_);
}

//! Call this method after updating any fields on meta_data_
void updated_metadata() {
if (logger_ != nullptr){
if(state_.evaluations != 0)
IOH_DBG(warning, "Updated meta_data with logger attached and already evaluated problem. State will be reset.")
reset();
}
}

//! Accessor for problem id
void set_id(const int new_id) {
meta_data_.problem_id = new_id;
updated_metadata();
}

//! Accessor for problem instance
void set_instance(const int new_instance){
meta_data_.instance = new_instance;
updated_metadata();
}

//! Accessor for problem name
void set_name(const std::string& new_name){
meta_data_.name = new_name;
updated_metadata();
}

//! Stream operator
friend std::ostream &operator<<(std::ostream &os, const Problem &obj)
Expand Down
2 changes: 0 additions & 2 deletions include/ioh/problem/structures.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,6 @@ namespace ioh
*/
State(Solution<T, double> initial) : initial_solution(std::move(initial)) { reset(); }



//! reset the state
void reset()
{
Expand Down
25 changes: 25 additions & 0 deletions ioh/src/problem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,10 @@ void define_base_class(py::module &m, const std::string &name)
how: The enforcement strategy, should be one of the 'ioh.ConstraintEnforcement' options
exponent: The exponent for scaling the contraint
)pbdoc")

.def("set_id", &ProblemType::set_id, py::arg("new_problem_id"), "update the problem id")
.def("set_instance", &ProblemType::set_instance, py::arg("new_instance"), "update the problem instance")
.def("set_name", &ProblemType::set_name, py::arg("new_name"),"update the problem name")
.def("__repr__", [=](const ProblemType &p) {
using namespace ioh::common;
const auto meta_data = p.meta_data();
Expand Down Expand Up @@ -1458,6 +1462,27 @@ void define_problem(py::module &m)
define_wmodels(m);
define_submodular_problems(m);
define_star_discrepancy_problems(m);

py::class_<ioh::problem::bbob::ManyAffine, ioh::problem::RealSingleObjective, std::shared_ptr<ioh::problem::bbob::ManyAffine>>(m, "ManyAffine")
.def(py::init<int, int>(), py::arg("instance"), py::arg("n_variables"))
.def(
py::init<
std::vector<double>,
std::array<double, 24>,
std::array<int, 24>,
int,
std::array<double, 24>>(),
py::arg("xopt"),
py::arg("weights"),
py::arg("instances"),
py::arg("n_variables"),
py::arg("scale_factors") = ioh::problem::bbob::ManyAffine::default_scales)
.def_property_readonly("weights", &ioh::problem::bbob::ManyAffine::get_weights)
.def_property_readonly("instances", &ioh::problem::bbob::ManyAffine::get_instances)
.def_property_readonly("scale_factors", &ioh::problem::bbob::ManyAffine::get_scale_factors)
.def_property_readonly("sub_problems", &ioh::problem::bbob::ManyAffine::get_problems)
.def_property_readonly("function_values", &ioh::problem::bbob::ManyAffine::get_function_values)
;

py::module_::import("atexit").attr("register")(py::cpp_function([]() {
// std::cout << "exiting gracefully...";
Expand Down
15 changes: 15 additions & 0 deletions tests/cpp/problem/test_bbob_affine.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include "../utils.hpp"

#include "ioh/problem/bbob/many_affine.hpp"


TEST_F(BaseTest, TestManyAffine)
{
using namespace ioh::problem::bbob;
ManyAffine affine(1, 2);

std::vector<double> x0 = {1, 2};
EXPECT_NEAR(affine(x0), 3.521076347, 1e-8);

//TODO: test based on indiviudal problems
}