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
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

---

## [0.4.24-beta] - 2026-06-28

### Changed

- **Contract co-location** — each strategy family now has its `NotImplemented`
contract stub adjacent to its abstract type, mirroring the existing `DOCP/contract.jl`
precedent:
- `Modelers/contract.jl` (new): `build_model` / `build_solution` stubs, now typed
`::AbstractNLPModeler` (previously untyped third argument).
- `Solvers/contract.jl` (new): mid-level `CommonSolve.solve(nlp, ::AbstractNLPSolver)`
stub + `__display` helper; high-level orchestrator moved to `Solvers/orchestration.jl`.
- `DOCP/discretized_model.jl` (new): merger of `types.jl` + `accessors.jl`;
`DOCP/conveniences.jl` (rename of `building.jl`).
- **Compat bumps** — `CTBase = "0.26"`, `CTModels = "0.14"` in `Project.toml` and
`docs/Project.toml`.

---

## [0.4.23-beta] - 2026-06-27

### Changed
Expand Down
6 changes: 3 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "CTSolvers"
uuid = "d3e8d392-8e4b-4d9b-8e92-d7d4e3650ef6"
version = "0.4.23-beta"
version = "0.4.24-beta"
authors = ["Olivier Cots <olivier.cots@toulouse-inp.fr>"]

[deps]
Expand Down Expand Up @@ -42,8 +42,8 @@ CTSolversZygote = "Zygote"
ADNLPModels = "0.8"
Aqua = "0.8"
BenchmarkTools = "1"
CTBase = "0.25"
CTModels = "0.13"
CTBase = "0.26"
CTModels = "0.14"
CUDA = "5, 6"
CommonSolve = "0.2"
DocStringExtensions = "0.9"
Expand Down
2 changes: 1 addition & 1 deletion docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ DocumenterMermaid = "a078cd44-4d9c-4618-b545-3ab9d77f9177"
MarkdownAST = "d0879d2d-cac2-40c8-9cee-1863dc0c7391"

[compat]
CTBase = "0.21"
CTBase = "0.26"
Documenter = "1"
DocumenterMermaid = "0.2"
MarkdownAST = "0.1"
Expand Down
32 changes: 9 additions & 23 deletions docs/api_reference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,6 @@ function generate_api_reference(src_dir::String, ext_dir::String)

