specsrbench.build.tune

Retune the classical deconvolution hyperparameters against all four guards.

Two things were wrong with the previous version of this file and are fixed here.

It optimised MAE alone. The guards were applied by hand afterwards, in the comments in specsrbench.build.classical_cache. MAE against a noisy reference is minimised by doing nothing, so an unguarded search converges on the most conservative setting available – which is how rl n_iter=1 and sparse n_iter=1 were arrived at. Every guard is now evaluated inside the search and a setting that fails any of them is not eligible.

It deconvolved with the wrong kernel. eval_set.npz ships a sigma_pix that is roughly constant in nanometres across the band; a real spectrograph has a fixed LSF in detector pixels. See specsrbench.build.lsf. With the shipped kernel Wiener, Tikhonov and Wiener+TV destroy a line pair that their own input still resolves, and no choice of parameters fixes it.

The four guards, each catching something the others cannot:

  1. smoothing – median line S/N >= 0.9x the no-deconvolution baseline. A filter that erases every line incurs no line-shaped residual.

  2. shrinkage – output std within [0.90, 1.15] of the target’s. Scaling toward zero lowers MAE regardless of reconstruction quality, and line S/N is blind to it (amplitude over sideband noise is rescale-invariant).

  3. blurring – median line FWHM bias <= the baseline’s. A unit-gain Wiener filter with snr <= 1 peaks at zero frequency, so it cannot amplify anything and can only broaden. The amplitude guard is width-invariant and cannot see this.

  4. merging – a resolvable line pair must survive. Guards 1-3 are all single-line Gaussian-fit statistics, and a Gaussian fitted to a blended doublet has much the same amplitude, S/N and width as one fitted to a separated pair. All three pass while the method merges the [OIII] doublet into one peak, which is what happened. Tested on a synthetic pair at z=6.5, blurred with the derived kernel, where the input resolves the pair – so failing this guard means destroying structure the method was handed.

Tuning uses cache_logR/tune_set.npz – 40 spectra sharing no galaxy with the 572-spectrum evaluation set. Guard 4 is synthetic rather than measured on real spectra because only 5 tune spectra put [OIII] where the pair is resolvable, and using evaluation spectra to choose parameters would leak.

Writes cache_logR_tuned/classical_params.json.

Functions

line_stats(pred, pool)

(median line S/N, median FWHM bias in nm) over the four diagnostic lines.

mae(pred)

main([argv])

Search each classical method's parameters under the four guards.

pair_survival(fn, pool[, src, with_z])

Fraction of resolvable calib pairs the reconstruction keeps resolved.

scan(label, fn, arrs, grid, pool, base_sn, ...)

Minimise MAE over settings that pass all four guards.

std_ratio(pred)

specsrbench.build.tune.mae(pred)[source]
specsrbench.build.tune.std_ratio(pred)[source]
specsrbench.build.tune.line_stats(pred, pool)[source]

(median line S/N, median FWHM bias in nm) over the four diagnostic lines.

specsrbench.build.tune.pair_survival(fn, pool, src=None, with_z=False, **kw)[source]

Fraction of resolvable calib pairs the reconstruction keeps resolved.

src defaults to the raw calib spectra; pass the Wiener output for the methods that run on top of it, so the guard sees what production does. with_z is for the matched filter, which takes (spectrum, redshift).

specsrbench.build.tune.scan(label, fn, arrs, grid, pool, base_sn, base_fwhm, pair_src=None)[source]

Minimise MAE over settings that pass all four guards.

specsrbench.build.tune.main(argv=None)[source]

Search each classical method’s parameters under the four guards.

Return type:

int