Skip to content

Commit c010f47

Browse files
committed
Add support for plain functions in @lie macro for Lie brackets
- Update documentation warning to reflect new CTFlows capability - Add comprehensive examples with plain functions (autonomous, non-autonomous, variable) - Add nested brackets and mixed VectorField/function examples - Add tip comparing plain functions vs explicit VectorField creation - Add extensive test suite for @lie macro with plain functions - All tests pass (89/89) ✅
1 parent e011b1d commit c010f47

2 files changed

Lines changed: 198 additions & 13 deletions

File tree

docs/src/manual-differential-geometry.md

Lines changed: 95 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ Xf([1, 2], 1)
173173

174174
You can also create the VectorField explicitly with the keywords, then use it without keywords in the Lie function:
175175

176-
```@example main-8
176+
```@example main-7a
177177
using OptimalControl # hide
178178
# Non-autonomous VectorField created with keywords
179179
X = OptimalControl.VectorField((t, x) -> [t + x[2], -x[1]]; autonomous=false)
@@ -184,7 +184,7 @@ Xf = Lie(X, f)
184184
Xf(1, [1, 2])
185185
```
186186

187-
```@example main-9
187+
```@example main-7b
188188
using OptimalControl # hide
189189
# Variable VectorField created with keywords
190190
X = OptimalControl.VectorField((x, v) -> [x[2] + v, -x[1]]; variable=true)
@@ -267,28 +267,28 @@ Hfg(1, [1, 2], [3, 4])
267267

268268
You can also create Hamiltonian objects explicitly with keywords, then use them without keywords in the Poisson function. **Important**: both Hamiltonians must have the same time and variable dependencies:
269269

270-
```@example main-10
270+
```@example main-9a
271271
using OptimalControl # hide
272272
# Non-autonomous Hamiltonians created with keywords
273-
f_na(t, x, p) = t + p[1] * x[2] + p[2] * x[1]
274-
g_na(t, x, p) = t^2 + x[1]^2 + p[2]^2
273+
f(t, x, p) = t + p[1] * x[2] + p[2] * x[1]
274+
g(t, x, p) = t^2 + x[1]^2 + p[2]^2
275275
276-
F = OptimalControl.Hamiltonian(f_na; autonomous=false)
277-
G = OptimalControl.Hamiltonian(g_na; autonomous=false)
276+
F = OptimalControl.Hamiltonian(f; autonomous=false)
277+
G = OptimalControl.Hamiltonian(g; autonomous=false)
278278
279279
# No keywords needed here - both Hamiltonians are already non-autonomous
280280
Hfg = Poisson(F, G)
281281
Hfg(1, [1, 2], [3, 4])
282282
```
283283

284-
```@example main-11
284+
```@example main-9b
285285
using OptimalControl # hide
286286
# Variable Hamiltonians created with keywords
287-
f_var(x, p, v) = x[1]^2 + p[2]^2 + v
288-
g_var(x, p, v) = x[2]^2 + p[1]^2 + 2*v
287+
f(x, p, v) = x[1]^2 + p[2]^2 + v
288+
g(x, p, v) = x[2]^2 + p[1]^2 + 2*v
289289
290-
F = OptimalControl.Hamiltonian(f_var; variable=true)
291-
G = OptimalControl.Hamiltonian(g_var; variable=true)
290+
F = OptimalControl.Hamiltonian(f; variable=true)
291+
G = OptimalControl.Hamiltonian(g; variable=true)
292292
293293
# Both are variable, so the Poisson bracket is also variable
294294
Hfg = Poisson(F, G)
@@ -384,6 +384,17 @@ println("Lift of Lie bracket: ", HZ(x, p))
384384

385385
The `@Lie` macro provides a convenient syntax for computing Lie brackets (for vector fields) and Poisson brackets (for Hamiltonians).
386386

387+
!!! warning "Important distinction"
388+
389+
- **Square brackets `[...]`** denote **Lie brackets** and work with:
390+
- `VectorField` objects
391+
- Plain Julia functions (automatically wrapped as `VectorField`)
392+
- **Curly braces `{...}`** denote **Poisson brackets** and work with:
393+
- Plain Julia functions (automatically wrapped as `Hamiltonian`)
394+
- `Hamiltonian` objects
395+
396+
When using plain functions, specify `autonomous` and `variable` keywords as needed to match your function signature.
397+
387398
### Lie brackets with VectorField
388399

