Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/SpellCheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ on:
jobs:
call:
uses: control-toolbox/CTActions/.github/workflows/spell-check.yml@main
with:
config-path: '_typos.toml'
16 changes: 8 additions & 8 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,24 @@ SolverCore = "ff4d7338-4cf1-434d-91df-b86cb86fb843"

[compat]
ADNLPModels = "0.8"
BenchmarkTools = "1"
CTBase = "0.18"
CTDirect = "1"
CTFlows = "0.8"
CTModels = "0.9"
CTParser = "0.8"
CTSolvers = "0.3"
CTSolvers = "0.4"
CUDA = "5"
CommonSolve = "0.2"
DifferentiationInterface = "0.7"
DocStringExtensions = "0.9"
ExaModels = "0.9"
ForwardDiff = "0.10, 1.0"
LinearAlgebra = "1"
MadNCL = "0.1"
MadNLP = "0.8"
MadNLPGPU = "0.7"
MadNLPMumps = "0.5"
NLPModels = "0.21.7"
MadNCL = "0.2"
MadNLP = "0.9"
MadNLPGPU = "0.8"
NLPModels = "0.21"
NLPModelsIpopt = "0.11"
NonlinearSolve = "4"
OrdinaryDiffEq = "6"
Expand All @@ -51,14 +51,14 @@ Test = "1"
julia = "1.10"

[extras]
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
DifferentiationInterface = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63"
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
MadNCL = "434a0bcb-5a7c-42b2-a9d3-9e3f760e7af0"
MadNLP = "2621e9c9-9eb4-46b1-8089-e8c72242dfb6"
MadNLPGPU = "d72a61cc-809d-412f-99be-fd81f4b8a598"
MadNLPMumps = "3b83494e-c0a4-4895-918b-9157a7a085a1"
NLPModelsIpopt = "f4238b75-b362-5c4c-b852-0801c9a21d71"
NonlinearSolve = "8913a72c-1f9b-4ce2-8d82-65094dcecaec"
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
Expand All @@ -67,4 +67,4 @@ SplitApplyCombine = "03a91e81-4c3e-53e1-a0a4-9c0c8f19dd66"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["CUDA", "DifferentiationInterface", "ForwardDiff", "LinearAlgebra", "MadNCL", "MadNLP", "MadNLPGPU", "MadNLPMumps", "NLPModelsIpopt", "NonlinearSolve", "OrdinaryDiffEq", "Printf", "SplitApplyCombine", "Test"]
test = ["BenchmarkTools", "CUDA", "DifferentiationInterface", "ForwardDiff", "LinearAlgebra", "MadNCL", "MadNLP", "MadNLPGPU", "NLPModelsIpopt", "NonlinearSolve", "OrdinaryDiffEq", "Printf", "SplitApplyCombine", "Test"]
12 changes: 12 additions & 0 deletions _typos.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[default]
locale = "en"
extend-ignore-re = [
"ded",
]

