Skip to content

refactor(DOCP): implement the CTSolvers dispatch contract#601

Draft
ocots wants to merge 5 commits into
mainfrom
refactor/docp-dispatch
Draft

refactor(DOCP): implement the CTSolvers dispatch contract#601
ocots wants to merge 5 commits into
mainfrom
refactor/docp-dispatch

Conversation

@ocots

@ocots ocots commented Jun 27, 2026

Copy link
Copy Markdown
Member

Context

Companion to control-toolbox/CTSolvers.jl#155, which replaces the closure-factory / builder-accessor indirection with a direct multiple-dispatch DOCP contract (problem-first, no closures, no builders). This PR migrates CTDirect to that contract.

Previously, (discretizer::Collocation)(ocp) built 4 closures wrapped into a DiscretizedModel{4 builders}, with a captured exa_getter = nothing mutated as a side effect (temporal coupling). That is replaced by named methods dispatching on (DiscretizedModel{<:Discretizer}, modeler).

Changes

Source

  • AbstractDiscretizer is now owned by CTSolvers; the local abstract type and the discretize wrappers are removed. Imports Strategies/Options/Core/Exceptions from CTBase.
  • New DOCP_cache.jl: DOCPCache <: CTBase.Core.AbstractCache (docp + exa_getter), making the build_model → build_solution Exa coupling explicit (no captured closure).
  • get_docp unified in DOCP_data.jl (shared by all discretizers; resolves the long-standing TODO).
  • collocation.jl / direct_shooting.jl: closure factory → named CTSolvers.discretize / build_model / build_solution methods. DirectShooting × Exa is intentionally left to the CTSolvers NotImplemented stub.

Project / compat

  • CTBase moved to [deps]; compat bumped to the new stack: CTBase 0.25, CTModels 0.13, ExaModels 0.11 (+ docs/Project.toml to CTBase 0.25, required by the [workspace]).
  • CTParser stays test-only ([extras] / [targets]), not in [deps].

Tests

  • CTDirect.discretizeCTSolvers.discretize; the manual-backend nnz test builds via CTSolvers.build_model(docp, init, ADNLP(backend=:manual)).

Validation

Full suite green against the CTBase 0.25 / CTModels 0.13 / CTSolvers 0.4 stack: 406 passed, 0 failed, 0 errored — all ADNLP and Exa cases (collocation, all schemes, continuation, param estimation, zero-control, …).

Note: an earlier run showed the 3 Exa testsets erroring. That turned out to be a DOCPCache parametrization bug on our side (the nothing default froze the exa_getter field to ::Nothing), not a CTParser/ExaModels issue. Fixed in this PR.

⚠️ Design note — mutable cache

DOCPCache is a mutable struct and its exa_getter field is typed Any so it can be reassigned after construction. A mutable cache is not ideal; the build_model → build_solution coupling (the Exa getter) would be cleaner handled some other way (recompute from dm, immutable cache rebuilt, a Ref, …). To revisit.

Dependencies (merge order)

This PR depends on, and should land after:

  1. refactor(DOCP): dispatch-based contract, no builders/callables CTSolvers.jl#155 (DOCP dispatch contract) — must be registered.
  2. compat: support CTBase 0.25 (beta) CTParser.jl#277 (CTBase 0.25 compat, beta) — must be registered.

CI here will stay red until CTSolvers 0.4.x and CTParser 0.8.16(+) are available from the registry. Locally validated with all four packages dev'd by path.

🤖 Generated with Claude Code

ocots and others added 2 commits June 27, 2026 10:23
Move to the CTSolvers dispatch-based DOCP contract (no closures/builders):

- AbstractDiscretizer now owned by CTSolvers; remove the local abstract type
  and the discretize wrappers; import Strategies/Options/Core/Exceptions from CTBase
- new DOCP_cache.jl: DOCPCache <: CTBase.Core.AbstractCache (docp + exa_getter),
  making the build_model -> build_solution Exa coupling explicit (no captured closure)
- unify get_docp into DOCP_data.jl (shared by all discretizers; resolves the TODO)
- collocation.jl / direct_shooting.jl: replace the (discretizer)(ocp) closure factory
  with named CTSolvers.discretize / build_model / build_solution methods dispatching
  on (DiscretizedModel{<:Disc}, modeler). DirectShooting x Exa left to the
  CTSolvers NotImplemented stub.
- Project.toml: CTBase moved to deps; bump compat (CTBase 0.25, CTModels 0.13,
  ExaModels 0.11); docs project CTBase 0.25
- tests: CTDirect.discretize -> CTSolvers.discretize; manual-backend nnz test via
  CTSolvers.build_model(docp, init, ADNLP(backend=:manual))

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
DOCPCache(docp) = DOCPCache(docp, nothing) inferred the field parameter E as
Nothing, freezing exa_getter::Nothing — so build_model(::Exa)'s
`dm.cache.exa_getter = exa_getter` threw "cannot convert a value to nothing".

Drop the E type parameter and type the field as Any (the cache is created with
nothing by discretize, then reassigned to a backend getter by build_model).

With this fix the full CTDirect suite is green against the CTBase 0.25 /
CTModels 0.13 / CTSolvers 0.4 stack (406 passed, incl. all ADNLP and Exa cases).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Breakage test results
Date: 2026-07-15 17:05:37

Name Latest Stable
OptimalControl compat: v1.0.12 compat: v1.0.12

@ocots
ocots marked this pull request as draft June 27, 2026 09:54
ocots and others added 3 commits June 27, 2026 23:30
DOCPCache becomes immutable (holds only the docp). The Exa getter, produced
together with the ExaModel, now rides an ExaBuildCache carried by the
CTSolvers.BuiltModel returned by build_model — no more mutation of the shared
DiscretizedModel cache.

- build_model returns CTSolvers.BuiltModel(dm, nlp, cache): NoCache for ADNLP /
  DirectShooting, ExaBuildCache(getter) for Exa.
- build_solution dispatches on the BuiltModel; reads docp via built.problem.cache
  and the getter via built.cache (drops the nothing-check, guaranteed by the type).
- test: use CTSolvers.nlp_model where a bare NLP is expected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Update compat: CTBase 0.28, CTModels 0.15, CTSolvers 0.4
- Add Strategies.parameter(::Type{<:Collocation}) = nothing
- Add Strategies.parameter(::Type{<:DirectShooting}) = nothing
- Add parameter contract for DummyDiscretizer in test
- Update docs/Project.toml CTBase compat to 0.28
- Add CTParser to [deps] (was only in [extras])

Co-Authored-By: Cascade <noreply@cascade.ai>
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.

2 participants