From 0c2d28b41cd5db0754f63732412be1b1594997ec Mon Sep 17 00:00:00 2001 From: jacobdenobel Date: Wed, 6 Oct 2021 14:55:54 +0200 Subject: [PATCH 1/8] change evaluate in experiment to use local algorithm variable --- ioh/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ioh/__init__.py b/ioh/__init__.py index c2675be77..0aa8cda83 100644 --- a/ioh/__init__.py +++ b/ioh/__init__.py @@ -217,8 +217,8 @@ def evaluate(self, fid: int, iid: int, dim: int): if self.logged: l = logger.Analyzer(**self.logger_params) l.set_experiment_attributes(self.experiment_attributes) - l.add_run_attributes(self.algorithm, self.run_attributes) - l.watch(self.algorithm, self.logged_attributes) + l.add_run_attributes(algorithm, self.run_attributes) + l.watch(algorithm, self.logged_attributes) p.attach_logger(l) self.apply(algorithm, p) From a9a17d9575e02c780ab1032a709a1c0011773220 Mon Sep 17 00:00:00 2001 From: jacobdenobel Date: Fri, 22 Oct 2021 17:43:01 +0200 Subject: [PATCH 2/8] fix NDEBUG issue --- CMakeLists.txt | 4 ++-- include/ioh/logger/eah.hpp | 3 ++- ioh/__init__.py | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b0a6aad88..a2fcf90c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,13 +25,13 @@ endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -std=c++17 -lstdc++fs") - set(CMAKE_CXX_FLAGS_RELEASE "-O3 -ffast-math") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -ffast-math") if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) link_libraries(stdc++fs) add_compile_definitions(FSEXPERIMENTAL) endif() endif() - + # Define project targets. file(GLOB_RECURSE HEADERS include/ioh *.hpp) diff --git a/include/ioh/logger/eah.hpp b/include/ioh/logger/eah.hpp index f94eba1b4..34bc074f4 100644 --- a/include/ioh/logger/eah.hpp +++ b/include/ioh/logger/eah.hpp @@ -1081,9 +1081,10 @@ namespace ioh template double accumulate(const EAH &logger, double init, BinaryOperation op) { +#ifndef NDEBUG const AttainmentSuite &attainment = logger.data(); assert(attainment.size() > 0); - +#endif Histogram histo; Histogram::Mat mat = histo(logger); assert(histo.nb_attainments() > 0); diff --git a/ioh/__init__.py b/ioh/__init__.py index 0aa8cda83..8f144ccf8 100644 --- a/ioh/__init__.py +++ b/ioh/__init__.py @@ -192,7 +192,7 @@ def __init__(self, for attr in itertools.chain(self.run_attributes, self.logged_attributes): if not hasattr(self.algorithm, attr): raise TypeError( - f"Attribute {attr} is a member of algorithm {self.algorithm}" + f"Attribute {attr} is not a member of algorithm {self.algorithm}" ) From bf038c13cfee19846ffda9f7e468116e24d481d5 Mon Sep 17 00:00:00 2001 From: jacobdenobel Date: Fri, 22 Oct 2021 17:59:54 +0200 Subject: [PATCH 3/8] fix wrap problem with same name --- include/ioh/common/factory.hpp | 9 +++++++++ include/ioh/problem/problem.hpp | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/include/ioh/common/factory.hpp b/include/ioh/common/factory.hpp index d8541db5a..3487a169c 100644 --- a/include/ioh/common/factory.hpp +++ b/include/ioh/common/factory.hpp @@ -100,6 +100,15 @@ namespace ioh::common return it == known_ids.end() ? id : get_next_id(known_ids); } + [[nodiscard]] int check_or_get_next_available(const int id, const std::string& name) const { + const auto already_defined = name_map.find(name) != std::end(name_map); + if(already_defined) + for (const auto kv: id_map) + if (kv.second == name) + return kv.first; + return check_or_get_next_available(id); + } + //! Accessor for the list of registered names [[nodiscard]] std::vector names() const { return common::keys(name_map); } diff --git a/include/ioh/problem/problem.hpp b/include/ioh/problem/problem.hpp index 1ad524825..460989ddb 100644 --- a/include/ioh/problem/problem.hpp +++ b/include/ioh/problem/problem.hpp @@ -362,7 +362,8 @@ namespace ioh Constraint constraint = Constraint()) { auto &factory = ProblemFactoryType>::instance(); - int id = factory.check_or_get_next_available(1); + + int id = factory.check_or_get_next_available(1, name); factory.include(name, id, [=](const int, const int dimension) { From 746e95dc8e2dcc50248f74a8fb39d94b1819cfd7 Mon Sep 17 00:00:00 2001 From: jacobdenobel Date: Sun, 24 Oct 2021 15:14:00 +0200 Subject: [PATCH 4/8] scoped wrap function --- include/ioh/common/factory.hpp | 1 + ioh/src/problem.cpp | 8 ++++---- tests/python/test_problem.py | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/include/ioh/common/factory.hpp b/include/ioh/common/factory.hpp index 3487a169c..1ad0a59c5 100644 --- a/include/ioh/common/factory.hpp +++ b/include/ioh/common/factory.hpp @@ -100,6 +100,7 @@ namespace ioh::common return it == known_ids.end() ? id : get_next_id(known_ids); } + //! Get the next available id [[nodiscard]] int check_or_get_next_available(const int id, const std::string& name) const { const auto already_defined = name_map.find(name) != std::end(name_map); if(already_defined) diff --git a/ioh/src/problem.cpp b/ioh/src/problem.cpp index 0d0cd7b1b..af940c060 100644 --- a/ioh/src/problem.cpp +++ b/ioh/src/problem.cpp @@ -3,6 +3,7 @@ #include #include "ioh.hpp" + namespace py = pybind11; using namespace ioh::problem; @@ -197,7 +198,6 @@ void define_wrapper_functions(py::module &m, const std::string &class_name, cons m.def( function_name.c_str(), [](py::function f, const std::string &name, int d, ioh::common::OptimizationType t, Constraint &c) { - f.dec_ref(); return wrap_function([f](const std::vector &x) { return PyFloat_AsDouble(f(x).ptr()); }, name, d, t, c); }, @@ -474,9 +474,9 @@ void define_wmodels(py::module &m) py::arg("ruggedness_gamma") = 0); py::class_>(m, "WModelOneMax") - .def(py::init(), - py::arg("instance"), py::arg("n_variables"), py::arg("dummy_select_rate") = 0.0, - py::arg("epistasis_block_size") = 0, py::arg("neutrality_mu") = 0, py::arg("ruggedness_gamma") = 0); + .def(py::init(), py::arg("instance"), py::arg("n_variables"), + py::arg("dummy_select_rate") = 0.0, py::arg("epistasis_block_size") = 0, py::arg("neutrality_mu") = 0, + py::arg("ruggedness_gamma") = 0); } void define_problem(py::module &m) diff --git a/tests/python/test_problem.py b/tests/python/test_problem.py index b9ced0515..a94af489b 100644 --- a/tests/python/test_problem.py +++ b/tests/python/test_problem.py @@ -5,6 +5,7 @@ import math import ioh +import numpy as np class Algorithm: def __init__(self): @@ -178,5 +179,38 @@ def test_file_comparisons(self): self.assertTrue(math.isclose(p(x), float(y), abs_tol = tol)) + def test_wrap_problem_scoped(self): + def w(): + l = lambda x: 0.0 + p = ioh.problem.wrap_real_problem(l, "l") + return p + p = w() + y = p([0]*5) + self.assertEqual(y, 0.0) + + def test_wrap_problem(self): + l = lambda x: 0.0 + p = ioh.problem.wrap_real_problem(l, "f") + p([0]*5) + + p2 = ioh.problem.wrap_real_problem(l, "f") + self.assertEqual(p.meta_data.problem_id, p2.meta_data.problem_id) + self.assertEqual(p.meta_data.name, p2.meta_data.name) + + def test_wrap_problem_builtins(self): + for f in (sum, min, max): + p = ioh.problem.wrap_real_problem(f, "f") + y = p([0]*5) + self.assertEqual(y, 0.0) + + def test_wrap_problem_numpy(self): + for f in (np.sum, np.mean, np.median, np.std): + p = ioh.problem.wrap_real_problem(f, "f") + y = p([0]*5) + self.assertEqual(y, 0.0) + + + + if __name__ == "__main__": unittest.main() From cd52a9005e1945e00da0c1a3578e4ec0bc94defc Mon Sep 17 00:00:00 2001 From: jacobdenobel Date: Sun, 24 Oct 2021 15:31:17 +0200 Subject: [PATCH 5/8] remove numpy --- tests/python/test_problem.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/python/test_problem.py b/tests/python/test_problem.py index a94af489b..8138ea0ae 100644 --- a/tests/python/test_problem.py +++ b/tests/python/test_problem.py @@ -5,7 +5,6 @@ import math import ioh -import numpy as np class Algorithm: def __init__(self): @@ -203,12 +202,6 @@ def test_wrap_problem_builtins(self): y = p([0]*5) self.assertEqual(y, 0.0) - def test_wrap_problem_numpy(self): - for f in (np.sum, np.mean, np.median, np.std): - p = ioh.problem.wrap_real_problem(f, "f") - y = p([0]*5) - self.assertEqual(y, 0.0) - From b8450463de3e2ec071c7f796a51694198f04df72 Mon Sep 17 00:00:00 2001 From: jacobdenobel Date: Sun, 24 Oct 2021 16:09:22 +0200 Subject: [PATCH 6/8] attempt with manual ref counting for wrapped fn --- ioh/iohcpp/problem.pyi | 4 ++-- ioh/src/problem.cpp | 12 +++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/ioh/iohcpp/problem.pyi b/ioh/iohcpp/problem.pyi index 92af8fbd1..9afa70610 100644 --- a/ioh/iohcpp/problem.pyi +++ b/ioh/iohcpp/problem.pyi @@ -206,5 +206,5 @@ class WModelOneMax(AbstractWModel): class Weierstrass(Real): def __init__(self, arg0: int, arg1: int) -> None: ... -def wrap_integer_problem(f: function, name: str, n_variables: int = ..., optimization_type: ioh.iohcpp.OptimizationType = ..., constraint: ioh.iohcpp.IntegerConstraint = ...) -> IntegerWrappedProblem: ... -def wrap_real_problem(f: function, name: str, n_variables: int = ..., optimization_type: ioh.iohcpp.OptimizationType = ..., constraint: ioh.iohcpp.RealConstraint = ...) -> RealWrappedProblem: ... +def wrap_integer_problem(f: handle, name: str, n_variables: int = ..., optimization_type: ioh.iohcpp.OptimizationType = ..., constraint: ioh.iohcpp.IntegerConstraint = ...) -> IntegerWrappedProblem: ... +def wrap_real_problem(f: handle, name: str, n_variables: int = ..., optimization_type: ioh.iohcpp.OptimizationType = ..., constraint: ioh.iohcpp.RealConstraint = ...) -> RealWrappedProblem: ... diff --git a/ioh/src/problem.cpp b/ioh/src/problem.cpp index af940c060..eed8f39e4 100644 --- a/ioh/src/problem.cpp +++ b/ioh/src/problem.cpp @@ -188,6 +188,8 @@ void define_base_class(py::module &m, const std::string &name) }); } +static std::vector WRAPPED_FUNCTIONS; + template void define_wrapper_functions(py::module &m, const std::string &class_name, const std::string &function_name) { @@ -197,7 +199,9 @@ void define_wrapper_functions(py::module &m, const std::string &class_name, cons m.def( function_name.c_str(), - [](py::function f, const std::string &name, int d, ioh::common::OptimizationType t, Constraint &c) { + [](py::handle f, const std::string &name, int d, ioh::common::OptimizationType t, Constraint &c) { + f.inc_ref(); + WRAPPED_FUNCTIONS.push_back(f); return wrap_function([f](const std::vector &x) { return PyFloat_AsDouble(f(x).ptr()); }, name, d, t, c); }, @@ -485,4 +489,10 @@ void define_problem(py::module &m) define_bbob_problems(m); define_pbo_problems(m); define_wmodels(m); + + py::module_::import("atexit").attr("register")(py::cpp_function([]() { + for (const auto fn : WRAPPED_FUNCTIONS){ + fn.dec_ref(); + } + })); } From 03227655d07bee2dc9462bc24d7a834a489adfe0 Mon Sep 17 00:00:00 2001 From: jacobdenobel Date: Sun, 24 Oct 2021 16:28:30 +0200 Subject: [PATCH 7/8] improve get_problem error checking --- ioh/__init__.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/ioh/__init__.py b/ioh/__init__.py index 8f144ccf8..6d917048e 100644 --- a/ioh/__init__.py +++ b/ioh/__init__.py @@ -1,8 +1,6 @@ '''Python specific functions for IOH package TODO: best far precision -TODO: tracking of out-of bounds -TODO: Check quadratic function, min dimension bbob=2 TODO: Rename Integer -> Discrete ''' @@ -36,7 +34,7 @@ ProblemType = typing.Union[problem.Real, problem.Integer] -def get_problem(fid: int, iid: int, dim: int, problem_type: str = "Real") -> ProblemType: +def get_problem(fid: typing.Union[int, str], iid: int, dim: int, problem_type: str = "Real") -> ProblemType: '''Instantiate a problem based on its function ID, dimension, instance and suite Parameters @@ -52,15 +50,21 @@ def get_problem(fid: int, iid: int, dim: int, problem_type: str = "Real") -> Pro Only used if fid is an int. ''' - if problem_type in ("BBOB", "Real",): - return getattr(problem, problem_type).create(fid, iid, dim) - elif problem_type in ("PBO", "Integer",): - if fid in [21, 23, "IsingTriangular", "NQueens"]: - if not math.sqrt(dim).is_integer(): - raise ValueError("For this function, the dimension needs to be a perfect square!") - return getattr(problem, problem_type).create(fid, iid, dim) - - raise ValueError(f"Suite {problem_type} is not yet supported") + if problem_type in ("PBO", "Integer",) and fid in [21, 23, "IsingTriangular", "NQueens"]: + if not math.sqrt(dim).is_integer(): + raise ValueError("For this function, the dimension needs to be a perfect square!") + + if problem_type == "BBOB" or (problem_type == "Real" and fid in range(1, 25)): + if not dim >= 2: + raise ValueError("For BBOB functions the minimal dimension is 2") + + if (base_problem:= getattr(problem, problem_type)): + if fid not in (base_problem.problems.values() |base_problem.problems.keys()): + raise ValueError(f"{fid} is not registered for problem type: {problem_type}") + + return base_problem.create(fid, iid, dim) + + raise ValueError(f"Problem type {problem_type} is not supported") class Experiment: def __init__(self, From 21491bb178e7210b8f98c2c17abd01fa4066219e Mon Sep 17 00:00:00 2001 From: jacobdenobel Date: Sun, 24 Oct 2021 16:58:45 +0200 Subject: [PATCH 8/8] reemove wallrus operator --- ioh/__init__.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ioh/__init__.py b/ioh/__init__.py index 6d917048e..6994321da 100644 --- a/ioh/__init__.py +++ b/ioh/__init__.py @@ -54,14 +54,15 @@ def get_problem(fid: typing.Union[int, str], iid: int, dim: int, problem_type: s if not math.sqrt(dim).is_integer(): raise ValueError("For this function, the dimension needs to be a perfect square!") - if problem_type == "BBOB" or (problem_type == "Real" and fid in range(1, 25)): + bbob_fns = getattr(problem, "BBOB").problems.values() | getattr(problem, "BBOB").problems.keys() + if problem_type in ("BBOB", "Real",) and fid in bbob_fns: if not dim >= 2: raise ValueError("For BBOB functions the minimal dimension is 2") - if (base_problem:= getattr(problem, problem_type)): - if fid not in (base_problem.problems.values() |base_problem.problems.keys()): + base_problem = getattr(problem, problem_type) + if base_problem: + if fid not in (base_problem.problems.values() | base_problem.problems.keys()): raise ValueError(f"{fid} is not registered for problem type: {problem_type}") - return base_problem.create(fid, iid, dim) raise ValueError(f"Problem type {problem_type} is not supported")