Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
718fd85
feat: Add modular architecture refactoring plan
ocots Jan 26, 2026
02db68a
foo
ocots Jan 26, 2026
7afa678
move
ocots Jan 26, 2026
b1674a9
move
ocots Jan 26, 2026
9965e6b
feat(phase1): Create modular architecture with Utils, Display, Serial…
ocots Jan 26, 2026
8ab0670
fix: Correct module dependencies - InitialGuess imports from Utils
ocots Jan 26, 2026
e4cfd05
feat(phase2): Activate Display, Serialization, and InitialGuess modules
ocots Jan 26, 2026
6435abe
fix: Add missing exports and imports to InitialGuess module
ocots Jan 26, 2026
428d6ef
refactor: Move RecipesBase.plot stub from Serialization to Display mo…
ocots Jan 26, 2026
efc4c42
feat(phase3): Complete OCP module reorganization
ocots Jan 26, 2026
46775ac
fix(phase3): Clean up OCP module structure
ocots Jan 26, 2026
849fdac
fix(phase4): Add missing OCP exports and qualify imports to resolve c…
ocots Jan 26, 2026
92854c3
fix: Remove import warnings by fixing module dependencies
ocots Jan 26, 2026
9d514b0
refactor: Move compatibility aliases to OCP module
ocots Jan 26, 2026
d0278f0
refactor: Move type definitions to their logical modules
ocots Jan 26, 2026
d941bde
docs: Improve CTModels.jl documentation quality
ocots Jan 26, 2026
68437a6
fix(phase4): Add missing exports for InitialGuess tests
ocots Jan 26, 2026
835436a
fix(phase4): Resolve build_solution conflict and add missing imports
ocots Jan 27, 2026
142ba46
fix(phase4): Add plot support and missing accessor exports
ocots Jan 27, 2026
60295ba
feat: Add plot! support to public API
ocots Jan 27, 2026
70e7a36
fix: Add objective accessor to OCP exports
ocots Jan 27, 2026
0871cae
fix: Add solver info accessor functions to OCP exports
ocots Jan 27, 2026
cd0eee6
fix: Add all missing accessor functions to OCP exports
ocots Jan 27, 2026
de8baca
fix: Complete serialization tests - 100% success
ocots Jan 27, 2026
4f7ab95
fix: Correct OCP test exports and improve test results
ocots Jan 27, 2026
92e6001
feat: Major OCP test fixes - achieve 99.7% test success rate
ocots Jan 27, 2026
bf235f0
feat: Add missing constraint accessor exports
ocots Jan 27, 2026
2f8889c
fix: Resolve remaining export and import issues
ocots Jan 27, 2026
2b06df5
fix: Resolve final Display and time function issues
ocots Jan 27, 2026
adf643f
fix: Add remaining components functions to Display imports
ocots Jan 27, 2026
e393135
�� feat: ACHIEVE 100% TEST SUCCESS RATE!
ocots Jan 27, 2026
2a07735
refactor: Improve Display imports readability
ocots Jan 27, 2026
80fdc33
refactor: Improve Display imports readability
ocots Jan 27, 2026
9a96fa1
docs: add and update analysis reports for DOCP and tools architecture
ocots Jan 27, 2026
c705189
docs: improve Mermaid diagram readability in DOCP audit
ocots Jan 27, 2026
d3ed6b3
�� feat: ACHIEVE PERFECT TEST/SOURCE ORTHOGONALITY!
ocots Jan 27, 2026
5178b9b
✅ feat: Complete Phase 3 - Final test structure optimization
ocots Jan 27, 2026
e223b76
docs: replace Mermaid with ASCII art for U-shape pipeline in DOCP audit
ocots Jan 27, 2026
469a6f8
docs: add ER diagram to visualize DOCP structure
ocots Jan 27, 2026
b305960
docs: fix ER diagram relationships and add missing builder closures
ocots Jan 27, 2026
1131e27
docs: add complete ER diagrams for all 5 alternative architectures (A-E)
ocots Jan 27, 2026
03a1a15
feat: Complete test orthogonality and fix final test error
ocots Jan 27, 2026
6baba5e
docs: refine Alternative B with Strategies.id automatic lookup
ocots Jan 27, 2026
6e556b7
docs: expand Long-term Vision into complete Alternative F synthesis
ocots Jan 27, 2026
c485745
docs: detail builder signature handling (ADNLPModeler vs ExaModeler)
ocots Jan 27, 2026
845803d
fix: Mermaid sequence diagram parse error (remove ... in labels)
ocots Jan 27, 2026
5f1c9ae
cleaning
ocots Jan 28, 2026
f5d244f
rename
ocots Jan 28, 2026
cab5953
fix: Replace FieldError with ErrorException for Julia 1.10 compatibility
ocots Jan 28, 2026
b766e71
fix: Use Exception base type for Julia 1.10-1.12 compatibility
ocots Jan 28, 2026
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
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ test/solution.jld2
test/solution.json

#
#reports/
profiling/
tmp/
.agent/
reports/
#reports/
26 changes: 18 additions & 8 deletions ext/CTModelsJSON.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ using DocStringExtensions

using JSON3

# ============================================================================
# Private helper: broadcast with Nothing fallback
# ============================================================================

"""
Apply a function over a grid (broadcast), or return nothing if input is nothing.
"""
_apply_over_grid(f::Function, grid) = f.(grid)
_apply_over_grid(::Nothing, grid) = nothing

# ============================================================================
# Helper functions for serializing/deserializing infos Dict{Symbol,Any}
# ============================================================================
Expand Down Expand Up @@ -112,26 +122,26 @@ function CTModels.export_ocp_solution(

blob = Dict(
"time_grid" => CTModels.time_grid(sol),
"state" => CTModels.discretize(CTModels.state(sol), T),
"control" => CTModels.discretize(CTModels.control(sol), T),
"state" => _apply_over_grid(CTModels.state(sol), T),
"control" => _apply_over_grid(CTModels.control(sol), T),
"variable" => CTModels.variable(sol),
"costate" => CTModels.discretize(CTModels.costate(sol), T),
"costate" => _apply_over_grid(CTModels.costate(sol), T),
"objective" => CTModels.objective(sol),
"iterations" => CTModels.iterations(sol),
"constraints_violation" => CTModels.constraints_violation(sol),
"message" => CTModels.message(sol),
"status" => CTModels.status(sol),
"successful" => CTModels.successful(sol),
"path_constraints_dual" =>
CTModels.discretize(CTModels.path_constraints_dual(sol), T),
_apply_over_grid(CTModels.path_constraints_dual(sol), T),
"state_constraints_lb_dual" =>
CTModels.discretize(CTModels.state_constraints_lb_dual(sol), T),
_apply_over_grid(CTModels.state_constraints_lb_dual(sol), T),
"state_constraints_ub_dual" =>
CTModels.discretize(CTModels.state_constraints_ub_dual(sol), T),
_apply_over_grid(CTModels.state_constraints_ub_dual(sol), T),
"control_constraints_lb_dual" =>
CTModels.discretize(CTModels.control_constraints_lb_dual(sol), T),
_apply_over_grid(CTModels.control_constraints_lb_dual(sol), T),
"control_constraints_ub_dual" =>
CTModels.discretize(CTModels.control_constraints_ub_dual(sol), T),
_apply_over_grid(CTModels.control_constraints_ub_dual(sol), T),
"boundary_constraints_dual" => CTModels.boundary_constraints_dual(sol), # ctVector or Nothing
"variable_constraints_lb_dual" => CTModels.variable_constraints_lb_dual(sol), # ctVector or Nothing
"variable_constraints_ub_dual" => CTModels.variable_constraints_ub_dual(sol), # ctVector or Nothing
Expand Down
2 changes: 1 addition & 1 deletion ext/CTModelsPlots.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module CTModelsPlots

#
using DocStringExtensions
using MLStyle # pattern matching
using MLStyle: MLStyle

#
using CTBase
Expand Down
18 changes: 9 additions & 9 deletions ext/plot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ function __plot_time!(
)

# t_label depends if time is normalize or not
t_label = @match time begin
t_label = MLStyle.@match time begin
:default => t_label
:normalize => t_label == "" ? "" : t_label * " (normalized)"
:normalise => t_label == "" ? "" : t_label * " (normalised)"
Expand Down Expand Up @@ -217,7 +217,7 @@ function __plot_tree(node::PlotNode, depth::Int=0; kwargs...)
end
#
kwargs_plot = depth == 0 ? kwargs : ()
ps = @match node.layout begin
ps = MLStyle.@match node.layout begin
:row => plot(subplots...; layout=(1, size(subplots, 1)), kwargs_plot...)
:column =>
plot(subplots...; layout=(size(subplots, 1), 1), leftmargin=3mm, kwargs_plot...)
Expand Down Expand Up @@ -274,7 +274,7 @@ function __initial_plot(

if layout == :group
plots = Vector{Plots.Plot}()
@match control begin
MLStyle.@match control begin
:components => begin
do_plot_state && push!(plots, Plots.plot()) # state
do_plot_costate && push!(plots, Plots.plot()) # costate
Expand Down Expand Up @@ -327,7 +327,7 @@ function __initial_plot(
# create the control plots
if do_plot_control
l = m
@match control begin
MLStyle.@match control begin
:components => begin
for i in 1:m
push!(control_plots, PlotLeaf())
Expand Down Expand Up @@ -577,7 +577,7 @@ function __plot!(

# control
if do_plot_control
@match control begin
MLStyle.@match control begin
:components => begin
__plot_time!(
p[icur],
Expand Down Expand Up @@ -758,7 +758,7 @@ function __plot!(

# control trajectory
l = m
@match control begin
MLStyle.@match control begin
:components => begin
for i in 1:m
title = i==1 ? "control" : ""
Expand Down Expand Up @@ -1422,16 +1422,16 @@ function __get_data_plot(
throw(CTBase.IncorrectArgument("The time grid is empty"))
end

vv, ii = @match xx begin
vv, ii = MLStyle.@match xx begin
::Symbol => (xx, 1)
_ => xx
end

T = CTModels.time_grid(sol)
m = size(T, 1)
return @match vv begin
return MLStyle.@match vv begin
:time => begin
@match time begin
MLStyle.@match time begin
:default => T
:normalize => (T .- T[1]) ./ (T[end] - T[1])
:normalise => (T .- T[1]) ./ (T[end] - T[1])
Expand Down
2 changes: 1 addition & 1 deletion ext/plot_default.jl
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ function __size_plot(
else
n = CTModels.state_dimension(sol)
m = CTModels.control_dimension(sol)
l = @match control begin
l = MLStyle.@match control begin
:components => m
:norm => 1
:all => m + 1
Expand Down
169 changes: 169 additions & 0 deletions reports/2026-01-22_tools/2026-01-23_tools_planning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# Tools Architecture Enhancement Planning

**Issue**: N/A
**Date**: 2026-01-23
**Status**: Planning Complete ✅

## TL;DR

Refactor the current `AbstractOCPTool` and generic options schema into a clean, 3-module architecture: **Options** (generic tools), **Strategies** (strategy management), and **Orchestration** (routing and dispatch). This will eliminate global mutable state, improve testability, and provide a clear contract for future extensions in the Control-Toolbox ecosystem.

---

## 1. Overview

### Goal

Replace the legacy `AbstractOCPTool` system with a modern architecture that separates option handling, strategy management, and action orchestration.

### Key Features

- **Options Module**: Generic option value tracking with provenance, schema-based validation, and aliases.
- **Strategies Module**: Explicit registry for strategy families, builders from IDs/methods, and a formal `AbstractStrategy` contract.
- **Orchestration Module**: Intelligent routing of options (action-specific vs strategy-specific) and method-based dispatch.

### References

- [Reference Materials](file:///Users/ocots/Research/logiciels/dev/control-toolbox/CTModels.jl/reports/2026-01-22_tools/reference/README.md)
- [3-Module Architecture (Doc 13)](file:///Users/ocots/Research/logiciels/dev/control-toolbox/CTModels.jl/reports/2026-01-22_tools/reference/13_module_dependencies_architecture.md)
- [Registry Design (Doc 11)](file:///Users/ocots/Research/logiciels/dev/control-toolbox/CTModels.jl/reports/2026-01-22_tools/reference/11_explicit_registry_architecture.md)
- [Strategy Contract (Doc 08)](file:///Users/ocots/Research/logiciels/dev/control-toolbox/CTModels.jl/reports/2026-01-22_tools/reference/08_complete_contract_specification.md)
- [Reference Implementation (solve_ideal.jl)](file:///Users/ocots/Research/logiciels/dev/control-toolbox/CTModels.jl/reports/2026-01-22_tools/reference/solve_ideal.jl)

---

## 2. User Stories

| ID | Description | Status |
|----|-------------|--------|
| US-1 | As a developer, I want a clear contract for implementing new strategies. | ⏳ |
| US-2 | As an user, I want helpful error messages, suggestions, and **validators** (e.g., positive tolerance) for my options. | ⏳ |
| US-3 | As a maintainer, I want to avoid global mutable state for strategy registration. | ⏳ |
| US-4 | As a developer, I want to easily route options via **intensive simulation tests** (2 strategies, 2 labels, etc.). | ⏳ |

---

## 2.5. Design Principles Assessment

### SOLID Compliance

- ✅ **Single Responsibility**: Each module has one clear purpose (Options: tools, Strategies: registry, Orchestration: routing).
- ✅ **Open/Closed**: New strategies can be added by implementing the contract and registering them without modifying core modules.
- ✅ **Liskov Substitution**: All strategies inherit from `AbstractStrategy` and follow its contract.
- ✅ **Interface Segregation**: Minimal, focused interfaces for each module.
- ✅ **Dependency Inversion**: Dependencies flow from high-level (Orchestration) to low-level (Options).

### Quality Objectives (Priority: 1=Low, 5=Critical)

| Objective | Priority | Score | Measures |
|-----------|----------|-------|----------|
| Reusability | 5 | 5 | Generic Options module can be used beyond OCP. |
| Maintainability| 5 | 4 | Clear boundaries reduce coupling. |
| Performance | 3 | 4 | Registry lookups and option extraction are optimized. |
| Safety | 4 | 5 | Robust validation and helpful error messages. |

---

## 3. Technical Decisions

| Decision | Choice | Rationale |
|----------|--------|-----------|
| Registry | Explicit Registry | Avoids global state, better for testing and thread-safety. |
| Contract | `AbstractStrategy` | Formalizes the interface for all "tools". |
| Options | `OptionValue` | Tracks BOTH value and provenance. |
| Routing | Centralized in Orchestration| Decouples strategies from the knowledge of other strategies. |

---

## 4. Tasks

### Phase 1: Infrastructure (Options)

| Task | Description |
|------|-------------|
| 1.1 | Implement `Options` module with `OptionValue` and `OptionSchema`. |
| 1.2 | Implement `extract_option` and `extract_options` with alias support. |
| 1.3 | Add unit tests for `Options`. |

### Phase 2: Strategies

| Task | Description |
|------|-------------|
| 2.1 | Implement `Strategies` module with `AbstractStrategy` contract. |
| 2.2 | Implement `StrategyRegistry` and `create_registry`. |
| 2.3 | Implement strategy builders from IDs and methods. |
| 2.4 | Add unit tests for `Strategies`. |

### Phase 3: Orchestration

| Task | Description |
|------|-------------|
| 3.1 | Implement `Orchestration` module with `route_all_options`. |
| 3.2 | Implement method-based strategy builders. |
| 3.3 | Add unit tests for `Orchestration`. |

### Phase 4: NLP & Core Refactoring

| Task | Description |
|------|-------------|
| 4.1 | Update `ADNLPModeler` and `ExaModeler` to use the new contract. |
| 4.2 | Refactor `CTModels.jl` to include and export new modules. |
| 4.3 | Update existing integration tests. |

---

## 5. Testing Guidelines

### Test file structure

```julia
# test/Strategies/test_strategies.jl

# ============================================================
# Fake types for unit testing
# ============================================================
struct FakeStrategy <: CTModels.Strategies.AbstractStrategy
options::CTModels.Strategies.StrategyOptions
end

# Implement contract...
CTModels.Strategies.symbol(::Type{FakeStrategy}) = :fake

function test_strategies()
@testset "Strategies registry" begin
# ...
end
end
```

---

## 6. Test Commands

```bash
# Run CTModels tests
julia --project=. -e 'using Pkg; Pkg.test("CTModels");'
```

---

## 7. Coverage Testing

Target: **≥ 90% coverage** for the new code.

---

## 8. GitHub Workflow

### Checklist for Issue

- [ ] Phase 1: Options Module
- [ ] Phase 2: Strategies Module
- [ ] Phase 3: Orchestration Module
- [ ] Phase 4: Integration and Refactoring

---

## 9. MVP (Minimum Viable Product)

**MVP** = Phase 1 + Phase 2 + Phase 3 (Core infrastructure ready for use)
Loading
Loading