Skip to content

Commit c37a979

Browse files
authored
Merge pull request #256 from control-toolbox/feature/strategies-modelers
feat: Refactor NLP modelers to use Strategies architecture
2 parents dca329e + 33e4461 commit c37a979

101 files changed

Lines changed: 8477 additions & 2212 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/CTModels.jl

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,28 @@ using KernelAbstractions
2828
using NLPModels
2929

3030
# Modules
31-
include("Options/Options.jl")
31+
include(joinpath(@__DIR__, "Options", "Options.jl"))
3232
using .Options
3333

34-
include("Strategies/Strategies.jl")
34+
include(joinpath(@__DIR__, "Strategies", "Strategies.jl"))
3535
using .Strategies
3636

37-
include("Orchestration/Orchestration.jl")
37+
include(joinpath(@__DIR__, "Orchestration", "Orchestration.jl"))
3838
using .Orchestration
3939

40+
# Optimization module provides general optimization types (AbstractOptimizationProblem, builders)
41+
include(joinpath(@__DIR__, "Optimization", "Optimization.jl"))
42+
using .Optimization
43+
44+
# Modelers module uses AbstractOptimizationProblem from Optimization (general)
45+
include(joinpath(@__DIR__, "Modelers", "Modelers.jl"))
46+
using .Modelers
47+
48+
# DOCP module provides concrete DOCP types (DiscretizedOptimalControlProblem)
49+
# Loaded after Modelers since Modelers only need the general AbstractOptimizationProblem
50+
include(joinpath(@__DIR__, "DOCP", "DOCP.jl"))
51+
using .DOCP
52+
4053
# ============================================================================ #
4154
# TYPES AND FOUNDATIONS
4255
# ============================================================================ #
@@ -45,7 +58,7 @@ using .Orchestration
4558

4659
# 1. Type aliases (Dimension, ctNumber, Time, etc.) and export/import types
4760
# These are the most basic types with no dependencies
48-
include("types/types.jl")
61+
include(joinpath(@__DIR__, "types", "types.jl"))
4962

5063
# 2. OCP defaults (functions returning default values)
5164
# Depends on: type aliases (uses Dimension, ctVector, etc.)
@@ -56,18 +69,18 @@ include(joinpath(@__DIR__, "ocp", "defaults.jl"))
5669
# Must be loaded before OCP types because @ensure macro is used in OCP types
5770
include(joinpath(@__DIR__, "utils", "utils.jl"))
5871

59-
# 4. OCP type definitions (components, model, solution)
72+
# 4. Initial guess types
73+
# Depends on: type aliases
74+
include(joinpath(@__DIR__, "init", "types.jl"))
75+
76+
# 5. OCP type definitions (components, model, solution)
6077
# Depends on: type aliases, defaults, and utils (@ensure macro)
6178
include(joinpath(@__DIR__, "ocp", "types", "components.jl"))
6279
include(joinpath(@__DIR__, "ocp", "types", "model.jl"))
6380
include(joinpath(@__DIR__, "ocp", "types", "solution.jl"))
6481

65-
# 5. NLP types (backends, builders, modelers)
66-
# Depends on: OCP types (uses AbstractModel, AbstractSolution)
67-
include(joinpath(@__DIR__, "nlp", "types.jl"))
68-
69-
# 6. Export/import functions (require OCP types)
70-
# Depends on: OCP types (uses AbstractModel, AbstractSolution)
82+
# # 6. Export/import functions (require OCP types)
83+
# # Depends on: OCP types (uses AbstractModel, AbstractSolution)
7184
include(joinpath(@__DIR__, "types", "export_import_functions.jl"))
7285

7386
# ============================================================================ #
@@ -99,16 +112,8 @@ const AbstractOptimalControlSolution = CTModels.AbstractSolution
99112
# Depends on: all OCP types
100113
include(joinpath(@__DIR__, "ocp", "ocp.jl"))
101114

102-
# 7. NLP implementations (problem core, backends, discretization)
103-
# Depends on: OCP and NLP types
104-
include(joinpath(@__DIR__, "nlp", "problem_core.jl"))
105-
include(joinpath(@__DIR__, "nlp", "nlp_backends.jl"))
106-
include(joinpath(@__DIR__, "nlp", "extract_solver_infos.jl"))
107-
include(joinpath(@__DIR__, "nlp", "discretized_ocp.jl"))
108-
include(joinpath(@__DIR__, "nlp", "model_api.jl"))
109-
# 8. Initialization (types and functions for initial guesses)
110-
# Depends on: OCP types (uses AbstractModel, AbstractSolution)
111-
include(joinpath(@__DIR__, "init", "types.jl"))
115+
# 7. Initial guess implementations
116+
# Depends on: OCP types (uses AbstractOptimalControlProblem)
112117
include(joinpath(@__DIR__, "init", "initial_guess.jl"))
113118

