C++#

This page contains the full documentation for the C++ engine of the IOHexperimenter.

Quickstart#

Installation#

The following toolkits are needed for compiling IOHexperimenter:

  • A C++ compiler. The minimum compiler version is g++7 or equivalent, but we recommend g++9 or equivalent.

  • CMake, version 3.15 or higher

Please use the following commands to download, compile, and install this package:

> git clone --recursive https://github.com/IOHprofiler/IOHexperimenter.git
> cd IOHexperimenter
> mkdir build
> cd build
> cmake .. && make install

which installs all header files to /usr/local/include/ioh by default. If you want to change this directory, please use the following flag cmake -DCMAKE_INSTALL_PREFIX=your/path ..

If you want to change build options, check the output of cmake -L or use cmake-gui or ccmake.

Dependencies#

IOHexperimenter has a few dependencies, however, only the fmt/fmt library is required to compile the C++ backend. If you’d like to compile the source code without CMake, you need to provide the includes of fmt/fmt as an additional include directory, which can be found in the external folder of the repository.

Usage#

To obtain a built-in problem, you could create a problem instance by passing the instance id and the search dimension to the constructor of the intended problem class, e.g.,

#include "ioh.hpp"
auto om = ioh::problem::pbo::OneMax(1, 10); // PBO problem: instance 1, dim 10
auto sp = ioh::problem::bbob::Sphere(1, 5); // BBOB problem: instance 1, dim 5

The instance id is intended to generalize a certain problem by some transformations, where it serves as the random seed for randomizing the transformations, e.g., affine transforms for BBOB problems and scaling of objective values for PBO problems. Please see

Alternatively, we can also obtain a std::shared_ptr to a problem by using a problem registry of a given type. Using the registry, we can obtain problems by their name, which can be usefull in a dynamic setting:

const auto &problem_factory = ioh::problem::ProblemRegistry<ioh::problem::IntegerSingleObjective>::instance();
const std::shared_ptr<ioh::problem::IntegerSingleObjective> om = problem_factory.create("OneMax", 1, 10);

Problem objects are callable objects, and a search point can be evaluated on a problem by passing it to the call operator of the problem, for example:

std::vector<double> x0 = {1., 2., 3., 4., 5.};
double y0 = sp(x0);

For more advanced usage examples, please take a look at the example folder in the github repository. Some examples include:

Adding new problems#

We offer a very simple and convenient interface for integrating new benchmark problems/functions. First, you could define a new test_problem as you like. Note that the <vector> header is already imported in “ioh.hpp”.

ioh::problem::SingleObjective test_problem(const std::vector<double> &)
{
    // the actual function body starts here
    // ...
    return 0.0;
}

Then, you only need to “wrap” this new function as follows:

ioh::problem::wrap_function<double, ioh::problem::SingleObjective>(
  &test_problem,
  "test_problem" // name for the new function
);

After wrapping, we could create this test_problem from the problem factory. Note that, the instance id is ineffective in this approach since we haven’t implemented any transformations for the wrapped problem.

auto &factory = ioh::problem::ProblemRegistry<ioh::problem::RealSingleObjective>::instance();
auto new_problem_f = factory.create(
  "test_problem",  // create by name
  1,               // instance id
  10               // number of search variables
);

Alternatively, one might wish to create the new problem by subclassing the abstract problem class in IOHexperimenter, taking benefits of implementing more details, e.g., aforementioned transformations. This can be done by inheriting the corresponding problem registration class, which is

  • ioh::problem::IntegerSingleObjective for pseudo-Boolean problems, and

  • ioh::problem::RealSingleObjective for continuous problems.

In the below example, we show how to do this for pseudo-Boolean problems.

class NewBooleanProblem final : public ioh::problem::RegisteredProblem<NewBooleanProblem, ioh::problem::IntegerSingleObjective>
{
protected:
    // [mandatory] The evaluate method is mandatory to implement
    double evaluate(const std::vector<int> &x) override
    {
        // the function body goes here
        return 0.0;
    }

    // [optional] If one wish to implement transformations on search variables
    std::vector<int> transform_variables(std::vector<int> x) override
    {
        return x;
    }

    // [optional] If one wish to implement transformations on objective values
    double transform_objectives(const double y) override
    {
        return y;
    }

public:
    /// [mandatory] This constructor always take `instance` as input even
    /// if it is ineffective by default. `instance` would be effective if and only if
    /// at least one of `transform_objectives` and `transform_objectives` is implemented
    NewBooleanProblem(const int instance, const int n_variables) :
        RegisteredProblem(
          ioh::problem::MetaData(
            1,                     // problem id, which will be overwritten when registering this class in all pseudo-Boolean problems
            instance,              // the instance id
            "NewBooleanProblem",   // problem name
            n_variables           // search dimensionality
            )
          )
    {
    }
};

Please check this example for adding continuous problems in this manner.

Logger Architecture#

API documentation#