Skip to content
Open
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
35 changes: 35 additions & 0 deletions BREAKING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,48 @@ Chronological, newest first. Each `CHANGELOG.md` entry links back here for the f
record; this file never duplicates a release's own compat table, only what a caller must
change or be aware of.

- [Breaking changes (unreleased)](#breaking-changes-unreleased)
- [Non-breaking notes (2.2.1-beta)](#non-breaking-notes-221-beta)
- [Non-breaking notes (2.2.0-beta)](#non-breaking-notes-220-beta)
- [Breaking changes: v2.0 → v2.1.0-beta](#breaking-changes-v20--v210-beta)
- [Breaking changes: v1.x → v2.0](#breaking-changes-v1x--v20)

---

## Breaking changes (unreleased)

### The module name `CTLie` is no longer exported

`using OptimalControl` used to bring the module `CTLie` into scope, only so that the code
generated by `@Lie` could name it. CTLie 0.3 made `@Lie` hygienic, so the export is gone
([#966](https://github.com/control-toolbox/OptimalControl.jl/issues/966)). The functions
and the macro (`ad`, `Lift`, `Poisson`, `∂ₜ`, `@Lie`, `dg_ad_backend`, `dg_ad_backend!`) are
still exported, and `@Lie` now works after `import OptimalControl` or
`using OptimalControl: @Lie` as well.

**Migration:** only code that wrote `CTLie.…` after `using OptimalControl` is affected.
Load the module yourself, or qualify it through OptimalControl:

```julia
# Before
using OptimalControl
CTLie.LiftedHamiltonianFunction

# After
using OptimalControl
using CTLie: CTLie # CTLie in your environment
CTLie.LiftedHamiltonianFunction
# or, without adding CTLie to your environment
OptimalControl.CTLie.LiftedHamiltonianFunction
```

### CTLie 0.3: some guard errors are `IncorrectArgument`

Through the CTLie bump: `ad` and `Lift` on a `HamiltonianVectorField`, and `ad` and `∂ₜ` on
an in-place vector field, now throw `IncorrectArgument` instead of `NotImplemented`. Only code
that catches these errors by type is affected; see the
[CTLie BREAKING notes](https://github.com/control-toolbox/CTLie.jl/blob/main/BREAKING.md).

## Non-breaking notes (2.2.1-beta)

Documentation infrastructure only. **Nothing to migrate** — OptimalControl's API and runtime
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,19 @@ Versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### ⚠️ Breaking

- **`using OptimalControl` no longer brings the module name `CTLie` into scope** ([#966](https://github.com/control-toolbox/OptimalControl.jl/issues/966)). It was exported only because the expansion of `@Lie` named `CTLie` and `CTBase` in the caller's module. Since CTLie 0.3 the expansion holds the modules themselves ([CTLie#40](https://github.com/control-toolbox/CTLie.jl/issues/40)), so `@Lie` now also works after `import OptimalControl` or `using OptimalControl: @Lie`. `ad`, `Lift`, `Poisson`, `∂ₜ`, `@Lie`, `dg_ad_backend` and `dg_ad_backend!` are still exported. See [BREAKING.md](BREAKING.md).

### 📚 Documentation

- The page "The `@Lie` macro" drops its section "`using OptimalControl` is needed".
- **Initial guess: check a guess before solving with `build_initial_guess`** ([#965](https://github.com/control-toolbox/OptimalControl.jl/issues/965)). It builds and validates the guess against the problem. The other initial-guess helpers of CTModels (`initial_guess`, `pre_initial_guess`, `validate_initial_guess`, `initial_state`, `initial_control`, `initial_variable`, `PreInitialGuess`) stay unexported, and a test now records that choice.

### 📦 Dependencies

- **CTLie `0.3`** (was `0.2`), in the package and the docs environment.

### 🐛 Bug Fixes

- **`init` and `initial_guess` together raise "Conflicting aliases" in descriptive mode too** ([#964](https://github.com/control-toolbox/OptimalControl.jl/issues/964)). Explicit mode already reported the conflict; descriptive mode, the common case, routed the leftover `init` to the strategies and rejected it as an unknown option, with unrelated suggestions. Both modes now run the same check before any routing.
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ BenchmarkTools = "1"
CTBase = "0.30"
CTDirect = "1"
CTFlows = "0.18"
CTLie = "0.2"
CTLie = "0.3"
CTModels = "0.19"
CTParser = "0.9"
CTSolvers = "0.5"
Expand Down
2 changes: 1 addition & 1 deletion docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ ADNLPModels = "0.8"
CTBase = "0.30"
CTDirect = "1"
CTFlows = "0.18"
CTLie = "0.2"
CTLie = "0.3"
CTModels = "0.19"
CTParser = "0.9"
CTSolvers = "0.5"
Expand Down
2 changes: 1 addition & 1 deletion docs/src/assets/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ ADNLPModels = "0.8"
CTBase = "0.30"
CTDirect = "1"
CTFlows = "0.18"
CTLie = "0.2"
CTLie = "0.3"
CTModels = "0.19"
CTParser = "0.9"
CTSolvers = "0.5"
Expand Down
20 changes: 0 additions & 20 deletions docs/src/geometry/lie-macro.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,26 +113,6 @@ function instead of a Boolean, without an error.
nothing # hide
```

## `using OptimalControl` is needed

The expansion of `@Lie` refers to the modules `CTLie` and `CTBase` by name, which
`using OptimalControl` brings into scope. With `import OptimalControl` or
`using OptimalControl: @Lie` only, the expanded code fails with an `UndefVarError`
([CTLie#40](https://github.com/control-toolbox/CTLie.jl/issues/40)).

```@example main
m = Module() # hide
Core.eval(m, :(import OptimalControl)) # hide
err = try # hide
Core.eval(m, :((OptimalControl.@Lie [x -> [x[2], -x[1]], x -> x])([1.0, 2.0]))) # hide
nothing # hide
catch e # hide
e # hide
end # hide
@assert err isa UndefVarError # hide
nothing # hide
```

## See also

- [Lie derivative and Lie bracket](@ref geometry-ad): `ad`, called by `@Lie [X, Y]`.
Expand Down
5 changes: 2 additions & 3 deletions src/imports/ctlie.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
# CTBase's `CTBaseDifferentiationInterface` extension to be *armed*.
# That is what `imports/ad.jl` is for.

# Generated code — the `@Lie` macro emits `CTLie.*` and `CTBase.Traits.*`
# prefixes in its expansion, so both modules must be in scope at the call site.
@reexport import CTLie: CTLie # for generated code (prefix)
# The module name `CTLie` is not exported: since CTLie 0.3 the expansion of `@Lie` holds the
# modules themselves, not their names, so it works from any caller (#966).

# Differential geometry
@reexport import CTLie: ad, Lift, Poisson, ∂ₜ, @Lie
Expand Down
6 changes: 0 additions & 6 deletions test/suite/indirect/test_shooting_sweep.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,6 @@ using NonlinearSolve: NonlinearProblem, SimpleNewtonRaphson, solve
import LinearAlgebra: norm
import OrdinaryDiffEqTsit5: OrdinaryDiffEqTsit5 # `Flow` needs an integrator

# `@Lie` (used by the Goddard shoot_builder) expands to bare `CTLie.*` /
# `CTBase.Traits.*` prefixes — both modules must be in scope here too, for the
# same reason `test/problems/goddard.jl` needs them via `TestProblems`' own
# `using OptimalControl`.
import CTLie: CTLie
import CTBase: CTBase

include(joinpath(@__DIR__, "..", "..", "problems", "TestProblems.jl"))
using .TestProblems
Expand Down
50 changes: 44 additions & 6 deletions test/suite/reexport/test_ctlie.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,53 @@ const CurrentModule = TestCtlie
const LIE_X1 = VectorField(x -> [x[2], -x[1]])
const LIE_X2 = VectorField(x -> [x[1], x[2]])

# `@Lie` called from modules that have neither `CTLie` nor `CTBase` in scope (#966). The
# expansion holds the modules themselves since CTLie 0.3, so it needs no name at the call site.
module LieImportOnly
import OptimalControl
X(x) = [x[2], -x[1]]
Y(x) = [x[1], x[2]]
Xt(t, x) = [t + x[2], -x[1]]
Yt(t, x) = [x[1], t * x[2]]
const H1 = OptimalControl.Hamiltonian((x, p) -> x[1] * p[1])
const H2 = OptimalControl.Hamiltonian((x, p) -> x[2] * p[2])
bracket() = OptimalControl.@Lie [X, Y]
bracket_nonautonomous() = OptimalControl.@Lie [Xt, Yt] is_autonomous = false
poisson() = OptimalControl.@Lie {H1, H2}
end

module LieMacroOnly
using OptimalControl: @Lie
X(x) = [x[2], -x[1]]
Y(x) = [x[1], x[2]]
bracket() = @Lie [X, Y]
end

function test_ctlie()
Test.@testset "CTLie reexports" verbose = VERBOSE showtiming = SHOWTIMING begin
Test.@testset "Generated Code Prefix" begin
# `@Lie` emits both `CTLie.*` and `CTBase.Traits.*` in its expansion,
# so both modules must reach the call site.
Test.@testset "Module name not exported" begin
# OptimalControl uses CTLie internally, but no longer exports its name (#966).
Test.@test isdefined(OptimalControl, :CTLie)
Test.@test isdefined(CurrentModule, :CTLie)
Test.@test CTLie isa Module
Test.@test isdefined(CurrentModule, :CTBase)
Test.@test !is_exported(OptimalControl, :CTLie)
end

Test.@testset "@Lie is hygienic" begin
Test.@testset "after import OptimalControl" begin
Test.@test !isdefined(LieImportOnly, :CTLie)
Test.@test !isdefined(LieImportOnly, :CTBase)
y = [1.0, 2.0]
Z = LieImportOnly.bracket()
Test.@test Z isa VectorField
Test.@test Z(y) ≈ ad(VectorField(LieImportOnly.X), VectorField(LieImportOnly.Y))(y)
Test.@test LieImportOnly.bracket_nonautonomous() isa VectorField
Test.@test LieImportOnly.poisson() isa Hamiltonian
end

Test.@testset "after using OptimalControl: @Lie" begin
Test.@test !isdefined(LieMacroOnly, :CTLie)
Test.@test !isdefined(LieMacroOnly, :CTBase)
Test.@test LieMacroOnly.bracket()([1.0, 2.0]) ≈ LieImportOnly.bracket()([1.0, 2.0])
end
end

Test.@testset "Differential geometry" begin
Expand Down
Loading