114119
end

src/Modelers/Modelers.jl

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Modelers Module
2+
#
3+
# This module provides strategy-based modelers for converting discretized optimal
4+
# control problems to NLP backend models using the new AbstractStrategy contract.
5+
#
6+
# Author: CTModels Development Team
7+
# Date: 2026-01-25
8+
9+
module Modelers
10+
11+
using CTBase: CTBase
12+
using DocStringExtensions
13+
using SolverCore
14+
using ADNLPModels
15+
using ExaModels
16+
using KernelAbstractions
17+
using ..CTModels.Options
18+
using ..CTModels.Strategies
19+
using ..CTModels.Optimization: AbstractOptimizationProblem,
20+
get_adnlp_model_builder, get_exa_model_builder,
21+
get_adnlp_solution_builder, get_exa_solution_builder
22+
23+
# Include submodules
24+
include(joinpath(@__DIR__, "abstract_modeler.jl"))
25+
include(joinpath(@__DIR__, "adnlp_modeler.jl"))
26+
include(joinpath(@__DIR__, "exa_modeler.jl"))
27+
28+
# Public API
29+
export AbstractOptimizationModeler
30+
export ADNLPModeler, ExaModeler
31+
32+
end # module Modelers

src/Modelers/abstract_modeler.jl

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Abstract Optimization Modeler
2+
#
3+
# Defines the AbstractOptimizationModeler strategy contract for all modeler strategies.
4+
# This extends the AbstractStrategy contract with modeler-specific interfaces.
5+
#
6+
# Author: CTModels Development Team
7+
# Date: 2026-01-25
8+
9+
"""
10+
AbstractOptimizationModeler
11+
12+
Abstract base type for all modeler strategies.
13+
14+
Modeler strategies are responsible for converting discretized optimal control
15+
problems (AbstractOptimizationProblem) into NLP backend models. They implement
16+
the `AbstractStrategy` contract and provide modeler-specific interfaces for
17+
model and solution building.
18+
19+
# Implementation Requirements
20+
All concrete modeler strategies must:
21+
- Implement the `AbstractStrategy` contract (see Strategies module)
22+
- Provide callable interfaces for model building from AbstractOptimizationProblem
23+
- Provide callable interfaces for solution building
24+
- Define strategy metadata with option specifications
25+
26+
# Example
27+
```julia
28+
struct MyModeler <: AbstractOptimizationModeler
29+
options::Strategies.StrategyOptions
30+
end
31+
32+
Strategies.id(::Type{<:MyModeler}) = :my_modeler
33+
34+
function (modeler::MyModeler)(
35+
prob::AbstractOptimizationProblem,
36+
initial_guess
37+
)
38+
# Build NLP model from problem and initial guess
39+
return nlp_model
40+
end
41+
```
42+
"""
43+
abstract type AbstractOptimizationModeler <: Strategies.AbstractStrategy end
44+
45+
"""
46+
(modeler::AbstractOptimizationModeler)(prob::AbstractOptimizationProblem, initial_guess)
47+
48+
Build an NLP model from a discretized optimal control problem and initial guess.
49+
50+
# Arguments
51+
- `modeler::AbstractOptimizationModeler`: The modeler strategy instance
52+
- `prob::AbstractOptimizationProblem`: The discretized optimal control problem
53+
- `initial_guess`: Initial guess for optimization variables
54+
55+
# Returns
56+
- An NLP model compatible with the target backend (e.g., ADNLPModel, ExaModel)
57+
58+
# Throws
59+
- `CTBase.NotImplemented`: If not implemented by concrete type
60+
"""
61+
function (modeler::AbstractOptimizationModeler)(
62+
::AbstractOptimizationProblem,
63+
initial_guess
64+
)
65+
throw(CTBase.NotImplemented(
66+
"Model building not implemented for $(typeof(modeler))"
67+
))
68+
end
69+
70+
"""
71+
(modeler::AbstractOptimizationModeler)(prob::AbstractOptimizationProblem, nlp_solution)
72+
73+
Build a solution object from a discretized optimal control problem and NLP solution.
74+
75+
# Arguments
76+
- `modeler::AbstractOptimizationModeler`: The modeler strategy instance
77+
- `prob::AbstractOptimizationProblem`: The discretized optimal control problem
78+
- `nlp_solution::SolverCore.AbstractExecutionStats`: Solution from NLP solver
79+
80+
# Returns
81+
- A solution object appropriate for the problem type
82+
83+
# Throws
84+
- `CTBase.NotImplemented`: If not implemented by concrete type
85+
"""
86+
function (modeler::AbstractOptimizationModeler)(
87+
::AbstractOptimizationProblem,
88+
::SolverCore.AbstractExecutionStats
89+
)
90+
throw(CTBase.NotImplemented(
91+
"Solution building not implemented for $(typeof(modeler))"
92+
))
93+
end

