C++ examples

Use #include "funccraft.h" for the public C++ API. Evaluations are batched: pass std::vector<std::vector<double>> and receive one value per point.

Construction framework

FuncCraft uses the same runtime model in C++ and Python:

BenchmarkFunction

One concrete callable benchmark function with fixed dimension, domain, assigned optimum, components, transforms, composition rule, and scale factor.

BenchmarkSuite

A materialized collection of benchmark functions for one chosen dimension. Use suite.function(index) to retrieve one BenchmarkFunction. Function indices are one-based.

FunctionSpec and SuiteSpec

Plain C++ structs corresponding to the YAML spec structure. In C++ you usually fill these structs directly or load them from YAML.

BenchmarkFunction

This example creates one composed BenchmarkFunction from explicit C++ FunctionSpec structs. One outer component is itself a nested composed function. The component scale_factor fields are omitted in this example, so FuncCraft estimates and materializes them during construction before the components are composed.

#include "funccraft.h"

#include <iostream>
#include <memory>
#include <vector>

int main() {
    using namespace FuncCraft;

    const int dimension = 2;
    const std::vector<double> assigned_xopt = {0.1, 3.6};
    const std::vector<double> nested_xopt = {1.0, -1.0};

    ComponentSpec nested_rosenbrock;
    nested_rosenbrock.base_function = BasicFunctionId::Rosenbrock;
    nested_rosenbrock.coordinate_transform.kind = CoordinateTransformKind::None;
    nested_rosenbrock.coordinate_transform.input_dimension = dimension;
    nested_rosenbrock.coordinate_transform.output_dimension = dimension;
    nested_rosenbrock.coordinate_transform.assigned_xopt = nested_xopt;
    nested_rosenbrock.value_transform.kind = ValueTransformKind::None;

    ComponentSpec nested_schwefel;
    nested_schwefel.base_function = BasicFunctionId::Schwefel;
    nested_schwefel.coordinate_transform.kind = CoordinateTransformKind::Rotation;
    nested_schwefel.coordinate_transform.input_dimension = dimension;
    nested_schwefel.coordinate_transform.output_dimension = dimension;
    nested_schwefel.coordinate_transform.assigned_xopt = nested_xopt;
    nested_schwefel.coordinate_transform.seed = 17;
    nested_schwefel.value_transform.kind = ValueTransformKind::Power;
    nested_schwefel.value_transform.parameters = {1.1, 1.0};

    FunctionSpec nested_spec;
    nested_spec.dimension = dimension;
    nested_spec.domain.dimension = dimension;
    nested_spec.domain.lower_bound = {-5.0, -5.0};
    nested_spec.domain.upper_bound = {5.0, 5.0};
    nested_spec.components = {nested_rosenbrock, nested_schwefel};
    nested_spec.composition.kind = CompositionKind::CpmWeightedSum;
    nested_spec.assigned_xopt = nested_xopt;
    nested_spec.assigned_fopt = 0.0;
    nested_spec.seed = 11;
    nested_spec.label = "nested-rosenbrock-schwefel";

    ComponentSpec outer_griewank;
    outer_griewank.base_function = BasicFunctionId::Griewank;
    outer_griewank.coordinate_transform.kind = CoordinateTransformKind::None;
    outer_griewank.coordinate_transform.input_dimension = dimension;
    outer_griewank.coordinate_transform.output_dimension = dimension;
    outer_griewank.coordinate_transform.assigned_xopt = assigned_xopt;
    outer_griewank.value_transform.kind = ValueTransformKind::None;

    ComponentSpec outer_nested;
    outer_nested.composed_function = std::make_shared<FunctionSpec>(nested_spec);
    outer_nested.coordinate_transform.kind = CoordinateTransformKind::Rotation;
    outer_nested.coordinate_transform.input_dimension = dimension;
    outer_nested.coordinate_transform.output_dimension = dimension;
    outer_nested.coordinate_transform.assigned_xopt = assigned_xopt;
    outer_nested.coordinate_transform.seed = 31;
    outer_nested.value_transform.kind = ValueTransformKind::Power;
    outer_nested.value_transform.parameters = {1.25, 1.0};

    FunctionSpec spec;
    spec.dimension = dimension;
    spec.domain.dimension = dimension;
    spec.domain.lower_bound = {-10.0, -10.0};
    spec.domain.upper_bound = {10.0, 10.0};
    spec.components = {outer_griewank, outer_nested};
    spec.composition.kind = CompositionKind::CpmWeightedSum;
    spec.assigned_xopt = assigned_xopt;
    spec.assigned_fopt = 0.0;
    spec.seed = 1;
    spec.label = "cpp-nested-composed-function";

    BenchmarkFunction f(spec);
    std::vector<std::vector<double>> points = {
        std::vector<double>(dimension, 0.0),
        assigned_xopt,
        std::vector<double>(dimension, 1.0),
    };
    std::vector<double> values = f(points);

    std::cout << "dimension: " << f.dimension() << '\n';
    std::cout << "label: " << f.label() << '\n';
    std::cout << "component_types: " << f.component_types() << '\n';
    std::cout << "fopt: " << f.get_fopt() << '\n';
    std::cout << "first value: " << values.front() << '\n';
}

