Nonlinear regressionΒΆ

In this example, we use the PANTR solver to fit a nonlinear model to some noisy data. The model is given by \(y = p_1\ \sin(p_2 x) + p_3\), and the goal is to estimate the parameters \(p_1\), \(p_2\) and \(p_3\) by minimizing the squared error between the model and the data.

True model, noisy data and estimated model.
 1# %% alpaqa nonlinear regression example
 2
 3from pprint import pprint
 4
 5import casadi as cs
 6import numpy as np
 7
 8import alpaqa as pa
 9
10# %% Build the problem (CasADi code, independent of alpaqa)
11
12# Symbolic model parameters
13p = cs.MX.sym("params", 3)
14# Symbolic data vectors
15N_data = 50
16data_x = cs.MX.sym("data_x", N_data)
17data_y = cs.MX.sym("data_y", N_data)
18data = cs.horzcat(data_x, data_y)
19
20# Objective function is the squared error between the model and the data
21x, y = cs.MX.sym("x"), cs.MX.sym("y")
22model = cs.Function("model", [x, p], [p[0] * cs.sin(p[1] * x) + p[2]])
23sample_error = cs.Function("err", [x, y, p], [y - model(x, p)])
24sum_sq_error = cs.sumsqr(sample_error.map(N_data)(data_x.T, data_y.T, p))
25
26# %% Generate and compile C code for the objective and constraints using alpaqa
27
28# Compile and load the problem (without general constraints)
29problem = (
30    pa.minimize(sum_sq_error, p)
31    .with_param(cs.vec(data))
32).compile(second_order="psi_prod", sym=cs.SX.sym)
33# Optionally, add constraints on the parameters
34problem.variable_bounds.lower[1] = 3
35problem.variable_bounds.upper[1] = 7
36
37# %% Generate some data
38
39true_params = [6, 5, 4]
40true_model = np.vectorize(lambda x: model(x, true_params))
41rng = np.random.default_rng(12345)
42data_x = np.linspace(-1, +1, N_data, endpoint=True)
43data_y = true_model(data_x) + rng.standard_normal(N_data)
44
45# Add data to the problem
46problem.param = np.concatenate((data_x, data_y))
47
48# %% Solve the problem using alpaqa's PANTR solver
49
50solver = pa.PANTRSolver({"print_interval": 1})
51# Add evaluation counters to the problem
52cnt = pa.problem_with_counters(problem)
53sol_params, stats = solver(cnt.problem, {"tolerance": 1e-10})
54
55# %% Print the results
56
57print(f"\nSolution:      {sol_params}")
58print(stats["status"])
59pprint(stats)
60print("\nEvaluations:")
61print(cnt.evaluations)
62
63# %% Plot the results
64
65import matplotlib.pyplot as plt
66
67model_str = r"${:.2f}\ \sin({:.2f} x) + {:.2f}$"
68sol_model = np.vectorize(lambda x: model(x, sol_params))
69plt.figure(figsize=(8, 5))
70x_fine = np.linspace(-1, 1, 256)
71plt.plot(data_x, data_y, "x", label="Samples")
72plt.plot(x_fine, true_model(x_fine),
73         label="True model\n" + model_str.format(*true_params))
74plt.plot(x_fine, sol_model(x_fine), "--",
75         label="Est. model\n" + model_str.format(*sol_params))
76plt.legend(loc='upper right')
77plt.title("PANTR nonlinear regression example")
78plt.tight_layout()
79plt.show()
80
81# %%