fix(quickadapter): refit iteration aliases, test_size None docs, live holdout_rmse (#124)
* fix(quickadapter): clear regressor iteration aliases before refit
The refit set the canonical iteration parameter on a deep copy of the user
model_training_parameters without removing configured synonyms. CatBoost aborts
on duplicate iteration aliases, so a selection fit that used n_estimators,
num_boost_round, or num_trees crashed at the post-holdout refit. Purge all known
iteration aliases per regressor before setting the canonical count.
None reaches sklearn dynamic sizing only through timeseries_split; the
train_test_split path rejects any non-int/float value. Document that None
applies to timeseries_split and that train_test_split requires a float or int.
* fix(quickadapter): restore holdout_rmse on live model reload
A live or dry-run restart that reuses a cached model without invoking fit left
_holdout_rmse at the constructor inf placeholder, so fit_live_predictions
published holdout_rmse=inf until the next retraining window. Track pairs fitted
in the current session and, when none ran, recover the last finite holdout_rmse
from historic_predictions.
* fix(quickadapter): complete lightgbm iteration aliases and guard alias-map coverage
The lightgbm alias set omitted max_iter, num_round, and num_tree. LightGBM does
not reject duplicate iteration synonyms; it silently lets a leftover alias win
over n_estimators, so a config using one of those names would train the refit at
its original capacity instead of the selected count. Complete the set to every
num_iterations synonym and add an import-time guard that the alias map covers
REGRESSORS.
* fix(quickadapter): coerce holdout_rmse to numeric before finite filter
Guard the live holdout_rmse restore against a non-float historic column: coerce
with pd.to_numeric(errors=coerce) so np.isfinite cannot raise on an object dtype,
matching the defensive handling of the non-live replay branch.
* refactor(quickadapter): consolidate per-regressor metadata into RegressorSpec
Replace the hand-maintained REGRESSORS tuple, the _REFIT_ITERATION_ALIASES map
and the scattered magic-index dispatch with a single RegressorSpec source: the
_REGRESSOR_SPECS NamedTuple singleton (mirroring _OPTUNA_NAMESPACES) carries each
regressor's canonical iteration parameter, its iteration aliases and its RNG seed
parameter. REGRESSORS, DEFAULT_REGRESSOR and the by-name lookup derive from it,
and import-time guards enforce coverage of the Regressor literal and that each
canonical iteration parameter is one of its own aliases.
get_refit, fit_regressor and get_optuna_study_model_parameters now dispatch on
named specs (regressor == _REGRESSOR_SPECS.xgboost.name) instead of REGRESSORS[i];
fit_regressor's identical per-branch seed setdefault + trial increment is hoisted
into one spec-driven prelude. Behavior preserved: same REGRESSORS values/order,
same seed handling (verified identical across all five regressors), introspection
and library-specific fit logic left in their branches.
QuickAdapterRegressorV3 uses DEFAULT_REGRESSOR instead of REGRESSORS[0].
* fix(quickadapter): recover last published holdout_rmse (incl. inf) on live reload
Restoring the last FINITE historic holdout_rmse discarded an intentional inf from
a model trained with test_size=0 and could resurrect a stale finite score from an
earlier holdout-enabled configuration. Take the last published (non-null) value
instead, so the recovered metric faithfully reflects the cached model.
* docs(quickadapter): restore inner-validation 0.1 fallback note for test_size
The test_size row documented only the outer None behavior after the applicability
fix; restore that the inner validation split falls back to 0.1, and tighten the
refit clause to keep the row within the table column width.