mirror of
https://github.com/slsdetectorgroup/aare.git
synced 2026-07-01 22:38:50 +02:00
Adds three Minuit2-backed spectrum models to the Python-exposed fitting API: - `GaussianErfcPlateau` - `GaussianChargeSharing` - `GaussianChargeSharingKb` Closes #297
108 KiB
108 KiB
In [1]:
import time
import numpy as np
np.set_printoptions(suppress=True, precision=6)
import matplotlib.pyplot as plt
from scipy.special import erf
from scipy.optimize import curve_fit
from pprint import pprint
import sys
sys.path.insert(0, "/home/ferjao_k/sw/aare/build")
try:
from aare import GaussianErfcPlateau
except ImportError:
import aare
print("Could not import GaussianErfcPlateau directly.")
print("Available Gaussian/Erf/Plateau-like names:")
pprint([name for name in dir(aare)
if any(token.lower() in name.lower()
for token in ("gauss", "erf", "plateau"))])
raiseIn [2]:
def gaussian_erfc_plateau(x, p):
A, S, mu, sigma = p
z = (x - mu) / (np.sqrt(2.0) * sigma)
return A * np.exp(-0.5 * ((x - mu) / sigma)**2) + 0.5 * S * (1.0 - erf(z))
def gaussian_erfc_plateau_curve_fit(x, A, S, mu, sigma):
return gaussian_erfc_plateau(x, np.array([A, S, mu, sigma], dtype=float))In [3]:
# Synthetic data
rng = np.random.default_rng(42)
x = np.linspace(-4.0, 6.0, 251)
p_true = np.array([
1.20, # A: Gaussian amplitude
1.00, # S: left plateau height
1.00, # mu: shared center / step position
0.65, # sigma: shared width
])
noise_sigma = 0.035
y_true = gaussian_erfc_plateau(x, p_true)
y = y_true + rng.normal(0.0, noise_sigma, size=x.shape)
y_err = np.full_like(x, noise_sigma)
plt.figure(figsize=(8, 4))
plt.plot(x, y_true, label="true")
plt.scatter(x, y, s=10, label="noisy data")
plt.xlabel("x")
plt.ylabel("y")
plt.title("Synthetic Gaussian + erfc plateau data")
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()In [4]:
# SciPy reference fit
p0_ref = np.array([1.0, 0.8, 0.5, 0.8])
bounds_ref = (
[-np.inf, -np.inf, -np.inf, 1e-12],
[ np.inf, np.inf, np.inf, np.inf],
)
p_scipy, cov_scipy = curve_fit(
gaussian_erfc_plateau_curve_fit,
x,
y,
p0=p0_ref,
sigma=y_err,
absolute_sigma=True,
bounds=bounds_ref,
maxfev=20_000,
)
print("True params : ", p_true)
print("SciPy params: ", p_scipy)
print("SciPy abs error:", np.abs(p_scipy - p_true))True params : [1.2 1. 1. 0.65] SciPy params: [1.19916 1.000479 1.005638 0.64249 ] SciPy abs error: [0.00084 0.000479 0.005638 0.00751 ]
In [5]:
# Aare / Minuit2 object API fit
model = GaussianErfcPlateau()
print("Parameter list:")
print(model.par_names)
print("n_par:", model.n_par)
print()
# Optional: provide explicit starts close enough to avoid relying only on estimate_par().
model.SetParameter("A", 1.0)
model.SetParameter("S", 0.8)
model.SetParameter("mu", 0.5)
model.SetParameter("sigma", 0.8)
# Optional fit settings
model.compute_errors = True
model.max_calls = 1000
model.tolerance = 0.01
print("== Tuned fit settings ==")
print(f"max_calls : {model.max_calls}")
print(f"tolerance : {model.tolerance}")
print(f"compute_errors : {model.compute_errors}")
print()
res_aare = model.fit(x, y, y_err)
print("== Aare / Minuit2 result ==")
pprint(res_aare, sort_dicts=False)
p_aare = np.array(res_aare["par"], dtype=float)
print()
print("True params : ", p_true)
print("SciPy params : ", p_scipy)
print("Aare params : ", p_aare)
print("SciPy abs error : ", np.abs(p_scipy - p_true))
print("Aare abs error : ", np.abs(p_aare - p_true))Parameter list:
['A', 'S', 'mu', 'sigma']
n_par: 4
== Tuned fit settings ==
max_calls : 1000
tolerance : 0.01
compute_errors : True
== Aare / Minuit2 result ==
{'par': array([1.199158, 1.000478, 1.00564 , 0.642494]),
'par_err': array([0.008525, 0.003768, 0.005623, 0.005163]),
'chi2': array([218.22232])}
True params : [1.2 1. 1. 0.65]
SciPy params : [1.19916 1.000479 1.005638 0.64249 ]
Aare params : [1.199158 1.000478 1.00564 0.642494]
SciPy abs error : [0.00084 0.000479 0.005638 0.00751 ]
Aare abs error : [0.000842 0.000478 0.00564 0.007506]
In [26]:
plt.figure(figsize=(8, 4))
# plt.plot(x, y_true, label="true")
plt.scatter(x, y, s=10, label="data", color="tab:blue")
plt.plot(x, gaussian_erfc_plateau(x, p_scipy), linewidth=4.0, label="SciPy curve_fit", color="tab:red")
plt.plot(x, model(x, p_aare), linewidth=1.0, label="Aare Minuit2", color="tab:cyan")
plt.xlabel("x")
plt.ylabel("y")
plt.title("Gaussian + erfc plateau fit comparison")
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()In [27]:
def bench(fn, n_repeats=200):
for _ in range(3):
fn()
t0 = time.perf_counter()
for _ in range(n_repeats):
res = fn()
t1 = time.perf_counter()
return res, (t1 - t0) / n_repeats
def fit_scipy_once():
return curve_fit(
gaussian_erfc_plateau_curve_fit,
x,
y,
p0=p0_ref,
sigma=y_err,
absolute_sigma=True,
bounds=bounds_ref,
maxfev=20_000,
)[0]
def fit_aare_once():
m = GaussianErfcPlateau()
m.SetParameter("A", 1.0)
m.SetParameter("S", 0.8)
m.SetParameter("mu", 0.5)
m.SetParameter("sigma", 0.8)
m.max_calls = 1000
m.tolerance = 0.01
return m.fit(x, y, y_err)["par"]
p_scipy_bench, t_scipy = bench(fit_scipy_once, n_repeats=200)
p_aare_bench, t_aare = bench(fit_aare_once, n_repeats=200)
print(f"SciPy curve_fit : {1e3*t_scipy:.3f} ms")
print(f"Aare Minuit2 : {1e3*t_aare:.3f} ms")
print()
print("SciPy bench params:", p_scipy_bench)
print("Aare bench params :", p_aare_bench)SciPy curve_fit : 3.746 ms Aare Minuit2 : 0.355 ms SciPy bench params: [1.19916 1.000479 1.005638 0.64249 ] Aare bench params : [1.199158 1.000478 1.00564 0.642494]
In [ ]: