Summary
DoubleExponentialFitComputer has two structural problems and one piece of dead code.
1. amp_bound / rate_bound look like bounds but are not
compute_initial_params() derives amp_bound and rate_bound and uses them only to clip the seed. compute_bounds() is not overridden, so the optimisation runs unbounded.
Do not "fix" this by applying them. Measured on the shipped reference dataset piecewiseexponential_fit.txt:
| Strategy |
residual RMS |
a_right |
| clip seed, unbounded (current) |
1.8965 |
22480 |
| clip seed, bounds applied |
16.3272 |
301 (pinned on the bound) |
| raw seed, unbounded |
1.8982 |
22358 |
rate_bound = 5 / x_range = 0.5 is half the true rate (≈ 1.0) and amp_bound = 301 is two orders of magnitude below the true amplitude. The bounds are miscalibrated; enforcing them pins three parameters on their limits.
Resolution applied on fix/fitting-audit: keep the fit unbounded, reword the misleading comment so the clamp is documented as a seed sanitiser, and record the measurement in the class docstring. Deriving genuinely usable bounds remains open.
2. x_center is not optimisable
y[x < x_center] = a_left * np.exp(b_left * x[x < x_center]) + y0
y[x >= x_center] = a_right * np.exp(b_right * x[x >= x_center]) + y0
x_center only enters through boolean masks, so the model is piecewise constant in x_center and its numerical derivative is zero almost everywhere. curve_fit cannot move it: the junction stays frozen on the (very rough) initial estimate. Documented on the branch; making the junction differentiable (smooth blend) is a model change that needs a separate decision.
3. Dead code
y0 = 0.0 is assigned then immediately overwritten; x_range is computed twice. Removed on the branch.
Summary
DoubleExponentialFitComputerhas two structural problems and one piece of dead code.1.
amp_bound/rate_boundlook like bounds but are notcompute_initial_params()derivesamp_boundandrate_boundand uses them only to clip the seed.compute_bounds()is not overridden, so the optimisation runs unbounded.Do not "fix" this by applying them. Measured on the shipped reference dataset
piecewiseexponential_fit.txt:a_rightrate_bound = 5 / x_range = 0.5is half the true rate (≈ 1.0) andamp_bound = 301is two orders of magnitude below the true amplitude. The bounds are miscalibrated; enforcing them pins three parameters on their limits.Resolution applied on
fix/fitting-audit: keep the fit unbounded, reword the misleading comment so the clamp is documented as a seed sanitiser, and record the measurement in the class docstring. Deriving genuinely usable bounds remains open.2.
x_centeris not optimisablex_centeronly enters through boolean masks, so the model is piecewise constant inx_centerand its numerical derivative is zero almost everywhere.curve_fitcannot move it: the junction stays frozen on the (very rough) initial estimate. Documented on the branch; making the junction differentiable (smooth blend) is a model change that needs a separate decision.3. Dead code
y0 = 0.0is assigned then immediately overwritten;x_rangeis computed twice. Removed on the branch.