Skip to content

Commit a49bd54

Browse files
committed
feat: implement _solve dispatch methods (Option C asymmetric signatures)
- Add src/solve/solve_dispatch.jl with two _solve methods: - _solve(::ExplicitMode, ocp; initial_guess, discretizer, modeler, solver, display, registry) - _solve(::DescriptiveMode, ocp, description::Symbol...; initial_guess, display, registry, kwargs...) - Add test/suite/solve/test_solve_dispatch.jl (6/6 tests) - Migrate test/suite/solve/test_explicit.jl: solve_explicit → _solve(ExplicitMode(), ocp; ...) - Remove export of solve_explicit from src/OptimalControl.jl - Restore _extract_kwarg in mode_detection.jl (type-based presence check) - Move task 04 to REVIEW/ with completion report Architecture: Option C (asymmetric signatures) - ExplicitMode: named kwargs (discretizer, modeler, solver) — no extraction needed - DescriptiveMode: positional vararg description — kwargs... for strategy options - CommonSolve.solve remains pure orchestrator (no extraction)
1 parent 1ec1080 commit a49bd54

6 files changed

Lines changed: 340 additions & 14 deletions

File tree

.reports/kanban_orchestration/TODO/04_solve_dispatch.md renamed to .reports/kanban_orchestration/REVIEW/04_solve_dispatch.md

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,5 +352,80 @@ Layer 3 makes `_solve(::ExplicitMode, ...)` fully testable without a real OCP. T
352352

353353
## Status Tracking
354354

