CppNumericalSolversBenchmark

May 10, 2026 · View on GitHub

Out-of-tree benchmark comparing CppNumericalSolvers against the canonical L-BFGS reference implementations on 83 unconstrained optimization problems plus the ~290-problem CUTEst unconstrained subset, with a separate constrained track against IPOPT and ALGENCAN on 29 Hock-Schittkowski 1981 problems.

View interactive results

Solvers

SolverHistory mWolfe c₂Line search
cppoptlib L-BFGS100.9Moré-Thuente
cppoptlib L-BFGS (HZ)100.9Hager-Zhang
LBFGSpp60.9Moré-Thuente
LBFGS-Lite80.9Lewis-Overton
libLBFGS60.9MINPACK Moré-Thuente
Nocedal Fortran L-BFGS50.9Moré-Thuente (MCSRCH)

All drivers use each library's out-of-the-box defaults — no per-problem tuning.

Problems

FamilyCountDim range
MGH 1–34 (Moré-Garbow-Hillstrom 1981)342–20
MGH 35 Chebyquad (bounded)150
Hock-Schittkowski / Toint-Petrova (NLSProblems.jl)472–100
Logistic regression on Iris15
CUTEst unconstrained subset (^[QSO]UR2-.+-[V0]$)~2932–50000

Running

Three independent tracks live in this repository:

TrackRunnerCSV outputProblems
Unconstrained./run.shunconstrained.csv83 MGH + Hock-Schittkowski + logistic regression
CUTEst./run_cutest.shcutest.csv~293 SIF problems classified as smooth-unconstrained
Constrained./run_constrained.shconstrained.csv29 Hock-Schittkowski 1981 (HS001..HS030 minus HS025)

All three runners distribute problems across all available CPU cores ($(nproc)) for the build phase and serialise the timing phase for fair wall-clock numbers. Per-iteration convergence traces land in the build cache automatically.

Unconstrained track

git clone --recursive https://github.com/PatWie/CppNumericalSolversBenchmark
cd CppNumericalSolversBenchmark
./run.sh > unconstrained.csv 2> unconstrained.err
python scripts/csv_to_json.py unconstrained.csv

The run takes ~5 seconds on a recent machine with 8 cores. csv_to_json.py writes site/data.json, which drives the site/index.html page.

CUTEst track

The CUTEst runner needs a Docker image that ships ARCHDefs + SIFDecode + CUTEst + MasterSIF pre-built; build it once with:

docker/cutest/build.sh

Then run:

./run_cutest.sh > cutest.csv 2> cutest.err

run_cutest.sh automatically re-executes itself inside the image, so no manual docker run wrapping is needed. It shares the /tmp/cppopt-bench/cutest-cache directory with the host so decoded SIF files and compiled object files persist across runs; a cold run finishes in ~30 seconds, a cache-hit run in ~5 seconds plus the per-problem timing window.

CUTEst rows share the CSV schema of the unconstrained track. A combined view lives in site/data.json:

cat unconstrained.csv <(tail -n +2 cutest.csv) \
  | python scripts/csv_to_json.py -

Constrained track

The constrained runner needs a Docker image that ships IPOPT 3.11.9 and ALGENCAN 3.1.1; build it once with:

docker/build.sh

Then run:

./run_constrained.sh > constrained.csv 2> constrained.err
python scripts/build_constrained_data.py constrained.csv

run_constrained.sh automatically re-executes itself inside the image, so no manual docker run wrapping is needed. The run takes ~18 seconds on the same machine. build_constrained_data.py writes site/constrained_data.json, which drives site/constrained.html.

Serving the site

python -m http.server -d site
# open http://localhost:8000/ for the unconstrained + CUTEst track
# open http://localhost:8000/constrained.html for the constrained track

The landing page filter control lets you narrow the summary cards, performance profiles, and results table to a single problem set (MGH, HS/TP, CUTEst, or logreg) without re-running anything.

Environment overrides

CPPOPT_BENCH_CACHE            tarball cache dir (default /tmp/cppopt-bench)
CPPOPT_CONSTRAINED_IMAGE      Docker image for the constrained runner
                              (default cppoptbench-constrained:latest)
CPPOPT_CUTEST_IMAGE           Docker image for the CUTEst runner
                              (default cppoptbench-cutest:latest)
CPPOPT_CUTEST_PROBLEMS        space-separated SIF names to run
                              (default: full classification-filtered set)
CC, CXX, FC                   compilers (default gcc, g++, gfortran)
CPPOPTLIB_ROOT                override CppNumericalSolvers source path

Adding a problem

Generated (scope B):

  1. Add a Spec(...) to scripts/problems_spec.py.
  2. Run uv run python scripts/gen_problems.py <name>.
  3. If multiple optima are valid, add to scripts/acceptance.py.

Hand-written (MGH):

  1. Create problems/<name>/ with meta.h, cpp.cc, lbfgspp.cc, lbfgslite.cc, liblbfgs.c, fortran.f.

Dependencies

Git submodules under third_party/:

CppNumericalSolvers    https://github.com/PatWie/CppNumericalSolvers
LBFGSpp                https://github.com/yixuan/LBFGSpp
LBFGS-Lite             https://github.com/ZJU-FAST-Lab/LBFGS-Lite
liblbfgs               https://github.com/chokkan/liblbfgs
eigen                  https://gitlab.com/libeigen/eigen (3.4.0)

Nocedal's Fortran L-BFGS and L-BFGS-B are fetched as tarballs on first run.

License

MIT.