pages = [

# ───────────────────────────────────────────────────────────────────
# Core
# ───────────────────────────────────────────────────────────────────
CTBase.automatic_reference_documentation(;
subdirectory="api",
primary_modules=[CTSolvers.Core => src(joinpath("Core", "Core.jl"))],
exclude=EXCLUDE_SYMBOLS,
public=true,
private=true,
title="Core",
title_in_menu="Core",
filename="core",
),

# ───────────────────────────────────────────────────────────────────
# DOCP
# ───────────────────────────────────────────────────────────────────
Expand All @@ -46,10 +32,10 @@ function generate_api_reference(src_dir::String, ext_dir::String)
primary_modules=[
CTSolvers.DOCP => src(
joinpath("DOCP", "DOCP.jl"),
joinpath("DOCP", "accessors.jl"),
joinpath("DOCP", "building.jl"),
joinpath("DOCP", "contract_impl.jl"),
joinpath("DOCP", "types.jl"),
joinpath("DOCP", "abstract_discretizer.jl"),
joinpath("DOCP", "discretized_model.jl"),
joinpath("DOCP", "contract.jl"),
joinpath("DOCP", "conveniences.jl"),
),
],
exclude=EXCLUDE_SYMBOLS,
Expand All @@ -69,6 +55,7 @@ function generate_api_reference(src_dir::String, ext_dir::String)
CTSolvers.Modelers => src(
joinpath("Modelers", "Modelers.jl"),
joinpath("Modelers", "abstract_modeler.jl"),
joinpath("Modelers", "contract.jl"),
joinpath("Modelers", "adnlp.jl"),
joinpath("Modelers", "exa.jl"),
joinpath("Modelers", "validation.jl"),
Expand All @@ -91,10 +78,7 @@ function generate_api_reference(src_dir::String, ext_dir::String)
CTSolvers.Optimization => src(
joinpath("Optimization", "Optimization.jl"),
joinpath("Optimization", "abstract_types.jl"),
joinpath("Optimization", "builders.jl"),
joinpath("Optimization", "building.jl"),
joinpath("Optimization", "contract.jl"),
joinpath("Optimization", "solver_info.jl"),
joinpath("Optimization", "built_model.jl"),
),
],
exclude=EXCLUDE_SYMBOLS,
Expand All @@ -114,7 +98,9 @@ function generate_api_reference(src_dir::String, ext_dir::String)
CTSolvers.Solvers => src(
joinpath("Solvers", "Solvers.jl"),
joinpath("Solvers", "abstract_solver.jl"),
joinpath("Solvers", "common_solve_api.jl"),
joinpath("Solvers", "contract.jl"),
joinpath("Solvers", "orchestration.jl"),
joinpath("Solvers", "solver_info.jl"),
joinpath("Solvers", "ipopt.jl"),
joinpath("Solvers", "knitro.jl"),
joinpath("Solvers", "madncl.jl"),
Expand Down
22 changes: 16 additions & 6 deletions docs/src/guides/implementing_a_modeler.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,17 +182,27 @@ end

## Integration with build_model / build_solution

The `Optimization` module provides two generic functions that delegate to the modeler's callables:
The `Optimization` module *owns* two generic functions, `build_model` and
`build_solution`. Their canonical `NotImplemented` contract stubs — the modeler
contract — live in the `Modelers` module, typed on `AbstractNLPModeler`; concrete
methods are provided by the package supplying the problem (e.g. CTDirect),
dispatched on the concrete `(problem, modeler)` pair. `build_model` returns an
`Optimization.BuiltModel` (the NLP plus an immutable build-time cache), and
`build_solution` dispatches on that bundle:

```julia
# In src/Optimization/building.jl
# In src/Modelers/contract.jl

function build_model(prob, initial_guess, modeler)
return modeler(prob, initial_guess)
function Optimization.build_model(
prob::Optimization.AbstractOptimizationProblem, initial_guess, modeler::AbstractNLPModeler
)
throw(Exceptions.NotImplemented(...)) # implemented per (problem, modeler) downstream
end

function build_solution(prob, model_solution, modeler)
return modeler(prob, model_solution)
function Optimization.build_solution(
built::Optimization.BuiltModel, model_solution, modeler::AbstractNLPModeler
)
throw(Exceptions.NotImplemented(...))
end
```

Expand Down
5 changes: 2 additions & 3 deletions src/DOCP/DOCP.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,9 @@ using ..CTSolvers.Modelers

# Submodules
include(joinpath(@__DIR__, "abstract_discretizer.jl"))
include(joinpath(@__DIR__, "types.jl"))
include(joinpath(@__DIR__, "discretized_model.jl"))
include(joinpath(@__DIR__, "contract.jl"))
include(joinpath(@__DIR__, "accessors.jl"))
include(joinpath(@__DIR__, "building.jl"))
include(joinpath(@__DIR__, "conveniences.jl"))

# Public API
export AbstractDiscretizer
Expand Down
23 changes: 0 additions & 23 deletions src/DOCP/accessors.jl

This file was deleted.

6 changes: 3 additions & 3 deletions src/DOCP/building.jl → src/DOCP/conveniences.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# DOCP building API
# DOCP conveniences
#
# Convenience wrappers for building NLP models and OCP solutions from a
# `DiscretizedModel`.
# Convenience wrappers (`nlp_model`, `ocp_solution`) over the modeler contract,
# specialized for `DiscretizedModel` / its `BuiltModel`.

"""
$(TYPEDSIGNATURES)
Expand Down
26 changes: 23 additions & 3 deletions src/DOCP/types.jl → src/DOCP/discretized_model.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# DOCP types
# DiscretizedModel
#
# Defines the `DiscretizedModel` type: a thin pairing of an optimal control
# problem with the discretizer that produced it, plus an optional backend cache.
# The `DiscretizedModel` typea thin pairing of an optimal control problem with the
# discretizer that produced it, plus a backend cache — and its accessors.

"""
$(TYPEDEF)
Expand Down Expand Up @@ -36,3 +36,23 @@ struct DiscretizedModel{
discretizer::TD
cache::TC
end

"""
$(TYPEDSIGNATURES)

Extract the original optimal control problem from a discretized problem.

# Arguments
- `docp::DiscretizedModel`: The discretized optimal control problem

# Returns
- The original optimal control problem

# Example
```julia
ocp = ocp_model(docp)
```

See also: `DiscretizedModel`
"""
ocp_model(docp::DiscretizedModel) = docp.ocp
1 change: 1 addition & 0 deletions src/Modelers/Modelers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ using ..Optimization

# Submodules
include(joinpath(@__DIR__, "abstract_modeler.jl"))
include(joinpath(@__DIR__, "contract.jl"))
include(joinpath(@__DIR__, "validation.jl"))
include(joinpath(@__DIR__, "adnlp.jl"))
include(joinpath(@__DIR__, "exa.jl"))
Expand Down
95 changes: 95 additions & 0 deletions src/Modelers/contract.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Modeler contract
#
# Canonical contract for modeler strategies: the generic `Optimization.build_model`
# / `Optimization.build_solution` methods, dispatched on `AbstractNLPModeler`. These
# are `NotImplemented` stubs; the package providing the concrete problem implements
# them by multiple dispatch on the concrete `(problem, modeler)` pair (e.g. CTDirect
# for `(DiscretizedModel, ADNLP/Exa)`). Defining these methods here also creates the
# `Optimization.build_model` / `Optimization.build_solution` generic functions.

"""
$(TYPEDSIGNATURES)

Build a [`Optimization.BuiltModel`](@ref) from an optimization problem using the
specified modeler.

This is the modeler contract: the modeler converts the problem into a backend NLP
model. The returned [`Optimization.BuiltModel`](@ref) carries the backend NLP model
together with any immutable build-time auxiliary needed later by
[`Optimization.build_solution`](@ref).

# Contract
Must be implemented in the package providing the concrete problem, dispatching on the
concrete `(problem, modeler)` pair, e.g.
`Optimization.build_model(prob::DiscretizedModel, init, ::ADNLP)` in CTDirect. This
generic stub throws [`CTBase.Exceptions.NotImplemented`](@extref).

# Arguments
- `prob::Optimization.AbstractOptimizationProblem`: The optimization problem.
- `initial_guess`: Initial guess for the NLP solver.
- `modeler::AbstractNLPModeler`: The modeler strategy (e.g. `ADNLP`, `Exa`).

# Returns
- A [`Optimization.BuiltModel`](@ref) bundling the backend NLP model and its build-time cache.

# Throws
- [`CTBase.Exceptions.NotImplemented`](@extref): when no method exists for this
`(problem, modeler)` pair.

See also: `Optimization.build_solution`, `Optimization.BuiltModel`.
"""
function Optimization.build_model(
prob::Optimization.AbstractOptimizationProblem, initial_guess, modeler::AbstractNLPModeler
)
throw(
Exceptions.NotImplemented(
"Model building not implemented";
required_method="Optimization.build_model(prob::$(typeof(prob)), initial_guess, modeler::$(typeof(modeler)))",
suggestion="Implement build_model for this (problem, modeler) pair in the package providing the problem",
context="Modelers.build_model - required method implementation",
),
)
end

"""
$(TYPEDSIGNATURES)

Build a problem-level solution from NLP execution statistics using the specified
modeler.

This is the modeler contract: it reconstructs the solution from the
[`Optimization.BuiltModel`](@ref) returned by [`Optimization.build_model`](@ref)
(which carries the problem and the immutable build-time cache) and the solver output.

# Contract
Must be implemented in the package providing the concrete problem, dispatching on the
concrete `(built, modeler)` pair, e.g.
`Optimization.build_solution(built::BuiltModel{<:DiscretizedModel}, stats, ::ADNLP)`
in CTDirect. This generic stub throws [`CTBase.Exceptions.NotImplemented`](@extref).

# Arguments
- `built::Optimization.BuiltModel`: The bundle returned by `build_model`.
- `model_solution`: NLP solver output (execution statistics).
- `modeler::AbstractNLPModeler`: The modeler strategy used for building.

# Returns
- A solution object appropriate for the problem type.

# Throws
- [`CTBase.Exceptions.NotImplemented`](@extref): when no method exists for this
`(problem, modeler)` pair.

See also: `Optimization.build_model`, `Optimization.BuiltModel`.
"""
function Optimization.build_solution(
built::Optimization.BuiltModel, model_solution, modeler::AbstractNLPModeler
)
throw(
Exceptions.NotImplemented(
"Solution building not implemented";
required_method="Optimization.build_solution(built::BuiltModel{$(typeof(built.problem))}, model_solution, modeler::$(typeof(modeler)))",
suggestion="Implement build_solution for this (problem, modeler) pair in the package providing the problem",
context="Modelers.build_solution - required method implementation",
),
)
end
16 changes: 9 additions & 7 deletions src/Optimization/Optimization.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,25 @@
Optimization module.

This module defines the abstract optimization problem interface
(`AbstractOptimizationProblem`) and the backend-agnostic model/solution building
contract (`build_model` / `build_solution`). Concrete problem types (e.g.
`DiscretizedModel`) and the packages providing them implement these by multiple
dispatch on `(problem, modeler)`.
(`AbstractOptimizationProblem`) and the value types of the model/solution building
contract (`BuiltModel`, `NoCache`). The generic `build_model` / `build_solution`
functions are *owned* here (and re-exported) but their canonical `NotImplemented`
stubs — the modeler contract — live in `Modelers` (`Modelers/contract.jl`), typed on
`AbstractNLPModeler`; concrete methods live in the package providing the problem
(e.g. CTDirect), dispatched on `(problem, modeler)`.

Solver-side utilities (e.g. `extract_solver_infos`) live in `Solvers`.
"""
module Optimization

# Imports
import CTBase.Core
import CTBase.Exceptions
import DocStringExtensions: TYPEDEF, TYPEDSIGNATURES
using SolverCore: SolverCore
import DocStringExtensions: TYPEDEF

# Submodules
include(joinpath(@__DIR__, "abstract_types.jl"))
include(joinpath(@__DIR__, "built_model.jl"))
# Declares the generic `build_model` / `build_solution`; contract stubs live in Modelers.
include(joinpath(@__DIR__, "building.jl"))

# Public API - Abstract types
Expand All @@ -34,6 +35,7 @@ export AbstractOptimizationProblem
export BuiltModel, NoCache

# Public API - Model building functions
# (declared in `building.jl`; contract stubs in `Modelers/contract.jl`)
export build_model, build_solution

end # module Optimization
Loading