Export and import one function

Exported function YAML contains generated matrices, resolved scale factor, and other materialized values needed to reproduce the exact function.

#include "funccraft.h"

#include <iostream>
#include <vector>

int main() {
    FuncCraft::BenchmarkSuite suite =
        FuncCraft::suite_collection(2026, 1).benchmark_suite(2);
    const FuncCraft::BenchmarkFunction& f = suite.function(1);

    f.export_spec("manual_function.yaml");

    FuncCraft::BenchmarkFunction reloaded =
        FuncCraft::make_benchmark_function("manual_function.yaml");

    std::vector<std::vector<double>> points = {
        std::vector<double>(2, 0.0),
        f.get_xopt(),
    };
    std::vector<double> original_values = f(points);
    std::vector<double> reloaded_values = reloaded(points);

    std::cout << f.label() << '\n';
    std::cout << original_values.front() << '\n';
    std::cout << reloaded_values.front() << '\n';
}

Load one function from YAML

Put this in my_function.yaml:

dimension: 2
domain:
  dimension: 2
  lower_bound: [-10.0, -10.0]
  upper_bound: [10.0, 10.0]
components:
  - base_function: Griewank
    coordinate_transform:
      kind: none
      input_dimension: 2
      output_dimension: 2
      assigned_xopt: [0.1, 3.6]
    value_transform:
      kind: none
  - composed_function:
      dimension: 2
      domain:
        dimension: 2
        lower_bound: [-5.0, -5.0]
        upper_bound: [5.0, 5.0]
      components:
        - base_function: Rosenbrock
          coordinate_transform:
            kind: none
            input_dimension: 2
            output_dimension: 2
            assigned_xopt: [1.0, -1.0]
          value_transform:
            kind: none
        - base_function: Schwefel
          coordinate_transform:
            kind: rotation
            input_dimension: 2
            output_dimension: 2
            assigned_xopt: [1.0, -1.0]
            seed: 17
          value_transform:
            kind: power
            parameters: [1.1, 1.0]
      composition:
        kind: cpm-wsum
      assigned_xopt: [1.0, -1.0]
      assigned_fopt: 0.0
      seed: 11
      label: nested-rosenbrock-schwefel
    coordinate_transform:
      kind: rotation
      input_dimension: 2
      output_dimension: 2
      assigned_xopt: [0.1, 3.6]
      seed: 31
    value_transform:
      kind: power
      parameters: [1.25, 1.0]
composition:
  kind: cpm-wsum
assigned_xopt: [0.1, 3.6]
assigned_fopt: 0.0
seed: 1
label: cpp-nested-composed-function

Then load and evaluate it:

#include "funccraft.h"

#include <iostream>
#include <vector>

int main() {
    FuncCraft::FunctionSpec config =
        FuncCraft::load_function_spec("my_function.yaml");
    FuncCraft::BenchmarkFunction f(config);

    std::vector<std::vector<double>> points = {
        std::vector<double>(2, 0.0),
        f.get_xopt(),
        std::vector<double>(2, 1.0),
    };
    std::vector<double> values = f(points);

    std::cout << "dimension: " << f.dimension() << '\n';
    std::cout << f.label() << '\n';
    std::cout << "component_types: " << f.component_types() << '\n';
    std::cout << "fopt: " << f.get_fopt() << '\n';
    std::cout << "first value: " << values.front() << '\n';
}

BenchmarkSuite

This example creates a custom BenchmarkSuite from an explicit SuiteSpec. It lists every current mechanism family. Set a choice probability to 0.0 to keep an option visible but disabled.

#include "funccraft.h"

#include <iostream>
#include <vector>

int main() {
    using namespace FuncCraft;

    SuiteSpec spec;
    spec.supported_dimensions = "2";
    spec.base_functions = all_suite_base_functions();
    spec.composition_base_functions = all_suite_base_functions();

    spec.coordinate_transforms = {
        make_choice(CoordinateTransformKind::None, 0.0),
        make_choice(CoordinateTransformKind::Rotation, 0.5),
        make_choice(CoordinateTransformKind::Affine, 0.0),
        make_choice(CoordinateTransformKind::SubspaceRotation, 0.5),
    };
    spec.value_transforms = {
        make_choice(ValueTransformKind::None, 0.5),
        make_choice(ValueTransformKind::Power, 0.25, 1.0, 1.0),
        make_choice(ValueTransformKind::Oscillatory, 0.25, 0.1, 1.0),
        make_choice(ValueTransformKind::CosineZero, 0.0, 1.0),
    };
    spec.compositions = {
        make_choice(CompositionKind::CpmWeightedSum, 0.1),
        make_choice(CompositionKind::CpmPowerMean, 0.1, 3.0),
        make_choice(CompositionKind::CpmPowerMean, 0.1, 0.1),
        make_choice(CompositionKind::CpmLevelWell, 0.2, 0.1, 1.0),
        make_choice(CompositionKind::DpmSoftmax, 0.25, 0.01),
        make_choice(CompositionKind::DpmBgSoftmax, 0.25, 0.01, 1.0, 0.01),
    };
    spec.min_components = 2;
    spec.max_components = 4;
    spec.max_nested_composition_depth = 1;
    spec.nested_probability = 0.1;
    spec.requested_number_of_functions = 200;
    spec.master_seed = 1;
    spec.lower_bound = -100.0;
    spec.upper_bound = 100.0;
    spec.assigned_fopt = 100.0;
    spec.xopt_domain_shrink_factor = 0.8;
    spec.suite_label = "cpp-suite";

    BenchmarkSuite suite(spec, 2);
    std::cout << "size: " << suite.size() << '\n';
    std::cout << "dimension: " << suite.dimension() << '\n';
    std::cout << "capacity: "
              << suite.theoretical_max_number_of_functions() << '\n';

    for (int index = 1; index <= 5; ++index) {
        const BenchmarkFunction& f = suite.function(index);
        std::cout << "F" << index << ": "
                  << f.component_types() << " | "
                  << f.label() << '\n';
    }
}

Export and import one suite

The suite manifest contains the normalized suite spec and every generated function spec, so the exact benchmark set can be reused later.

#include "funccraft.h"

#include <iostream>
#include <vector>

int main() {
    const int dimension = 10;
    FuncCraft::SuiteSpec spec;
    spec.supported_dimensions = "any";
    spec.base_functions = {
        FuncCraft::BasicFunctionId::Sphere,
        FuncCraft::BasicFunctionId::Rosenbrock,
        FuncCraft::BasicFunctionId::Rastrigin,
    };
    spec.composition_base_functions = spec.base_functions;
    spec.coordinate_transforms = {
        FuncCraft::make_choice(FuncCraft::CoordinateTransformKind::None, 1.0),
    };
    spec.value_transforms = {
        FuncCraft::make_choice(FuncCraft::ValueTransformKind::None, 1.0),
    };
    spec.compositions = {
        FuncCraft::make_choice(FuncCraft::CompositionKind::CpmWeightedSum, 1.0),
    };
    spec.min_components = 2;
    spec.max_components = 3;
    spec.requested_number_of_functions = 5;
    spec.master_seed = 1;
    spec.suite_label = "small-export-suite";

    FuncCraft::BenchmarkSuite suite(spec, dimension);

    suite.export_manifest("generated_suite_manifest.yaml");

    FuncCraft::BenchmarkSuite reloaded_suite(
        "generated_suite_manifest.yaml",
        dimension);

    const FuncCraft::BenchmarkFunction& f1 = reloaded_suite.function(1);
    std::vector<double> values =
        f1({std::vector<double>(dimension, 0.0)});

    std::cout << reloaded_suite.size() << '\n';
    std::cout << f1.label() << '\n';
    std::cout << values.front() << '\n';
}

