Skip to content

optimize() returns parameters that methods then internally correct #219

Description

@pavelkomarov

Third warning made visible by #206 scoping optimize()'s silencing to the call itself. Notebook 4's sweep prints this repeatedly:

pynumdiff/polynomial_fit.py:152: UserWarning: Kernel window size should be odd. Added 1 to length.

The warning is correct and the correction is correct. The problem is that optimize() handed the method a parameter set it had to fix up, and then returned that same uncorrected set to the caller.

How it happens

_objective_function converts a continuous point to parameters generically:

point_params= {k:(vifsearch_space_types[k] ==floatelseint(np.round(v)))
fork,vinzip(search_space_types, point)}

Nothing stops that from producing window_size=10. savgoldiff then bumps it to 11 and warns. Measured over notebook 4's six simulations, optimize(savgoldiff, ...) returned an even window_size in 3 of 6 cases (cruise 10, triangle 10, lorenz 44).

Three consequences:

  • The returned best_params are not the parameters that actually ran. Re-calling the method with them re-triggers the correction and the warning.
  • window_size=10 and window_size=11 are aliases for the same fit, but hash to different cache keys, so identical work gets done and stored twice.
  • Log noise proportional to the size of the sweep.

The dilemma

int(np.round(v)) is deliberately generic — it must serve every integer parameter, and only some of them are odd-constrained. Special-casing inside it would put per-parameter knowledge in a general mechanism.

But the per-parameter conversion rule already exists at optimize.py:213:

search_space_types= {k:type(v[0]) fork,vinsearch_space.items() ifisinstance(v, list)}

That maps a parameter name to a type, and the consumer switches on it. Storing a converter instead — identity for float, round for int, round-to-nearest-odd for odd ints — keeps the cast fully generic while moving the odd-ness knowledge into the search space definition, next to bounds, where each method's parameter knowledge already lives.

Open question: how to express "odd int" in the existing spec vocabulary, where list/set/scalar already mean numerical/categorical/singleton and the type is inferred from type(v[0]). Probably a marker class or a parallel dict.

Scope limit

This only fully solves the per-parameter constraints. savgoldiff also clips window_size to [degree+1, x.shape[axis]-1] and polydiff raises it to degree*3+1 — both depend on other parameters, so no per-parameter rule can guarantee "returned parameters are always exactly what ran." Fully closing that would require methods reporting their canonicalization back to the optimizer, which is a much larger change than this warrants. The odd constraint is the one actually generating noise.

Note this changes results: restricting proposals to odd values alters the Nelder-Mead trajectory, so it shouldn't land mid-experiment.


🤖 Written by Claude Code on behalf of @pavelkomarov

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions