Skip to content

Add comprehensive slice notation tests#251

Draft
ocots wants to merge 3 commits into
mainfrom
slices
Draft

Add comprehensive slice notation tests#251
ocots wants to merge 3 commits into
mainfrom
slices

Conversation

@ocots

@ocots ocots commented Apr 10, 2026

Copy link
Copy Markdown
Member
  • Test slices in constraints: x[1:3](0), x[1:end](0), x[1:n](0)
  • Test slices in dynamics: ∂(x[1:3])(t), ∂(x[1:end])(t), ∂(x[1:n])(t)
  • Test slices in Mayer cost: x[1:3](tf), x[1:end](tf), x[1:n](tf)
  • Mark broken tests for unsupported features (end keyword, variable n)
  • Literal integer ranges work for parsing, some broken for discretisation

- Test slices in constraints: x[1:3](0), x[1:end](0), x[1:n](0)
- Test slices in dynamics: ∂(x[1:3])(t), ∂(x[1:end])(t), ∂(x[1:n])(t)
- Test slices in Mayer cost: x[1:3](tf), x[1:end](tf), x[1:n](tf)
- Mark broken tests for unsupported features (end keyword, variable n)
- Literal integer ranges work for parsing, some broken for discretisation
@ocots

ocots commented Apr 10, 2026

Copy link
Copy Markdown
Member Author

@jbcaillau

Comprehensive Slice Notation Tests

This PR adds extensive tests for slice notation (x[...]) in different contexts of optimal control problem definitions.

Test Coverage

The test suite covers 3 main contexts where slices can appear:

  1. Constraints (initial conditions): x[...](0) == x0
  2. Dynamics: ∂(x[...])(t) == ...
  3. Mayer cost (terminal cost): x[...](tf)

For each context, we test 3 slice variants:

  • Literal integer range: x[1:3] ✅ Most reliable
  • end keyword: x[1:end] ❌ Parsing fails
  • Variable: x[1:n] where n=3 ⚠️ Context-dependent

Current Status

✅ What Works (End-to-End)

Context Slice Type Status
Constraints x[1:3](0) ✅ Parsing + Discretisation + Solve

⚠️ What Partially Works (Parsing OK, Discretisation Fails)

Context Slice Type Parsing Discretisation
Dynamics ∂(x[1:3])(t)
Dynamics ∂(x[1:n])(t)
Mayer cost x[1:3](tf)
Mayer cost x[1:end](tf)
Mayer cost x[1:n](tf)

❌ What's Broken (Parsing Fails)

Context Slice Type Error
Constraints x[1:end](0) MethodError: no method matching (::Colon)(::Int64, ::Symbol)
Constraints x[1:n](0) MethodError: no method matching (::Colon)(::Int64, ::Symbol)
Dynamics ∂(x[1:end])(t) MethodError: no method matching (::Colon)(::Int64, ::Symbol)

Key Findings

  1. Literal integer ranges (x[1:3]) work everywhere for parsing
  2. end keyword fails during parsing in constraints and dynamics contexts
  3. Variable slices (x[1:n]) work for parsing in dynamics and Mayer cost, but fail in constraints
  4. Discretisation currently only works for constraints with literal ranges

Test Structure

Each context has:

  • Model builders: Helper functions to create test problems
  • Unit tests: Verify parsing and model construction
  • Integration tests: Test discretisation and solver execution

All broken tests are marked with @test_broken to document known limitations without failing the test suite.

Next Steps

  1. High Priority: Fix end keyword support in slice parsing (affects all contexts)
  2. High Priority: Fix variable slice support (x[1:n]) in constraints
  3. Medium Priority: Fix discretisation for sliced dynamics (∂(x[1:3])(t))
  4. Medium Priority: Fix discretisation for sliced Mayer costs (x[...](tf))

@github-actions

github-actions Bot commented Apr 10, 2026

Copy link
Copy Markdown
Contributor

Breakage test results
Date: 2026-06-11 16:39:31

Name Latest Stable
OptimalControl compat: v0.8.15 compat: v0.8.15

@jbcaillau

jbcaillau commented May 17, 2026

Copy link
Copy Markdown
Member

@ocots agreed

  • never thought about end, why not
  • using 1:n is OK, should be evaluable at runtime
  • the main issue seems to be the discrepancy between constraints and the rest
  • top priority: correct that and add extensive tests as proposed

Comment thread test/test_slices.jl
@jbcaillau

jbcaillau commented May 18, 2026

Copy link
Copy Markdown
Member

@ocots NB. Also need to allow ẋ₁(t) (dotted notation for scalar components) for consistance. for slices, could be sth like ẋ[1:3](t), though the notation ∂(x[1:3])(t) seems more robust and versatile 👀

