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
45 changes: 45 additions & 0 deletions BREAKING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,51 @@

This document describes breaking changes in CTModels releases and how to migrate your code.

## [0.9.2] - 2026-03-05

**No breaking changes** - This release adds new multi-time-grid functionality while maintaining full backward compatibility. All existing APIs continue to work unchanged.

### New Features (Non-Breaking)

While not breaking changes, the following new features are available:

```julia
# New time grid model access
sol = build_solution(...)
time_grid_model(sol) # Returns UnifiedTimeGridModel or MultipleTimeGridModel

# Enhanced time_grid with component specification
time_grid(sol, :state) # Component-specific time grid
time_grid(sol, :control) # Control time grid
time_grid(sol, :costate) # Costate time grid

# Multi-grid build solution (new signature)
build_solution(ocp, T_state, T_control, T_costate, T_dual, X, U, v, P; kwargs...)

# Component symbol cleaning
clean_component_symbols((:states, :controls, :constraint)) # Returns (:state, :control, :path)
```

### Serialization Format Changes

The serialization format has been enhanced to support multi-time-grids, but existing files remain compatible:

- **Legacy Format**: Automatically detected and loaded
- **Multi-Grid Format**: New format with component-specific time grids
- **Automatic Conversion**: Seamless handling of both formats

### Plotting Enhancements

Plotting now supports additional component symbols with automatic mapping:

- `:control_norm` β†’ `:control`
- `:path_constraint` β†’ `:state`
- `:dual_path_constraint` β†’ `:dual`

Existing plotting code continues to work unchanged.

---

## [0.9.1] - 2026-03-02

**No breaking changes** - This release only removes experimental test files from `test/extras/` directory. All public APIs remain unchanged.
Expand Down
66 changes: 66 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,72 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.9.2] - 2026-03-05

### Added

- **Multi-Time-Grid System**: Complete implementation of multiple time grid support
- New `UnifiedTimeGridModel` for single time grid solutions
- New `MultipleTimeGridModel` for different time grids per component
- New `time_grid_model()` getter function for accessing time grid models
- Enhanced `time_grid()` function with component-specific access
- Support for empty dual grids with `nothing` values

- **Enhanced Serialization**: Dual format support for backward compatibility
- Legacy format preservation for existing solutions
- New multi-grid format with component-specific time grids
- `_serialize_solution()` function now exported for advanced usage
- Automatic format detection and conversion

- **Component Symbol Cleaning**: Order-preserving component name normalization
- `clean_component_symbols()` function preserves input order
- Plural to singular conversion (`:states` β†’ `:state`, `:controls` β†’ `:control`)
- Ambiguous term mapping (`:constraint` β†’ `:path`, `:cons` β†’ `:path`)
- Duplicate removal while maintaining original sequence

- **Plotting Enhancements**: Multi-time-grid compatible plotting system
- Component mapping for special plotting symbols (`:control_norm` β†’ `:control`)
- Path constraint plotting support (`:path_constraint` β†’ `:state`)
- Dual constraint plotting (`:dual_path_constraint` β†’ `:dual`)
- Robust error handling for invalid component specifications

### Changed

- **Build Solution API**: Enhanced multi-grid support in `build_solution()`
- Accepts separate time grids for state, control, costate, and dual components
- Automatic conversion from `LinRange` to `Vector{Float64}` for compatibility
- Improved error messages for mismatched grid and data sizes
- Better type stability for `UnifiedTimeGridModel` operations

- **Exception Handling**: Improved error messages and formatting
- `IncorrectArgument` exceptions with semicolon-separated named arguments
- Better localization of errors with file, line, and function information
- Actionable error messages with suggestions for fixes

### Fixed

- **Type Stability**: Resolved type inference issues in multi-time-grid operations
- `UnifiedTimeGridModel` operations are now fully type-stable
- `MultipleTimeGridModel` handles Union return types gracefully
- Proper type annotations for time grid getter functions

- **Data Grid Consistency**: Fixed bounds errors in multi-grid test cases
- Corrected data matrix sizes to match corresponding time grids
- Proper handling of different grid sizes in test scenarios
- Improved interpolation for mismatched grid dimensions

### Test Coverage

- **Comprehensive Test Suite**: 79 tests passing (100% success rate)
- Time Grid Models: 10 tests
- Component Symbol Cleaning: 15 tests
- Build Solution with Multiple Grids: 14 tests
- Time Grid Getters: 17 tests
- Serialization with Multiple Grids: 9 tests
- Backward Compatibility: 5 tests
- Error Handling: 3 tests
- Type Stability: 6 tests

## [0.9.1] - 2026-03-02