Load one suite from YAML

Put this in my_suite.yaml:

supported_dimensions: "2"
base_functions: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34]
composition_base_functions: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34]
coordinate_transforms:
  - kind: none
    probability: 0.0
    parameters: []
  - kind: rotation
    probability: 0.5
    parameters: []
  - kind: affine
    probability: 0.0
    parameters: []
  - kind: subspace-rotation
    probability: 0.5
    parameters: []
value_transforms:
  - kind: none
    probability: 0.5
    parameters: []
  - kind: power
    probability: 0.25
    parameters: [1.0, 1.0]
  - kind: oscillatory
    probability: 0.25
    parameters: [0.1, 1.0]
  - kind: cosine-zero
    probability: 0.0
    parameters: [1.0]
compositions:
  - kind: cpm-wsum
    probability: 0.1
    parameters: []
  - kind: cpm-power-mean
    probability: 0.1
    parameters: [3.0]
  - kind: cpm-power-mean
    probability: 0.1
    parameters: [0.1]
  - kind: cpm-level-well
    probability: 0.2
    parameters: [0.1, 1.0]
  - kind: dpm-softmax
    probability: 0.25
    parameters: [0.01]
  - kind: dpm-bgsoftmax
    probability: 0.25
    parameters: [0.01, 1.0, 0.01]
min_components: 2
max_components: 4
max_nested_composition_depth: 1
nested_probability: 0.1
requested_number_of_functions: 200
master_seed: 1
lower_bound: -100
upper_bound: 100
assigned_fopt: 100.0
xopt_domain_shrink_factor: 0.8
suite_label: cpp-suite

Then load it at an explicit dimension and inspect the first five functions:

#include "funccraft.h"

#include <iostream>
#include <vector>

int main() {
    const int dimension = 2;
    FuncCraft::SuiteSpec config =
        FuncCraft::load_suite_spec("my_suite.yaml");
    FuncCraft::BenchmarkSuite suite(config, dimension);

    std::cout << "size: " << suite.size() << '\n';
    std::cout << "dimension: " << suite.dimension() << '\n';
    std::cout << "capacity: "
              << suite.theoretical_max_number_of_functions() << '\n';

    for (int index = 1; index <= 5; ++index) {
        const FuncCraft::BenchmarkFunction& f = suite.function(index);
        std::cout << "F" << index << ": "
                  << f.component_types() << " | "
                  << f.label() << '\n';
    }
}

Shipped suite: FuncCraft Benchmark Suite 2026 v1

FuncCraft ships the versioned 2026_v1 benchmark suite. Load it through suite_collection(2026, 1), then materialize a BenchmarkSuite at the dimension you want.

#include "funccraft.h"

#include <iostream>

int main() {
    FuncCraft::SuiteCollection collection =
        FuncCraft::suite_collection(2026, 1);
    FuncCraft::BenchmarkSuite suite =
        collection.benchmark_suite(2);

    std::cout << "collection name: " << collection.name() << '\n';
    std::cout << "default number of functions: "
              << collection.number_of_functions() << '\n';
    std::cout << "suite size: " << suite.size() << '\n';
    std::cout << "suite dimension: " << suite.dimension() << '\n';

    const FuncCraft::BenchmarkFunction& f1 = suite.function(1);
    std::cout << "F1 label: " << f1.label() << '\n';
    std::cout << "F1 component_types: "
              << f1.component_types() << '\n';
}