see also: #22 (comment)

PS. after a few years of usage, regarding the syntax, i find the instrumental parts are

  • keeping scalars (instead of length one vectors), that is u(t) instead of u[1](t)
  • using indices, that is u₂(t) instead of u[2](t)

@ocots ocots linked an issue May 18, 2026 that may be closed by this pull request
@ocots
ocots marked this pull request as draft June 11, 2026 13:40
@ocots

ocots commented Jun 11, 2026

Copy link
Copy Markdown
Member Author

@jbcaillau

Update (supersedes the earlier comment): the test file has been completely rebuilt as a systematic notation matrix. Current result: 111 passed, 42 broken, 0 errors (153 tests total). All broken cases use @test_broken so CI stays green.


Comprehensive Slice / Index Notation Test Matrix

The file test/test_slices.jl covers 11 sections × 3 assertion levels (build with the :fun backend → discretise_exasolve with MadNLP). The :exa backend is activated at the top of the module so both backends are exercised simultaneously.

Notation forms tested

Form Example Description
scalar bracket x[2](t) integer index
scalar subscript x₂(t) Unicode subscript alias
scalar ASCII x2(t) ASCII alias
range from 1 x[1:3] full literal range
offset range x[2:3] range not starting at 1
step range x[1:2:3] range with step
end range x[1:end] symbolic end
variable range x[1:n] outer-scope variable
dotted full ẋ(t) alias for ∂(x)(t)
dotted scalar ẋ₁(t), ẋ1(t) per-coordinate dotted
dotted slice ẋ[1:3](t) sliced dotted derivative

Summary table (✅ = @test passing · 🔧 = @test_broken · — = not tested)

Context Scalar [1:3] [2:3] offset [1:2:3] step [1:end] [1:n] var
IC x[…](0) == c
 build 🔧 🔧
 discretise 🔧 🔧
 solve 🔧 🔧
FC x[…](tf) == c
 build 🔧 🔧
 discretise 🔧 🔧
 solve 🔧 🔧
Path x[…](t) ≤ c
 build 🔧 🔧
 discretise 🔧 🔧 🔧 🔧
 solve 🔧 🔧 🔧 🔧
Box -c ≤ x[…](t) ≤ c
 build 🔧
Dynamics ∂(x[…])(t) == e
 build 🔧
 discretise 🔧 🔧 🔧 🔧
 solve 🔧 🔧
Dotted ẋ(t) / ẋᵢ(t) / ẋ[1:3](t)
 build ( full)
 build (ẋᵢ / ẋi) 🔧
 build (ẋ[1:3]) 🔧
 discretise / solve ()
Lagrange ∫(f(x[…](t))) → min
 build
 discretise 🔧 🔧 🔧 🔧
 solve 🔧
Mayer x[…](tf) in cost
 build
 discretise 🔧 🔧 🔧 🔧
 solve 🔧 🔧 🔧 🔧
Control u[…](t)
 build (path cstr) 🔧 🔧
 discretise 🔧
 solve 🔧
Variable v[…] in cost
 build (v[1]+v[2], sum(v[1:2].^2))
 discretise ((v[1]+v[2])+∫(…)) 🔧

Key findings

  • Offset/step ranges in boundary constraints (x[2:3](0), x[1:2:3](0), same for tf) work at all three levels — this was a pleasant surprise.
  • Literal range x[1:3] in path constraints works end-to-end (build + discretise + solve); offset x[2:3] and step x[1:2:3] build but fail at the exa discretisation step.
  • Range dynamics ∂(x[1:3])(t) builds with :fun but fails at :exa discretisation because p_dynamics_coord_exa! only accepts Integer coordinates (src/onepass.jl:985-989).
  • ẋᵢ and ẋ[1:3] are not yet aliased in p_state! (TODO at src/onepass.jl:519) — both fail at build.
  • x[1:end] and x[1:n] fail at build in constraint contexts (eager range evaluation hits (::Colon)(::Int64, ::Symbol)); they work in Lagrange/Mayer cost contexts where they appear inside sum(…).
  • Bolza cost with variable brackets: (v[1] + v[2]) + ∫(u(t)²) → min works with explicit parentheses; the unparenthesised form v[1] + v[2] + ∫(…) is a parser ambiguity and fails.
  • Notation equivalence: x[2](t), x₂(t), x2(t) produce identical dynamics (confirmed by evaluating at a sample point).
  • Idiomatic forms u(t) (scalar control) and u₁(t), u₂(t) (index notation for vector control) work end-to-end as expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Comprehensive Slice Notation Tests

2 participants