### Removed
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "CTModels"
uuid = "34c4fa32-2049-4079-8329-de33c2a22e2d"
version = "0.9.1"
version = "0.9.2-beta"
authors = ["Olivier Cots <olivier.cots@toulouse-inp.fr>"]

[deps]
Expand Down
25 changes: 8 additions & 17 deletions ext/CTModelsJLD.jl
Original file line number Diff line number Diff line change
Expand Up @@ -80,27 +80,17 @@ function CTModels.import_ocp_solution(
file_data = load(filename * ".jld2")
data = file_data["solution_data"]

# Extract time grid - handle both TimeGridModel and raw Vector
T = if data["time_grid"] isa CTModels.TimeGridModel
data["time_grid"].value
# Extract solver infos if present
infos = if haskey(data, "infos")
data["infos"]
else
data["time_grid"]
Dict{Symbol,Any}()
end

# Reconstruct solution using build_solution with provided ocp
sol = CTModels.build_solution(
# Reconstruct solution using helper function (handles both single and multiple time grids)
sol = CTModels.Serialization._reconstruct_solution_from_data(
ocp,
T,
data["state"],
data["control"],
data["variable"],
data["costate"];
objective=data["objective"],
iterations=data["iterations"],
constraints_violation=data["constraints_violation"],
message=data["message"],
status=data["status"],
successful=data["successful"],
data;
path_constraints_dual=data["path_constraints_dual"],
boundary_constraints_dual=data["boundary_constraints_dual"],
state_constraints_lb_dual=data["state_constraints_lb_dual"],
Expand All @@ -109,6 +99,7 @@ function CTModels.import_ocp_solution(
control_constraints_ub_dual=data["control_constraints_ub_dual"],
variable_constraints_lb_dual=data["variable_constraints_lb_dual"],
variable_constraints_ub_dual=data["variable_constraints_ub_dual"],
infos=infos,
)

return sol
Expand Down
52 changes: 38 additions & 14 deletions ext/CTModelsJSON.jl
Original file line number Diff line number Diff line change
Expand Up @@ -340,26 +340,50 @@ function CTModels.import_ocp_solution(
Dict{Symbol,Any}()
end

# NB. convert vect{vect} to matrix
return CTModels.build_solution(
# Create data dictionary compatible with helper function
data = Dict{String,Any}(
"objective" => blob.objective,
"iterations" => blob.iterations,
"constraints_violation" => blob.constraints_violation,
"message" => blob.message,
"status" => blob.status,
"successful" => blob.successful,
"state" => X,
"control" => U,
"variable" => Vector{Float64}(blob.variable),
"costate" => P,
"path_constraints_dual" => path_constraints_dual,
"boundary_constraints_dual" => boundary_constraints_dual,
"state_constraints_lb_dual" => state_constraints_lb_dual,
"state_constraints_ub_dual" => state_constraints_ub_dual,
"control_constraints_lb_dual" => control_constraints_lb_dual,
"control_constraints_ub_dual" => control_constraints_ub_dual,
"variable_constraints_lb_dual" => variable_constraints_lb_dual,
"variable_constraints_ub_dual" => variable_constraints_ub_dual,
)

# Add time grid data (format detection handled by helper)
if haskey(blob, "time_grid_state")
# New format: multiple time grids
data["time_grid_state"] = blob.time_grid_state
data["time_grid_control"] = blob.time_grid_control
data["time_grid_costate"] = blob.time_grid_costate
data["time_grid_dual"] = blob.time_grid_dual
else
# Legacy format: single time grid
data["time_grid"] = blob.time_grid
end

# Reconstruct solution using helper function (handles both single and multiple time grids)
return CTModels.Serialization._reconstruct_solution_from_data(
ocp,
Vector{Float64}(blob.time_grid),
X,
U,
Vector{Float64}(blob.variable),
P;
objective=Float64(blob.objective),
iterations=blob.iterations,
constraints_violation=Float64(blob.constraints_violation),
message=blob.message,
status=Symbol(blob.status),
successful=blob.successful,
data;
path_constraints_dual=path_constraints_dual,
boundary_constraints_dual=boundary_constraints_dual,
state_constraints_lb_dual=state_constraints_lb_dual,
state_constraints_ub_dual=state_constraints_ub_dual,
control_constraints_lb_dual=control_constraints_lb_dual,
control_constraints_ub_dual=control_constraints_ub_dual,
boundary_constraints_dual=boundary_constraints_dual,
variable_constraints_lb_dual=variable_constraints_lb_dual,
variable_constraints_ub_dual=variable_constraints_ub_dual,
infos=infos,
Expand Down
Loading
Loading