C++
Boost
Functors
Optimization
Brent's Method

How do I construct a functor for use with an algorithm like boost's brent_find_minima?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

For boost::math::tools::brent_find_minima, you need a callable object that accepts one numeric input and returns the function value at that point. That callable can be a functor, a lambda, or a free function, but a functor is useful when the function depends on stored parameters. The key is to implement operator() with the signature the algorithm expects.

What Brent's Minimizer Needs

Brent's method searches for a minimum of a one-dimensional function on a bounded interval. The callable should behave like:

  • input: one numeric value, usually double
  • output: the value of the function at that input

A minimal stateful functor looks like this:

cpp
1struct Quadratic {
2    double a;
3    double b;
4    double c;
5
6    double operator()(double x) const {
7        return a * x * x + b * x + c;
8    }
9};

The const on operator() is important because many algorithms expect the callable not to mutate itself during evaluation.

Complete Example with brent_find_minima

Here is a basic example using Boost.

cpp
1#include <boost/math/tools/minima.hpp>
2#include <cstdint>
3#include <iostream>
4#include <utility>
5
6struct Quadratic {
7    double a;
8    double b;
9    double c;
10
11    double operator()(double x) const {
12        return a * x * x + b * x + c;
13    }
14};
15
16int main() {
17    Quadratic f{1.0, -4.0, 5.0};
18
19    std::uintmax_t iterations = 50;
20    auto result = boost::math::tools::brent_find_minima(f, 0.0, 5.0, 30, iterations);
21
22    std::cout << "x = " << result.first << '
23';
24    std::cout << "f(x) = " << result.second << '
25';
26}

This works because Quadratic is callable with one double argument.

Why a Functor Is Useful

A plain function can work if the target function has no configurable parameters. A functor becomes useful when you want to bundle state such as coefficients, offsets, scaling factors, or external configuration.

That is exactly what happens in the quadratic example. The coefficients a, b, and c travel with the callable object.

Use Templates If You Need Generic Numeric Types

If you want the same functor to work across different numeric types, you can template the call operator or the whole struct.

cpp
1template <typename T>
2struct Quadratic {
3    T a;
4    T b;
5    T c;
6
7    T operator()(T x) const {
8        return a * x * x + b * x + c;
9    }
10};

This can be useful when experimenting with float, double, or higher-precision types.

A Lambda Often Works Too

If you do not need a named type, a lambda is often shorter.

cpp
auto f = [a = 1.0, b = -4.0, c = 5.0](double x) {
    return a * x * x + b * x + c;
};

Then pass f directly to brent_find_minima. A lambda is often the best answer for small, local optimization tasks, while a named functor is better when the callable has domain meaning or will be reused.

Keep the Function Well-Behaved on the Interval

The algorithm assumes your function makes sense on the interval you provide. If the callable throws, uses invalid math, or has discontinuities in the search region, results can be misleading or the optimization can fail.

That means the real contract is not only the signature. It is also:

  • valid evaluations across the interval
  • reasonable numeric behavior
  • a bounded region likely to contain the target minimum

Stateful Functors Should Stay Lightweight

A functor can hold state, but try to keep that state simple and cheap to copy unless you know the algorithm's usage pattern and performance needs. If the functor owns large objects, you may need to think more carefully about capture and copying behavior.

For most optimization use cases, a small immutable parameter bundle is the right design.

Common Pitfalls

  • Forgetting to implement operator() with the expected single-argument callable shape.
  • Omitting const on the call operator and running into algorithm compatibility issues.
  • Passing a function that is undefined or unstable on parts of the search interval.
  • Using a functor when a simple lambda would be clearer for a tiny one-off case.
  • Confusing the search interval with the algorithm's output precision or iteration controls.

Summary

  • 'brent_find_minima needs a callable that maps one numeric input to one numeric output.'
  • A functor is just a class with operator() implementing that callable behavior.
  • Functors are especially useful when the function needs stored parameters.
  • Lambdas can solve the same problem for smaller local cases.
  • The callable signature is important, but so is the numeric behavior of the function across the search interval.

Course illustration
Course illustration

All Rights Reserved.