389400
```@example main-12
@@ -407,6 +418,78 @@ F123 = @Lie [[F1, F2], F3]
407418
F123([1, 2, 3])
408419
```
409420

421+
### Lie brackets with plain Julia functions
422+
423+
You can also use plain Julia functions directly with the `@Lie` macro. The functions will be automatically wrapped in `VectorField` objects:
424+
425+
```@example main-12a
426+
using OptimalControl # hide
427+
# Define plain Julia functions
428+
X(x) = [x[2], -x[1]]
429+
Y(x) = [x[1], x[2]]
430+
431+
# Compute Lie bracket using macro with plain functions
432+
Z = @Lie [X, Y]
433+
434+
# Evaluate
435+
Z([1, 2])
436+
```
437+
438+
### With keyword arguments for plain functions
439+
440+
For non-autonomous or variable cases, specify the keywords:
441+
442+
```@example main-12b
443+
using OptimalControl # hide
444+
# Non-autonomous plain functions
445+
X(t, x) = [t + x[2], -x[1]]
446+
Y(t, x) = [x[1], t*x[2]]
447+
448+
# Use autonomous=false keyword
449+
Z = @Lie [X, Y] autonomous=false
450+
Z(1, [1, 2])
451+
```
452+
453+
```@example main-12c
454+
using OptimalControl # hide
455+
# Variable plain functions
456+
X(x, v) = [x[2] + v, -x[1]]
457+
Y(x, v) = [x[1], x[2] + v]
458+
459+
# Use variable=true keyword
460+
Z = @Lie [X, Y] variable=true
461+
Z([1, 2], 1)
462+
```
463+
464+
### Nested brackets with plain functions
465+
466+
```@example main-12d
467+
using OptimalControl # hide
468+
X(x) = [0, -x[3], x[2]]
469+
Y(x) = [x[3], 0, -x[1]]
470+
Z_func(x) = [x[1], x[2], x[3]]
471+
472+
# Nested Lie brackets
473+
nested = @Lie [[X, Y], Z_func]
474+
nested([1, 2, 3])
475+
```
476+
477+
!!! tip "Plain functions vs VectorField"
478+
479+
Using plain functions with `@Lie [X, Y]` is convenient for quick computations. However, if you need to reuse the same vector field multiple times or want explicit control over the autonomy/variability, consider creating `VectorField` objects explicitly:
480+
481+
```julia
482+
# Explicit VectorField (keywords at creation)
483+
X = OptimalControl.VectorField((t, x) -> [t + x[2], -x[1]]; autonomous=false)
484+
Y = OptimalControl.VectorField((t, x) -> [x[1], t*x[2]]; autonomous=false)
485+
Z = @Lie [X, Y] # No keywords needed
486+
487+
# Plain functions (keywords at macro call)
488+
X_func(t, x) = [t + x[2], -x[1]]
489+
Y_func(t, x) = [x[1], t*x[2]]
490+
Z = @Lie [X_func, Y_func] autonomous=false
491+
```
492+
410493
### Poisson brackets from plain functions
411494

412495
For Hamiltonian functions (plain Julia functions), use curly braces `{_, _}`:

test/suite/reexport/test_ctflows.jl

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ function test_ctflows()
129129
end
130130

131131
Test.@testset "@Lie Macro" begin
132-
# Test basic macro usage
132+
# Test basic macro usage with VectorField
133133
X1 = CTFlows.VectorField(x -> [x[2], -x[1]])
134134
X2 = CTFlows.VectorField(x -> [x[1], x[2]])
135135

@@ -141,6 +141,108 @@ function test_ctflows()
141141
Test.@test lie_macro_result([1, 2]) isa Vector
142142
end
143143

144+
Test.@testset "@Lie Macro with Plain Functions" begin
145+
# ================================================================
146+
# Autonomous plain functions
147+
# ================================================================
148+
Test.@testset "Autonomous plain functions" begin
149+
X(x) = [x[2], -x[1]]
150+
Y(x) = [x[1], x[2]]
151+
152+
# Lie bracket with macro
153+
Z = @Lie [X, Y]
154+
Test.@test Z isa CTFlows.VectorField
155+
Test.@test Z([1, 2]) isa Vector
156+
157+
# Should give same result as with VectorField objects
158+
X_vf = CTFlows.VectorField(X)
159+
Y_vf = CTFlows.VectorField(Y)
160+
Z_vf = @Lie [X_vf, Y_vf]
161+
Test.@test Z([1, 2]) Z_vf([1, 2])
162+
end
163+
164+
# ================================================================
165+
# Non-autonomous plain functions
166+
# ================================================================
167+
Test.@testset "Non-autonomous plain functions" begin
168+
X(t, x) = [t + x[2], -x[1]]
169+
Y(t, x) = [x[1], t * x[2]]
170+
171+
Z = @Lie [X, Y] autonomous = false
172+
Test.@test Z isa CTFlows.VectorField
173+
Test.@test Z(1, [1, 2]) isa Vector
174+
175+
# Verify against VectorField version
176+
X_vf = CTFlows.VectorField(X; autonomous=false)
177+
Y_vf = CTFlows.VectorField(Y; autonomous=false)
178+
Z_vf = @Lie [X_vf, Y_vf]
179+
Test.@test Z(1, [1, 2]) Z_vf(1, [1, 2])
180+
end
181+
182+
# ================================================================
183+
# Variable plain functions
184+
# ================================================================
185+
Test.@testset "Variable plain functions" begin
186+
X(x, v) = [x[2] + v, -x[1]]
187+
Y(x, v) = [x[1], x[2] + v]
188+
189+
Z = @Lie [X, Y] variable = true
190+
Test.@test Z isa CTFlows.VectorField
191+
Test.@test Z([1, 2], 1) isa Vector
192+
193+
# Verify against VectorField version
194+
X_vf = CTFlows.VectorField(X; variable=true)
195+
Y_vf = CTFlows.VectorField(Y; variable=true)
196+
Z_vf = @Lie [X_vf, Y_vf]
197+
Test.@test Z([1, 2], 1) Z_vf([1, 2], 1)
198+
end
199+
200+
# ================================================================
201+
# Non-autonomous + variable plain functions
202+
# ================================================================
203+
Test.@testset "Non-autonomous variable plain functions" begin
204+
X(t, x, v) = [t + x[2] + v, -x[1]]
205+
Y(t, x, v) = [x[1], t * x[2] + v]
206+
207+
Z = @Lie [X, Y] autonomous = false variable = true
208+
Test.@test Z isa CTFlows.VectorField
209+
Test.@test Z(1, [1, 2], 1) isa Vector
210+
211+
# Verify against VectorField version
212+
X_vf = CTFlows.VectorField(X; autonomous=false, variable=true)
213+
Y_vf = CTFlows.VectorField(Y; autonomous=false, variable=true)
214+
Z_vf = @Lie [X_vf, Y_vf]
215+
Test.@test Z(1, [1, 2], 1) Z_vf(1, [1, 2], 1)
216+
end
217+
218+
# ================================================================
219+
# Nested Lie brackets with plain functions
220+
# ================================================================
221+
Test.@testset "Nested brackets with plain functions" begin
222+
X(x) = [x[2], -x[1]]
223+
Y(x) = [x[1], x[2]]
224+
Z_func(x) = [0, x[1]]
225+
226+
# [[X, Y], Z]
227+
nested = @Lie [[X, Y], Z_func]
228+
Test.@test nested isa CTFlows.VectorField
229+
Test.@test nested([1, 2]) isa Vector
230+
end
231+
232+
# ================================================================
233+
# Mixed: plain function + VectorField
234+
# ================================================================
235+
Test.@testset "Mixed plain function and VectorField" begin
236+
X(x) = [x[2], -x[1]]
237+
Y_vf = CTFlows.VectorField(x -> [x[1], x[2]])
238+
239+
# Should work with one plain function and one VectorField
240+
Z = @Lie [X, Y_vf]
241+
Test.@test Z isa CTFlows.VectorField
242+
Test.@test Z([1, 2]) isa Vector
243+
end
244+
end
245+
144246
Test.@testset "Complex Signature Tests" begin
145247
# Test with different arities and keyword arguments
146248

0 commit comments

Comments
 (0)