Warning

The packaged 2026_v1 suite contains 1,000,000 functions. Do not export the full shipped suite manifest unless you intentionally want a very large file containing every generated function record. To export a smaller reproducible subset, copy the collection SuiteSpec, lower requested_number_of_functions, build a smaller BenchmarkSuite, and export that suite.

#include "funccraft.h"

int main() {
    FuncCraft::SuiteSpec spec =
        FuncCraft::suite_collection_spec(2026, 1);
    spec.requested_number_of_functions = 500;

    FuncCraft::BenchmarkSuite suite(spec, 2);
    suite.export_manifest("suite_2026_v1_first_500.yaml");
}

Plot functions 1 through 500

C++ does not include a plotting backend. A common workflow is to export a value table or manifest from C++, then plot it with Python. For plotting inside FuncCraft documentation, see the Python example page.

#include "funccraft.h"

#include <string>

int main() {
    FuncCraft::BenchmarkSuite suite =
        FuncCraft::suite_collection(2026, 1).benchmark_suite(2);

    for (int index = 1; index <= 500; ++index) {
        const FuncCraft::BenchmarkFunction& f = suite.function(index);
        f.export_spec("function_" + std::to_string(index) + ".yaml");
    }
}

Minimize F45 at 10D with Minion

Build examples with BUILD_EXAMPLES=ON to fetch and build the Minion dependency. Function indices are one-based, so F45 is loaded with suite.function(45).

#include "funccraft.h"
#include <minion.h>

#include <cmath>
#include <cstddef>
#include <iostream>
#include <utility>
#include <vector>

int main() {
    const int dimension = 10;
    const int function_index = 45;

    FuncCraft::BenchmarkSuite suite =
        FuncCraft::suite_collection(2026, 1).benchmark_suite(dimension);
    const FuncCraft::BenchmarkFunction& f45 =
        suite.function(function_index);
    const FuncCraft::Domain& domain = f45.domain();

    std::vector<std::pair<double, double>> bounds;
    bounds.reserve(static_cast<std::size_t>(domain.dimension()));
    for (int i = 0; i < domain.dimension(); ++i) {
        bounds.emplace_back(domain.lower[i], domain.upper[i]);
    }

    std::vector<double> x0(dimension, 1.0);
    auto objective = [&f45](
        const std::vector<std::vector<double>>& X,
        void*) {
        return f45(X);
    };

    auto settings =
        minion::DefaultSettings().getDefaultSettings("ARRDE");
    settings["convergence_tol"] = 1e-8;

    minion::Minimizer optimizer(
        objective,
        bounds,
        x0,
        nullptr,
        nullptr,
        "ARRDE",
        50000,
        1,
        settings);

    minion::MinionResult result = optimizer.optimize();
    const double error = std::abs(result.fun - f45.get_fopt());

    std::cout << "function: F" << function_index << '\n';
    std::cout << "label: " << f45.label() << '\n';
    std::cout << "dimension: " << f45.dimension() << '\n';
    std::cout << "assigned fopt: " << f45.get_fopt() << '\n';
    std::cout << "best value: " << result.fun << '\n';
    std::cout << "|best - fopt|: " << error << '\n';
}

Primitive base functions

Primitive functions can be evaluated directly with BasicF. Use BasicFunctionId values or names from the primitive function ID table.

#include "funccraft.h"

#include <iostream>
#include <vector>

int main() {
    for (FuncCraft::BasicFunctionId id :
         FuncCraft::list_basic_functions()) {
        FuncCraft::BasicF f(id, 2);
        FuncCraft::Domain domain = f.default_domain();
        std::vector<double> x = f.x_opt;
        double value = f.evaluate(x);

        std::cout << static_cast<int>(id) << " "
                  << FuncCraft::to_string(id) << " "
                  << "domain=[" << domain.lower[0] << ", "
                  << domain.upper[0] << "] "
                  << "f(x_opt)=" << value << '\n';
    }
}

See Primitive base function IDs for the full primitive function ID table.