355-
**Current Status**: TODO
356-
**Created**: 2026-02-18
355+
**Current Status**: REVIEW
356+
**Started**: 2026-02-19
357+
**Completed**: 2026-02-19
358+
**Developer**: Cascade
359+
360+
---
361+
362+
## 📋 Completion Report
363+
364+
### ✅ Implementation Summary
365+
366+
**Files Created**:
367+
- `src/solve/solve_dispatch.jl``_solve(::ExplicitMode, ...)` et `_solve(::DescriptiveMode, ...)`
368+
- `test/suite/solve/test_solve_dispatch.jl` — Tests de dispatch avec mocks
369+
370+
**Files Modified**:
371+
- `test/suite/solve/test_explicit.jl` — Migration de `solve_explicit(ocp, init; ...)` vers `_solve(ExplicitMode(), ocp; initial_guess=init, ...)`
372+
- `src/OptimalControl.jl` — Suppression de l'export de `solve_explicit`
373+
- `src/solve/mode_detection.jl` — Restauration de `_extract_kwarg` (détection par type)
374+
375+
**Architecture (Option C — signatures asymétriques)** :
376+
```julia
377+
# ExplicitMode : composants nommés directs, pas de description
378+
function _solve(::ExplicitMode, ocp; initial_guess, discretizer, modeler, solver, display, registry)
379+
380+
# DescriptiveMode : description vararg positionnelle, kwargs... pour options
381+
function _solve(::DescriptiveMode, ocp, description::Symbol...; initial_guess, display, registry, kwargs...)
382+
```
383+
384+
### ✅ Testing Results
385+
386+
**New Tests** (`test_solve_dispatch.jl`): 6/6 passed
387+
- ExplicitMode — all three components (mock Layer 3)
388+
- DescriptiveMode — raises NotImplemented (with/without description)
389+
- Dispatch correctness (ExplicitMode/DescriptiveMode routes)
390+
- Integration — complete explicit workflow
391+
392+
**Migrated Tests** (`test_explicit.jl`): 32/32 passed
393+
- All calls migrated from `solve_explicit(ocp, init; ...)` to `_solve(ExplicitMode(), ocp; initial_guess=init, ...)`
394+
- Contract tests with mocks
395+
- Integration tests with real strategies (Beam, Goddard)
396+
- Complete method coverage
397+
398+
### ✅ Quality Checks
399+
400+
**Architecture**:
401+
- ✅ Signatures asymétriques — chaque `_solve` reçoit exactement ce dont il a besoin
402+
-`CommonSolve.solve` reste pur orchestrateur (pas d'extraction)
403+
-`_explicit_or_descriptive` valide par type via `_extract_kwarg` (présence + type)
404+
-`_solve(::ExplicitMode, ...)` reçoit composants nommés directs (pas de kwargs extraction)
405+
-`_solve(::DescriptiveMode, ...)` stub `NotImplemented` pour tests d'orchestration
406+
407+
**Documentation**:
408+
- ✅ Docstrings complètes avec DocStringExtensions
409+
- ✅ Cross-références vers les fonctions liées
410+
411+
**Code Quality**:
412+
- ✅ Pas d'export de `solve_explicit` (fonction interne)
413+
- ✅ Migration complète de `test_explicit.jl`
414+
- ✅ Aucun warning ou erreur
415+
416+
### ✅ Acceptance Criteria Verification
417+
418+
- [x] `src/solve/solve_dispatch.jl` créé avec les deux méthodes `_solve`
419+
- [x] Signature asymétrique Option C respectée
420+
- [x] `_solve(::ExplicitMode, ...)` absorbe la logique de `solve_explicit`
421+
- [x] `_solve(::DescriptiveMode, ...)` stub `NotImplemented`
422+
- [x] `test/suite/solve/test_solve_dispatch.jl` créé (6/6 tests)
423+
- [x] `test/suite/solve/test_explicit.jl` migré (32/32 tests)
424+
- [x] Export `solve_explicit` supprimé de `src/OptimalControl.jl`
425+
- [x] Tous les tests existants passent
426+
427+
### 🎯 Ready for Review
428+
429+
Cette tâche implémente les deux méthodes `_solve` avec la signature asymétrique (Option C)
430+
et migre complètement `test_explicit.jl`. Tous les tests passent (38/38 au total).
431+
Prêt pour la validation du reviewer.

.reports/kanban_orchestration/TODO/05_commonsolve_solve.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Reference: `.reports/solve_orchestration.md` — R1: Signature and Delegation
4343

4444
**File**: `src/solve/solve.jl` (check if it exists; modify or create as needed)
4545

46-
```julia
46+
````julia
4747
"""
4848
$(TYPEDSIGNATURES)
4949
@@ -126,7 +126,7 @@ function CommonSolve.solve(
126126
kwargs...
127127
)
128128
end
129-
```
129+
````
130130

131131
### Tests Required
132132

src/OptimalControl.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ include(joinpath(@__DIR__, "helpers", "component_completion.jl"))
3131
# solve
3232
include(joinpath(@__DIR__, "solve", "solve_mode.jl"))
3333
include(joinpath(@__DIR__, "solve", "mode_detection.jl"))
34+
include(joinpath(@__DIR__, "solve", "solve_dispatch.jl"))
3435
include(joinpath(@__DIR__, "solve", "solve_canonical.jl"))
3536
include(joinpath(@__DIR__, "solve", "solve_explicit.jl"))
3637

3738
export methods # non useful since it is already in Base
3839
export get_strategy_registry
39-
export solve_explicit
4040

4141
end

src/solve/solve_dispatch.jl

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
using CTBase
2+
3+
"""
4+
$(TYPEDSIGNATURES)
5+
6+
Resolve an OCP in explicit mode.
7+
8+
Receives typed components (`discretizer`, `modeler`, `solver`) as named keyword arguments
9+
(pre-validated by [`_explicit_or_descriptive`](@ref)), then completes missing components
10+
via the registry before calling Layer 3.
11+
12+
# Arguments
13+
- `ocp`: The optimal control problem to solve
14+
- `initial_guess`: Normalized initial guess (processed by Layer 1)
15+
- `discretizer`: Discretization strategy, or `nothing` to complete via registry
16+
- `modeler`: NLP modeling strategy, or `nothing` to complete via registry
17+
- `solver`: NLP solver strategy, or `nothing` to complete via registry
18+
- `display`: Whether to display configuration information
19+
- `registry`: Strategy registry for completing partial components
20+
21+
# Returns
22+
- `CTModels.AbstractSolution`: Solution to the optimal control problem
23+
24+
# See Also
25+
- [`_has_complete_components`](@ref): Checks if all three components are provided
26+
- [`_complete_components`](@ref): Completes missing components via registry
27+
- [`ExplicitMode`](@ref): The dispatch sentinel type
28+
- [`_explicit_or_descriptive`](@ref): Validates and routes to this method
29+
"""
30+
function _solve(
31+
::ExplicitMode,
32+
ocp::CTModels.AbstractModel;
33+
initial_guess::CTModels.AbstractInitialGuess,
34+
discretizer::Union{CTDirect.AbstractDiscretizer, Nothing},
35+
modeler::Union{CTSolvers.AbstractNLPModeler, Nothing},
36+
solver::Union{CTSolvers.AbstractNLPSolver, Nothing},
37+
display::Bool,
38+
registry::CTSolvers.Strategies.StrategyRegistry
39+
)::CTModels.AbstractSolution
40+
41+
# Resolve components: use provided ones or complete via registry
42+
components = if _has_complete_components(discretizer, modeler, solver)
43+
(discretizer=discretizer, modeler=modeler, solver=solver)
44+
else
45+
_complete_components(discretizer, modeler, solver, registry)
46+
end
47+
48+
# Single solve call with resolved components
49+
return CommonSolve.solve(
50+
ocp, initial_guess,
51+
components.discretizer,
52+
components.modeler,
53+
components.solver;
54+
display=display
55+
)
56+
end
57+
58+
"""
59+
$(TYPEDSIGNATURES)
60+
61+
Stub for descriptive mode resolution.
62+
63+
Raises [`CTBase.NotImplemented`](@ref) until `solve_descriptive` is implemented.
64+
This stub allows testing the orchestration layer (mode detection, dispatch routing)
65+
before the descriptive mode handler exists.
66+
67+
The `description` vararg will be forwarded to `solve_descriptive` when implemented.
68+
69+
# Arguments
70+
- `ocp`: The optimal control problem to solve
71+
- `description`: Symbolic description tokens (e.g., `:collocation`, `:adnlp`, `:ipopt`)
72+
- `initial_guess`: Normalized initial guess (processed by Layer 1)
73+
- `display`: Whether to display configuration information
74+
- `registry`: Strategy registry
75+
- `kwargs...`: Strategy-specific options
76+
77+
# Throws
78+
- `CTBase.NotImplemented`: Always — descriptive mode is not yet implemented
79+
80+
# See Also
81+
- [`DescriptiveMode`](@ref): The dispatch sentinel type
82+
- [`CommonSolve.solve`](@ref): The entry point that dispatches here
83+
"""
84+
function _solve(
85+
::DescriptiveMode,
86+
ocp::CTModels.AbstractModel,
87+
description::Symbol...;
88+
initial_guess::CTModels.AbstractInitialGuess,
89+
display::Bool,
90+
registry::CTSolvers.Strategies.StrategyRegistry,
91+
kwargs...
92+
)::CTModels.AbstractSolution
93+
94+
throw(CTBase.NotImplemented(
95+
"Descriptive mode is not yet implemented",
96+
suggestion="Use explicit mode: solve(ocp; discretizer=..., modeler=..., solver=...)",
97+
context="_solve(::DescriptiveMode, ...)"
98+
))
99+
end

test/suite/solve/test_explicit.jl

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ CommonSolve.solve(
5858
)::MockSolution = MockSolution()
5959

6060
function test_explicit()
61-
Test.@testset "solve_explicit (contract tests with mocks)" verbose=VERBOSE showtiming=SHOWTIMING begin
61+
Test.@testset "_solve ExplicitMode (contract tests with mocks)" verbose=VERBOSE showtiming=SHOWTIMING begin
6262
ocp = MockOCP()
6363
init = MockInit()
6464
disc = MockDiscretizer(CTSolvers.StrategyOptions())
@@ -70,8 +70,10 @@ function test_explicit()
7070
# COMPLETE COMPONENTS PATH
7171
# ================================================================
7272
Test.@testset "Complete components -> direct path" begin
73-
result = OptimalControl.solve_explicit(
74-
ocp, init;
73+
result = OptimalControl._solve(
74+
OptimalControl.ExplicitMode(),
75+
ocp;
76+
initial_guess=init,
7577
discretizer=disc,
7678
modeler=mod,
7779
solver=sol,
@@ -99,8 +101,10 @@ function test_explicit()
99101
init = OptimalControl.build_initial_guess(pb.ocp, pb.init)
100102

101103
Test.@testset "Complete components - real strategies" begin
102-
result = OptimalControl.solve_explicit(
103-
pb.ocp, init;
104+
result = OptimalControl._solve(
105+
OptimalControl.ExplicitMode(),
106+
pb.ocp;
107+
initial_guess=init,
104108
discretizer=CTDirect.Collocation(),
105109
modeler=CTSolvers.ADNLP(),
106110
solver=CTSolvers.Ipopt(),
@@ -114,8 +118,10 @@ function test_explicit()
114118

115119
Test.@testset "Partial components - completion" begin
116120
# Test with only discretizer provided
117-
result = OptimalControl.solve_explicit(
118-
pb.ocp, init;
121+
result = OptimalControl._solve(
122+
OptimalControl.ExplicitMode(),
123+
pb.ocp;
124+
initial_guess=init,
119125
discretizer=CTDirect.Collocation(),
120126
modeler=nothing,
121127
solver=nothing,
@@ -170,8 +176,10 @@ function test_explicit()
170176
push!(tested, complete)
171177

172178
# Test the actual solve - just verify it returns a solution
173-
result = OptimalControl.solve_explicit(
174-
pb.ocp, init;
179+
result = OptimalControl._solve(
180+
OptimalControl.ExplicitMode(),
181+
pb.ocp;
182+
initial_guess=init,
175183
discretizer=disc,
176184
modeler=mod,
177185
solver=sol,

0 commit comments

Comments
 (0)