src/Modelers/adnlp_modeler.jl

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# ADNLP Modeler
2+
#
3+
# Implementation of ADNLPModeler using the AbstractStrategy contract.
4+
# This modeler converts discretized optimal control problems to ADNLPModels.
5+
#
6+
# Author: CTModels Development Team
7+
# Date: 2026-01-25
8+
9+
# Default option values
10+
"""
11+
$(TYPEDSIGNATURES)
12+
13+
Return the default value for the `show_time` option of [`ADNLPModeler`](@ref).
14+
15+
Default is `false`.
16+
"""
17+
__adnlp_model_show_time() = false
18+
19+
"""
20+
$(TYPEDSIGNATURES)
21+
22+
Return the default automatic differentiation backend for [`ADNLPModeler`](@ref).
23+
24+
Default is `:optimized`.
25+
"""
26+
__adnlp_model_backend() = :optimized
27+
28+
"""
29+
ADNLPModeler
30+
31+
Modeler for building ADNLPModels from discretized optimal control problems.
32+
33+
This modeler uses the ADNLPModels.jl package to create NLP models with
34+
automatic differentiation support. It provides configurable options for
35+
timing information and AD backend selection.
36+
37+
# Options
38+
- `show_time::Bool`: Whether to show timing information (default: `false`)
39+
- `backend::Symbol`: AD backend to use (default: `:optimized`)
40+
41+
# Example
42+
```julia
43+
modeler = ADNLPModeler(show_time=true, backend=:forwarddiff)
44+
nlp_model = modeler(problem, initial_guess)
45+
```
46+
"""
47+
struct ADNLPModeler <: AbstractOptimizationModeler
48+
options::Strategies.StrategyOptions
49+
end
50+
51+
# Strategy identification
52+
Strategies.id(::Type{<:ADNLPModeler}) = :adnlp
53+
54+
# Strategy metadata with option definitions
55+
function Strategies.metadata(::Type{<:ADNLPModeler})
56+
return Strategies.StrategyMetadata(
57+
Strategies.OptionDefinition(;
58+
name=:show_time,
59+
type=Bool,
60+
default=__adnlp_model_show_time(),
61+
description="Whether to show timing information while building the ADNLP model"
62+
),
63+
Strategies.OptionDefinition(;
64+
name=:backend,
65+
type=Symbol,
66+
default=__adnlp_model_backend(),
67+
description="Automatic differentiation backend used by ADNLPModels"
68+
)
69+
)
70+
end
71+
72+
# Constructor with option validation
73+
function ADNLPModeler(; kwargs...)
74+
opts = Strategies.build_strategy_options(
75+
ADNLPModeler; kwargs...
76+
)
77+
return ADNLPModeler(opts)
78+
end
79+
80+
# Access to strategy options
81+
Strategies.options(m::ADNLPModeler) = m.options
82+
83+
# Model building interface
84+
function (modeler::ADNLPModeler)(
85+
prob::AbstractOptimizationProblem,
86+
initial_guess
87+
)::ADNLPModels.ADNLPModel
88+
opts = Strategies.options(modeler)
89+
90+
# Get the appropriate builder for this problem type
91+
builder = get_adnlp_model_builder(prob)
92+
93+
# Extract raw values from OptionValue wrappers and filter out nothing values
94+
raw_opts = Options.extract_raw_options(opts.options)
95+
96+
# Build the ADNLP model passing all options generically
97+
return builder(initial_guess; raw_opts...)
98+
end
99+
100+
# Solution building interface
101+
function (modeler::ADNLPModeler)(
102+
prob::AbstractOptimizationProblem,
103+
nlp_solution::SolverCore.AbstractExecutionStats
104+
)
105+
# Get the appropriate solution builder for this problem type
106+
builder = get_adnlp_solution_builder(prob)
107+
return builder(nlp_solution)
108+
end

0 commit comments

Comments
 (0)