[files]
extend-exclude = [
"*.json",
"*.toml",
"*.svg",
]
10 changes: 7 additions & 3 deletions src/helpers/component_completion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,19 @@ function _complete_components(
# Step 2: Complete the method description
complete_description = _complete_description(partial_description)

# Step 2.5: Resolve method with parameter information
families = _descriptive_families()
resolved = CTSolvers.resolve_method(complete_description, families, registry)

# Step 3: Build or use strategies for each family
final_discretizer = _build_or_use_strategy(
complete_description, discretizer, CTDirect.AbstractDiscretizer, registry
resolved, discretizer, :discretizer, families, registry
)
final_modeler = _build_or_use_strategy(
complete_description, modeler, CTSolvers.AbstractNLPModeler, registry
resolved, modeler, :modeler, families, registry
)
final_solver = _build_or_use_strategy(
complete_description, solver, CTSolvers.AbstractNLPSolver, registry
resolved, solver, :solver, families, registry
)

return (discretizer=final_discretizer, modeler=final_modeler, solver=final_solver)
Expand Down
34 changes: 15 additions & 19 deletions src/helpers/descriptive_routing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ See also: [`_descriptive_families`](@ref), [`_descriptive_action_defs`](@ref),
[`_build_components_from_routed`](@ref)
"""
function _route_descriptive_options(
complete_description::Tuple{Symbol, Symbol, Symbol},
complete_description::Tuple{Symbol, Symbol, Symbol, Symbol},
registry::CTSolvers.StrategyRegistry,
kwargs,
)
Expand All @@ -192,7 +192,7 @@ $(TYPEDSIGNATURES)
Build concrete strategy instances and extract action options from a routed options result.

Each strategy is constructed via
[`CTSolvers.build_strategy_from_method`](@ref) using the options
[`CTSolvers.build_strategy_from_resolved`](@ref) using the options
that were routed to its family by [`_route_descriptive_options`](@ref).

Action options (`initial_guess`, `display`) are extracted from `routed.action`
Expand Down Expand Up @@ -220,31 +220,27 @@ true
```

See also: [`_route_descriptive_options`](@ref),
[`CTSolvers.build_strategy_from_method`](@ref)
[`CTSolvers.build_strategy_from_resolved`](@ref)
"""
function _build_components_from_routed(
ocp::CTModels.AbstractModel,
complete_description::Tuple{Symbol, Symbol, Symbol},
complete_description::Tuple{Symbol, Symbol, Symbol, Symbol},
registry::CTSolvers.StrategyRegistry,
routed::NamedTuple,
)
discretizer = CTSolvers.build_strategy_from_method(
complete_description,
CTDirect.AbstractDiscretizer,
registry;
routed.strategies.discretizer...,
# Resolve method with parameter information as early as possible
families = _descriptive_families()
resolved = CTSolvers.resolve_method(complete_description, families, registry)

# Build strategies using resolved method
discretizer = CTSolvers.build_strategy_from_resolved(
resolved, :discretizer, families, registry; routed.strategies.discretizer...
)
modeler = CTSolvers.build_strategy_from_method(
complete_description,
CTSolvers.AbstractNLPModeler,
registry;
routed.strategies.modeler...,
modeler = CTSolvers.build_strategy_from_resolved(
resolved, :modeler, families, registry; routed.strategies.modeler...
)
solver = CTSolvers.build_strategy_from_method(
complete_description,
CTSolvers.AbstractNLPSolver,
registry;
routed.strategies.solver...,
solver = CTSolvers.build_strategy_from_resolved(
resolved, :solver, families, registry; routed.strategies.solver...
)

# Extract and unwrap action options (OptionValue → raw value)
Expand Down
55 changes: 40 additions & 15 deletions src/helpers/methods.jl
Original file line number Diff line number Diff line change
@@ -1,38 +1,63 @@
"""
$(TYPEDSIGNATURES)

Return the tuple of available method triplets for solving optimal control problems.
Return the tuple of available method quadruplets for solving optimal control problems.

Each triplet consists of `(discretizer_id, modeler_id, solver_id)` where:
Each quadruplet consists of `(discretizer_id, modeler_id, solver_id, parameter)` where:
- `discretizer_id`: Symbol identifying the discretization strategy
- `modeler_id`: Symbol identifying the NLP modeling strategy
- `modeler_id`: Symbol identifying the NLP modeling strategy
- `solver_id`: Symbol identifying the NLP solver
- `parameter`: Symbol identifying the parameter (`:cpu` or `:gpu`)

GPU-capable methods use parameterized strategies that automatically get appropriate defaults:
- `Exa{GPU}` gets `CUDA.CUDABackend()` by default
- `MadNLP{GPU}` gets `MadNLPGPU.CUDSSSolver` by default
- `MadNCL{GPU}` gets `MadNLPGPU.CUDSSSolver` by default

# Returns
- `Tuple{Vararg{Tuple{Symbol, Symbol, Symbol}}}`: Available method combinations
- `Tuple{Vararg{Tuple{Symbol, Symbol, Symbol, Symbol}}}`: Available method combinations

# Examples
```julia
julia> m = methods()
((:collocation, :adnlp, :ipopt), (:collocation, :adnlp, :madnlp), ...)
((:collocation, :adnlp, :ipopt, :cpu), (:collocation, :adnlp, :madnlp, :cpu), ...)

julia> length(m)
8
10 # CPU methods + GPU methods

julia> # CPU methods (existing behavior maintained)
julia> methods()[1]
(:collocation, :adnlp, :ipopt, :cpu)

julia> # GPU methods (new functionality)
julia> methods()[9] # First GPU method
(:collocation, :exa, :madnlp, :gpu)
```

# Notes
- All existing methods are now explicitly marked with `:cpu` parameter
- GPU methods are available when CUDA.jl is loaded
- Parameterized strategies provide smart defaults automatically

# See Also
- [`solve`](@ref): Main solve function that uses these methods
- [`CTBase.complete`](@ref): Completes partial method descriptions
- [`get_strategy_registry`](@ref): Registry with parameterized strategies
"""
function Base.methods()::Tuple{Vararg{Tuple{Symbol, Symbol, Symbol}}}
function Base.methods()::Tuple{Vararg{Tuple{Symbol, Symbol, Symbol, Symbol}}}
return (
(:collocation, :adnlp, :ipopt ),
(:collocation, :adnlp, :madnlp),
(:collocation, :exa, :ipopt ),
(:collocation, :exa, :madnlp),
(:collocation, :adnlp, :madncl),
(:collocation, :exa, :madncl),
(:collocation, :adnlp, :knitro),
(:collocation, :exa, :knitro),
# CPU methods (all existing methods now with :cpu parameter)
(:collocation, :adnlp, :ipopt, :cpu),
(:collocation, :adnlp, :madnlp, :cpu),
(:collocation, :exa, :ipopt, :cpu),
(:collocation, :exa, :madnlp, :cpu),
(:collocation, :adnlp, :madncl, :cpu),
(:collocation, :exa, :madncl, :cpu),
(:collocation, :adnlp, :knitro, :cpu),
(:collocation, :exa, :knitro, :cpu),

# GPU methods (only combinations that make sense)
(:collocation, :exa, :madnlp, :gpu),
(:collocation, :exa, :madncl, :gpu),
)
end
46 changes: 35 additions & 11 deletions src/helpers/registry.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,43 @@ $(TYPEDSIGNATURES)

Create and return the strategy registry for the solve system.

The registry maps abstract strategy families to their concrete implementations:
The registry maps abstract strategy families to their concrete implementations
with their supported parameters:
- `CTDirect.AbstractDiscretizer` → Discretization strategies
- `CTSolvers.AbstractNLPModeler` → NLP modeling strategies
- `CTSolvers.AbstractNLPSolver` → NLP solver strategies
- `CTSolvers.AbstractNLPModeler` → NLP modeling strategies (with CPU/GPU support)
- `CTSolvers.AbstractNLPSolver` → NLP solver strategies (with CPU/GPU support)

Each strategy entry specifies which parameters it supports:
- `CPU`: All strategies support CPU execution
- `GPU`: Only GPU-capable strategies support GPU execution (Exa, MadNLP, MadNCL)

# Returns
- `CTSolvers.StrategyRegistry`: Registry with all available strategies
- `CTSolvers.StrategyRegistry`: Registry with all available strategies and their parameters

# Examples
```julia
julia> registry = OptimalControl.get_strategy_registry()
StrategyRegistry with 3 families

julia> CTSolvers.strategy_ids(CTSolvers.AbstractNLPModeler, registry)
(:adnlp, :exa)

julia> CTSolvers.strategy_ids(CTSolvers.AbstractNLPSolver, registry)
(:ipopt, :madnlp, :madncl, :knitro)

julia> # Check which parameters a strategy supports
julia> CTSolvers.available_parameters(:modeler, CTSolvers.Exa, registry)
(CPU, GPU)

julia> CTSolvers.available_parameters(:solver, CTSolvers.Ipopt, registry)
(CPU,)
```

# Notes
- GPU-capable strategies (Exa, MadNLP, MadNCL) support both CPU and GPU parameters
- CPU-only strategies (ADNLP, Ipopt, Knitro) support only CPU parameter
- Parameterization is handled at the method level in `methods()`
- GPU strategies automatically get appropriate default configurations when parameterized
"""
function get_strategy_registry()::CTSolvers.StrategyRegistry
return CTSolvers.create_registry(
Expand All @@ -24,14 +48,14 @@ function get_strategy_registry()::CTSolvers.StrategyRegistry
# Add other discretizers as they become available
),
CTSolvers.AbstractNLPModeler => (
CTSolvers.ADNLP,
CTSolvers.Exa,
(CTSolvers.ADNLP, [CTSolvers.CPU]),
(CTSolvers.Exa, [CTSolvers.CPU, CTSolvers.GPU])
),
CTSolvers.AbstractNLPSolver => (
CTSolvers.Ipopt,
CTSolvers.MadNLP,
CTSolvers.MadNCL,
CTSolvers.Knitro,
)
(CTSolvers.Ipopt, [CTSolvers.CPU]),
(CTSolvers.MadNLP, [CTSolvers.CPU, CTSolvers.GPU]),
(CTSolvers.MadNCL, [CTSolvers.CPU, CTSolvers.GPU]),
(CTSolvers.Knitro, [CTSolvers.CPU]),
),
)
end
Loading
Loading