Skip to content

Commit e2233e2

Browse files
authored
Merge pull request #302 from control-toolbox/definition
refactor: make definition optional with EmptyDefinition sentinel
2 parents 81d5e69 + dc2afa4 commit e2233e2

18 files changed

Lines changed: 768 additions & 379 deletions

CHANGELOG.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2727
- **Display improvement**: Dual variables only displayed if model has declared constraints
2828
- **New exports**: `dim_dual_state_constraints_box`, `dim_dual_control_constraints_box`, `dim_dual_variable_constraints_box`
2929

30+
#### Optional Definition with EmptyDefinition Sentinel
31+
32+
- **Type hierarchy**: Introduced `AbstractDefinition` with concrete types `Definition(expr::Expr)` and `EmptyDefinition` (sentinel)
33+
- **Optional definition**: `PreModel.definition` defaults to `EmptyDefinition()` instead of `nothing`
34+
- **Model parametric**: `Model` is now parametric on `DefinitionType<<:AbstractDefinition`
35+
- **Expression getter**: Added `expression()` function to extract `Expr` from `AbstractDefinition`
36+
- **Build relaxation**: Removed precondition requiring definition in `build()`
37+
- **Display refactor**: Split `Display/print.jl` into 5 focused files by responsibility
38+
- **Code organization**: Moved definition setters to `Components/definition.jl`, Model getters to `Building/model.jl`
39+
40+
#### API Enhancements
41+
42+
```julia
43+
# Definition is now optional
44+
pre = PreModel()
45+
pre.definition isa EmptyDefinition # true by default
46+
47+
# Set definition via setter (auto-wraps Expr)
48+
definition!(pre, quote
49+
t [0, 1], time
50+
x R, state
51+
u R, control
52+
(t) == u(t)
53+
(0.5u(t)^2) min
54+
end)
55+
56+
# Extract expression
57+
expr = expression(pre.definition) # Returns the Expr
58+
59+
# Build without definition is now valid
60+
model = build(pre) # Works even without definition
61+
```
62+
3063
#### Consistent Variable and Control Checking Functions
3164

3265
- **New functions**: Added `is_variable()` and `is_control_free()` for checking problem properties

src/Display/Display.jl

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ using MacroTools: MacroTools
3636
# Import types from parent module (will be available after CTModels loads this)
3737
# These are forward declarations - actual types defined in OCP module
3838
import ..OCP: Model, PreModel, Solution, AbstractSolution
39+
import ..OCP: AbstractDefinition, Definition, EmptyDefinition
3940

4041
# Import internal helpers from OCP for display
4142
import ..OCP: __is_empty, __is_definition_set, definition, __is_consistent
@@ -49,8 +50,12 @@ import ..OCP:
4950
dim_state_constraints_box, dim_control_constraints_box, dim_variable_constraints_box
5051
import ..OCP: build
5152

52-
# Include display functions
53-
include("print.jl")
53+
# Include display functions (split by responsibility)
54+
include("ansi.jl")
55+
include("definition.jl")
56+
include("mathematical.jl")
57+
include("model.jl")
58+
include("pre_model.jl")
5459

5560
# -----------------------------
5661
# RecipesBase.plot stub - to be extended by CTModelsPlots extension

src/Display/ansi.jl

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# ------------------------------------------------------------------------------ #
2+
# ANSI helpers and generic Expr printing
3+
# ------------------------------------------------------------------------------ #
4+
5+
"""
6+
Generate ANSI escape sequence for the specified color and formatting.
7+
"""
8+
function _ansi_color(color::Symbol, bold::Bool=false)
9+
color_codes = Dict(
10+
:black => 30,
11+
:red => 31,
12+
:green => 32,
13+
:yellow => 33,
14+
:blue => 34,
15+
:magenta => 35,
16+
:cyan => 36,
17+
:white => 37,
18+
:default => 39,
19+
)
20+
21+
code = get(color_codes, color, 39)
22+
return bold ? "\033[1;$(code)m" : "\033[$(code)m"
23+
end
24+
25+
"""Generate ANSI reset sequence to clear formatting."""
26+
_ansi_reset() = "\033[0m"
27+
28+
"""
29+
Print text with ANSI color formatting for Documenter compatibility.
30+
"""
31+
function _print_ansi_styled(
32+
io, text::Union{String,Symbol,Type}, color::Symbol, bold::Bool=false
33+
)
34+
print(io, _ansi_color(color, bold), string(text), _ansi_reset())
35+
end
36+
37+
"""
38+
$(TYPEDSIGNATURES)
39+
40+
Print an expression with indentation.
41+
42+
# Arguments
43+
44+
- `e::Expr`: The expression to print.
45+
- `io::IO`: The output stream.
46+
- `l::Int`: The indentation level (number of spaces).
47+
"""
48+
function __print(e::Expr, io::IO, l::Int)
49+
MLStyle.@match e begin
50+
:(($a, $b)) => println(io, " "^l, a, ", ", b)
51+
_ => println(io, " "^l, e)
52+
end
53+
end

src/Display/definition.jl

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# ------------------------------------------------------------------------------ #
2+
# Abstract (symbolic) definition printing
3+
# ------------------------------------------------------------------------------ #
4+
5+
"""
6+
$(TYPEDSIGNATURES)
7+
8+
Print an [`EmptyDefinition`](@ref): no output is produced.
9+
10+
Returns `false` to indicate that nothing was printed.
11+
"""
12+
_print_abstract_definition(::IO, ::EmptyDefinition)::Bool = false
13+
14+
"""
15+
$(TYPEDSIGNATURES)
16+
17+
Print a [`Definition`](@ref) under an "Abstract definition:" header.
18+
19+
Block expressions are unfolded line-by-line; other expression heads are
20+
printed as a single indented entry.
21+
22+
Returns `true` to indicate that output was produced.
23+
24+
# Arguments
25+
26+
- `io::IO`: The output stream.
27+
- `d::Definition`: The symbolic definition to display.
28+
"""
29+
function _print_abstract_definition(io::IO, d::Definition)::Bool
30+
_print_ansi_styled(io, "Abstract definition:\n\n", :default, true)
31+
tab = 4
32+
code = MacroTools.striplines(d.expr)
33+
MLStyle.@match code.head begin
34+
:block => [__print(code.args[i], io, tab) for i in eachindex(code.args)]
35+
_ => __print(code, io, tab)
36+
end
37+
return true
38+
end
39+
40+
"""
41+
$(TYPEDSIGNATURES)
42+
43+
Display method for any [`AbstractDefinition`](@ref).
44+
45+
Delegates to [`_print_abstract_definition`](@ref).
46+
"""
47+
Base.show(io::IO, ::MIME"text/plain", d::AbstractDefinition) =
48+
_print_abstract_definition(io, d)

0 commit comments

Comments
 (0)