Skip to content

Commit 9c082d9

Browse files
authored
Merge pull request #446 from control-toolbox/feature/integrate-utils-from-ctmodels
[WIP] Integrate utilities from CTModels.Utils into CTBase
2 parents 64155c4 + d598d0f commit 9c082d9

15 files changed

Lines changed: 692 additions & 5 deletions

docs/api_reference.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ function generate_api_reference(src_dir::String)
2222
joinpath("Core", "Core.jl"),
2323
joinpath("Core", "default.jl"),
2424
joinpath("Core", "types.jl"),
25+
joinpath("Core", "matrix_utils.jl"),
26+
joinpath("Core", "function_utils.jl"),
27+
joinpath("Core", "macros.jl"),
2528
),
2629
],
2730
exclude=EXCLUDE_SYMBOLS,
@@ -31,6 +34,23 @@ function generate_api_reference(src_dir::String)
3134
title_in_menu="Core",
3235
filename="core",
3336
),
37+
CTBase.automatic_reference_documentation(;
38+
subdirectory="api",
39+
primary_modules=[
40+
CTBase.Interpolation => src(
41+
joinpath("Interpolation", "Interpolation.jl"),
42+
joinpath("Interpolation", "types.jl"),
43+
joinpath("Interpolation", "ctinterpolate.jl"),
44+
joinpath("Interpolation", "display.jl"),
45+
),
46+
],
47+
exclude=EXCLUDE_SYMBOLS,
48+
public=true,
49+
private=true,
50+
title="Interpolation",
51+
title_in_menu="Interpolation",
52+
filename="interpolation",
53+
),
3454
CTBase.automatic_reference_documentation(;
3555
subdirectory="api",
3656
primary_modules=[

docs/src/index.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
# CTBase.jl — Ecosystem Foundation
22

3-
!!! tip "Ask DeepWiki"
4-
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/control-toolbox/CTBase.jl) offers an interactive, AI-generated overview of this codebase. Answers may be inaccurate — use this
5-
reference documentation as the source of truth.
6-
73
```@meta
84
CurrentModule = CTBase
95
```
@@ -20,6 +16,10 @@ It provides the **base layer** shared by all packages: common types, structured
2016
Downstream packages (e.g. [OptimalControl.jl](https://github.com/control-toolbox/OptimalControl.jl))
2117
may re-export selected symbols for convenience.
2218

19+
!!! tip "Ask DeepWiki"
20+
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/control-toolbox/CTBase.jl) offers an interactive, AI-generated overview of this codebase. Answers may be inaccurate — use this
21+
reference documentation as the source of truth.
22+
2323
## Submodule overview
2424

2525
| Submodule | Role |

src/CTBase.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,8 @@ using .Descriptions
3232
include(joinpath(@__DIR__, "Extensions", "Extensions.jl"))
3333
using .Extensions
3434

35+
# Interpolation module - interpolation utilities
36+
include(joinpath(@__DIR__, "Interpolation", "Interpolation.jl"))
37+
using .Interpolation
38+
3539
end

src/Core/Core.jl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,14 @@ import DocStringExtensions: TYPEDEF, TYPEDSIGNATURES
1313
include("types.jl")
1414
include("default.jl")
1515

16+
# Private utilities
17+
include("function_utils.jl")
18+
include("macros.jl")
19+
20+
# Public utilities
21+
include("matrix_utils.jl")
22+
1623
# Export public API
17-
export ctNumber
24+
export ctNumber, matrix2vec, to_out_of_place, @ensure
1825

1926
end # module

src/Core/function_utils.jl

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""
2+
$(TYPEDSIGNATURES)
3+
4+
Convert an in-place function `f!` to an out-of-place function `f`.
5+
6+
The resulting function `f` returns a vector of type `T` and length `n` by first allocating
7+
memory and then calling `f!` to fill it.
8+
9+
# Arguments
10+
- `f!`: An in-place function of the form `f!(result, args...)`.
11+
- `n`: The length of the output vector.
12+
- `T`: The element type of the output vector (default is `Float64`).
13+
14+
# Returns
15+
An out-of-place function `f(args...; kwargs...)` that returns the result as a vector or
16+
scalar, depending on `n`.
17+
18+
# Example
19+
```julia-repl
20+
julia> f!(r, x) = (r[1] = sin(x); r[2] = cos(x))
21+
julia> f = to_out_of_place(f!, 2)
22+
julia> f(π/4) # returns approximately [0.707, 0.707]
23+
```
24+
"""
25+
function to_out_of_place(f!, n; T=Float64)
26+
function f(args...; kwargs...)
27+
r = zeros(T, n)
28+
f!(r, args...; kwargs...)
29+
return n == 1 ? r[1] : r
30+
end
31+
return isnothing(f!) ? nothing : f
32+
end

src/Core/macros.jl

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""
2+
@ensure condition exception
3+
4+
Throws the provided `exception` if `condition` is false.
5+
6+
# Usage
7+
```julia-repl
8+
julia> @ensure true Exceptions.IncorrectArgument("This won't throw")
9+
julia> @ensure false Exceptions.IncorrectArgument("This will throw")
10+
ERROR: IncorrectArgument("This will throw")
11+
```
12+
13+
# Arguments
14+
- `condition`: A Boolean expression to test.
15+
- `exception`: An instance of an exception to throw if `condition` is false.
16+
17+
# Throws
18+
- The provided `exception` if the condition is not satisfied.
19+
"""
20+
macro ensure(cond, exc)
21+
return esc(:(
22+
if !($cond)
23+
throw($exc)
24+
end
25+
))
26+
end

src/Core/matrix_utils.jl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""
2+
$(TYPEDSIGNATURES)
3+
4+
Return the default dimension for matrix storage.
5+
"""
6+
__matrix_dimension_storage() = 1
7+
8+
"""
9+
$(TYPEDSIGNATURES)
10+
11+
Transform a matrix into a vector of vectors along the specified dimension.
12+
13+
Each row or column of the matrix `A` is extracted and stored as an individual vector,
14+
depending on `dim`.
15+
16+
# Arguments
17+
- `A`: A matrix of elements of type `<:ctNumber`.
18+
- `dim`: The dimension along which to split the matrix (`1` for rows, `2` for columns).
19+
Defaults to `1`.
20+
21+
# Returns
22+
A `Vector` of `Vector`s extracted from the rows or columns of `A`.
23+
24+
# Note
25+
This is useful when data needs to be represented as a sequence of state or control vectors
26+
in optimal control problems.
27+
28+
# Example
29+
```julia-repl
30+
julia> A = [1 2 3; 4 5 6]
31+
julia> matrix2vec(A, 1) # splits into rows: [[1, 2, 3], [4, 5, 6]]
32+
julia> matrix2vec(A, 2) # splits into columns: [[1, 4], [2, 5], [3, 6]]
33+
```
34+
"""
35+
function matrix2vec(
36+
A::Matrix{<:ctNumber}, dim::Int=__matrix_dimension_storage()
37+
)::Vector{<:Vector{<:ctNumber}}
38+
return dim == 1 ? [A[i, :] for i in 1:size(A, 1)] : [A[:, i] for i in 1:size(A, 2)]
39+
end

src/Interpolation/Interpolation.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"""
2+
Interpolation
3+
4+
Interpolation utilities for the Control Toolbox (CT) ecosystem.
5+
6+
# Public API
7+
- `ctinterpolate`: linear interpolation with flat extrapolation
8+
- `ctinterpolate_constant`: piecewise-constant (steppost) interpolation
9+
"""
10+
module Interpolation
11+
12+
import DocStringExtensions: TYPEDEF, TYPEDSIGNATURES
13+
14+
include(joinpath(@__DIR__, "types.jl"))
15+
include(joinpath(@__DIR__, "ctinterpolate.jl"))
16+
include(joinpath(@__DIR__, "display.jl"))
17+
18+
export ctinterpolate, ctinterpolate_constant
19+
export Interpolant, LinearInterpolant, ConstantInterpolant
20+
21+
end # module Interpolation

src/Interpolation/ctinterpolate.jl

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# =============================================================================
2+
# Call methods
3+
# =============================================================================
4+
5+
# Linear interpolation with flat extrapolation outside [x[1], x[end]].
6+
function (interp::Interpolant{Linear})(t)
7+
x, f = interp.x, interp.f
8+
if t < x[1]
9+
return f[1]
10+
elseif t >= x[end]
11+
return f[end]
12+
else
13+
i = searchsortedlast(x, t)
14+
i == length(x) && return f[end]
15+
α = (t - x[i]) / (x[i + 1] - x[i])
16+
return f[i] + α * (f[i + 1] - f[i])
17+
end
18+
end
19+
20+
# Right-continuous piecewise-constant (steppost) interpolation.
21+
function (interp::Interpolant{Constant})(t)
22+
x, f = interp.x, interp.f
23+
if t < x[1]
24+
return f[1]
25+
elseif t >= x[end]
26+
return f[end]
27+
else
28+
return f[searchsortedlast(x, t)]
29+
end
30+
end
31+
32+
# =============================================================================
33+
# Factories (public API)
34+
# =============================================================================
35+
36+
"""
37+
$(TYPEDSIGNATURES)
38+
39+
Return a linear interpolation function for the data `f` defined at points `x`.
40+
41+
This creates a one-dimensional linear [`Interpolant`](@ref) with flat extrapolation beyond
42+
the bounds of `x` (returns `f[1]` for `t < x[1]` and `f[end]` for `t >= x[end]`).
43+
44+
# Arguments
45+
- `x`: A vector of points at which the values `f` are defined.
46+
- `f`: A vector of values to interpolate.
47+
48+
# Returns
49+
A callable [`LinearInterpolant`](@ref) that can be evaluated at new points.
50+
51+
# Example
52+
```julia-repl
53+
julia> x = [0.0, 1.0, 2.0]
54+
julia> f = [1.0, 2.0, 3.0]
55+
julia> interp = ctinterpolate(x, f)
56+
julia> interp(0.5) # Returns 1.5 (linear interpolation)
57+
julia> interp(-1.0) # Returns 1.0 (flat extrapolation)
58+
julia> interp(3.0) # Returns 3.0 (flat extrapolation)
59+
```
60+
"""
61+
ctinterpolate(x, f) = Interpolant{Linear}(x, f)
62+
63+
"""
64+
$(TYPEDSIGNATURES)
65+
66+
Return a piecewise-constant interpolation function for the data `f` defined at points `x`.
67+
68+
This creates a right-continuous piecewise-constant [`Interpolant`](@ref): the value at knot
69+
`x[i]` is held constant on the interval `[x[i], x[i+1})`.
70+
71+
This implements the standard steppost behavior for optimal control:
72+
- `u(t_i) = u_i` (value at the knot)
73+
- `u(t) = u_i` for all `t ∈ [t_i, t_{i+1})`
74+
- Right-continuous: `lim_{t→t_i^+} u(t) = u(t_i)`
75+
76+
# Arguments
77+
- `x`: A vector of points at which the values `f` are defined.
78+
- `f`: A vector of values to interpolate.
79+
80+
# Returns
81+
A callable [`ConstantInterpolant`](@ref) that can be evaluated at new points.
82+
83+
# Example
84+
```julia-repl
85+
julia> x = [0.0, 1.0, 2.0]
86+
julia> f = [1.0, 2.0, 3.0]
87+
julia> interp = ctinterpolate_constant(x, f)
88+
julia> interp(0.0) # Returns 1.0 (value at x[1])
89+
julia> interp(0.5) # Returns 1.0 (held from x[1] on [0.0, 1.0))
90+
julia> interp(1.0) # Returns 2.0 (value at x[2], right-continuous)
91+
julia> interp(1.5) # Returns 2.0 (held from x[2] on [1.0, 2.0))
92+
```
93+
"""
94+
ctinterpolate_constant(x, f) = Interpolant{Constant}(x, f)

src/Interpolation/display.jl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""
2+
$(TYPEDSIGNATURES)
3+
4+
Display a compact representation of an [`Interpolant`](@ref).
5+
6+
# Example
7+
```julia-repl
8+
julia> ctinterpolate([0.0, 1.0, 2.0], [0.0, 1.0, 0.0])
9+
Interpolant (linear): 3 nodes
10+
```
11+
"""
12+
function Base.show(io::IO, interp::Interpolant{M}) where {M}
13+
label = M === Linear ? "linear" : "piecewise-constant"
14+
print(io, "Interpolant ($label): $(length(interp.x)) nodes")
15+
end
16+
17+
"""
18+
$(TYPEDSIGNATURES)
19+
20+
Display an [`Interpolant`](@ref) in the REPL with the same format as `Base.show(io, interp)`.
21+
"""
22+
function Base.show(io::IO, ::MIME"text/plain", interp::Interpolant)
23+
return show(io, interp)
24+
end

0 commit comments

Comments
 (0)