Libmir Archive
mir-optimmir-optim

mir-optim

Optimization algorithms for D — nonlinear least-squares (Levenberg-Marquardt), L-BFGS, and trust-region methods. Works with ndslice, BetterC compatible.

📖 Full API reference → mir-optim.libmir.org — every module and symbol of mir-optim, regenerated from current source.

mir-optim provides numerical optimization algorithms for D, focused on nonlinear least-squares problems common in data fitting, machine learning, and scientific computing.

GitHub: libmir/mir-optim · Last commit: May 2025

Install

dependency "mir-optim" version="~>2.0"
dependency "mir-algorithm" version="~>3.0"

Nonlinear Least-Squares (Levenberg-Marquardt)

The primary use case: fit a model to data by minimizing the sum of squared residuals.

import mir.optim.least_squares;
import mir.ndslice;

// Fit y = a * exp(-b * x) to data points
void residuals(
    Slice!(double*, 1) params,  // [a, b]
    Slice!(double*, 1) res,     // output residuals
) @nogc nothrow {
    double a = params[0], b = params[1];
    foreach (i; 0 .. res.length)
        res[i] = a * exp(-b * xdata[i]) - ydata[i];
}

auto result = levenbergMarquardt(
    &residuals,
    params0,   // initial guess as ndslice
    nResiduals,
);

if (result.success)
    writeln("Fitted params: ", result.x);

L-BFGS — general gradient-based optimization

For unconstrained minimization of smooth functions:

import mir.optim.lbfgs;

double objective(Slice!(double*, 1) x, Slice!(double*, 1) grad) @nogc nothrow {
    // Rosenbrock function
    double f = 0;
    foreach (i; 0 .. x.length - 1) {
        double t = x[i + 1] - x[i] * x[i];
        f += 100 * t * t + (1 - x[i]) * (1 - x[i]);
        grad[i]     += -400 * x[i] * t - 2 * (1 - x[i]);
        grad[i + 1] += 200 * t;
    }
    return f;
}

auto x0 = [-1.2, 1.0].sliced;
auto result = lbfgs(&objective, x0);

Result types

mir-optim uses mir.algebraic.Variant for results:

auto r = levenbergMarquardt(...);

r.match!(
    (LMSuccess s) => writeln("converged in ", s.iterations, " iters"),
    (LMFailure f) => writeln("failed: ", f.reason),
);

Jacobian — automatic vs manual

For least-squares, you can provide an analytical Jacobian for speed, or let mir-optim approximate it numerically:

// Numerical Jacobian (slower but easier)
auto result = levenbergMarquardt(&residuals, params0, nResiduals,
    LMOptions(jacobiApproximation: true));

// Analytical Jacobian (faster)
void jacobian(
    Slice!(double*, 1) params,
    Slice!(double*, 2) J,   // nResiduals × nParams
) @nogc nothrow { /* fill J */ }

auto result2 = levenbergMarquardt(&residuals, &jacobian, params0, nResiduals);

Use cases

  • Curve fitting (exponential, Gaussian, power law)
  • Calibration of physical models
  • Neural network training (small networks)
  • Inverse kinematics
  • Computer vision (bundle adjustment)

On this page