Note
Go to the end to download the full example code.
Levenberg-Marquardt: fitting a damped oscillation#
Fits a four-parameter damped cosine to noisy data. The residuals depend nonlinearly on the decay rate and frequency, so the fit needs an iterative method; Levenberg-Marquardt blends gradient descent (far from the solution) with Gauss-Newton (close to it).
import matplotlib.pyplot as plt
import numpy as np
from mathematicskit.optimization import levenberg_marquardt
Noisy data from a known model#
Fit from a rough initial guess#
x0 = [1.0, 0.1, 1.3, 0.0]
result = levenberg_marquardt(lambda p: model(p, t) - y, x0)
print(f"true parameters: {true}")
print(f"fitted parameters: {result.x.round(4)}")
print(f"residual cost 0.5*||r||^2 = {result.cost:.4f} after {result.nfev} evaluations")
fig, ax = plt.subplots()
ax.plot(t, y, ".", color="0.5", label="data")
ax.plot(t, model(x0, t), "--", label="initial guess")
ax.plot(t, model(result.x, t), label="Levenberg-Marquardt fit")
ax.set_xlabel("t")
ax.legend()
ax.set_title("Nonlinear least squares (Levenberg 1944, Marquardt 1963)")

true parameters: [2. 0.3 1.5 0.4]
fitted parameters: [2.0297 0.3011 1.5011 0.4084]
residual cost 0.5*||r||^2 = 0.1332 after 8 evaluations
Text(0.5, 1.0, 'Nonlinear least squares (Levenberg 1944, Marquardt 1963)')
Total running time of the script: (0 minutes 0.031 seconds)