Skip to content

Use pydantic for algo option validation (instead of our own validation logict). - #696

Open
janosg wants to merge 6 commits into
mainfrom
pydantic-for-algo-options
Open

Use pydantic for algo option validation (instead of our own validation logict).#696
janosg wants to merge 6 commits into
mainfrom
pydantic-for-algo-options

Conversation

@janosg

@janosg janosg commented Aug 2, 2026

Copy link
Copy Markdown
Member

Current situation

Algorithm options are converted and validated by a hand-written converter table (TYPE_CONVERTERS in type_conversion.py) that Algorithm.post_init looks up per field, keyed by type objects for eight supported types. AlgoInfo and InternalOptimizeResult carry ~50 lines of manual isinstance checks each.

Two problems are hard to solve within this design:

  • In modules with from future import annotations — which the how-to-document-optimizers guide requires — field.type is an annotation string, so the table lookup silently fails and options are neither coerced nor validated. This already affects most optimizer modules.
  • Anything beyond the eight exact types is skipped: PositiveInt | None, Literals, unions, nested option types. Supporting them would mean reimplementing generic type introspection and coercion — exactly what pydantic provides. Notably, our type aliases are already built on annotated-types (Annotated[int, Gt(0)]), which is pydantic's native constraint vocabulary.

Proposed Implementation

Replace our validation logic with pydantic.

Backwards compatibility

  • Algorithm classes (including user-defined ones) are still written as plain frozen dataclasses; validation is added by mark.minimizer, so no algorithm definition changes.
  • All public exception types and message prefixes are preserved (InvalidAlgoOptionError, InvalidAlgoInfoError, TypeError for InternalOptimizeResult), including for unknown constructor kwargs (extra="forbid").
  • The internal classes use strict mode, mirroring the old isinstance semantics.

Remaining breaking changes:

  • Fractional floats for int options raise instead of being silently truncated (stopping_maxiter=5.5 was 5; integral floats like 1e6 still coerce). Numeric strings in float notation ("1e6") are rejected for int options; plain int strings still work.
  • Fields that were previously unvalidated (Literals, unions, str, arrays, ...) are now enforced, so invalid values that used to pass silently now raise.

Alternatives

PR #691 patches the string-annotation symptom by adding a second, string-keyed converter table (TYPE_CONVERTERS_BY_NAME) and branching on the annotation kind in post_init. That fixes the silently-skipped coercion but keeps everything else: optional and union types remain unvalidated, coverage stays limited to eight hand-written converters, and two parallel tables must be kept in sync with typing.py forever. This PR solves the whole class of problems, deletes code instead of adding it, and extends naturally to the other validation boilerplate in the codebase (#694, #695). If this is merged, #691 should be closed and #690 rebased onto main afterwards.

@janosg
janosg requested a review from timmens August 2, 2026 17:55
@codecov

codecov Bot commented Aug 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
src/optimagic/mark.py 91.07% <100.00%> (+1.07%) ⬆️
src/optimagic/optimization/algorithm.py 94.53% <100.00%> (-0.45%) ⬇️
src/optimagic/optimizers/bayesian_optimizer.py 91.66% <100.00%> (+0.07%) ⬆️
src/optimagic/optimizers/fides.py 96.05% <100.00%> (+0.16%) ⬆️
src/optimagic/optimizers/gfo_optimizers.py 100.00% <100.00%> (ø)
src/optimagic/optimizers/pygmo_optimizers.py 94.98% <100.00%> (+0.01%) ⬆️
src/optimagic/optimizers/tranquilo.py 99.21% <100.00%> (+0.06%) ⬆️
src/optimagic/typing.py 91.15% <100.00%> (+2.38%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant