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
20 changes: 20 additions & 0 deletions docs/api_reference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ function generate_api_reference(src_dir::String)
joinpath("Core", "Core.jl"),
joinpath("Core", "default.jl"),
joinpath("Core", "types.jl"),
joinpath("Core", "matrix_utils.jl"),
joinpath("Core", "function_utils.jl"),
joinpath("Core", "macros.jl"),
),
],
exclude=EXCLUDE_SYMBOLS,
Expand All @@ -31,6 +34,23 @@ function generate_api_reference(src_dir::String)
title_in_menu="Core",
filename="core",
),
CTBase.automatic_reference_documentation(;
subdirectory="api",
primary_modules=[
CTBase.Interpolation => src(
joinpath("Interpolation", "Interpolation.jl"),
joinpath("Interpolation", "types.jl"),
joinpath("Interpolation", "ctinterpolate.jl"),
joinpath("Interpolation", "display.jl"),
),
],
exclude=EXCLUDE_SYMBOLS,
public=true,
private=true,
title="Interpolation",
title_in_menu="Interpolation",
filename="interpolation",
),
CTBase.automatic_reference_documentation(;
subdirectory="api",
primary_modules=[
Expand Down
8 changes: 4 additions & 4 deletions docs/src/index.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
# CTBase.jl — Ecosystem Foundation

!!! tip "Ask DeepWiki"
[![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
reference documentation as the source of truth.

```@meta
CurrentModule = CTBase
```
Expand All @@ -20,6 +16,10 @@ It provides the **base layer** shared by all packages: common types, structured
Downstream packages (e.g. [OptimalControl.jl](https://github.com/control-toolbox/OptimalControl.jl))
may re-export selected symbols for convenience.

!!! tip "Ask DeepWiki"
[![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
reference documentation as the source of truth.

## Submodule overview

| Submodule | Role |
Expand Down
4 changes: 4 additions & 0 deletions src/CTBase.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,8 @@ using .Descriptions
include(joinpath(@__DIR__, "Extensions", "Extensions.jl"))
using .Extensions

# Interpolation module - interpolation utilities
include(joinpath(@__DIR__, "Interpolation", "Interpolation.jl"))
using .Interpolation

end
9 changes: 8 additions & 1 deletion src/Core/Core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ import DocStringExtensions: TYPEDEF, TYPEDSIGNATURES
include("types.jl")
include("default.jl")

# Private utilities
include("function_utils.jl")
include("macros.jl")

# Public utilities
include("matrix_utils.jl")

# Export public API
export ctNumber
export ctNumber, matrix2vec, to_out_of_place, @ensure

end # module
32 changes: 32 additions & 0 deletions src/Core/function_utils.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""
$(TYPEDSIGNATURES)

Convert an in-place function `f!` to an out-of-place function `f`.

The resulting function `f` returns a vector of type `T` and length `n` by first allocating
memory and then calling `f!` to fill it.

# Arguments
- `f!`: An in-place function of the form `f!(result, args...)`.
- `n`: The length of the output vector.
- `T`: The element type of the output vector (default is `Float64`).

# Returns
An out-of-place function `f(args...; kwargs...)` that returns the result as a vector or
scalar, depending on `n`.

# Example
```julia-repl
julia> f!(r, x) = (r[1] = sin(x); r[2] = cos(x))
julia> f = to_out_of_place(f!, 2)
julia> f(π/4) # returns approximately [0.707, 0.707]
```
"""
function to_out_of_place(f!, n; T=Float64)
function f(args...; kwargs...)
r = zeros(T, n)
f!(r, args...; kwargs...)
return n == 1 ? r[1] : r
end
return isnothing(f!) ? nothing : f
end
26 changes: 26 additions & 0 deletions src/Core/macros.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""
@ensure condition exception

Throws the provided `exception` if `condition` is false.

# Usage
```julia-repl
julia> @ensure true Exceptions.IncorrectArgument("This won't throw")
julia> @ensure false Exceptions.IncorrectArgument("This will throw")
ERROR: IncorrectArgument("This will throw")
```

# Arguments
- `condition`: A Boolean expression to test.
- `exception`: An instance of an exception to throw if `condition` is false.

# Throws
- The provided `exception` if the condition is not satisfied.
"""
macro ensure(cond, exc)
return esc(:(
if !($cond)
throw($exc)
end
))
end
39 changes: 39 additions & 0 deletions src/Core/matrix_utils.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""
$(TYPEDSIGNATURES)

Return the default dimension for matrix storage.
"""
__matrix_dimension_storage() = 1

"""
$(TYPEDSIGNATURES)

Transform a matrix into a vector of vectors along the specified dimension.

Each row or column of the matrix `A` is extracted and stored as an individual vector,
depending on `dim`.

# Arguments
- `A`: A matrix of elements of type `<:ctNumber`.
- `dim`: The dimension along which to split the matrix (`1` for rows, `2` for columns).
Defaults to `1`.

# Returns
A `Vector` of `Vector`s extracted from the rows or columns of `A`.

# Note
This is useful when data needs to be represented as a sequence of state or control vectors
in optimal control problems.

# Example
```julia-repl
julia> A = [1 2 3; 4 5 6]
julia> matrix2vec(A, 1) # splits into rows: [[1, 2, 3], [4, 5, 6]]
julia> matrix2vec(A, 2) # splits into columns: [[1, 4], [2, 5], [3, 6]]
```
"""
function matrix2vec(
A::Matrix{<:ctNumber}, dim::Int=__matrix_dimension_storage()
)::Vector{<:Vector{<:ctNumber}}
return dim == 1 ? [A[i, :] for i in 1:size(A, 1)] : [A[:, i] for i in 1:size(A, 2)]
end
21 changes: 21 additions & 0 deletions src/Interpolation/Interpolation.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""
Interpolation

Interpolation utilities for the Control Toolbox (CT) ecosystem.

# Public API
- `ctinterpolate`: linear interpolation with flat extrapolation
- `ctinterpolate_constant`: piecewise-constant (steppost) interpolation
"""
module Interpolation

import DocStringExtensions: TYPEDEF, TYPEDSIGNATURES

include(joinpath(@__DIR__, "types.jl"))
include(joinpath(@__DIR__, "ctinterpolate.jl"))
include(joinpath(@__DIR__, "display.jl"))

export ctinterpolate, ctinterpolate_constant
export Interpolant, LinearInterpolant, ConstantInterpolant

end # module Interpolation
94 changes: 94 additions & 0 deletions src/Interpolation/ctinterpolate.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# =============================================================================
# Call methods
# =============================================================================

# Linear interpolation with flat extrapolation outside [x[1], x[end]].
function (interp::Interpolant{Linear})(t)
x, f = interp.x, interp.f
if t < x[1]
return f[1]
elseif t >= x[end]
return f[end]
else
i = searchsortedlast(x, t)
i == length(x) && return f[end]
α = (t - x[i]) / (x[i + 1] - x[i])
return f[i] + α * (f[i + 1] - f[i])
end
end

# Right-continuous piecewise-constant (steppost) interpolation.
function (interp::Interpolant{Constant})(t)
x, f = interp.x, interp.f
if t < x[1]
return f[1]
elseif t >= x[end]
return f[end]
else
return f[searchsortedlast(x, t)]
end
end

# =============================================================================
# Factories (public API)
# =============================================================================

"""
$(TYPEDSIGNATURES)

Return a linear interpolation function for the data `f` defined at points `x`.

This creates a one-dimensional linear [`Interpolant`](@ref) with flat extrapolation beyond
the bounds of `x` (returns `f[1]` for `t < x[1]` and `f[end]` for `t >= x[end]`).

# Arguments
- `x`: A vector of points at which the values `f` are defined.
- `f`: A vector of values to interpolate.

# Returns
A callable [`LinearInterpolant`](@ref) that can be evaluated at new points.

# Example
```julia-repl
julia> x = [0.0, 1.0, 2.0]
julia> f = [1.0, 2.0, 3.0]
julia> interp = ctinterpolate(x, f)
julia> interp(0.5) # Returns 1.5 (linear interpolation)
julia> interp(-1.0) # Returns 1.0 (flat extrapolation)
julia> interp(3.0) # Returns 3.0 (flat extrapolation)
```
"""
ctinterpolate(x, f) = Interpolant{Linear}(x, f)

"""
$(TYPEDSIGNATURES)

Return a piecewise-constant interpolation function for the data `f` defined at points `x`.

This creates a right-continuous piecewise-constant [`Interpolant`](@ref): the value at knot
`x[i]` is held constant on the interval `[x[i], x[i+1})`.

This implements the standard steppost behavior for optimal control:
- `u(t_i) = u_i` (value at the knot)
- `u(t) = u_i` for all `t ∈ [t_i, t_{i+1})`
- Right-continuous: `lim_{t→t_i^+} u(t) = u(t_i)`

# Arguments
- `x`: A vector of points at which the values `f` are defined.
- `f`: A vector of values to interpolate.

# Returns
A callable [`ConstantInterpolant`](@ref) that can be evaluated at new points.

# Example
```julia-repl
julia> x = [0.0, 1.0, 2.0]
julia> f = [1.0, 2.0, 3.0]
julia> interp = ctinterpolate_constant(x, f)
julia> interp(0.0) # Returns 1.0 (value at x[1])
julia> interp(0.5) # Returns 1.0 (held from x[1] on [0.0, 1.0))
julia> interp(1.0) # Returns 2.0 (value at x[2], right-continuous)
julia> interp(1.5) # Returns 2.0 (held from x[2] on [1.0, 2.0))
```
"""
ctinterpolate_constant(x, f) = Interpolant{Constant}(x, f)
24 changes: 24 additions & 0 deletions src/Interpolation/display.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""
$(TYPEDSIGNATURES)

Display a compact representation of an [`Interpolant`](@ref).

# Example
```julia-repl
julia> ctinterpolate([0.0, 1.0, 2.0], [0.0, 1.0, 0.0])
Interpolant (linear): 3 nodes
```
"""
function Base.show(io::IO, interp::Interpolant{M}) where {M}
label = M === Linear ? "linear" : "piecewise-constant"
print(io, "Interpolant ($label): $(length(interp.x)) nodes")
end

"""
$(TYPEDSIGNATURES)

Display an [`Interpolant`](@ref) in the REPL with the same format as `Base.show(io, interp)`.
"""
function Base.show(io::IO, ::MIME"text/plain", interp::Interpolant)
return show(io, interp)
end
68 changes: 68 additions & 0 deletions src/Interpolation/types.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"""
$(TYPEDEF)

Abstract interpolation method (trait).

Concrete subtypes (`Linear`, `Constant`) are singleton trait values carried as a type
parameter of [`Interpolant`](@ref), so the call method is resolved statically.
"""
abstract type AbstractInterpolation end

"""
$(TYPEDEF)

Linear interpolation method (with flat extrapolation outside the node range).
"""
struct Linear <: AbstractInterpolation end

"""
$(TYPEDEF)

Piecewise-constant (right-continuous steppost) interpolation method.
"""
struct Constant <: AbstractInterpolation end

"""
$(TYPEDEF)

Callable interpolant of method `M` over nodes `x` with values `f`.

`Interpolant` subtypes `Function`: an instance `interp` is evaluated as `interp(t)`. The
method `M` (a subtype of [`AbstractInterpolation`](@ref)) is a compile-time trait parameter
that selects the evaluation rule, so the call is type-stable.

# Fields
- `x::TX`: nodes at which the values are defined.
- `f::TF`: values to interpolate.

# Construction
Build instances through the factories [`ctinterpolate`](@ref) (linear) and
[`ctinterpolate_constant`](@ref) (piecewise-constant), or directly via the aliases
[`LinearInterpolant`](@ref) / [`ConstantInterpolant`](@ref).
"""
struct Interpolant{M<:AbstractInterpolation,TX,TF} <: Function
x::TX
f::TF
end

# Outer constructor: infer TX, TF from the arguments.
function Interpolant{M}(x::TX, f::TF) where {M<:AbstractInterpolation,TX,TF}
return Interpolant{M,TX,TF}(x, f)
end

"""
Alias for a linear [`Interpolant`](@ref).
"""
const LinearInterpolant = Interpolant{Linear}

"""
Alias for a piecewise-constant [`Interpolant`](@ref).
"""
const ConstantInterpolant = Interpolant{Constant}

"""
$(TYPEDSIGNATURES)

Return the interpolation method (a subtype of [`AbstractInterpolation`](@ref)) of `interp`.
"""
method(::Interpolant{M}) where {M} = M
Loading