diff --git a/Project.toml b/Project.toml index ec82be17..6ce69e9c 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "CTBase" uuid = "54762871-cc72-4466-b8e8-f6c8b58076cd" -version = "0.22.0-beta" +version = "0.23.0-beta" authors = ["Olivier Cots ", "Jean-Baptiste Caillau "] [deps] diff --git a/docs/api_reference.jl b/docs/api_reference.jl index 8a4db600..b87cea63 100644 --- a/docs/api_reference.jl +++ b/docs/api_reference.jl @@ -24,6 +24,14 @@ function generate_api_reference(src_dir::String) joinpath("Core", "function_utils.jl"), joinpath("Core", "macros.jl"), joinpath("Core", "palette.jl"), joinpath("Core", "display.jl"), )), + (mod=CTBase.Data, title="Data", filename="data", files=src( + joinpath("Data", "Data.jl"), joinpath("Data", "default.jl"), + joinpath("Data", "helpers.jl"), + joinpath("Data", "abstract_vector_field.jl"), joinpath("Data", "vector_field.jl"), + joinpath("Data", "abstract_hamiltonian.jl"), joinpath("Data", "hamiltonian.jl"), + joinpath("Data", "abstract_hamiltonian_vector_field.jl"), + joinpath("Data", "hamiltonian_vector_field.jl"), + )), (mod=CTBase.Descriptions, title="Descriptions", filename="descriptions", files=src( joinpath("Descriptions", "Descriptions.jl"), joinpath("Descriptions", "types.jl"), joinpath("Descriptions", "similarity.jl"), joinpath("Descriptions", "display.jl"), @@ -42,13 +50,6 @@ function generate_api_reference(src_dir::String) joinpath("Interpolation", "Interpolation.jl"), joinpath("Interpolation", "types.jl"), joinpath("Interpolation", "ctinterpolate.jl"), joinpath("Interpolation", "display.jl"), )), - (mod=CTBase.Traits, title="Traits", filename="traits", files=src( - joinpath("Traits", "Traits.jl"), joinpath("Traits", "helpers.jl"), - joinpath("Traits", "abstract.jl"), joinpath("Traits", "mode.jl"), - joinpath("Traits", "dynamics.jl"), joinpath("Traits", "ad.jl"), - joinpath("Traits", "variable_costate.jl"), joinpath("Traits", "mutability.jl"), - joinpath("Traits", "time_dependence.jl"), joinpath("Traits", "variable_dependence.jl"), - )), (mod=CTBase.Options, title="Options", filename="options", files=src( joinpath("Options", "Options.jl"), joinpath("Options", "not_provided.jl"), joinpath("Options", "option_value.jl"), joinpath("Options", "option_definition.jl"), @@ -77,6 +78,13 @@ function generate_api_reference(src_dir::String) joinpath("Strategies", "api", "validation_helpers.jl"), joinpath("Strategies", "api", "disambiguation.jl"), )), + (mod=CTBase.Traits, title="Traits", filename="traits", files=src( + joinpath("Traits", "Traits.jl"), joinpath("Traits", "helpers.jl"), + joinpath("Traits", "abstract.jl"), joinpath("Traits", "mode.jl"), + joinpath("Traits", "dynamics.jl"), joinpath("Traits", "ad.jl"), + joinpath("Traits", "variable_costate.jl"), joinpath("Traits", "mutability.jl"), + joinpath("Traits", "time_dependence.jl"), joinpath("Traits", "variable_dependence.jl"), + )), (mod=CTBase.Unicode, title="Unicode", filename="unicode", files=src( joinpath("Unicode", "Unicode.jl"), joinpath("Unicode", "subscripts.jl"), joinpath("Unicode", "superscripts.jl"), diff --git a/docs/make.jl b/docs/make.jl index 9ef1b8d6..3817c9b0 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -70,9 +70,10 @@ with_api_reference(src_dir) do api_pages pages=[ "Getting Started" => "getting-started.md", "Core Concepts" => [ - "Descriptions" => joinpath("guide", "descriptions.md"), "Exceptions" => joinpath("guide", "exceptions.md"), "Traits" => joinpath("guide", "traits.md"), + "Data" => joinpath("guide", "data.md"), + "Descriptions" => joinpath("guide", "descriptions.md"), ], "Strategies & Options" => [ "Options System" => joinpath("guide", "options-system.md"), diff --git a/docs/src/getting-started.md b/docs/src/getting-started.md index 029e4536..7199f1ba 100644 --- a/docs/src/getting-started.md +++ b/docs/src/getting-started.md @@ -1,9 +1,9 @@ +# Getting Started + ```@meta CurrentModule = CTBase ``` -# Getting Started - ## Installation CTBase.jl is typically installed as a dependency of another package in the ecosystem @@ -31,7 +31,7 @@ Three things to keep in mind: CTBase.Exceptions.NotImplemented ``` 2. **Submodule-first API.** The public API lives in named submodules - (`Core`, `Descriptions`, `Exceptions`, `Traits`, `DevTools`, `Unicode`, …). + (`Core`, `Descriptions`, `Exceptions`, `Traits`, `Data`, `DevTools`, `Unicode`, …). You can bring a submodule's exports into scope explicitly: ```julia using CTBase.Exceptions # brings IncorrectArgument, NotImplemented, … into scope @@ -106,13 +106,34 @@ end # hide For more, see the **[Exceptions guide](guide/exceptions.md)**. +### Working with Data + +The [`CTBase.Data`](@ref) submodule wraps a Julia function together with its +**trait metadata** (time, variable, and mutability dependence). The wrapper picks +the right call path at compile time, and the traits can be recovered from the type. + +```@repl walkthrough +# Autonomous, fixed, out-of-place vector field: X(x) +vf = CTBase.Data.VectorField(x -> -x) + +CTBase.Traits.time_dependence(vf) # Autonomous +CTBase.Traits.mutability(vf) # OutOfPlace (auto-detected from arity) +CTBase.Traits.dynamics_trait(vf) # StateDynamics + +vf([1.0, 2.0]) # natural call +vf(0.0, [1.0, 2.0], nothing) # uniform call (ignores t and v) +``` + +For more, see the **[Data guide](guide/data.md)**. + ## Next Steps | Topic | Guide | | :--- | :--- | -| Descriptions catalogue and completion | [Descriptions](guide/descriptions.md) | | Exception hierarchy and best practices | [Exceptions](guide/exceptions.md) | | Compile-time traits and dispatch | [Traits](guide/traits.md) | +| Trait-carrying vector fields and Hamiltonians | [Data](guide/data.md) | +| Descriptions catalogue and completion | [Descriptions](guide/descriptions.md) | | Modular test runner setup | [Test Runner](guide/test-runner.md) | | Coverage report generation | [Coverage](guide/coverage.md) | | Auto-generated API reference | [API Documentation](guide/api-documentation.md) | diff --git a/docs/src/guide/data.md b/docs/src/guide/data.md new file mode 100644 index 00000000..72d65a0b --- /dev/null +++ b/docs/src/guide/data.md @@ -0,0 +1,265 @@ +# Data: typed function wrappers + +```@meta +CurrentModule = CTBase +``` + +The [`CTBase.Data`](@ref CTBase.Data) submodule provides typed wrappers that carry +a Julia function together with its **trait metadata** (see the [Traits](traits.md) +guide). Each wrapper knows, at the type level, whether it depends on time, whether +it depends on an extra variable, and whether it is evaluated in-place. + +```@setup data +using CTBase +using CTBase.Data +using CTBase.Traits +using LinearAlgebra +``` + +## Overview + +| Type | Mathematical object | Natural signature (autonomous/fixed) | +|---|---|---| +| [`Data.VectorField`](@ref CTBase.Data.VectorField) | ``X : \mathcal{X} \to \mathbb{R}^n`` | `X(x)` | +| [`Data.Hamiltonian`](@ref CTBase.Data.Hamiltonian) | ``H : T^*\mathcal{X} \to \mathbb{R}`` | `H(x, p)` | +| [`Data.HamiltonianVectorField`](@ref CTBase.Data.HamiltonianVectorField) | ``\vec{H} : T^*\mathcal{X} \to \mathbb{R}^{2n}`` | `HVF(x, p)` | + +All three share the same trait axes — time dependence, variable dependence and +mutability. See [Traits](traits.md) for the full call-signature tables. + +--- + +## VectorField + +A `VectorField` wraps a Julia function representing ``\dot{x} = X(\cdots)``. + +### Construction + +```@example data +using CTBase.Data, CTBase.Traits + +# Autonomous, fixed (default): X(x) +vf1 = Data.VectorField(x -> -x) + +# Non-autonomous, fixed: X(t, x) +vf2 = Data.VectorField((t, x) -> t .* x; is_autonomous=false) + +# Autonomous, non-fixed: X(x, v) +vf3 = Data.VectorField((x, v) -> x .* v; is_variable=true) + +# Non-autonomous, non-fixed: X(t, x, v) +vf4 = Data.VectorField((t, x, v) -> t .* x .+ v; is_autonomous=false, is_variable=true) + +vf1 +``` + +The `show` output summarises the traits and both call signatures (natural and +uniform). + +### In-place construction + +Prefer out-of-place for clarity. Use in-place when avoiding allocations matters: + +```@example data +# In-place Autonomous/Fixed: f(dx, x) — mutability auto-detected +vf_ip = Data.VectorField((dx, x) -> (dx .= -x; nothing)) +Traits.mutability(vf_ip) +``` + +If the function has multiple methods, auto-detection fails; pass `is_inplace` +explicitly: + +```@example data +f_multi(x) = -x +f_multi(dx, x) = (dx .= -x; nothing) + +vf_explicit = Data.VectorField(f_multi; is_inplace=true) +``` + +### Calling + +```@example data +x0 = [1.0, 0.5] + +vf1(x0) # natural call +vf1(0.0, x0, nothing) # uniform call (ignores t and v) +vf2(0.5, x0) # non-autonomous natural call +``` + +--- + +## Hamiltonian + +A `Hamiltonian` wraps a scalar function ``H(x, p) \in \mathbb{R}``. + +```math +H : T^*\mathcal{X} \to \mathbb{R}, \quad (x, p) \mapsto H(x, p). +``` + +### Construction + +```@example data +# Autonomous, fixed (default): H(x, p) +h1 = Data.Hamiltonian((x, p) -> dot(p, x)) + +# Non-autonomous, fixed: H(t, x, p) +h2 = Data.Hamiltonian((t, x, p) -> t * dot(p, x); is_autonomous=false) + +# Autonomous, non-fixed: H(x, p, v) +h3 = Data.Hamiltonian((x, p, v) -> dot(p, x) * v[1]; is_variable=true) + +h1 +``` + +### Calling + +```@example data +x0, p0 = [1.0, 0.5], [0.3, 0.7] + +h1(x0, p0) # natural call +h1(0.0, x0, p0, nothing) # uniform call +h2(0.5, x0, p0) # non-autonomous +``` + +--- + +## HamiltonianVectorField + +A `HamiltonianVectorField` wraps the map +``(x, p) \mapsto (\dot{x}, \dot{p}) = (\partial_p H, -\partial_x H)`` +when the derivatives are provided **explicitly** (no automatic differentiation). + +```math +\vec{H}(x, p) = \bigl(\partial_p H(x,p),\; -\partial_x H(x,p)\bigr). +``` + +Use this when the Hamiltonian equations are known analytically. When only the +scalar Hamiltonian is available and the derivatives must be obtained by automatic +differentiation, use [`Data.Hamiltonian`](@ref CTBase.Data.Hamiltonian) instead. + +### Construction + +```@example data +# Harmonic oscillator H = (x²+p²)/2: ẋ = p, ṗ = -x +hvf = Data.HamiltonianVectorField((x, p) -> (p, -x)) + +# With time dependence +hvf_na = Data.HamiltonianVectorField((t, x, p) -> (p, -x .* t); is_autonomous=false) + +hvf +``` + +### Calling + +```@example data +dx, dp = hvf(x0, p0) # natural call: returns (ẋ, ṗ) +(dx, dp) +``` + +### The `variable_costate` keyword + +For **non-fixed** Hamiltonian vector fields, the call accepts an optional +`variable_costate` keyword (default `false`). + +When `false` (default), the out-of-place call returns `(dx, dp)`; when `true`, +it returns the extended tuple `(dx, dp, dpv)` where `dpv = ∂ṗ/∂v` is the +derivative of the costate equations with respect to the variable `v`. For +in-place fields, `dpv` is passed as an optional pre-allocated buffer +(`dpv=nothing` skips the computation): + +```@example data +# Inner function that implements the variable_costate path. +# H(x, p, v) = v[1] * dot(p, x) → ẋ = v[1]*x, ṗ = -v[1]*p, ∂ṗ/∂v[1] = -p +function f_vc(x, p, v; variable_costate=false) + dx = v[1] .* x + dp = -v[1] .* p + variable_costate ? (dx, dp, -p) : (dx, dp) +end + +hvf_nf = Data.HamiltonianVectorField(f_vc; is_variable=true) +v0 = [2.0] + +dx, dp = hvf_nf(x0, p0, v0) # returns (ẋ, ṗ) +(dx, dp) +``` + +```@example data +dx, dp, dpv = hvf_nf(x0, p0, v0; variable_costate=true) # returns (ẋ, ṗ, ∂ṗ/∂v) +(dx, dp, dpv) +``` + +For in-place non-fixed fields the signature is: + +```julia +dpv = zeros(length(p0)) +hvf_ip_nf(dx, dp, x, p, v; dpv=dpv, variable_costate=true) # fills dx, dp, dpv in place +``` + +This is only supported by inner functions that implement the `variable_costate` +keyword path — typically those **derived from a +[`Data.Hamiltonian`](@ref CTBase.Data.Hamiltonian)** by automatic differentiation. +For a plain user-supplied function that does not accept `variable_costate`, passing +`true` raises a [`CTBase.Exceptions.PreconditionError`](@ref) (see the +[Exceptions](exceptions.md) guide). + +--- + +## Querying traits + +Because the trait metadata lives in the type, it can be recovered from any data +object through the [Traits](traits.md) accessors — with no runtime cost: + +```@example data +Traits.time_dependence(vf2) # NonAutonomous +``` + +```@example data +Traits.variable_dependence(vf3) # NonFixed +``` + +```@example data +Traits.mutability(vf_ip) # InPlace +``` + +The boolean predicates follow generically: + +```@example data +Traits.is_autonomous(vf1), Traits.is_variable(vf1), Traits.is_inplace(vf1) +``` + +Each data family also reports its dynamics trait via +[`Traits.dynamics_trait`](@ref CTBase.Traits.dynamics_trait): + +```@example data +Traits.dynamics_trait(vf1), Traits.dynamics_trait(h1), Traits.dynamics_trait(hvf) +``` + +A `VectorField` carries [`Traits.StateDynamics`](@ref CTBase.Traits.StateDynamics), +while both `Hamiltonian` and `HamiltonianVectorField` carry +[`Traits.HamiltonianDynamics`](@ref CTBase.Traits.HamiltonianDynamics). + +--- + +## Typed constructors + +A lower-level constructor takes the trait types directly (no keyword inference). +It is used internally by code that produces typed data objects programmatically: + +```@example data +vf_typed = Data.VectorField(x -> 2x, Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace) +Traits.time_dependence(vf_typed) +``` + +The same positional form exists for [`Data.Hamiltonian`](@ref CTBase.Data.Hamiltonian) +(`Hamiltonian(f, TD, VD)`) and +[`Data.HamiltonianVectorField`](@ref CTBase.Data.HamiltonianVectorField) +(`HamiltonianVectorField(f, TD, VD, MD)`). + +--- + +## See also + +- [`Data.VectorField`](@ref CTBase.Data.VectorField), [`Data.Hamiltonian`](@ref CTBase.Data.Hamiltonian), [`Data.HamiltonianVectorField`](@ref CTBase.Data.HamiltonianVectorField) — concrete data types. +- [`Data.AbstractVectorField`](@ref CTBase.Data.AbstractVectorField), [`Data.AbstractHamiltonian`](@ref CTBase.Data.AbstractHamiltonian), [`Data.AbstractHamiltonianVectorField`](@ref CTBase.Data.AbstractHamiltonianVectorField) — abstract supertypes. +- [Traits](traits.md) — the three trait axes, the dynamics trait, and call-signature tables. +- [Exceptions](exceptions.md) — `PreconditionError` and `IncorrectArgument`, raised by the constructors and the `variable_costate` path. diff --git a/docs/src/index.md b/docs/src/index.md index cca940d1..858ef2ef 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -5,7 +5,7 @@ CurrentModule = CTBase ``` CTBase.jl is the foundational package of the [control-toolbox](https://github.com/control-toolbox) ecosystem. -It provides the **base layer** shared by all packages: common types, structured exceptions, description management, extension infrastructure, and developer tools. +It provides the **base layer** shared by all packages: common types, structured exceptions, description management, compile-time traits, trait-carrying data wrappers, extension infrastructure, and developer tools. !!! note "Qualified access" CTBase exports **no symbols** at the package level. Every public symbol is accessed @@ -25,18 +25,20 @@ It provides the **base layer** shared by all packages: common types, structured | Submodule | Role | | :--- | :--- | | [`CTBase.Core`](@ref) | Fundamental numeric type alias (`ctNumber`) and internal display helpers | -| [`CTBase.Descriptions`](@ref) | Symbolic description tuples: catalogue management, pattern completion, similarity search | | [`CTBase.Exceptions`](@ref) | Typed exception hierarchy with rich context fields | | [`CTBase.Traits`](@ref) | Compile-time trait types for time dependence, variable dependence, mutability, and dynamics dispatch | +| [`CTBase.Data`](@ref) | Trait-carrying function wrappers: `VectorField`, `Hamiltonian`, `HamiltonianVectorField` | +| [`CTBase.Descriptions`](@ref) | Symbolic description tuples: catalogue management, pattern completion, similarity search | | [`CTBase.DevTools`](@ref) | Developer tools with tag-based dispatch for `run_tests`, `postprocess_coverage`, and `automatic_reference_documentation` | | [`CTBase.Unicode`](@ref) | Unicode subscript/superscript helpers for display | ## User Guides - **[Getting Started](getting-started.md)** — installation, mental model, 5-minute walkthrough. -- **[Descriptions](guide/descriptions.md)** — catalogue API, pattern matching, error handling. - **[Exceptions](guide/exceptions.md)** — exception hierarchy, choosing the right type, best practices. - **[Traits](guide/traits.md)** — compile-time trait types, the opt-in contract, and predicate functions. +- **[Data](guide/data.md)** — trait-carrying wrappers for vector fields and Hamiltonians. +- **[Descriptions](guide/descriptions.md)** — catalogue API, pattern matching, error handling. - **[Test Runner](guide/test-runner.md)** — modular test infrastructure with `CTBase.DevTools.run_tests`. - **[Coverage](guide/coverage.md)** — post-processing coverage artifacts with `CTBase.postprocess_coverage`. - **[API Documentation](guide/api-documentation.md)** — auto-generating per-module API pages. diff --git a/src/CTBase.jl b/src/CTBase.jl index 61e7e79a..8a31d0cd 100644 --- a/src/CTBase.jl +++ b/src/CTBase.jl @@ -36,6 +36,10 @@ using .Orchestration include(joinpath(@__DIR__, "Traits", "Traits.jl")) using .Traits +# Data module - vector fields and Hamiltonians with traits (moved from CTFlows) +include(joinpath(@__DIR__, "Data", "Data.jl")) +using .Data + # Unicode module - Unicode character utilities include(joinpath(@__DIR__, "Unicode", "Unicode.jl")) using .Unicode diff --git a/src/Data/Data.jl b/src/Data/Data.jl new file mode 100644 index 00000000..20c2ab5a --- /dev/null +++ b/src/Data/Data.jl @@ -0,0 +1,40 @@ +""" + Data + +Data structures including vector fields and Hamiltonian vector fields with traits. + +This module defines the `VectorField` and `HamiltonianVectorField` types which encapsulate +vector-field functions together with their time-dependence and variable-dependence traits. +""" +module Data + +# 1. External-package imports (qualified, pollution-free) +import DocStringExtensions: TYPEDEF, TYPEDSIGNATURES +import CTBase.Exceptions +import CTBase.Traits + +# ============================================================================== +# Include files +# ============================================================================== + +include(joinpath(@__DIR__, "default.jl")) +include(joinpath(@__DIR__, "helpers.jl")) +include(joinpath(@__DIR__, "abstract_vector_field.jl")) +include(joinpath(@__DIR__, "vector_field.jl")) +include(joinpath(@__DIR__, "abstract_hamiltonian.jl")) +include(joinpath(@__DIR__, "hamiltonian.jl")) +include(joinpath(@__DIR__, "abstract_hamiltonian_vector_field.jl")) +include(joinpath(@__DIR__, "hamiltonian_vector_field.jl")) + +# ============================================================================== +# Module exports +# ============================================================================== + +export AbstractVectorField +export VectorField +export HamiltonianVectorField +export AbstractHamiltonianVectorField +export AbstractHamiltonian +export Hamiltonian + +end # module Data diff --git a/src/Data/abstract_hamiltonian.jl b/src/Data/abstract_hamiltonian.jl new file mode 100644 index 00000000..1a29e357 --- /dev/null +++ b/src/Data/abstract_hamiltonian.jl @@ -0,0 +1,100 @@ +""" +$(TYPEDEF) + +Abstract supertype for scalar Hamiltonian functions together with their +time-dependence and variable-dependence traits. + +A Hamiltonian is a scalar function `H(t, x, p[, v]) → ℝ` from which a +Hamiltonian vector field can be derived via automatic differentiation. +Unlike vector fields, a Hamiltonian has no mutability trait (in-place vs +out-of-place) because a scalar return has no meaningful in-place form. + +# Type Parameters +- `TD <: TimeDependence`: `Autonomous` or `NonAutonomous`. +- `VD <: VariableDependence`: `Fixed` or `NonFixed`. + +# Notes +- All Hamiltonian types support both natural and uniform call signatures. +- The uniform signature `(t, x, p, v)` is used internally by systems. + +See also: [`CTBase.Data.Hamiltonian`](@ref), [`CTBase.Traits.TimeDependence`](@ref), [`CTBase.Traits.VariableDependence`](@ref). +""" +abstract type AbstractHamiltonian{ + TD <: Traits.TimeDependence, + VD <: Traits.VariableDependence +} end + +# ============================================================================= +# Trait accessors for AbstractHamiltonian +# ============================================================================= + +""" +$(TYPEDSIGNATURES) + +Indicates that all `AbstractHamiltonian` types support time-dependence queries. + +# Returns +- `true`: Always returns `true` for Hamiltonian types. + +See also: [`CTBase.Traits.time_dependence`](@ref), [`CTBase.Data.AbstractHamiltonian`](@ref). +""" +function Traits.has_time_dependence_trait(::AbstractHamiltonian) + return true +end + +""" +$(TYPEDSIGNATURES) + +Indicates that all `AbstractHamiltonian` types support variable-dependence queries. + +# Returns +- `true`: Always returns `true` for Hamiltonian types. + +See also: [`CTBase.Traits.variable_dependence`](@ref), [`CTBase.Data.AbstractHamiltonian`](@ref). +""" +function Traits.has_variable_dependence_trait(::AbstractHamiltonian) + return true +end + +""" +$(TYPEDSIGNATURES) + +Return the time-dependence trait of a Hamiltonian. + +# Arguments +- `h::AbstractHamiltonian`: The Hamiltonian object. + +# Returns +- `TD`: The time-dependence type (`Autonomous` or `NonAutonomous`). + +See also: [`CTBase.Traits.time_dependence`](@ref), [`CTBase.Traits.TimeDependence`](@ref). +""" +function Traits.time_dependence(::AbstractHamiltonian{TD, <:Traits.VariableDependence}) where {TD <: Traits.TimeDependence} + return TD +end + +""" +$(TYPEDSIGNATURES) + +Return the variable-dependence trait of a Hamiltonian. + +# Arguments +- `h::AbstractHamiltonian`: The Hamiltonian object. + +# Returns +- `VD`: The variable-dependence type (`Fixed` or `NonFixed`). + +See also: [`CTBase.Traits.variable_dependence`](@ref), [`CTBase.Traits.VariableDependence`](@ref). +""" +function Traits.variable_dependence(::AbstractHamiltonian{<:Traits.TimeDependence, VD}) where {VD <: Traits.VariableDependence} + return VD +end + +""" +$(TYPEDSIGNATURES) + +Return the dynamics trait of an `AbstractHamiltonian`, namely [`CTBase.Traits.HamiltonianDynamics`](@ref). + +See also: [`CTBase.Traits.dynamics_trait`](@ref), [`CTBase.Data.AbstractHamiltonian`](@ref). +""" +Traits.dynamics_trait(::AbstractHamiltonian) = Traits.HamiltonianDynamics diff --git a/src/Data/abstract_hamiltonian_vector_field.jl b/src/Data/abstract_hamiltonian_vector_field.jl new file mode 100644 index 00000000..83f54781 --- /dev/null +++ b/src/Data/abstract_hamiltonian_vector_field.jl @@ -0,0 +1,49 @@ +# ============================================================================= +# AbstractHamiltonianVectorField — Abstract type for Hamiltonian vector fields +# ============================================================================= + +""" +$(TYPEDEF) + +Abstract supertype for Hamiltonian vector fields. + +A Hamiltonian vector field represents the dynamics of a Hamiltonian system, +combining state and costate evolution according to Hamilton's equations. +It extends `AbstractVectorField` with the additional structure required +for Hamiltonian mechanics. + +# Type Parameters +- `TD <: TimeDependence`: Time dependence trait (Autonomous or NonAutonomous) +- `VD <: VariableDependence`: Variable dependence trait (Fixed or NonFixed) +- `MD <: AbstractMutabilityTrait`: Mutability trait (InPlace or OutOfPlace) + +# Interface Requirements + +All subtypes must implement: +- Call signature: `(t, x, p)` or `(x, p)` depending on time dependence +- Returns the combined state-costate derivative vector + +# Example +\`\`\`julia-repl +julia> using CTBase.Data + +julia> HamiltonianVectorField <: Data.AbstractHamiltonianVectorField +true +\`\`\` + +See also: [`CTBase.Data.AbstractVectorField`](@ref), [`CTBase.Data.HamiltonianVectorField`](@ref), [`CTBase.Data.Hamiltonian`](@ref). +""" +abstract type AbstractHamiltonianVectorField{ + TD <: Traits.TimeDependence, + VD <: Traits.VariableDependence, + MD <: Traits.AbstractMutabilityTrait +} <: AbstractVectorField{TD, VD, MD} end + +""" +$(TYPEDSIGNATURES) + +Return the dynamics trait of an `AbstractHamiltonianVectorField`, namely [`CTBase.Traits.HamiltonianDynamics`](@ref). + +See also: [`CTBase.Traits.dynamics_trait`](@ref), [`CTBase.Data.AbstractHamiltonianVectorField`](@ref). +""" +Traits.dynamics_trait(::AbstractHamiltonianVectorField) = Traits.HamiltonianDynamics diff --git a/src/Data/abstract_vector_field.jl b/src/Data/abstract_vector_field.jl new file mode 100644 index 00000000..a473ada1 --- /dev/null +++ b/src/Data/abstract_vector_field.jl @@ -0,0 +1,171 @@ +""" +$(TYPEDEF) + +Abstract type for all vector fields in the control-toolbox ecosystem. + +An `AbstractVectorField` represents a vector field function with time-dependence, +variable-dependence, and mutability traits encoded in the type parameters. + +# Contract + +All subtypes must have type parameters: +- `TD <: Traits.TimeDependence`: `Autonomous` or `NonAutonomous` +- `VD <: Traits.VariableDependence`: `Fixed` or `NonFixed` +- `MD <: Traits.AbstractMutabilityTrait`: `InPlace` or `OutOfPlace` + +Trait accessors are implemented at the abstract level and work for all subtypes. + +# Example + +\`\`\`julia +using CTBase.Data +using CTBase.Traits + +# Define a concrete vector field +struct MyVectorField{F, TD, VD, MD} <: AbstractVectorField{TD, VD, MD} + f::F +end + +# Trait accessors work automatically +vf = MyVectorField(x -> -x, Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace) +Traits.time_dependence(vf) # Returns Autonomous +Traits.variable_dependence(vf) # Returns Fixed +Traits.mutability(vf) # Returns OutOfPlace +\`\`\` + +See also: [`CTBase.Data.VectorField`](@ref), [`CTBase.Data.HamiltonianVectorField`](@ref), [`CTBase.Traits.time_dependence`](@ref), [`CTBase.Traits.variable_dependence`](@ref), [`CTBase.Traits.mutability`](@ref). +""" +abstract type AbstractVectorField{TD<:Traits.TimeDependence, VD<:Traits.VariableDependence, MD<:Traits.AbstractMutabilityTrait} end + +# ============================================================================= +# Trait accessors for AbstractVectorField +# ============================================================================= + +""" +$(TYPEDSIGNATURES) + +Indicate that `AbstractVectorField` has the time-dependence trait. + +This implementation declares that all vector fields support time-dependence queries. +Concrete `AbstractVectorField` instances have their time dependence encoded in the type parameter `TD`. + +See also: [`CTBase.Traits.time_dependence`](@ref), [`CTBase.Data.AbstractVectorField`](@ref). +""" +function Traits.has_time_dependence_trait(::AbstractVectorField) + return true +end + +""" +$(TYPEDSIGNATURES) + +Indicate that `AbstractVectorField` has the variable-dependence trait. + +This implementation declares that all vector fields support variable-dependence queries. +Concrete `AbstractVectorField` instances have their variable dependence encoded in the type parameter `VD`. + +See also: [`CTBase.Traits.variable_dependence`](@ref), [`CTBase.Data.AbstractVectorField`](@ref). +""" +function Traits.has_variable_dependence_trait(::AbstractVectorField) + return true +end + +""" +$(TYPEDSIGNATURES) + +Indicate that `AbstractVectorField` has the mutability trait. + +This implementation declares that all vector fields support mutability queries. +Concrete `AbstractVectorField` instances have their mutability encoded in the type parameter `MD`. + +See also: [`CTBase.Traits.mutability`](@ref), [`CTBase.Data.AbstractVectorField`](@ref). +""" +function Traits.has_mutability_trait(::AbstractVectorField) + return true +end + +""" +$(TYPEDSIGNATURES) + +Extract the time dependence trait from an AbstractVectorField. + +# Returns +- `Type{<:TimeDependence}`: The time dependence trait type (Autonomous or NonAutonomous). + +# Example +\`\`\`julia +using CTBase.Data +using CTBase.Traits + +vf = Data.VectorField(x -> -x; is_autonomous=true) +Traits.time_dependence(vf) # Returns Autonomous + +hvf = Data.HamiltonianVectorField((t, x, p) -> (x, -p); is_autonomous=false) +Traits.time_dependence(hvf) # Returns NonAutonomous +\`\`\` + +See also: [`CTBase.Traits.has_time_dependence_trait`](@ref), `is_autonomous`. +""" +function Traits.time_dependence(vf::AbstractVectorField{TD, <:Traits.VariableDependence, <:Traits.AbstractMutabilityTrait}) where {TD <: Traits.TimeDependence} + return TD +end + +""" +$(TYPEDSIGNATURES) + +Extract the variable dependence trait from an AbstractVectorField. + +# Returns +- `Type{<:VariableDependence}`: The variable dependence trait type (Fixed or NonFixed). + +# Example +\`\`\`julia +using CTBase.Data +using CTBase.Traits + +vf = Data.VectorField(x -> -x; is_variable=false) +Traits.variable_dependence(vf) # Returns Fixed + +hvf = Data.HamiltonianVectorField((x, p, v) -> (x .* v, -p); is_variable=true) +Traits.variable_dependence(hvf) # Returns NonFixed +\`\`\` + +See also: [`CTBase.Traits.has_variable_dependence_trait`](@ref), `is_variable`. +""" +function Traits.variable_dependence(vf::AbstractVectorField{<:Traits.TimeDependence, VD, <:Traits.AbstractMutabilityTrait}) where {VD <: Traits.VariableDependence} + return VD +end + +""" +$(TYPEDSIGNATURES) + +Extract the mutability trait from an AbstractVectorField. + +# Returns +- `Type{<:AbstractMutabilityTrait}`: The mutability trait type (InPlace or OutOfPlace). + +# Example +\`\`\`julia +using CTBase.Data +using CTBase.Traits + +vf = Data.VectorField((dx, x) -> (dx .= -x; nothing)) +Traits.mutability(vf) # Returns InPlace + +vf2 = Data.VectorField(x -> -x) +Traits.mutability(vf2) # Returns OutOfPlace +\`\`\` + +See also: [`CTBase.Traits.has_mutability_trait`](@ref), [`CTBase.Traits.is_inplace`](@ref). +""" +function Traits.mutability(vf::AbstractVectorField{<:Traits.TimeDependence, <:Traits.VariableDependence, MD}) where {MD <: Traits.AbstractMutabilityTrait} + return MD +end + +""" +$(TYPEDSIGNATURES) + +Return the dynamics trait of an `AbstractVectorField`, namely [`CTBase.Traits.StateDynamics`](@ref). + +See also: [`CTBase.Traits.dynamics_trait`](@ref), [`CTBase.Data.AbstractVectorField`](@ref). +""" +Traits.dynamics_trait(::AbstractVectorField) = Traits.StateDynamics diff --git a/src/Data/default.jl b/src/Data/default.jl new file mode 100644 index 00000000..0280f785 --- /dev/null +++ b/src/Data/default.jl @@ -0,0 +1,40 @@ +# ============================================================================= +# Default values for trait-carrying object constructors (VectorField, +# Hamiltonian, HamiltonianVectorField). Moved from CTFlows.Common so that +# CTBase.Data is self-contained. +# ============================================================================= + +""" +$(TYPEDSIGNATURES) + +Default value for the autonomous flag in time-dependent object constructors. + +Returns `true` by default, meaning objects do not explicitly depend on time +unless specified otherwise. +""" +__is_autonomous()::Bool = true + +""" +$(TYPEDSIGNATURES) + +Default value for the variable flag in time-dependent object constructors. + +Returns `false` by default, meaning objects have fixed parameters unless +specified otherwise. +""" +__is_variable()::Bool = false + +""" +$(TYPEDSIGNATURES) + +Default value for the in-place flag in vector field constructors. + +Returns `nothing` by default, meaning mutability is auto-detected from the +function signature unless specified otherwise. + +# Returns +- `Nothing`: The default value for the `is_inplace` parameter. + +See also: [`CTBase.Data.VectorField`](@ref), [`CTBase.Data.HamiltonianVectorField`](@ref). +""" +__is_inplace() = nothing diff --git a/src/Data/hamiltonian.jl b/src/Data/hamiltonian.jl new file mode 100644 index 00000000..aec42c28 --- /dev/null +++ b/src/Data/hamiltonian.jl @@ -0,0 +1,202 @@ +# ============================================================================= +# Concrete Hamiltonian type +# ============================================================================= + +""" +$(TYPEDEF) + +Parametric container for a scalar Hamiltonian function together with its +time-dependence and variable-dependence traits. + +The function returns a scalar value `H(t, x, p[, v]) → ℝ` representing the +Hamiltonian of the system. + +# Type Parameters +- `F`: concrete type of the wrapped function. +- `TD <: TimeDependence`: `Autonomous` or `NonAutonomous`. +- `VD <: VariableDependence`: `Fixed` or `NonFixed`. + +# Fields +- `f::F`: the Hamiltonian function. + +# Construction + +Use the keyword constructor: + +```julia +Hamiltonian(f; is_autonomous = true, is_variable = false) # default: h(x, p) +Hamiltonian((t, x, p) -> ...; is_autonomous = false) # h(t, x, p) +Hamiltonian((x, p, v) -> ...; is_variable = true) # h(x, p, v) +Hamiltonian((t, x, p, v) -> ...; is_autonomous = false, is_variable = true) +``` + +# Call Signatures + +Every `Hamiltonian` is callable via its **natural** signature (matching the +traits), and via a **uniform** signature `(t, x, p, v)` that ignores the +unused arguments. + +For Autonomous/Fixed: natural `h(x, p)`, uniform `h(t, x, p, v)`. +For NonAutonomous/Fixed: natural `h(t, x, p)`, uniform `h(t, x, p, v)`. +For Autonomous/NonFixed: natural `h(x, p, v)`, uniform `h(t, x, p, v)`. +For NonAutonomous/NonFixed: natural `h(t, x, p, v)`, uniform `h(t, x, p, v)`. + +# Example +```julia-repl +julia> using CTBase.Data + +julia> h = Hamiltonian((x, p) -> dot(x, p)) # Uses defaults: is_autonomous=true, is_variable=false +Hamiltonian: autonomous, fixed (no variable) + natural call: h(x, p) + uniform call: h(t, x, p, v) + +julia> h = Hamiltonian((t, x, p) -> t * dot(x, p); is_autonomous=false) +Hamiltonian: non-autonomous, fixed (no variable) + natural call: h(t, x, p) + uniform call: h(t, x, p, v) +``` + +See also: [`CTBase.Data.AbstractHamiltonian`](@ref), [`CTBase.Traits.TimeDependence`](@ref), [`CTBase.Traits.VariableDependence`](@ref). +""" +struct Hamiltonian{F<:Function, TD, VD} <: AbstractHamiltonian{TD, VD} + f::F +end + +# ============================================================================= +# Constructor +# ============================================================================= + +""" +$(TYPEDSIGNATURES) + +Construct a `Hamiltonian` with trait flags. + +# Arguments +- `f::Function`: The Hamiltonian function returning a scalar value. +- `is_autonomous::Bool`: If true, system is autonomous (default: `__is_autonomous()`). +- `is_variable::Bool`: If true, system depends on variable parameters (default: `__is_variable()`). + +# Returns +- `Hamiltonian`: A Hamiltonian with appropriate traits. + +# Example +```julia-repl +julia> using CTBase.Data + +julia> h = Hamiltonian((x, p) -> dot(x, p)) # Uses defaults: is_autonomous=true, is_variable=false +Hamiltonian: autonomous, fixed (no variable) + natural call: h(x, p) + uniform call: h(t, x, p, v) + +julia> h = Hamiltonian((t, x, p) -> t * dot(x, p); is_autonomous=false) +Hamiltonian: non-autonomous, fixed (no variable) + natural call: h(t, x, p) + uniform call: h(t, x, p, v) + +julia> h = Hamiltonian((x, p, v) -> v * dot(x, p); is_variable=true) +Hamiltonian: autonomous, non-fixed (variable) + natural call: h(x, p, v) + uniform call: h(t, x, p, v) +``` + +# Notes +- The default values for `is_autonomous` and `is_variable` come from `__is_autonomous()` and `__is_variable()`. +- The function signature should match the specified traits (e.g., if `is_autonomous=true` and `is_variable=false`, the function should accept `(x, p)`). + +See also: [`CTBase.Data.Hamiltonian`](@ref), [`CTBase.Traits.Autonomous`](@ref), [`CTBase.Traits.NonAutonomous`](@ref), [`CTBase.Traits.Fixed`](@ref), [`CTBase.Traits.NonFixed`](@ref). +""" +function Hamiltonian(f; + is_autonomous::Bool = __is_autonomous(), + is_variable::Bool = __is_variable() +) + TD = is_autonomous ? Traits.Autonomous : Traits.NonAutonomous + VD = is_variable ? Traits.NonFixed : Traits.Fixed + return Hamiltonian{typeof(f), TD, VD}(f) +end + +# ============================================================================= +# Typed constructor — calls struct inner constructor directly +# ============================================================================= + +function Hamiltonian( + f, + ::Type{TD}, ::Type{VD}, +) where { + TD <: Traits.TimeDependence, + VD <: Traits.VariableDependence, +} + return Hamiltonian{typeof(f), TD, VD}(f) +end + +# ============================================================================= +# Natural call signatures - one per trait combination +# ============================================================================= + +(H::Hamiltonian{<:Function, Traits.Autonomous, Traits.Fixed})(x, p) = H.f(x, p) +(H::Hamiltonian{<:Function, Traits.NonAutonomous, Traits.Fixed})(t, x, p) = H.f(t, x, p) +(H::Hamiltonian{<:Function, Traits.Autonomous, Traits.NonFixed})(x, p, v) = H.f(x, p, v) +(H::Hamiltonian{<:Function, Traits.NonAutonomous, Traits.NonFixed})(t, x, p, v) = H.f(t, x, p, v) + +# ============================================================================= +# Uniform (t, x, p, v) call - used by future HamiltonianSystem.rhs +# Every combination forwards to its natural call, ignoring unused args. +# (NonAutonomous, NonFixed) is already covered by the natural signature above. +# ============================================================================= + +(H::Hamiltonian{<:Function, Traits.Autonomous, Traits.Fixed})(_, x, p, _) = H.f(x, p) +(H::Hamiltonian{<:Function, Traits.NonAutonomous, Traits.Fixed})(t, x, p, _) = H.f(t, x, p) +(H::Hamiltonian{<:Function, Traits.Autonomous, Traits.NonFixed})(_, x, p, v) = H.f(x, p, v) + +# ============================================================================= +# Base.show +# ============================================================================= + +""" +$(TYPEDSIGNATURES) + +Display a compact representation of a Hamiltonian showing its traits and call signatures. + +# Arguments +- `io::IO`: The IO stream. +- `h::Hamiltonian`: The Hamiltonian object. + +# Output +Displays three lines: +- Header with time and variable dependence traits +- Natural call signature +- Uniform call signature + +# Example +```julia-repl +julia> h = Hamiltonian((x, p) -> dot(x, p)) +Hamiltonian: autonomous, fixed (no variable) + natural call: h(x, p) + uniform call: h(t, x, p, v) +``` +""" +function Base.show(io::IO, ::Hamiltonian{F, TD, VD}) where {F, TD, VD} + header = "Hamiltonian: $(_td_label(TD)), $(_vd_label(VD))" + natural = _natural_sig_h(TD, VD) + uniform = _uniform_sig_h() + println(io, header) + println(io, " natural call: ", natural) + print(io, " uniform call: ", uniform) +end + +""" +$(TYPEDSIGNATURES) + +Display a Hamiltonian in the REPL with the same format as the compact `show`. + +This method is called automatically when displaying a Hamiltonian in the Julia REPL. + +# Arguments +- `io::IO`: The IO stream. +- `mime::MIME"text/plain"`: The MIME type. +- `h::Hamiltonian`: The Hamiltonian object. + +See also: [`CTBase.Data.Hamiltonian`](@ref). +""" +function Base.show(io::IO, ::MIME"text/plain", h::Hamiltonian{F, TD, VD}) where {F, TD, VD} + show(io, h) +end diff --git a/src/Data/hamiltonian_vector_field.jl b/src/Data/hamiltonian_vector_field.jl new file mode 100644 index 00000000..b767d5b3 --- /dev/null +++ b/src/Data/hamiltonian_vector_field.jl @@ -0,0 +1,319 @@ +""" +$(TYPEDEF) + +Parametric container for a Hamiltonian vector field function together with its +time-dependence, variable-dependence, and mutability traits. + +The function returns a tuple `(dx, dp)` representing the derivatives of state `x` +and costate `p` according to Hamiltonian dynamics. + +# Type Parameters +- `F`: concrete type of the wrapped function. +- `TD <: TimeDependence`: `Autonomous` or `NonAutonomous`. +- `VD <: VariableDependence`: `Fixed` or `NonFixed`. +- `MD <: AbstractMutabilityTrait`: `InPlace` or `OutOfPlace`. + +# Fields +- `f::F`: the Hamiltonian vector field function. + +# Construction + +Use the keyword constructor: + +```julia +HamiltonianVectorField(f; is_autonomous = true, is_variable = false) # default: f(x, p) +HamiltonianVectorField((t, x, p) -> ...; is_autonomous = false) # f(t, x, p) +HamiltonianVectorField((x, p, v) -> ...; is_variable = true) # f(x, p, v) +HamiltonianVectorField((t, x, p, v) -> ...; is_autonomous = false, is_variable = true) +``` + +The mutability trait (InPlace/OutOfPlace) is auto-detected from the function signature. + +# Call Signatures + +Every `HamiltonianVectorField` is callable via its **natural** signature (matching the +traits), and via a **uniform** signature `(t, x, p, v)` that ignores the +unused arguments. + +For InPlace Hamiltonian vector fields, the natural signature includes the derivative +buffers as the first two arguments (e.g., `(dx, dp, x, p)` for Autonomous/Fixed). + +See also: [`CTBase.Data.AbstractVectorField`](@ref), [`CTBase.Traits.TimeDependence`](@ref), [`CTBase.Traits.VariableDependence`](@ref), [`CTBase.Traits.AbstractMutabilityTrait`](@ref). +""" +struct HamiltonianVectorField{F<:Function, TD<:Traits.TimeDependence, VD<:Traits.VariableDependence, MD<:Traits.AbstractMutabilityTrait} <: AbstractHamiltonianVectorField{TD, VD, MD} + f::F +end + +# ============================================================================= +# Internal helpers for mutability detection +# ============================================================================= + +""" + _oop_arity_hvf(::Type{Traits.Autonomous}, ::Type{Traits.Fixed}) -> Int + +Return the out-of-place arity for Autonomous/Fixed Hamiltonian vector fields (2: x, p). +""" +_oop_arity_hvf(::Type{Traits.Autonomous}, ::Type{Traits.Fixed}) = 2 + +""" + _oop_arity_hvf(::Type{Traits.NonAutonomous}, ::Type{Traits.Fixed}) -> Int + +Return the out-of-place arity for NonAutonomous/Fixed Hamiltonian vector fields (3: t, x, p). +""" +_oop_arity_hvf(::Type{Traits.NonAutonomous}, ::Type{Traits.Fixed}) = 3 + +""" + _oop_arity_hvf(::Type{Traits.Autonomous}, ::Type{Traits.NonFixed}) -> Int + +Return the out-of-place arity for Autonomous/NonFixed Hamiltonian vector fields (3: x, p, v). +""" +_oop_arity_hvf(::Type{Traits.Autonomous}, ::Type{Traits.NonFixed}) = 3 + +""" + _oop_arity_hvf(::Type{Traits.NonAutonomous}, ::Type{Traits.NonFixed}) -> Int + +Return the out-of-place arity for NonAutonomous/NonFixed Hamiltonian vector fields (4: t, x, p, v). +""" +_oop_arity_hvf(::Type{Traits.NonAutonomous}, ::Type{Traits.NonFixed}) = 4 + +""" + _detect_mutability_hvf(f::Function, TD, VD) -> Type{<:AbstractMutabilityTrait} + +Detect the mutability trait from the Hamiltonian vector field function signature. + +Compares the function arity to the expected out-of-place arity and in-place arity +(arity + 2 for HamiltonianVectorField, which has two output buffers). Returns `InPlace` or `OutOfPlace` accordingly. + +If the function has multiple methods, throws a `PreconditionError` indicating that +auto-detection is ambiguous and the user should specify `is_inplace` explicitly. + +# Arguments +- `f::Function`: The Hamiltonian vector-field function. +- `TD`: Time dependence trait type. +- `VD`: Variable dependence trait type. + +# Returns +- `Type{InPlace}` or `Type{OutOfPlace}`. + +# Throws +- `Exceptions.PreconditionError`: If the function has multiple methods, making automatic arity detection ambiguous. +- `Exceptions.IncorrectArgument`: If the arity is invalid (does not match expected out-of-place or in-place arity). + +# Notes +- This function is called automatically by the `HamiltonianVectorField` constructor when `is_inplace` is `nothing`. +- Users can bypass auto-detection by specifying `is_inplace=true` or `is_inplace=false` explicitly in the constructor. +- HamiltonianVectorField has two output buffers (dx, dp), so the in-place arity is `oop_arity + 2`. + +See also: [`CTBase.Data.HamiltonianVectorField`](@ref), [`CTBase.Traits.InPlace`](@ref), [`CTBase.Traits.OutOfPlace`](@ref). +""" +function _detect_mutability_hvf(f::Function, TD, VD) + method_count = length(methods(f)) + if method_count > 1 + throw(Exceptions.PreconditionError( + "Cannot auto-detect mutability: function has multiple methods"; + reason = "The function has $method_count methods, making automatic arity detection ambiguous", + suggestion = "Specify `is_inplace=true` or `is_inplace=false` explicitly in the constructor", + context = "HamiltonianVectorField mutability detection", + )) + end + + arity = first(methods(f)).nargs - 1 + oop_arity = _oop_arity_hvf(TD, VD) + ip_arity = oop_arity + 2 # HamiltonianVectorField has two output buffers (dx, dp) + + if arity == oop_arity + return Traits.OutOfPlace + elseif arity == ip_arity + return Traits.InPlace + else + throw(Exceptions.IncorrectArgument( + "Invalid function arity: expected $oop_arity (out-of-place) or $ip_arity (in-place), got $arity"; + suggestion = "Ensure your function signature matches the expected pattern for the given traits.", + context = "HamiltonianVectorField mutability detection", + )) + end +end + +""" +$(TYPEDSIGNATURES) + +Construct a `HamiltonianVectorField` with trait flags. + +# Arguments +- `f::Function`: The Hamiltonian vector field function returning `(dx, dp)`. +- `is_autonomous::Bool`: If true, system is autonomous (default: `__is_autonomous()`). +- `is_variable::Bool`: If true, system depends on variable parameters (default: `__is_variable()`). +- `is_inplace::Union{Bool, Nothing}`: If true, function is in-place; if false, function is out-of-place; if `nothing`, mutability is auto-detected from function signature (default: `__is_inplace()`). + +# Returns +- `HamiltonianVectorField`: A HamiltonianVectorField with appropriate traits. + +# Example +```julia-repl +julia> using CTBase.Data + +julia> hvf = HamiltonianVectorField((x, p) -> (x, -p)) # Uses defaults: is_autonomous=true, is_variable=false +HamiltonianVectorField: autonomous, fixed (no variable), out-of-place + natural call: f(x, p) + uniform call: f(t, x, p, v) + +julia> hvf = HamiltonianVectorField((t, x, p) -> (t .* x, -p); is_autonomous=false) +HamiltonianVectorField: non-autonomous, fixed (no variable), out-of-place + natural call: f(t, x, p) + uniform call: f(t, x, p, v) + +julia> hvf = HamiltonianVectorField((x, p) -> (x, -p); is_inplace=true) # Explicit in-place +HamiltonianVectorField: autonomous, fixed (no variable), in-place + natural call: f(dx, dp, x, p) + uniform call: f(dx, dp, t, x, p, v) +``` + +# Notes +- If `is_inplace` is `nothing` (default), the mutability is auto-detected from the function signature by checking the number of arguments. +- If the function has multiple methods, auto-detection will fail with a `PreconditionError`. In this case, specify `is_inplace` explicitly. + +See also: [`CTBase.Data.HamiltonianVectorField`](@ref), [`CTBase.Traits.Autonomous`](@ref), [`CTBase.Traits.NonAutonomous`](@ref), [`CTBase.Traits.Fixed`](@ref), [`CTBase.Traits.NonFixed`](@ref), [`CTBase.Traits.InPlace`](@ref), [`CTBase.Traits.OutOfPlace`](@ref). +""" +function HamiltonianVectorField(f; + is_autonomous::Bool = __is_autonomous(), + is_variable::Bool = __is_variable(), + is_inplace::Union{Bool, Nothing} = __is_inplace() +) + TD = is_autonomous ? Traits.Autonomous : Traits.NonAutonomous + VD = is_variable ? Traits.NonFixed : Traits.Fixed + MD = if is_inplace === nothing + _detect_mutability_hvf(f, TD, VD) + else + is_inplace ? Traits.InPlace : Traits.OutOfPlace + end + return HamiltonianVectorField{typeof(f), TD, VD, MD}(f) +end + +# ============================================================================= +# Typed constructor — calls struct inner constructor directly +# ============================================================================= + +function HamiltonianVectorField( + f, + ::Type{TD}, ::Type{VD}, ::Type{MD}, +) where { + TD <: Traits.TimeDependence, + VD <: Traits.VariableDependence, + MD <: Traits.AbstractMutabilityTrait, +} + return HamiltonianVectorField{typeof(f), TD, VD, MD}(f) +end + +# ============================================================================= +# Natural call signatures - one per trait combination +# ============================================================================= + +# OutOfPlace signatures (existing) +(H::HamiltonianVectorField{<:Function, Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace})(x, p) = H.f(x, p) +(H::HamiltonianVectorField{<:Function, Traits.NonAutonomous, Traits.Fixed, Traits.OutOfPlace})(t, x, p) = H.f(t, x, p) +function (H::HamiltonianVectorField{<:Function, Traits.Autonomous, Traits.NonFixed, Traits.OutOfPlace})(x, p, v; variable_costate::Bool=false) + variable_costate || return H.f(x, p, v) + hasmethod(H.f, Tuple{typeof(x), typeof(p), typeof(v)}, (:variable_costate,)) || + throw(Exceptions.PreconditionError( + "variable_costate=true is not supported by this HamiltonianVectorField's inner function"; + suggestion = "Use hamiltonian_vector_field(h; ...) to obtain a HVF that supports variable_costate", + context = "HamiltonianVectorField Autonomous/NonFixed call", + )) + return H.f(x, p, v; variable_costate=true) +end + +function (H::HamiltonianVectorField{<:Function, Traits.NonAutonomous, Traits.NonFixed, Traits.OutOfPlace})(t, x, p, v; variable_costate::Bool=false) + variable_costate || return H.f(t, x, p, v) + hasmethod(H.f, Tuple{typeof(t), typeof(x), typeof(p), typeof(v)}, (:variable_costate,)) || + throw(Exceptions.PreconditionError( + "variable_costate=true is not supported by this HamiltonianVectorField's inner function"; + suggestion = "Use hamiltonian_vector_field(h; ...) to obtain a HVF that supports variable_costate", + context = "HamiltonianVectorField NonAutonomous/NonFixed call", + )) + return H.f(t, x, p, v; variable_costate=true) +end + +# InPlace signatures (new) +(H::HamiltonianVectorField{<:Function, Traits.Autonomous, Traits.Fixed, Traits.InPlace})(dx, dp, x, p) = H.f(dx, dp, x, p) +(H::HamiltonianVectorField{<:Function, Traits.NonAutonomous, Traits.Fixed, Traits.InPlace})(dx, dp, t, x, p) = H.f(dx, dp, t, x, p) +function (H::HamiltonianVectorField{<:Function, Traits.Autonomous, Traits.NonFixed, Traits.InPlace})(dx, dp, x, p, v; dpv=nothing, variable_costate::Bool=false) + variable_costate || return H.f(dx, dp, x, p, v) + hasmethod(H.f, Tuple{typeof(dx), typeof(dp), typeof(x), typeof(p), typeof(v)}, (:variable_costate,)) || + throw(Exceptions.PreconditionError( + "variable_costate=true is not supported by this HamiltonianVectorField's inner function"; + suggestion = "Use hamiltonian_vector_field(h; inplace=true) to obtain a HVF that supports variable_costate", + context = "HamiltonianVectorField IP Autonomous/NonFixed call", + )) + return H.f(dx, dp, x, p, v; dpv=dpv, variable_costate=true) +end + +function (H::HamiltonianVectorField{<:Function, Traits.NonAutonomous, Traits.NonFixed, Traits.InPlace})(dx, dp, t, x, p, v; dpv=nothing, variable_costate::Bool=false) + variable_costate || return H.f(dx, dp, t, x, p, v) + hasmethod(H.f, Tuple{typeof(dx), typeof(dp), typeof(t), typeof(x), typeof(p), typeof(v)}, (:variable_costate,)) || + throw(Exceptions.PreconditionError( + "variable_costate=true is not supported by this HamiltonianVectorField's inner function"; + suggestion = "Use hamiltonian_vector_field(h; inplace=true) to obtain a HVF that supports variable_costate", + context = "HamiltonianVectorField IP NonAutonomous/NonFixed call", + )) + return H.f(dx, dp, t, x, p, v; dpv=dpv, variable_costate=true) +end + +# ============================================================================= +# Uniform (t, x, p, v) call - used by HamiltonianVectorFieldSystem.rhs +# Every combination forwards to its natural call, ignoring unused args. +# (NonAutonomous, NonFixed) is already covered by the natural signature above. +# ============================================================================= + +# OutOfPlace uniform signatures +(H::HamiltonianVectorField{<:Function, Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace})(t, x, p, v; variable_costate::Bool=false) = H.f(x, p) +(H::HamiltonianVectorField{<:Function, Traits.NonAutonomous, Traits.Fixed, Traits.OutOfPlace})(t, x, p, v; variable_costate::Bool=false) = H.f(t, x, p) +(H::HamiltonianVectorField{<:Function, Traits.Autonomous, Traits.NonFixed, Traits.OutOfPlace})(t, x, p, v; variable_costate::Bool=false) = H(x, p, v; variable_costate=variable_costate) # delegate to natural signature + +# InPlace uniform signatures +(H::HamiltonianVectorField{<:Function, Traits.Autonomous, Traits.Fixed, Traits.InPlace})(dx, dp, t, x, p, v; variable_costate::Bool=false, dpv=nothing) = H.f(dx, dp, x, p) +(H::HamiltonianVectorField{<:Function, Traits.NonAutonomous, Traits.Fixed, Traits.InPlace})(dx, dp, t, x, p, v; variable_costate::Bool=false, dpv=nothing) = H.f(dx, dp, t, x, p) +(H::HamiltonianVectorField{<:Function, Traits.Autonomous, Traits.NonFixed, Traits.InPlace})(dx, dp, t, x, p, v; variable_costate::Bool=false, dpv=nothing) = H(dx, dp, x, p, v; dpv=dpv, variable_costate=variable_costate) # delegate to natural signature + +# ============================================================================= +# Base.show +# ============================================================================= + +""" +$(TYPEDSIGNATURES) + +Display a compact representation of a HamiltonianVectorField. + +Shows the type name, time dependence, variable dependence, mutability, and function type. + +# Arguments +- `io::IO`: The IO stream to write to. +- `hvf::HamiltonianVectorField`: The HamiltonianVectorField to display. + +See also: [`CTBase.Data.HamiltonianVectorField`](@ref). +""" +function Base.show(io::IO, ::HamiltonianVectorField{F, TD, VD, MD}) where {F, TD, VD, MD} + header = "HamiltonianVectorField: $(_td_label(TD)), $(_vd_label(VD)), $(_md_label(MD))" + natural = _natural_sig_hvf(TD, VD, MD) + uniform = _uniform_sig_hvf(MD) + println(io, header) + println(io, " natural call: ", natural) + print(io, " uniform call: ", uniform) +end + +""" +$(TYPEDSIGNATURES) + +Display a HamiltonianVectorField in the REPL with text/plain MIME type. + +Delegates to the compact show method. + +# Arguments +- `io::IO`: The IO stream to write to. +- `::MIME"text/plain"`: The MIME type for REPL display. +- `hvf::HamiltonianVectorField`: The HamiltonianVectorField to display. + +See also: [`CTBase.Data.HamiltonianVectorField`](@ref). +""" +function Base.show(io::IO, ::MIME"text/plain", hvf::HamiltonianVectorField{F, TD, VD, MD}) where {F, TD, VD, MD} + show(io, hvf) +end diff --git a/src/Data/helpers.jl b/src/Data/helpers.jl new file mode 100644 index 00000000..64a2a6ac --- /dev/null +++ b/src/Data/helpers.jl @@ -0,0 +1,258 @@ +""" +Internal helper functions for display formatting. + +This module provides helper functions for generating user-friendly display +representations of VectorField and HamiltonianVectorField types. +""" + +# ============================================================================= +# Shared label helpers +# ============================================================================= + +""" + _td_label(::Type{Traits.Autonomous}) -> String + _td_label(::Type{Traits.NonAutonomous}) -> String + +Return a user-friendly label for time dependence traits. + +# Arguments +- `TD`: Type parameter for time dependence (`Autonomous` or `NonAutonomous`) + +# Returns +- `String`: User-friendly label ("autonomous" or "non-autonomous") + +See also: [`_vd_label`](@ref), [`_md_label`](@ref). +""" +function _td_label(::Type{Traits.Autonomous}) + return "autonomous" +end +function _td_label(::Type{Traits.NonAutonomous}) + return "non-autonomous" +end + +""" + _vd_label(::Type{Traits.Fixed}) -> String + _vd_label(::Type{Traits.NonFixed}) -> String + +Return a user-friendly label for variable dependence traits. + +# Arguments +- `VD`: Type parameter for variable dependence (`Fixed` or `NonFixed`) + +# Returns +- `String`: User-friendly label ("fixed (no variable)" or "variable") + +See also: [`_td_label`](@ref), [`_md_label`](@ref). +""" +function _vd_label(::Type{Traits.Fixed}) + return "fixed (no variable)" +end +function _vd_label(::Type{Traits.NonFixed}) + return "variable" +end + +""" + _md_label(::Type{OutOfPlace}) -> String + _md_label(::Type{InPlace}) -> String + +Return a user-friendly label for mutability traits. + +# Arguments +- `MD`: Type parameter for mutability (`OutOfPlace` or `InPlace`) + +# Returns +- `String`: User-friendly label ("out-of-place" or "in-place") + +See also: [`_td_label`](@ref), [`_vd_label`](@ref). +""" +function _md_label(::Type{Traits.OutOfPlace}) + return "out-of-place" +end +function _md_label(::Type{Traits.InPlace}) + return "in-place" +end + +# ============================================================================= +# VectorField-specific signature helpers +# ============================================================================= + +""" + _natural_sig_vf(::Type{TD}, ::Type{VD}, ::Type{Traits.OutOfPlace}) where {TD, VD} -> String + _natural_sig_vf(::Type{TD}, ::Type{VD}, ::Type{Traits.InPlace}) where {TD, VD} -> String + +Return the natural call signature for a VectorField based on its traits. + +# Arguments +- `TD`: Time dependence type (`Autonomous` or `NonAutonomous`) +- `VD`: Variable dependence type (`Fixed` or `NonFixed`) +- `MD`: Mutability type (`Traits.OutOfPlace` or `Traits.InPlace`) + +# Returns +- `String`: Natural call signature (e.g., "f(x)", "f(t, x)", "f(dx, x)") + +# Example +\`\`\`julia +_natural_sig_vf(Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace) # Returns "f(x)" +_natural_sig_vf(Traits.NonAutonomous, Traits.Fixed, Traits.OutOfPlace) # Returns "f(t, x)" +_natural_sig_vf(Traits.Autonomous, Traits.Fixed, Traits.InPlace) # Returns "f(dx, x)" +\`\`\` + +See also: [`_uniform_sig_vf`](@ref). +""" +function _natural_sig_vf(::Type{TD}, ::Type{VD}, ::Type{Traits.OutOfPlace}) where {TD, VD} + args = String[] + TD === Traits.NonAutonomous && push!(args, "t") + push!(args, "x") + VD === Traits.NonFixed && push!(args, "v") + return "f(" * join(args, ", ") * ")" +end + +function _natural_sig_vf(::Type{TD}, ::Type{VD}, ::Type{Traits.InPlace}) where {TD, VD} + args = ["dx"] + TD === Traits.NonAutonomous && push!(args, "t") + push!(args, "x") + VD === Traits.NonFixed && push!(args, "v") + return "f(" * join(args, ", ") * ")" +end + +""" + _uniform_sig_vf(::Type{Traits.OutOfPlace}) -> String + _uniform_sig_vf(::Type{Traits.InPlace}) -> String + +Return the uniform call signature for a VectorField. + +The uniform signature always includes all arguments (t, x, v) regardless of traits, +and includes the derivative buffer (dx) for in-place variants. + +# Arguments +- `MD`: Mutability type (`Traits.OutOfPlace` or `Traits.InPlace`) + +# Returns +- `String`: Uniform call signature ("f(t, x, v)" or "f(dx, t, x, v)") + +See also: [`_natural_sig_vf`](@ref). +""" +function _uniform_sig_vf(::Type{Traits.OutOfPlace}) + return "f(t, x, v)" +end +function _uniform_sig_vf(::Type{Traits.InPlace}) + return "f(dx, t, x, v)" +end + +# ============================================================================= +# HamiltonianVectorField-specific signature helpers +# ============================================================================= + +""" + _natural_sig_hvf(::Type{TD}, ::Type{VD}, ::Type{Traits.OutOfPlace}) where {TD, VD} -> String + _natural_sig_hvf(::Type{TD}, ::Type{VD}, ::Type{Traits.InPlace}) where {TD, VD} -> String + +Return the natural call signature for a HamiltonianVectorField based on its traits. + +# Arguments +- `TD`: Time dependence type (`Autonomous` or `NonAutonomous`) +- `VD`: Variable dependence type (`Fixed` or `NonFixed`) +- `MD`: Mutability type (`Traits.OutOfPlace` or `Traits.InPlace`) + +# Returns +- `String`: Natural call signature (e.g., "f(x, p)", "f(t, x, p)", "f(dx, dp, x, p)") + +# Example +\`\`\`julia +_natural_sig_hvf(Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace) # Returns "f(x, p)" +_natural_sig_hvf(Traits.NonAutonomous, Traits.Fixed, Traits.OutOfPlace) # Returns "f(t, x, p)" +_natural_sig_hvf(Traits.Autonomous, Traits.Fixed, Traits.InPlace) # Returns "f(dx, dp, x, p)" +\`\`\` + +See also: [`_uniform_sig_hvf`](@ref). +""" +function _natural_sig_hvf(::Type{TD}, ::Type{VD}, ::Type{Traits.OutOfPlace}) where {TD, VD} + args = String[] + TD === Traits.NonAutonomous && push!(args, "t") + push!(args, "x") + push!(args, "p") + VD === Traits.NonFixed && push!(args, "v") + return "f(" * join(args, ", ") * ")" +end + +function _natural_sig_hvf(::Type{TD}, ::Type{VD}, ::Type{Traits.InPlace}) where {TD, VD} + args = ["dx", "dp"] + TD === Traits.NonAutonomous && push!(args, "t") + push!(args, "x") + push!(args, "p") + VD === Traits.NonFixed && push!(args, "v") + return "f(" * join(args, ", ") * ")" +end + +""" + _uniform_sig_hvf(::Type{Traits.OutOfPlace}) -> String + _uniform_sig_hvf(::Type{Traits.InPlace}) -> String + +Return the uniform call signature for a HamiltonianVectorField. + +The uniform signature always includes all arguments (t, x, p, v) regardless of traits, +and includes the derivative buffers (dx, dp) for in-place variants. + +# Arguments +- `MD`: Mutability type (`Traits.OutOfPlace` or `Traits.InPlace`) + +# Returns +- `String`: Uniform call signature ("f(t, x, p, v)" or "f(dx, dp, t, x, p, v)") + +See also: [`_natural_sig_hvf`](@ref). +""" +function _uniform_sig_hvf(::Type{Traits.OutOfPlace}) + return "f(t, x, p, v)" +end +function _uniform_sig_hvf(::Type{Traits.InPlace}) + return "f(dx, dp, t, x, p, v)" +end + +# ============================================================================= +# Hamiltonian-specific signature helpers +# ============================================================================= + +""" + _natural_sig_h(::Type{TD}, ::Type{VD}) where {TD, VD} -> String + +Return the natural call signature for a Hamiltonian based on its traits. + +# Arguments +- `TD`: Time dependence type (`Autonomous` or `NonAutonomous`) +- `VD`: Variable dependence type (`Fixed` or `NonFixed`) + +# Returns +- `String`: Natural call signature (e.g., "h(x, p)", "h(t, x, p)") + +# Example +\`\`\`julia +_natural_sig_h(Autonomous, Fixed) # Returns "h(x, p)" +_natural_sig_h(NonAutonomous, Fixed) # Returns "h(t, x, p)" +\`\`\` + +See also: [`_uniform_sig_h`](@ref). +""" +function _natural_sig_h(::Type{TD}, ::Type{VD}) where {TD, VD} + args = String[] + TD === Traits.NonAutonomous && push!(args, "t") + push!(args, "x") + push!(args, "p") + VD === Traits.NonFixed && push!(args, "v") + return "h(" * join(args, ", ") * ")" +end + +""" + _uniform_sig_h() -> String + +Return the uniform call signature for a Hamiltonian. + +The uniform signature always includes all arguments (t, x, p, v) regardless of traits. + +# Returns +- `String`: Uniform call signature ("h(t, x, p, v)") + +See also: [`_natural_sig_h`](@ref). +""" +function _uniform_sig_h() + return "h(t, x, p, v)" +end diff --git a/src/Data/vector_field.jl b/src/Data/vector_field.jl new file mode 100644 index 00000000..799784c0 --- /dev/null +++ b/src/Data/vector_field.jl @@ -0,0 +1,278 @@ +""" +$(TYPEDEF) + +Parametric container for a vector-field function together with its +time-dependence, variable-dependence, and mutability traits. + +# Type Parameters +- `F`: concrete type of the wrapped function. +- `TD <: TimeDependence`: `Autonomous` or `NonAutonomous`. +- `VD <: VariableDependence`: `Fixed` or `NonFixed`. +- `MD <: AbstractMutabilityTrait`: `InPlace` or `OutOfPlace`. + +# Fields +- `f::F`: the vector-field function. + +# Construction + +Use the keyword constructor: + +```julia +VectorField(f; is_autonomous = true, is_variable = false) # default: f(x) +VectorField((t, x) -> ...; is_autonomous = false) # f(t, x) +VectorField((x, v) -> ...; is_variable = true) # f(x, v) +VectorField((t, x, v) -> ...; is_autonomous = false, is_variable = true) +``` + +The mutability trait (InPlace/OutOfPlace) is auto-detected from the function signature. + +# Call Signatures + +Every `VectorField` is callable via its **natural** signature (matching the +traits), and via a **uniform** signature `(t, x, v)` that ignores the +unused arguments — this uniform form is used internally to build the right-hand +side of the ODE in a trait-agnostic way. + +For InPlace vector fields, the natural signature includes the derivative buffer +as the first argument (e.g., `(dx, x)` for Autonomous/Fixed). + +See also: [`CTBase.Data.AbstractVectorField`](@ref), [`CTBase.Traits.TimeDependence`](@ref), [`CTBase.Traits.VariableDependence`](@ref), [`CTBase.Traits.AbstractMutabilityTrait`](@ref). +""" +struct VectorField{F<:Function, TD<:Traits.TimeDependence, VD<:Traits.VariableDependence, MD<:Traits.AbstractMutabilityTrait} <: AbstractVectorField{TD, VD, MD} + f::F +end + +# ============================================================================= +# Internal helpers for mutability detection +# ============================================================================= + +""" + _oop_arity_vf(::Type{Traits.Autonomous}, ::Type{Traits.Fixed}) -> Int + +Return the out-of-place arity for Autonomous/Fixed vector fields (1: x). +""" +_oop_arity_vf(::Type{Traits.Autonomous}, ::Type{Traits.Fixed}) = 1 + +""" + _oop_arity_vf(::Type{Traits.NonAutonomous}, ::Type{Traits.Fixed}) -> Int + +Return the out-of-place arity for NonAutonomous/Fixed vector fields (2: t, x). +""" +_oop_arity_vf(::Type{Traits.NonAutonomous}, ::Type{Traits.Fixed}) = 2 + +""" + _oop_arity_vf(::Type{Traits.Autonomous}, ::Type{Traits.NonFixed}) -> Int + +Return the out-of-place arity for Autonomous/NonFixed vector fields (2: x, v). +""" +_oop_arity_vf(::Type{Traits.Autonomous}, ::Type{Traits.NonFixed}) = 2 + +""" + _oop_arity_vf(::Type{Traits.NonAutonomous}, ::Type{Traits.NonFixed}) -> Int + +Return the out-of-place arity for NonAutonomous/NonFixed vector fields (3: t, x, v). +""" +_oop_arity_vf(::Type{Traits.NonAutonomous}, ::Type{Traits.NonFixed}) = 3 + +""" + _detect_mutability_vf(f::Function, TD, VD) -> Type{<:AbstractMutabilityTrait} + +Detect the mutability trait from the function signature. + +Compares the function arity to the expected out-of-place arity and in-place arity +(arity + 1 for VectorField). Returns `InPlace` or `OutOfPlace` accordingly. + +If the function has multiple methods, throws a `PreconditionError` indicating that +auto-detection is ambiguous and the user should specify `is_inplace` explicitly. + +# Arguments +- `f::Function`: The vector-field function. +- `TD`: Time dependence trait type. +- `VD`: Variable dependence trait type. + +# Returns +- `Type{InPlace}` or `Type{OutOfPlace}`. + +# Throws +- `Exceptions.PreconditionError`: If the function has multiple methods, making automatic arity detection ambiguous. +- `Exceptions.IncorrectArgument`: If the arity is invalid (does not match expected out-of-place or in-place arity). + +# Notes +- This function is called automatically by the `VectorField` constructor when `is_inplace` is `nothing`. +- Users can bypass auto-detection by specifying `is_inplace=true` or `is_inplace=false` explicitly in the constructor. + +See also: [`CTBase.Data.VectorField`](@ref), [`CTBase.Traits.InPlace`](@ref), [`CTBase.Traits.OutOfPlace`](@ref). +""" +function _detect_mutability_vf(f::Function, TD, VD) + method_count = length(methods(f)) + if method_count > 1 + throw(Exceptions.PreconditionError( + "Cannot auto-detect mutability: function has multiple methods"; + reason = "The function has $method_count methods, making automatic arity detection ambiguous", + suggestion = "Specify `is_inplace=true` or `is_inplace=false` explicitly in the constructor", + context = "VectorField mutability detection", + )) + end + + arity = first(methods(f)).nargs - 1 + oop_arity = _oop_arity_vf(TD, VD) + ip_arity = oop_arity + 1 + + if arity == oop_arity + return Traits.OutOfPlace + elseif arity == ip_arity + return Traits.InPlace + else + throw(Exceptions.IncorrectArgument( + "Invalid function arity: expected $oop_arity (out-of-place) or $ip_arity (in-place), got $arity"; + suggestion = "Ensure your function signature matches the expected pattern for the given traits.", + context = "VectorField mutability detection", + )) + end +end + +""" +$(TYPEDSIGNATURES) + +Construct a `VectorField` with trait flags. + +# Arguments +- `f::Function`: The vector-field function. +- `is_autonomous::Bool`: If true, system is autonomous (default: `__is_autonomous()`). +- `is_variable::Bool`: If true, system depends on variable parameters (default: `__is_variable()`). +- `is_inplace::Union{Bool, Nothing}`: If true, function is in-place; if false, function is out-of-place; if `nothing`, mutability is auto-detected from function signature (default: `__is_inplace()`). + +# Returns +- `VectorField`: A VectorField with appropriate traits. + +# Example +\`\`\`julia-repl +julia> using CTBase.Data + +julia> vf = VectorField(x -> -x) # Uses defaults: is_autonomous=true, is_variable=false +VectorField: autonomous, fixed (no variable), out-of-place + natural call: f(x) + uniform call: f(t, x, v) + +julia> vf = VectorField((t, x) -> t .* x; is_autonomous=false) +VectorField: non-autonomous, fixed (no variable), out-of-place + natural call: f(t, x) + uniform call: f(t, x, v) + +julia> vf = VectorField(x -> -x; is_inplace=true) # Explicit in-place +VectorField: autonomous, fixed (no variable), in-place + natural call: f(dx, x) + uniform call: f(dx, t, x, v) +\`\`\` + +# Notes +- If `is_inplace` is `nothing` (default), the mutability is auto-detected from the function signature by checking the number of arguments. +- If the function has multiple methods, auto-detection will fail with a `PreconditionError`. In this case, specify `is_inplace` explicitly. + +See also: [`CTBase.Data.VectorField`](@ref), [`CTBase.Traits.Autonomous`](@ref), [`CTBase.Traits.NonAutonomous`](@ref), [`CTBase.Traits.Fixed`](@ref), [`CTBase.Traits.NonFixed`](@ref), [`CTBase.Traits.InPlace`](@ref), [`CTBase.Traits.OutOfPlace`](@ref). +""" +function VectorField(f; + is_autonomous::Bool = __is_autonomous(), + is_variable::Bool = __is_variable(), + is_inplace::Union{Bool, Nothing} = __is_inplace() +) + TD = is_autonomous ? Traits.Autonomous : Traits.NonAutonomous + VD = is_variable ? Traits.NonFixed : Traits.Fixed + MD = if is_inplace === nothing + _detect_mutability_vf(f, TD, VD) + else + is_inplace ? Traits.InPlace : Traits.OutOfPlace + end + return VectorField{typeof(f), TD, VD, MD}(f) +end + +# ============================================================================= +# Typed constructor — calls struct inner constructor directly +# ============================================================================= + +function VectorField( + f, + ::Type{TD}, ::Type{VD}, ::Type{MD}, +) where { + TD <: Traits.TimeDependence, + VD <: Traits.VariableDependence, + MD <: Traits.AbstractMutabilityTrait, +} + return VectorField{typeof(f), TD, VD, MD}(f) +end + +# ============================================================================= +# Natural call signatures - one per trait combination +# ============================================================================= + +# OutOfPlace signatures (existing) +(F::VectorField{<:Function, Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace})(x) = F.f(x) +(F::VectorField{<:Function, Traits.NonAutonomous, Traits.Fixed, Traits.OutOfPlace})(t, x) = F.f(t, x) +(F::VectorField{<:Function, Traits.Autonomous, Traits.NonFixed, Traits.OutOfPlace})(x, v) = F.f(x, v) +(F::VectorField{<:Function, Traits.NonAutonomous, Traits.NonFixed, Traits.OutOfPlace})(t, x, v) = F.f(t, x, v) + +# InPlace signatures (new) +(F::VectorField{<:Function, Traits.Autonomous, Traits.Fixed, Traits.InPlace})(dx, x) = F.f(dx, x) +(F::VectorField{<:Function, Traits.NonAutonomous, Traits.Fixed, Traits.InPlace})(dx, t, x) = F.f(dx, t, x) +(F::VectorField{<:Function, Traits.Autonomous, Traits.NonFixed, Traits.InPlace})(dx, x, v) = F.f(dx, x, v) +(F::VectorField{<:Function, Traits.NonAutonomous, Traits.NonFixed, Traits.InPlace})(dx, t, x, v) = F.f(dx, t, x, v) + +# ============================================================================= +# Uniform (t, x, v) call - used by VectorFieldSystem.rhs +# Every combination forwards to its natural call, ignoring unused args. +# (NonAutonomous, NonFixed) is already covered by the natural signature above. +# ============================================================================= + +# OutOfPlace uniform signatures (existing) +(F::VectorField{<:Function, Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace})(t, x, v) = F.f(x) +(F::VectorField{<:Function, Traits.NonAutonomous, Traits.Fixed, Traits.OutOfPlace})(t, x, v) = F.f(t, x) +(F::VectorField{<:Function, Traits.Autonomous, Traits.NonFixed, Traits.OutOfPlace})(t, x, v) = F.f(x, v) + +# InPlace uniform signatures (new) +(F::VectorField{<:Function, Traits.Autonomous, Traits.Fixed, Traits.InPlace})(dx, t, x, v) = F.f(dx, x) +(F::VectorField{<:Function, Traits.NonAutonomous, Traits.Fixed, Traits.InPlace})(dx, t, x, v) = F.f(dx, t, x) +(F::VectorField{<:Function, Traits.Autonomous, Traits.NonFixed, Traits.InPlace})(dx, t, x, v) = F.f(dx, x, v) + +# ============================================================================= +# Base.show +# ============================================================================= + +""" +$(TYPEDSIGNATURES) + +Display a compact representation of a VectorField. + +Shows the type name, time dependence, variable dependence, mutability, and function type. + +# Arguments +- `io::IO`: The IO stream to write to. +- `vf::VectorField`: The VectorField to display. + +See also: [`CTBase.Data.VectorField`](@ref). +""" +function Base.show(io::IO, vf::VectorField{F, TD, VD, MD}) where {F, TD, VD, MD} + header = "VectorField: $(_td_label(TD)), $(_vd_label(VD)), $(_md_label(MD))" + natural = _natural_sig_vf(TD, VD, MD) + uniform = _uniform_sig_vf(MD) + println(io, header) + println(io, " natural call: ", natural) + print(io, " uniform call: ", uniform) +end + +""" +$(TYPEDSIGNATURES) + +Display a VectorField in the REPL with text/plain MIME type. + +Delegates to the compact show method. + +# Arguments +- `io::IO`: The IO stream to write to. +- `::MIME"text/plain"`: The MIME type for REPL display. +- `vf::VectorField`: The VectorField to display. + +See also: [`CTBase.Data.VectorField`](@ref). +""" +function Base.show(io::IO, ::MIME"text/plain", vf::VectorField{F, TD, VD, MD}) where {F, TD, VD, MD} + show(io, vf) +end diff --git a/test/runtests.jl b/test/runtests.jl index 08623a07..43e0aa8d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -9,11 +9,11 @@ using Test: Test using CTBase: CTBase # Controls nested testset output formatting (used by individual test files) -module TestOptions +module TestData const VERBOSE = true const SHOWTIMING = true end -using .TestOptions: VERBOSE, SHOWTIMING +using .TestData: VERBOSE, SHOWTIMING # Macro to check if an expression is type-stable and inferred correctly macro test_inferred(expr) diff --git a/test/suite/core/test_core_display.jl b/test/suite/core/test_core_display.jl index 407f60ec..d0d416ae 100644 --- a/test/suite/core/test_core_display.jl +++ b/test/suite/core/test_core_display.jl @@ -3,8 +3,8 @@ module TestCoreDisplay using Test: Test import CTBase.Core -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_core_display() io_plain = IOContext(devnull, :color => false) diff --git a/test/suite/core/test_core_types.jl b/test/suite/core/test_core_types.jl index 71e07554..7d79491f 100644 --- a/test/suite/core/test_core_types.jl +++ b/test/suite/core/test_core_types.jl @@ -3,8 +3,8 @@ module TestCoreTypes using Test: Test import CTBase.Core -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_types() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "Type aliases" begin diff --git a/test/suite/core/test_function_utils.jl b/test/suite/core/test_function_utils.jl index 8e953f91..002dcaca 100644 --- a/test/suite/core/test_function_utils.jl +++ b/test/suite/core/test_function_utils.jl @@ -3,8 +3,8 @@ module TestCoreFunctionUtils using Test: Test import CTBase.Core -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_function_utils() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "to_out_of_place" begin diff --git a/test/suite/core/test_macros.jl b/test/suite/core/test_macros.jl index 7a652432..79be71b8 100644 --- a/test/suite/core/test_macros.jl +++ b/test/suite/core/test_macros.jl @@ -4,8 +4,8 @@ using Test: Test import CTBase.Core import CTBase.Exceptions -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_macros() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "@ensure" begin diff --git a/test/suite/core/test_matrix_utils.jl b/test/suite/core/test_matrix_utils.jl index 9cc8b053..2a50f91e 100644 --- a/test/suite/core/test_matrix_utils.jl +++ b/test/suite/core/test_matrix_utils.jl @@ -3,8 +3,8 @@ module TestCoreMatrixUtils using Test: Test import CTBase.Core -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_matrix_utils() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "matrix2vec" begin diff --git a/test/suite/core/test_palette.jl b/test/suite/core/test_palette.jl index 44bcb4c2..b02ed451 100644 --- a/test/suite/core/test_palette.jl +++ b/test/suite/core/test_palette.jl @@ -3,8 +3,8 @@ module TestPalette using Test: Test import CTBase.Core -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_palette() io_plain = IOContext(devnull, :color => false) diff --git a/test/suite/core/test_utils.jl b/test/suite/core/test_utils.jl index a8e00aa5..b57e236c 100644 --- a/test/suite/core/test_utils.jl +++ b/test/suite/core/test_utils.jl @@ -3,8 +3,8 @@ module TestCoreUtils using Test: Test import CTBase.Core -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_utils() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "Default value of the display during resolution" begin diff --git a/test/suite/data/test_abstract_hamiltonian.jl b/test/suite/data/test_abstract_hamiltonian.jl new file mode 100644 index 00000000..6e75dfc5 --- /dev/null +++ b/test/suite/data/test_abstract_hamiltonian.jl @@ -0,0 +1,158 @@ +module TestAbstractHamiltonian + +import Test +import CTBase.Data +import CTBase.Traits + +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true + +# ============================================================================== +# Fake type for contract testing (defined at module top-level per testing-creation.md) +# ============================================================================== + +struct FakeHamiltonian{TD, VD} <: Data.AbstractHamiltonian{TD, VD} end + +# ============================================================================== +# Test function +# ============================================================================== + +function test_abstract_hamiltonian() + Test.@testset "AbstractHamiltonian Tests" verbose=VERBOSE showtiming=SHOWTIMING begin + + # ==================================================================== + # UNIT TESTS - Abstract Type Definition + # ==================================================================== + + Test.@testset "Abstract Type Definition" begin + Test.@testset "AbstractHamiltonian exists" begin + Test.@test isdefined(Data, :AbstractHamiltonian) + end + + Test.@testset "AbstractHamiltonian is exported" begin + Test.@test isdefined(Data, :AbstractHamiltonian) + end + + Test.@testset "FakeHamiltonian subtypes AbstractHamiltonian" begin + fake = FakeHamiltonian{Traits.Autonomous, Traits.Fixed}() + Test.@test fake isa Data.AbstractHamiltonian + end + end + + # ==================================================================== + # UNIT TESTS - Trait Accessors on Abstract Type + # ==================================================================== + + Test.@testset "Trait Accessors on Abstract Type" begin + Test.@testset "has_time_dependence_trait returns true" begin + fake = FakeHamiltonian{Traits.Autonomous, Traits.Fixed}() + Test.@test Traits.has_time_dependence_trait(fake) === true + Test.@test Base.invokelatest(Traits.has_time_dependence_trait, fake) === true + end + + Test.@testset "has_variable_dependence_trait returns true" begin + fake = FakeHamiltonian{Traits.Autonomous, Traits.Fixed}() + Test.@test Traits.has_variable_dependence_trait(fake) === true + Test.@test Base.invokelatest(Traits.has_variable_dependence_trait, fake) === true + end + + Test.@testset "time_dependence returns correct trait" begin + fake_aut = FakeHamiltonian{Traits.Autonomous, Traits.Fixed}() + fake_nonaut = FakeHamiltonian{Traits.NonAutonomous, Traits.Fixed}() + Test.@test Traits.time_dependence(fake_aut) === Traits.Autonomous + Test.@test Traits.time_dependence(fake_nonaut) === Traits.NonAutonomous + end + + Test.@testset "variable_dependence returns correct trait" begin + fake_fixed = FakeHamiltonian{Traits.Autonomous, Traits.Fixed}() + fake_nonfixed = FakeHamiltonian{Traits.Autonomous, Traits.NonFixed}() + Test.@test Traits.variable_dependence(fake_fixed) === Traits.Fixed + Test.@test Traits.variable_dependence(fake_nonfixed) === Traits.NonFixed + end + + Test.@testset "explicit dispatch on AbstractHamiltonian methods" begin + fake = FakeHamiltonian{Traits.NonAutonomous, Traits.NonFixed}() + + Test.@test invoke( + Traits.has_time_dependence_trait, + Tuple{Data.AbstractHamiltonian}, + fake, + ) === true + + Test.@test invoke( + Traits.has_variable_dependence_trait, + Tuple{Data.AbstractHamiltonian}, + fake, + ) === true + + Test.@test invoke( + Traits.time_dependence, + Tuple{Data.AbstractHamiltonian{Traits.NonAutonomous, Traits.NonFixed}}, + fake, + ) === Traits.NonAutonomous + + Test.@test invoke( + Traits.variable_dependence, + Tuple{Data.AbstractHamiltonian{Traits.NonAutonomous, Traits.NonFixed}}, + fake, + ) === Traits.NonFixed + end + end + + # ==================================================================== + # UNIT TESTS - Dynamics Trait + # ==================================================================== + + Test.@testset "Dynamics Trait" begin + Test.@testset "abstract type returns HamiltonianDynamics" begin + fake = FakeHamiltonian{Traits.Autonomous, Traits.Fixed}() + Test.@test Traits.dynamics_trait(fake) === Traits.HamiltonianDynamics + end + + Test.@testset "concrete Hamiltonian returns HamiltonianDynamics" begin + h = Data.Hamiltonian((x, p) -> x + p; is_autonomous=true, is_variable=false) + Test.@test Traits.dynamics_trait(h) === Traits.HamiltonianDynamics + end + end + + # ==================================================================== + # UNIT TESTS - Liskov Substitution + # ==================================================================== + + Test.@testset "Liskov Substitution" begin + Test.@testset "Hamiltonian is an AbstractHamiltonian" begin + h = Data.Hamiltonian((x, p) -> x + p; is_autonomous=true, is_variable=false) + Test.@test h isa Data.AbstractHamiltonian + end + end + + # ==================================================================== + # UNIT TESTS - Type Stability + # ==================================================================== + + Test.@testset "Type Stability" begin + Test.@testset "Trait accessors are type-stable" begin + fake = FakeHamiltonian{Traits.Autonomous, Traits.Fixed}() + Test.@test Test.@inferred(Traits.has_time_dependence_trait(fake)) === true + Test.@test Test.@inferred(Traits.has_variable_dependence_trait(fake)) === true + Test.@test Test.@inferred(Traits.time_dependence(fake)) === Traits.Autonomous + Test.@test Test.@inferred(Traits.variable_dependence(fake)) === Traits.Fixed + end + end + + # ==================================================================== + # UNIT TESTS - Exports Verification + # ==================================================================== + + Test.@testset "Exports Verification" begin + Test.@testset "Exported types" begin + Test.@test isdefined(Data, :AbstractHamiltonian) + end + end + end +end + +end # module + +# CRITICAL: Redefine in outer scope for TestRunner +test_abstract_hamiltonian() = TestAbstractHamiltonian.test_abstract_hamiltonian() diff --git a/test/suite/data/test_abstract_hamiltonian_vector_field.jl b/test/suite/data/test_abstract_hamiltonian_vector_field.jl new file mode 100644 index 00000000..d80a24b6 --- /dev/null +++ b/test/suite/data/test_abstract_hamiltonian_vector_field.jl @@ -0,0 +1,111 @@ +module TestAbstractHamiltonianVectorField + +import Test +import CTBase.Data +import CTBase.Traits + +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true + +# ============================================================================== +# Fake type for contract testing (defined at module top-level per testing-creation.md) +# ============================================================================== + +struct FakeHamiltonianVectorField{TD, VD, MD} <: Data.AbstractHamiltonianVectorField{TD, VD, MD} end + +# ============================================================================== +# Test function +# ============================================================================== + +function test_abstract_hamiltonian_vector_field() + Test.@testset "AbstractHamiltonianVectorField Tests" verbose=VERBOSE showtiming=SHOWTIMING begin + + # ==================================================================== + # UNIT TESTS - Abstract Type Definition + # ==================================================================== + + Test.@testset "Abstract Type Definition" begin + Test.@testset "AbstractHamiltonianVectorField exists" begin + Test.@test isdefined(Data, :AbstractHamiltonianVectorField) + end + + Test.@testset "AbstractHamiltonianVectorField is abstract" begin + Test.@test isabstracttype(Data.AbstractHamiltonianVectorField) + end + + Test.@testset "Subtypes AbstractVectorField" begin + Test.@test Data.AbstractHamiltonianVectorField <: Data.AbstractVectorField + end + + Test.@testset "FakeHamiltonianVectorField subtypes AbstractHamiltonianVectorField" begin + fake = FakeHamiltonianVectorField{Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace}() + Test.@test fake isa Data.AbstractHamiltonianVectorField + Test.@test fake isa Data.AbstractVectorField + end + end + + # ==================================================================== + # UNIT TESTS - Trait Accessors on Abstract Type + # ==================================================================== + + Test.@testset "Trait Accessors on Abstract Type" begin + Test.@testset "has_*_trait return true (inherited from AbstractVectorField)" begin + fake = FakeHamiltonianVectorField{Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace}() + Test.@test Traits.has_time_dependence_trait(fake) === true + Test.@test Traits.has_variable_dependence_trait(fake) === true + Test.@test Traits.has_mutability_trait(fake) === true + end + + Test.@testset "trait values are read from type parameters" begin + fake = FakeHamiltonianVectorField{Traits.NonAutonomous, Traits.NonFixed, Traits.InPlace}() + Test.@test Traits.time_dependence(fake) === Traits.NonAutonomous + Test.@test Traits.variable_dependence(fake) === Traits.NonFixed + Test.@test Traits.mutability(fake) === Traits.InPlace + end + end + + # ==================================================================== + # UNIT TESTS - Dynamics Trait + # ==================================================================== + + Test.@testset "Dynamics Trait" begin + Test.@testset "abstract type returns HamiltonianDynamics" begin + fake = FakeHamiltonianVectorField{Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace}() + Test.@test Traits.dynamics_trait(fake) === Traits.HamiltonianDynamics + end + + Test.@testset "concrete HamiltonianVectorField returns HamiltonianDynamics" begin + hvf = Data.HamiltonianVectorField((x, p) -> (x, -p); is_autonomous=true, is_variable=false) + Test.@test Traits.dynamics_trait(hvf) === Traits.HamiltonianDynamics + end + end + + # ==================================================================== + # UNIT TESTS - Liskov Substitution + # ==================================================================== + + Test.@testset "Liskov Substitution" begin + Test.@testset "HamiltonianVectorField is an AbstractHamiltonianVectorField" begin + hvf = Data.HamiltonianVectorField((x, p) -> (x, -p); is_autonomous=true, is_variable=false) + Test.@test hvf isa Data.AbstractHamiltonianVectorField + end + + Test.@testset "HamiltonianVectorField type subtypes the abstract type" begin + Test.@test Data.HamiltonianVectorField <: Data.AbstractHamiltonianVectorField + end + end + + # ==================================================================== + # UNIT TESTS - Exports Verification + # ==================================================================== + + Test.@testset "Exports Verification" begin + Test.@test isdefined(Data, :AbstractHamiltonianVectorField) + end + end +end + +end # module + +# CRITICAL: Redefine in outer scope for TestRunner +test_abstract_hamiltonian_vector_field() = TestAbstractHamiltonianVectorField.test_abstract_hamiltonian_vector_field() diff --git a/test/suite/data/test_abstract_vector_field.jl b/test/suite/data/test_abstract_vector_field.jl new file mode 100644 index 00000000..d715b595 --- /dev/null +++ b/test/suite/data/test_abstract_vector_field.jl @@ -0,0 +1,174 @@ +module TestAbstractVectorField + +import Test +import CTBase.Data +import CTBase.Traits + +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true + +# ============================================================================== +# Fake type for contract testing (defined at module top-level per testing-creation.md) +# ============================================================================== + +struct FakeVectorField{TD, VD, MD} <: Data.AbstractVectorField{TD, VD, MD} end + +# ============================================================================== +# Test function +# ============================================================================== + +function test_abstract_vector_field() + Test.@testset "AbstractVectorField Tests" verbose=VERBOSE showtiming=SHOWTIMING begin + + # ==================================================================== + # UNIT TESTS - Abstract Type Definition + # ==================================================================== + + Test.@testset "Abstract Type Definition" begin + Test.@testset "AbstractVectorField exists" begin + Test.@test isdefined(Data, :AbstractVectorField) + end + + Test.@testset "AbstractVectorField is exported" begin + Test.@test isdefined(Data, :AbstractVectorField) + end + + Test.@testset "FakeVectorField subtypes AbstractVectorField" begin + fake = FakeVectorField{Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace}() + Test.@test fake isa Data.AbstractVectorField + end + end + + # ==================================================================== + # UNIT TESTS - Trait Accessors on Abstract Type + # ==================================================================== + + Test.@testset "Trait Accessors on Abstract Type" begin + Test.@testset "has_time_dependence_trait returns true" begin + fake = FakeVectorField{Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace}() + Test.@test Traits.has_time_dependence_trait(fake) === true + Test.@test Base.invokelatest(Traits.has_time_dependence_trait, fake) === true + end + + Test.@testset "has_variable_dependence_trait returns true" begin + fake = FakeVectorField{Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace}() + Test.@test Traits.has_variable_dependence_trait(fake) === true + Test.@test Base.invokelatest(Traits.has_variable_dependence_trait, fake) === true + end + + Test.@testset "has_mutability_trait returns true" begin + fake = FakeVectorField{Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace}() + Test.@test Traits.has_mutability_trait(fake) === true + Test.@test Base.invokelatest(Traits.has_mutability_trait, fake) === true + end + + Test.@testset "time_dependence returns correct trait" begin + fake_aut = FakeVectorField{Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace}() + fake_nonaut = FakeVectorField{Traits.NonAutonomous, Traits.Fixed, Traits.OutOfPlace}() + Test.@test Traits.time_dependence(fake_aut) === Traits.Autonomous + Test.@test Traits.time_dependence(fake_nonaut) === Traits.NonAutonomous + end + + Test.@testset "variable_dependence returns correct trait" begin + fake_fixed = FakeVectorField{Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace}() + fake_nonfixed = FakeVectorField{Traits.Autonomous, Traits.NonFixed, Traits.OutOfPlace}() + Test.@test Traits.variable_dependence(fake_fixed) === Traits.Fixed + Test.@test Traits.variable_dependence(fake_nonfixed) === Traits.NonFixed + end + + Test.@testset "mutability returns correct trait" begin + fake_oop = FakeVectorField{Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace}() + fake_ip = FakeVectorField{Traits.Autonomous, Traits.Fixed, Traits.InPlace}() + Test.@test Traits.mutability(fake_oop) === Traits.OutOfPlace + Test.@test Traits.mutability(fake_ip) === Traits.InPlace + end + + Test.@testset "explicit dispatch on AbstractVectorField methods" begin + fake = FakeVectorField{Traits.NonAutonomous, Traits.NonFixed, Traits.InPlace}() + + Test.@test invoke( + Traits.has_time_dependence_trait, + Tuple{Data.AbstractVectorField}, + fake, + ) === true + + Test.@test invoke( + Traits.has_variable_dependence_trait, + Tuple{Data.AbstractVectorField}, + fake, + ) === true + + Test.@test invoke( + Traits.has_mutability_trait, + Tuple{Data.AbstractVectorField}, + fake, + ) === true + + Test.@test invoke( + Traits.time_dependence, + Tuple{Data.AbstractVectorField{Traits.NonAutonomous, Traits.NonFixed, Traits.InPlace}}, + fake, + ) === Traits.NonAutonomous + + Test.@test invoke( + Traits.variable_dependence, + Tuple{Data.AbstractVectorField{Traits.NonAutonomous, Traits.NonFixed, Traits.InPlace}}, + fake, + ) === Traits.NonFixed + + Test.@test invoke( + Traits.mutability, + Tuple{Data.AbstractVectorField{Traits.NonAutonomous, Traits.NonFixed, Traits.InPlace}}, + fake, + ) === Traits.InPlace + end + end + + # ==================================================================== + # UNIT TESTS - Dynamics Trait + # ==================================================================== + + Test.@testset "Dynamics Trait" begin + Test.@testset "abstract type returns StateDynamics" begin + fake = FakeVectorField{Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace}() + Test.@test Traits.dynamics_trait(fake) === Traits.StateDynamics + end + + Test.@testset "concrete VectorField returns StateDynamics" begin + vf = Data.VectorField(x -> -x; is_autonomous=true, is_variable=false) + Test.@test Traits.dynamics_trait(vf) === Traits.StateDynamics + end + end + + # ==================================================================== + # UNIT TESTS - Liskov Substitution + # ==================================================================== + + Test.@testset "Liskov Substitution" begin + Test.@testset "VectorField is an AbstractVectorField" begin + vf = Data.VectorField(x -> -x; is_autonomous=true, is_variable=false) + Test.@test vf isa Data.AbstractVectorField + end + + Test.@testset "HamiltonianVectorField is an AbstractVectorField" begin + hvf = Data.HamiltonianVectorField((x, p) -> (x, -p); is_autonomous=true, is_variable=false) + Test.@test hvf isa Data.AbstractVectorField + end + end + + # ==================================================================== + # UNIT TESTS - Exports Verification + # ==================================================================== + + Test.@testset "Exports Verification" begin + Test.@testset "Exported types" begin + Test.@test isdefined(Data, :AbstractVectorField) + end + end + end +end + +end # module + +# CRITICAL: Redefine in outer scope for TestRunner +test_abstract_vector_field() = TestAbstractVectorField.test_abstract_vector_field() diff --git a/test/suite/data/test_data_module.jl b/test/suite/data/test_data_module.jl new file mode 100644 index 00000000..38fb2853 --- /dev/null +++ b/test/suite/data/test_data_module.jl @@ -0,0 +1,168 @@ +""" +# ============================================================================ +# Data Module Exports Tests +# ============================================================================ +# This file tests the exports from the `Data` module. It verifies that +# the expected types are properly exported by `CTBase.Data` and readily +# accessible to the end user. +# +# Functionality tests are in separate files: +# - test_abstract_vector_field.jl for abstract vector field types +# - test_vector_field.jl for VectorField constructor and functionality +# - test_abstract_hamiltonian.jl for abstract Hamiltonian types +# - test_hamiltonian.jl for Hamiltonian constructor and functionality +# - test_abstract_hamiltonian_vector_field.jl for abstract HVF types +# - test_hamiltonian_vector_field.jl for HVF constructor and functionality +# - test_helpers.jl for helper functions +""" + +module TestDataModule + +import Test +import CTBase +import CTBase.Data +using CTBase.Data # For testing exported symbols + +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true + +const CurrentModule = TestDataModule + +# ============================================================================ +# Hardcoded export lists +# ============================================================================ +# These lists define the expected public API of the Data module. + +const EXPORTED_ABSTRACT_TYPES = ( + :AbstractVectorField, + :AbstractHamiltonianVectorField, + :AbstractHamiltonian, +) + +const EXPORTED_CONCRETE_TYPES = ( + :VectorField, + :HamiltonianVectorField, + :Hamiltonian, +) + +const PRIVATE_SYMBOLS = ( + :__is_autonomous, + :__is_inplace, + :__is_variable, + :_detect_mutability_hvf, + :_detect_mutability_vf, + :_md_label, + :_natural_sig_h, + :_natural_sig_hvf, + :_natural_sig_vf, + :_oop_arity_hvf, + :_oop_arity_vf, + :_td_label, + :_uniform_sig_h, + :_uniform_sig_hvf, + :_uniform_sig_vf, + :_vd_label, +) + +# ============================================================================ +# Helper functions (generic for reuse in other modules) +# ============================================================================ + +""" + test_exported_symbols(module_ref::Module, symbols::Tuple, test_module::Module) + +Test that symbols are exported from a module and available via `using`. +""" +function test_exported_symbols(module_ref::Module, symbols::Tuple, test_module::Module) + for sym in symbols + Test.@testset "$(sym)" begin + Test.@test isdefined(module_ref, sym) + Test.@test isdefined(test_module, sym) + end + end +end + +""" + test_internal_symbols(module_ref::Module, symbols::Tuple, test_module::Module) + +Test that symbols are defined in a module but NOT exported (not available via `using`). +Generic helper for modules with private symbols. +""" +function test_internal_symbols(module_ref::Module, symbols::Tuple, test_module::Module) + for sym in symbols + Test.@testset "$(sym)" begin + Test.@test isdefined(module_ref, sym) + Test.@test !isdefined(test_module, sym) + end + end +end + +# ============================================================================ +# Test function +# ============================================================================ + +function test_data_module() + Test.@testset "Data Module Exports" verbose=VERBOSE showtiming=SHOWTIMING begin + + # ==================================================================== + # Module availability + # ==================================================================== + + Test.@testset "Module availability" begin + Test.@testset "Data module exists" begin + Test.@test isdefined(CTBase, :Data) + Test.@test CTBase.Data isa Module + end + end + + # ==================================================================== + # Exported abstract types verification + # ==================================================================== + + Test.@testset "Exported abstract types" begin + test_exported_symbols(Data, EXPORTED_ABSTRACT_TYPES, CurrentModule) + end + + # ==================================================================== + # Exported concrete types verification + # ==================================================================== + + Test.@testset "Exported concrete types" begin + test_exported_symbols(Data, EXPORTED_CONCRETE_TYPES, CurrentModule) + end + + # ==================================================================== + # Private symbols (not exported) verification + # ==================================================================== + + Test.@testset "Private symbols (not exported)" begin + test_internal_symbols(Data, PRIVATE_SYMBOLS, CurrentModule) + end + + # ==================================================================== + # Type hierarchy tests + # ==================================================================== + + Test.@testset "Type hierarchy" begin + Test.@testset "Abstract types are abstract" begin + Test.@test isabstracttype(Data.AbstractVectorField) + Test.@test isabstracttype(Data.AbstractHamiltonianVectorField) + Test.@test isabstracttype(Data.AbstractHamiltonian) + end + + Test.@testset "Concrete types inherit from abstract types" begin + Test.@test Data.VectorField <: Data.AbstractVectorField + Test.@test Data.HamiltonianVectorField <: Data.AbstractHamiltonianVectorField + # Note: Hamiltonian is parametric (Hamiltonian{F, TD, VD} <: AbstractHamiltonian{TD, VD}) + # Test via instance instead of type + h = Data.Hamiltonian((x, p) -> 0.0) + Test.@test h isa Data.AbstractHamiltonian + end + end + end +end + +end # module TestDataModule + +# CRITICAL: Redefine in outer scope for TestRunner +test_data_module() = TestDataModule.test_data_module() diff --git a/test/suite/data/test_hamiltonian.jl b/test/suite/data/test_hamiltonian.jl new file mode 100644 index 00000000..855b6c59 --- /dev/null +++ b/test/suite/data/test_hamiltonian.jl @@ -0,0 +1,171 @@ +module TestHamiltonian + +import Test +import CTBase.Data +import CTBase.Traits +import CTBase.Exceptions + +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true + +function test_hamiltonian() + Test.@testset "Hamiltonian Tests" verbose=VERBOSE showtiming=SHOWTIMING begin + + # ==================================================================== + # UNIT TESTS - Construction with all trait combinations + # ==================================================================== + + Test.@testset "Unit: Construction with all trait combinations" begin + # Autonomous, Fixed + h_aut_fixed = Data.Hamiltonian((x, p) -> x + p; is_autonomous=true, is_variable=false) + Test.@test h_aut_fixed isa Data.Hamiltonian + Test.@test Traits.time_dependence(h_aut_fixed) == Traits.Autonomous + Test.@test Traits.variable_dependence(h_aut_fixed) == Traits.Fixed + + # NonAutonomous, Fixed + h_nonaut_fixed = Data.Hamiltonian((t, x, p) -> t + x + p; is_autonomous=false, is_variable=false) + Test.@test h_nonaut_fixed isa Data.Hamiltonian + Test.@test Traits.time_dependence(h_nonaut_fixed) == Traits.NonAutonomous + Test.@test Traits.variable_dependence(h_nonaut_fixed) == Traits.Fixed + + # Autonomous, NonFixed + h_aut_nonfixed = Data.Hamiltonian((x, p, v) -> x + p + v; is_autonomous=true, is_variable=true) + Test.@test h_aut_nonfixed isa Data.Hamiltonian + Test.@test Traits.time_dependence(h_aut_nonfixed) == Traits.Autonomous + Test.@test Traits.variable_dependence(h_aut_nonfixed) == Traits.NonFixed + + # NonAutonomous, NonFixed + h_nonaut_nonfixed = Data.Hamiltonian((t, x, p, v) -> t + x + p + v; is_autonomous=false, is_variable=true) + Test.@test h_nonaut_nonfixed isa Data.Hamiltonian + Test.@test Traits.time_dependence(h_nonaut_nonfixed) == Traits.NonAutonomous + Test.@test Traits.variable_dependence(h_nonaut_nonfixed) == Traits.NonFixed + end + + # ==================================================================== + # UNIT TESTS - Natural call signatures + # ==================================================================== + + Test.@testset "Unit: Natural call signatures" begin + # (x, p) for Autonomous, Fixed + h1 = Data.Hamiltonian((x, p) -> x .+ p; is_autonomous=true, is_variable=false) + result = h1([1.0, 2.0], [3.0, 4.0]) + Test.@test result == [4.0, 6.0] + + # (t, x, p) for NonAutonomous, Fixed + h2 = Data.Hamiltonian((t, x, p) -> x .+ p; is_autonomous=false, is_variable=false) + result = h2(2.0, [1.0, 2.0], [3.0, 4.0]) + Test.@test result == [4.0, 6.0] + + # (x, p, v) for Autonomous, NonFixed + h3 = Data.Hamiltonian((x, p, v) -> x .+ p .+ v; is_autonomous=true, is_variable=true) + result = h3([1.0, 2.0], [3.0, 4.0], 2.0) + Test.@test result == [6.0, 8.0] + + # (t, x, p, v) for NonAutonomous, NonFixed + h4 = Data.Hamiltonian((t, x, p, v) -> x .+ p .+ v; is_autonomous=false, is_variable=true) + result = h4(2.0, [1.0, 2.0], [3.0, 4.0], 2.0) + Test.@test result == [6.0, 8.0] + end + + # ==================================================================== + # UNIT TESTS - Uniform call signature + # ==================================================================== + + Test.@testset "Unit: Uniform call signature (t, x, p, v)" begin + # Autonomous Fixed - ignores t and v + h1 = Data.Hamiltonian((x, p) -> x .+ p; is_autonomous=true, is_variable=false) + result = h1(0.0, [1.0, 2.0], [3.0, 4.0], nothing) + Test.@test result == [4.0, 6.0] + + # NonAutonomous Fixed - ignores v + h2 = Data.Hamiltonian((t, x, p) -> x .+ p; is_autonomous=false, is_variable=false) + result = h2(2.0, [1.0, 2.0], [3.0, 4.0], nothing) + Test.@test result == [4.0, 6.0] + + # Autonomous NonFixed - ignores t + h3 = Data.Hamiltonian((x, p, v) -> x .+ p .+ v; is_autonomous=true, is_variable=true) + result = h3(0.0, [1.0, 2.0], [3.0, 4.0], 2.0) + Test.@test result == [6.0, 8.0] + + # NonAutonomous NonFixed - uses all + h4 = Data.Hamiltonian((t, x, p, v) -> x .+ p .+ v; is_autonomous=false, is_variable=true) + result = h4(2.0, [1.0, 2.0], [3.0, 4.0], 2.0) + Test.@test result == [6.0, 8.0] + end + + # ==================================================================== + # UNIT TESTS - Type stability + # ==================================================================== + + Test.@testset "Unit: Type stability" begin + # Uniform call type stability + h = Data.Hamiltonian((x, p) -> x .+ p; is_autonomous=true, is_variable=false) + Test.@test Test.@inferred(h(0.0, [1.0, 2.0], [3.0, 4.0], nothing)) == [4.0, 6.0] + end + + # ==================================================================== + # UNIT TESTS - Show Methods + # ==================================================================== + + Test.@testset "Show Methods" begin + h = Data.Hamiltonian((x, p) -> x .+ p; is_autonomous=true, is_variable=false) + + Test.@testset "Base.show (compact)" begin + io = IOBuffer() + show(io, h) + str = String(take!(io)) + Test.@test occursin("Hamiltonian", str) + Test.@test occursin("autonomous", str) + Test.@test occursin("fixed (no variable)", str) + Test.@test occursin("natural call", str) + Test.@test occursin("uniform call", str) + end + + Test.@testset "Base.show (text/plain)" begin + io = IOBuffer() + show(io, MIME("text/plain"), h) + str = String(take!(io)) + Test.@test occursin("Hamiltonian", str) + Test.@test occursin("autonomous", str) + Test.@test occursin("fixed (no variable)", str) + end + end + + # ==================================================================== + # UNIT TESTS - Typed constructor (trait types passed positionally) + # ==================================================================== + + Test.@testset "Typed constructor" begin + h = Data.Hamiltonian((x, p) -> x .+ p, Traits.Autonomous, Traits.Fixed) + Test.@test h isa Data.Hamiltonian + Test.@test Traits.time_dependence(h) === Traits.Autonomous + Test.@test Traits.variable_dependence(h) === Traits.Fixed + Test.@test h([1.0, 2.0], [3.0, 4.0]) == [4.0, 6.0] + end + + # ==================================================================== + # UNIT TESTS - Subtyping + # ==================================================================== + + Test.@testset "Subtyping" begin + Test.@testset "Hamiltonian is an AbstractHamiltonian" begin + h = Data.Hamiltonian((x, p) -> x .+ p; is_autonomous=true, is_variable=false) + Test.@test h isa Data.AbstractHamiltonian + end + end + + # ==================================================================== + # EXPORTS TESTS + # ==================================================================== + + Test.@testset "Exports" begin + Test.@testset "Hamiltonian is exported" begin + Test.@test isdefined(Data, :Hamiltonian) + end + end + end +end + +end # module TestHamiltonian + +test_hamiltonian() = TestHamiltonian.test_hamiltonian() diff --git a/test/suite/data/test_hamiltonian_vector_field.jl b/test/suite/data/test_hamiltonian_vector_field.jl new file mode 100644 index 00000000..e1e2b5f3 --- /dev/null +++ b/test/suite/data/test_hamiltonian_vector_field.jl @@ -0,0 +1,429 @@ +module TestHamiltonianVectorField + +import Test +import CTBase.Data: Data +import CTBase.Traits: Traits +import CTBase.Exceptions + +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true + +# TOP-LEVEL: Fake function with multiple methods for testing +_multi_method_hvf(x::Int, p) = (x, -p) +_multi_method_hvf(x::Float64, p) = (x, -p) +_multi_method_hvf(x::AbstractVector, p) = (x, -p) + +# TOP-LEVEL: Fake function with invalid arity for testing error branches +_bad_arity_hvf(x) = (x, -x) + +function test_hamiltonian_vector_field() + Test.@testset "Hamiltonian Vector Field Tests" verbose=VERBOSE showtiming=SHOWTIMING begin + + # ==================================================================== + # UNIT TESTS - Construction + # ==================================================================== + + Test.@testset "Construction" begin + # Autonomous, Fixed + hvf_autonomous_fixed = Data.HamiltonianVectorField((x, p) -> (x, -p); is_autonomous=true, is_variable=false) + Test.@test hvf_autonomous_fixed isa Data.HamiltonianVectorField + Test.@test Traits.time_dependence(hvf_autonomous_fixed) == Traits.Autonomous + Test.@test Traits.variable_dependence(hvf_autonomous_fixed) == Traits.Fixed + + # NonAutonomous, Fixed + hvf_nonautonomous_fixed = Data.HamiltonianVectorField((t, x, p) -> (x, -p); is_autonomous=false, is_variable=false) + Test.@test hvf_nonautonomous_fixed isa Data.HamiltonianVectorField + Test.@test Traits.time_dependence(hvf_nonautonomous_fixed) == Traits.NonAutonomous + Test.@test Traits.variable_dependence(hvf_nonautonomous_fixed) == Traits.Fixed + + # Autonomous, NonFixed + hvf_autonomous_nonfixed = Data.HamiltonianVectorField((x, p, v) -> (x .* v, -p); is_autonomous=true, is_variable=true) + Test.@test hvf_autonomous_nonfixed isa Data.HamiltonianVectorField + Test.@test Traits.time_dependence(hvf_autonomous_nonfixed) == Traits.Autonomous + Test.@test Traits.variable_dependence(hvf_autonomous_nonfixed) == Traits.NonFixed + + # NonAutonomous, NonFixed + hvf_nonautonomous_nonfixed = Data.HamiltonianVectorField((t, x, p, v) -> (x .* v, -p); is_autonomous=false, is_variable=true) + Test.@test hvf_nonautonomous_nonfixed isa Data.HamiltonianVectorField + Test.@test Traits.time_dependence(hvf_nonautonomous_nonfixed) == Traits.NonAutonomous + Test.@test Traits.variable_dependence(hvf_nonautonomous_nonfixed) == Traits.NonFixed + end + + # ==================================================================== + # UNIT TESTS - Natural signatures + # ==================================================================== + + Test.@testset "Natural Signatures" begin + # (x, p) for Autonomous, Fixed + hvf1 = Data.HamiltonianVectorField((x, p) -> (x, -p); is_autonomous=true, is_variable=false) + dx, dp = hvf1([1.0, 2.0], [3.0, 4.0]) + Test.@test dx == [1.0, 2.0] + Test.@test dp == [-3.0, -4.0] + + # (t, x, p) for NonAutonomous, Fixed + hvf2 = Data.HamiltonianVectorField((t, x, p) -> (t .* x, -p); is_autonomous=false, is_variable=false) + dx, dp = hvf2(2.0, [1.0, 2.0], [3.0, 4.0]) + Test.@test dx == [2.0, 4.0] + Test.@test dp == [-3.0, -4.0] + + # (x, p, v) for Autonomous, NonFixed + hvf3 = Data.HamiltonianVectorField((x, p, v) -> (x .* v, -p); is_autonomous=true, is_variable=true) + dx, dp = hvf3([1.0, 2.0], [3.0, 4.0], 2.0) + Test.@test dx == [2.0, 4.0] + Test.@test dp == [-3.0, -4.0] + + # (t, x, p, v) for NonAutonomous, NonFixed + hvf4 = Data.HamiltonianVectorField((t, x, p, v) -> (t .* x .* v, -p); is_autonomous=false, is_variable=true) + dx, dp = hvf4(2.0, [1.0, 2.0], [3.0, 4.0], 2.0) + Test.@test dx == [4.0, 8.0] + Test.@test dp == [-3.0, -4.0] + end + + # ==================================================================== + # UNIT TESTS - Uniform signature + # ==================================================================== + + Test.@testset "Uniform Signature" begin + # All combinations should work with (t, x, p, v) + hvf_autonomous_fixed = Data.HamiltonianVectorField((x, p) -> (x, -p); is_autonomous=true, is_variable=false) + dx, dp = hvf_autonomous_fixed(0.0, [1.0, 2.0], [3.0, 4.0], nothing) + Test.@test dx == [1.0, 2.0] + Test.@test dp == [-3.0, -4.0] + + hvf_nonautonomous_fixed = Data.HamiltonianVectorField((t, x, p) -> (t .* x, -p); is_autonomous=false, is_variable=false) + dx, dp = hvf_nonautonomous_fixed(2.0, [1.0, 2.0], [3.0, 4.0], nothing) + Test.@test dx == [2.0, 4.0] + Test.@test dp == [-3.0, -4.0] + + hvf_autonomous_nonfixed = Data.HamiltonianVectorField((x, p, v) -> (x .* v, -p); is_autonomous=true, is_variable=true) + dx, dp = hvf_autonomous_nonfixed(0.0, [1.0, 2.0], [3.0, 4.0], 2.0) + Test.@test dx == [2.0, 4.0] + Test.@test dp == [-3.0, -4.0] + + # Test variable_costate kwarg for NonFixed + dx, dp = hvf_autonomous_nonfixed(0.0, [1.0, 2.0], [3.0, 4.0], 2.0; variable_costate=false) + Test.@test dx == [2.0, 4.0] + Test.@test dp == [-3.0, -4.0] + end + + # ==================================================================== + # UNIT TESTS - Trait accessors + # ==================================================================== + + Test.@testset "Trait Accessors" begin + hvf = Data.HamiltonianVectorField((x, p) -> (x, -p); is_autonomous=true, is_variable=false) + Test.@test Traits.has_time_dependence_trait(hvf) == true + Test.@test Traits.has_variable_dependence_trait(hvf) == true + Test.@test Traits.time_dependence(hvf) == Traits.Autonomous + Test.@test Traits.variable_dependence(hvf) == Traits.Fixed + end + + # ==================================================================== + # UNIT TESTS - Typed constructor (trait types passed positionally) + # ==================================================================== + + Test.@testset "Typed constructor" begin + hvf = Data.HamiltonianVectorField( + (x, p) -> (x, -p), Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace + ) + Test.@test hvf isa Data.HamiltonianVectorField + Test.@test Traits.time_dependence(hvf) === Traits.Autonomous + Test.@test Traits.variable_dependence(hvf) === Traits.Fixed + Test.@test Traits.mutability(hvf) === Traits.OutOfPlace + dx, dp = hvf([1.0, 2.0], [3.0, 4.0]) + Test.@test dx == [1.0, 2.0] + Test.@test dp == [-3.0, -4.0] + end + + # ==================================================================== + # UNIT TESTS - Subtyping + # ==================================================================== + + Test.@testset "Subtyping" begin + Test.@testset "HamiltonianVectorField is an AbstractVectorField" begin + hvf = Data.HamiltonianVectorField((x, p) -> (x, -p); is_autonomous=true, is_variable=false) + Test.@test hvf isa Data.AbstractVectorField + end + end + + # ==================================================================== + # UNIT TESTS - Base.show + # ==================================================================== + + Test.@testset "Base.show" begin + hvf = Data.HamiltonianVectorField((x, p) -> (x, -p); is_autonomous=true, is_variable=false) + # Just check that show doesn't throw + Test.@test_nowarn sprint(show, hvf) + end + + # ==================================================================== + # UNIT TESTS - Explicit is_inplace parameter + # ==================================================================== + + Test.@testset "Explicit is_inplace parameter" begin + Test.@testset "is_inplace=true creates InPlace HamiltonianVectorField" begin + # Define an out-of-place function but force InPlace + f(x, p) = (x, -p) + hvf = Data.HamiltonianVectorField(f; is_inplace=true) + Test.@test Traits.mutability(hvf) === Traits.InPlace + end + + Test.@testset "is_inplace=false creates OutOfPlace HamiltonianVectorField" begin + # Define an in-place function but force OutOfPlace + f(x, p) = (x, -p) + hvf = Data.HamiltonianVectorField(f; is_inplace=false) + Test.@test Traits.mutability(hvf) === Traits.OutOfPlace + end + end + + # ==================================================================== + # UNIT TESTS - PreconditionError for multiple methods + # ==================================================================== + + Test.@testset "PreconditionError for multiple methods" begin + Test.@testset "Throws PreconditionError when is_inplace is not specified" begin + Test.@test_throws Exceptions.PreconditionError Data.HamiltonianVectorField(_multi_method_hvf) + end + + Test.@testset "No error when is_inplace is explicitly specified" begin + hvf = Data.HamiltonianVectorField(_multi_method_hvf; is_inplace=false) + Test.@test Traits.mutability(hvf) === Traits.OutOfPlace + end + end + + # ==================================================================== + # UNIT TESTS - Internal Helpers + # ==================================================================== + + Test.@testset "Internal Helpers" begin + Test.@testset "_oop_arity_hvf" begin + Test.@test Data._oop_arity_hvf(Traits.Autonomous, Traits.Fixed) == 2 + Test.@test Data._oop_arity_hvf(Traits.NonAutonomous, Traits.Fixed) == 3 + Test.@test Data._oop_arity_hvf(Traits.Autonomous, Traits.NonFixed) == 3 + Test.@test Data._oop_arity_hvf(Traits.NonAutonomous, Traits.NonFixed) == 4 + end + + Test.@testset "_natural_sig_hvf helpers" begin + Test.@test Data._natural_sig_hvf(Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace) == "f(x, p)" + Test.@test Data._natural_sig_hvf(Traits.NonAutonomous, Traits.Fixed, Traits.OutOfPlace) == "f(t, x, p)" + Test.@test Data._natural_sig_hvf(Traits.Autonomous, Traits.Fixed, Traits.InPlace) == "f(dx, dp, x, p)" + Test.@test Data._uniform_sig_hvf(Traits.OutOfPlace) == "f(t, x, p, v)" + Test.@test Data._uniform_sig_hvf(Traits.InPlace) == "f(dx, dp, t, x, p, v)" + end + + Test.@testset "_detect_mutability_hvf invalid arity" begin + Test.@test_throws Exceptions.IncorrectArgument Data._detect_mutability_hvf(_bad_arity_hvf, Traits.Autonomous, Traits.Fixed) + end + end + + # ==================================================================== + # UNIT TESTS - InPlace Call Signatures + # ==================================================================== + + Test.@testset "InPlace Call Signatures" begin + Test.@testset "Autonomous Fixed - (dx, dp, x, p)" begin + f(dx, dp, x, p) = (dx .= -x; dp .= -p) + hvf = Data.HamiltonianVectorField(f; is_autonomous=true, is_variable=false, is_inplace=true) + + Test.@testset "scalar" begin + dx = [0.0] + dp = [0.0] + hvf(dx, dp, 3.0, 1.0) + Test.@test dx[1] == -3.0 + Test.@test dp[1] == -1.0 + end + + Test.@testset "vector" begin + dx = [0.0, 0.0] + dp = [0.0, 0.0] + hvf(dx, dp, [1.0, 2.0], [0.5, 1.0]) + Test.@test dx == [-1.0, -2.0] + Test.@test dp == [-0.5, -1.0] + end + end + + Test.@testset "NonAutonomous Fixed - (dx, dp, t, x, p)" begin + f(dx, dp, t, x, p) = (dx .= t .* x; dp .= t .* p) + hvf = Data.HamiltonianVectorField(f; is_autonomous=false, is_variable=false, is_inplace=true) + + Test.@testset "scalar" begin + dx = [0.0] + dp = [0.0] + hvf(dx, dp, 2.0, 3.0, 1.0) + Test.@test dx[1] == 6.0 + Test.@test dp[1] == 2.0 + end + + Test.@testset "vector" begin + dx = [0.0, 0.0] + dp = [0.0, 0.0] + hvf(dx, dp, 2.0, [1.0, 2.0], [0.5, 1.0]) + Test.@test dx == [2.0, 4.0] + Test.@test dp == [1.0, 2.0] + end + end + + Test.@testset "Autonomous NonFixed - (dx, dp, x, p, v)" begin + f(dx, dp, x, p, v) = (dx .= x .+ v; dp .= p .+ v) + hvf = Data.HamiltonianVectorField(f; is_autonomous=true, is_variable=true, is_inplace=true) + + Test.@testset "scalar" begin + dx = [0.0] + dp = [0.0] + hvf(dx, dp, 3.0, 1.0, 0.5) + Test.@test dx[1] == 3.5 + Test.@test dp[1] == 1.5 + end + + Test.@testset "vector" begin + dx = [0.0, 0.0] + dp = [0.0, 0.0] + hvf(dx, dp, [1.0, 2.0], [0.5, 1.0], 0.5) + Test.@test dx == [1.5, 2.5] + Test.@test dp == [1.0, 1.5] + end + end + + Test.@testset "NonAutonomous NonFixed - (dx, dp, t, x, p, v)" begin + f(dx, dp, t, x, p, v) = (dx .= t .* x .+ v; dp .= t .* p .+ v) + hvf = Data.HamiltonianVectorField(f; is_autonomous=false, is_variable=true, is_inplace=true) + + Test.@testset "scalar" begin + dx = [0.0] + dp = [0.0] + hvf(dx, dp, 2.0, 3.0, 1.0, 0.5) + Test.@test dx[1] == 6.5 + Test.@test dp[1] == 2.5 + end + + Test.@testset "vector" begin + dx = [0.0, 0.0] + dp = [0.0, 0.0] + hvf(dx, dp, 2.0, [1.0, 2.0], [0.5, 1.0], 0.5) + Test.@test dx == [2.5, 4.5] + Test.@test dp == [1.5, 2.5] + end + end + end + + # ==================================================================== + # UNIT TESTS - Uniform InPlace Call Signature + # ==================================================================== + + Test.@testset "Uniform InPlace Signature" begin + Test.@testset "Autonomous Fixed InPlace uniform" begin + f(dx, dp, x, p) = (dx .= -x; dp .= -p) + hvf = Data.HamiltonianVectorField(f; is_autonomous=true, is_variable=false, is_inplace=true) + + dx = [0.0, 0.0] + dp = [0.0, 0.0] + hvf(dx, dp, 0.0, [1.0, 2.0], [0.5, 1.0], 0.0) + Test.@test dx == [-1.0, -2.0] + Test.@test dp == [-0.5, -1.0] + end + + Test.@testset "NonAutonomous Fixed InPlace uniform" begin + f(dx, dp, t, x, p) = (dx .= t .* x; dp .= t .* p) + hvf = Data.HamiltonianVectorField(f; is_autonomous=false, is_variable=false, is_inplace=true) + + dx = [0.0, 0.0] + dp = [0.0, 0.0] + hvf(dx, dp, 2.0, [1.0, 2.0], [0.5, 1.0], 0.0) + Test.@test dx == [2.0, 4.0] + Test.@test dp == [1.0, 2.0] + end + + Test.@testset "Autonomous NonFixed InPlace uniform" begin + f(dx, dp, x, p, v) = (dx .= x .+ v; dp .= p .+ v) + hvf = Data.HamiltonianVectorField(f; is_autonomous=true, is_variable=true, is_inplace=true) + + dx = [0.0, 0.0] + dp = [0.0, 0.0] + hvf(dx, dp, 0.0, [1.0, 2.0], [0.5, 1.0], 0.5) + Test.@test dx == [1.5, 2.5] + Test.@test dp == [1.0, 1.5] + + # Test variable_costate kwarg + dx = [0.0, 0.0] + dp = [0.0, 0.0] + hvf(dx, dp, 0.0, [1.0, 2.0], [0.5, 1.0], 0.5; variable_costate=false) + Test.@test dx == [1.5, 2.5] + Test.@test dp == [1.0, 1.5] + end + end + + Test.@testset "Show Methods" begin + hvf = Data.HamiltonianVectorField((x, p) -> (x, -p); is_autonomous=true, is_variable=false) + + Test.@testset "Base.show (compact)" begin + io = IOBuffer() + show(io, hvf) + str = String(take!(io)) + Test.@test occursin("HamiltonianVectorField", str) + Test.@test occursin("autonomous", str) + Test.@test occursin("fixed (no variable)", str) + Test.@test occursin("out-of-place", str) + Test.@test occursin("natural call", str) + Test.@test occursin("uniform call", str) + end + + Test.@testset "Base.show (text/plain)" begin + io = IOBuffer() + show(io, MIME("text/plain"), hvf) + str = String(take!(io)) + Test.@test occursin("HamiltonianVectorField", str) + Test.@test occursin("autonomous", str) + Test.@test occursin("fixed (no variable)", str) + end + end + + # ==================================================================== + # UNIT TESTS - variable_costate kwarg on user-built HVF + # ==================================================================== + + Test.@testset "variable_costate kwarg on user-built HVF" begin + Test.@testset "OOP NonFixed: variable_costate=true on user-built HVF throws PreconditionError" begin + f = (x, p, v) -> (p, -x) # no variable_costate kwarg + hvf = Data.HamiltonianVectorField(f; is_autonomous=true, is_variable=true) + x = [1.0]; p = [0.5]; v = [0.1] + Test.@test_throws Exception hvf(x, p, v; variable_costate=true) + end + + Test.@testset "IP NonFixed: variable_costate=true on user-built HVF throws PreconditionError" begin + f = (dx, dp, x, p, v) -> (dx .= p; dp .= .-x; nothing) + hvf = Data.HamiltonianVectorField(f; is_autonomous=true, is_variable=true, is_inplace=true) + x = [1.0]; p = [0.5]; v = [0.1] + dx = similar(x); dp = similar(p) + Test.@test_throws Exception hvf(dx, dp, x, p, v; variable_costate=true) + end + + Test.@testset "OOP NonAutonomous NonFixed: variable_costate=true on user-built HVF throws PreconditionError" begin + f = (t, x, p, v) -> (p, -x) # no variable_costate kwarg + hvf = Data.HamiltonianVectorField(f; is_autonomous=false, is_variable=true) + t = 0.5; x = [1.0]; p = [0.5]; v = [0.1] + Test.@test_throws Exception hvf(t, x, p, v; variable_costate=true) + end + + Test.@testset "IP NonAutonomous NonFixed: variable_costate=true on user-built HVF throws PreconditionError" begin + f = (dx, dp, t, x, p, v) -> (dx .= p; dp .= .-x; nothing) + hvf = Data.HamiltonianVectorField(f; is_autonomous=false, is_variable=true, is_inplace=true) + t = 0.5; x = [1.0]; p = [0.5]; v = [0.1] + dx = similar(x); dp = similar(p) + Test.@test_throws Exception hvf(dx, dp, t, x, p, v; variable_costate=true) + end + + Test.@testset "OOP NonFixed: variable_costate=false (default) on user-built HVF works" begin + f = (x, p, v) -> (p, -x) + hvf = Data.HamiltonianVectorField(f; is_autonomous=true, is_variable=true) + x = [1.0]; p = [0.5]; v = [0.1] + result = hvf(x, p, v) + Test.@test result == (p, -x) + end + end + end +end + +end # module + +# CRITICAL: Redefine in outer scope for TestRunner +test_hamiltonian_vector_field() = TestHamiltonianVectorField.test_hamiltonian_vector_field() diff --git a/test/suite/data/test_helpers.jl b/test/suite/data/test_helpers.jl new file mode 100644 index 00000000..ee69991e --- /dev/null +++ b/test/suite/data/test_helpers.jl @@ -0,0 +1,139 @@ +module TestHelpers + +import Test +import CTBase.Data +import CTBase.Traits + +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true + +# ============================================================================== +# Test function +# ============================================================================== + +function test_helpers() + Test.@testset "Helpers Tests" verbose=VERBOSE showtiming=SHOWTIMING begin + + # ==================================================================== + # UNIT TESTS - Label Helpers + # ==================================================================== + + Test.@testset "Label Helpers" begin + Test.@testset "_td_label" begin + Test.@test Data._td_label(Traits.Autonomous) == "autonomous" + Test.@test Data._td_label(Traits.NonAutonomous) == "non-autonomous" + end + + Test.@testset "_vd_label" begin + Test.@test Data._vd_label(Traits.Fixed) == "fixed (no variable)" + Test.@test Data._vd_label(Traits.NonFixed) == "variable" + end + + Test.@testset "_md_label" begin + Test.@test Data._md_label(Traits.OutOfPlace) == "out-of-place" + Test.@test Data._md_label(Traits.InPlace) == "in-place" + end + end + + # ==================================================================== + # UNIT TESTS - Hamiltonian Signature Helpers + # ==================================================================== + + Test.@testset "Hamiltonian Signature Helpers" begin + Test.@testset "_natural_sig_h" begin + Test.@test Data._natural_sig_h(Traits.Autonomous, Traits.Fixed) == "h(x, p)" + Test.@test Data._natural_sig_h(Traits.NonAutonomous, Traits.Fixed) == "h(t, x, p)" + Test.@test Data._natural_sig_h(Traits.Autonomous, Traits.NonFixed) == "h(x, p, v)" + Test.@test Data._natural_sig_h(Traits.NonAutonomous, Traits.NonFixed) == "h(t, x, p, v)" + end + + Test.@testset "_uniform_sig_h" begin + Test.@test Data._uniform_sig_h() == "h(t, x, p, v)" + end + end + + # ==================================================================== + # UNIT TESTS - VectorField Signature Helpers + # ==================================================================== + + Test.@testset "VectorField Signature Helpers" begin + Test.@testset "_natural_sig_vf - OutOfPlace" begin + Test.@test Data._natural_sig_vf(Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace) == "f(x)" + Test.@test Data._natural_sig_vf(Traits.NonAutonomous, Traits.Fixed, Traits.OutOfPlace) == "f(t, x)" + Test.@test Data._natural_sig_vf(Traits.Autonomous, Traits.NonFixed, Traits.OutOfPlace) == "f(x, v)" + Test.@test Data._natural_sig_vf(Traits.NonAutonomous, Traits.NonFixed, Traits.OutOfPlace) == "f(t, x, v)" + end + + Test.@testset "_natural_sig_vf - InPlace" begin + Test.@test Data._natural_sig_vf(Traits.Autonomous, Traits.Fixed, Traits.InPlace) == "f(dx, x)" + Test.@test Data._natural_sig_vf(Traits.NonAutonomous, Traits.Fixed, Traits.InPlace) == "f(dx, t, x)" + Test.@test Data._natural_sig_vf(Traits.Autonomous, Traits.NonFixed, Traits.InPlace) == "f(dx, x, v)" + Test.@test Data._natural_sig_vf(Traits.NonAutonomous, Traits.NonFixed, Traits.InPlace) == "f(dx, t, x, v)" + end + + Test.@testset "_uniform_sig_vf" begin + Test.@test Data._uniform_sig_vf(Traits.OutOfPlace) == "f(t, x, v)" + Test.@test Data._uniform_sig_vf(Traits.InPlace) == "f(dx, t, x, v)" + end + end + + # ==================================================================== + # UNIT TESTS - HamiltonianVectorField Signature Helpers + # ==================================================================== + + Test.@testset "HamiltonianVectorField Signature Helpers" begin + Test.@testset "_natural_sig_hvf - OutOfPlace" begin + Test.@test Data._natural_sig_hvf(Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace) == "f(x, p)" + Test.@test Data._natural_sig_hvf(Traits.NonAutonomous, Traits.Fixed, Traits.OutOfPlace) == "f(t, x, p)" + Test.@test Data._natural_sig_hvf(Traits.Autonomous, Traits.NonFixed, Traits.OutOfPlace) == "f(x, p, v)" + Test.@test Data._natural_sig_hvf(Traits.NonAutonomous, Traits.NonFixed, Traits.OutOfPlace) == "f(t, x, p, v)" + end + + Test.@testset "_natural_sig_hvf - InPlace" begin + Test.@test Data._natural_sig_hvf(Traits.Autonomous, Traits.Fixed, Traits.InPlace) == "f(dx, dp, x, p)" + Test.@test Data._natural_sig_hvf(Traits.NonAutonomous, Traits.Fixed, Traits.InPlace) == "f(dx, dp, t, x, p)" + Test.@test Data._natural_sig_hvf(Traits.Autonomous, Traits.NonFixed, Traits.InPlace) == "f(dx, dp, x, p, v)" + Test.@test Data._natural_sig_hvf(Traits.NonAutonomous, Traits.NonFixed, Traits.InPlace) == "f(dx, dp, t, x, p, v)" + end + + Test.@testset "_uniform_sig_hvf" begin + Test.@test Data._uniform_sig_hvf(Traits.OutOfPlace) == "f(t, x, p, v)" + Test.@test Data._uniform_sig_hvf(Traits.InPlace) == "f(dx, dp, t, x, p, v)" + end + + Test.@testset "Uniform signatures" begin + Test.@testset "VectorField uniform signatures" begin + Test.@test Data._uniform_sig_vf(Traits.OutOfPlace) == "f(t, x, v)" + Test.@test Data._uniform_sig_vf(Traits.InPlace) == "f(dx, t, x, v)" + end + + Test.@testset "HamiltonianVectorField uniform signatures" begin + Test.@test Data._uniform_sig_hvf(Traits.OutOfPlace) == "f(t, x, p, v)" + Test.@test Data._uniform_sig_hvf(Traits.InPlace) == "f(dx, dp, t, x, p, v)" + end + end + + Test.@testset "Direct label calls" begin + Test.@testset "Time dependence labels" begin + Test.@test Data._td_label(Traits.Autonomous) == "autonomous" + Test.@test Data._td_label(Traits.NonAutonomous) == "non-autonomous" + end + + Test.@testset "Variable dependence labels" begin + Test.@test Data._vd_label(Traits.Fixed) == "fixed (no variable)" + Test.@test Data._vd_label(Traits.NonFixed) == "variable" + end + + Test.@testset "Mutability labels" begin + Test.@test Data._md_label(Traits.OutOfPlace) == "out-of-place" + Test.@test Data._md_label(Traits.InPlace) == "in-place" + end + end + end + end +end + +end # module + +# CRITICAL: Redefine in outer scope for TestRunner +test_helpers() = TestHelpers.test_helpers() diff --git a/test/suite/data/test_vector_field.jl b/test/suite/data/test_vector_field.jl new file mode 100644 index 00000000..324505b7 --- /dev/null +++ b/test/suite/data/test_vector_field.jl @@ -0,0 +1,507 @@ +module TestVectorField + +import Test +import CTBase.Data +import CTBase.Traits +import CTBase.Exceptions + +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true + +# TOP-LEVEL: Fake function with multiple methods for testing +_multi_method_f(x::Int) = -x +_multi_method_f(x::Float64) = -x +_multi_method_f(x::AbstractVector) = -x + +# TOP-LEVEL: Fake function with invalid arity for testing error branches +_bad_arity_f(x, y, z) = x + y + z + +# ============================================================================== +# Test function +# ============================================================================== + +function test_vector_field() + Test.@testset "VectorField Tests" verbose=VERBOSE showtiming=SHOWTIMING begin + + # ==================================================================== + # UNIT TESTS - Abstract Types + # ==================================================================== + + Test.@testset "Abstract Types" begin + vf = Data.VectorField(x -> x; is_autonomous=true, is_variable=false) + Test.@test vf isa Data.VectorField + end + + # ==================================================================== + # UNIT TESTS - Construction + # ==================================================================== + + Test.@testset "Construction" begin + Test.@testset "keyword constructor with defaults" begin + vf = Data.VectorField(x -> x) + Test.@test vf isa Data.VectorField + Test.@test Traits.time_dependence(vf) === Traits.Autonomous + Test.@test Traits.variable_dependence(vf) === Traits.Fixed + end + + Test.@testset "keyword constructor with explicit flags" begin + vf_autonomous = Data.VectorField(x -> x; is_autonomous=true, is_variable=false) + Test.@test Traits.time_dependence(vf_autonomous) === Traits.Autonomous + Test.@test Traits.variable_dependence(vf_autonomous) === Traits.Fixed + + vf_nonautonomous = Data.VectorField((t, x) -> t .* x; is_autonomous=false, is_variable=false) + Test.@test Traits.time_dependence(vf_nonautonomous) === Traits.NonAutonomous + Test.@test Traits.variable_dependence(vf_nonautonomous) === Traits.Fixed + + vf_nonfixed = Data.VectorField((x, v) -> x .+ v; is_autonomous=true, is_variable=true) + Test.@test Traits.time_dependence(vf_nonfixed) === Traits.Autonomous + Test.@test Traits.variable_dependence(vf_nonfixed) === Traits.NonFixed + + vf_full = Data.VectorField((t, x, v) -> t .* x .+ v; is_autonomous=false, is_variable=true) + Test.@test Traits.time_dependence(vf_full) === Traits.NonAutonomous + Test.@test Traits.variable_dependence(vf_full) === Traits.NonFixed + end + end + + # ==================================================================== + # UNIT TESTS - Trait Methods + # ==================================================================== + + Test.@testset "Trait Methods" begin + vf_aut = Data.VectorField(x -> x; is_autonomous=true, is_variable=false) + vf_nonaut = Data.VectorField((t, x) -> t .* x; is_autonomous=false, is_variable=false) + vf_fixed = Data.VectorField(x -> x; is_autonomous=true, is_variable=false) + vf_nonfixed = Data.VectorField((x, v) -> x .+ v; is_autonomous=true, is_variable=true) + + Test.@testset "has_time_dependence_trait returns true" begin + Test.@test Traits.has_time_dependence_trait(vf_aut) === true + Test.@test Traits.has_time_dependence_trait(vf_nonaut) === true + end + + Test.@testset "has_variable_dependence_trait returns true" begin + Test.@test Traits.has_variable_dependence_trait(vf_fixed) === true + Test.@test Traits.has_variable_dependence_trait(vf_nonfixed) === true + end + + Test.@testset "time_dependence returns correct trait" begin + Test.@test Traits.time_dependence(vf_aut) === Traits.Autonomous + Test.@test Traits.time_dependence(vf_nonaut) === Traits.NonAutonomous + end + + Test.@testset "variable_dependence returns correct trait" begin + Test.@test Traits.variable_dependence(vf_fixed) === Traits.Fixed + Test.@test Traits.variable_dependence(vf_nonfixed) === Traits.NonFixed + end + end + + # ==================================================================== + # UNIT TESTS - Natural Call Signatures + # ==================================================================== + + Test.@testset "Natural Call Signatures" begin + Test.@testset "Autonomous Fixed - (x)" begin + vf = Data.VectorField(x -> -x; is_autonomous=true, is_variable=false) + + Test.@testset "scalar" begin + Test.@test vf(3.0) == -3.0 + end + + Test.@testset "vector" begin + Test.@test vf([1.0, 2.0]) == [-1.0, -2.0] + end + + Test.@testset "matrix" begin + x0 = [1.0 2.0; 3.0 4.0] + result = vf(x0) + Test.@test result == -x0 + end + end + + Test.@testset "NonAutonomous Fixed - (t, x)" begin + vf = Data.VectorField((t, x) -> t .* x; is_autonomous=false, is_variable=false) + + Test.@testset "scalar" begin + Test.@test vf(2.0, 3.0) == 6.0 + end + + Test.@testset "vector" begin + Test.@test vf(2.0, [1.0, 2.0]) == [2.0, 4.0] + end + + Test.@testset "matrix" begin + x0 = [1.0 2.0; 3.0 4.0] + result = vf(2.0, x0) + Test.@test result == 2 .* x0 + end + end + + Test.@testset "Autonomous NonFixed - (x, v)" begin + vf = Data.VectorField((x, v) -> x .+ v; is_autonomous=true, is_variable=true) + + Test.@testset "scalar" begin + Test.@test vf(3.0, 0.5) == 3.5 + end + + Test.@testset "vector" begin + Test.@test vf([1.0, 2.0], 0.5) == [1.5, 2.5] + end + + Test.@testset "matrix" begin + x0 = [1.0 2.0; 3.0 4.0] + result = vf(x0, 0.5) + Test.@test result == x0 .+ 0.5 + end + end + + Test.@testset "NonAutonomous NonFixed - (t, x, v)" begin + vf = Data.VectorField((t, x, v) -> t .* x .+ v; is_autonomous=false, is_variable=true) + + Test.@testset "scalar" begin + Test.@test vf(2.0, 3.0, 0.5) == 6.5 + end + + Test.@testset "vector" begin + Test.@test vf(2.0, [1.0, 2.0], 0.5) == [2.5, 4.5] + end + + Test.@testset "matrix" begin + x0 = [1.0 2.0; 3.0 4.0] + result = vf(2.0, x0, 0.5) + Test.@test result == 2 .* x0 .+ 0.5 + end + end + end + + # ==================================================================== + # UNIT TESTS - Uniform Call Signature (t, x, v) + # ==================================================================== + + Test.@testset "Uniform Call Signature" begin + Test.@testset "Autonomous Fixed ignores t and v" begin + vf = Data.VectorField(x -> -x; is_autonomous=true, is_variable=false) + + Test.@testset "scalar" begin + Test.@test vf(0.0, 3.0, 0.0) == -3.0 + end + + Test.@testset "vector" begin + Test.@test vf(0.0, [1.0, 2.0], 0.0) == [-1.0, -2.0] + end + + Test.@testset "matrix" begin + x0 = [1.0 2.0; 3.0 4.0] + result = vf(0.0, x0, 0.0) + Test.@test result == -x0 + end + end + + Test.@testset "NonAutonomous Fixed uses t, ignores v" begin + vf = Data.VectorField((t, x) -> t .* x; is_autonomous=false, is_variable=false) + + Test.@testset "scalar" begin + Test.@test vf(2.0, 3.0, 0.0) == 6.0 + end + + Test.@testset "vector" begin + Test.@test vf(2.0, [1.0, 2.0], 0.0) == [2.0, 4.0] + end + + Test.@testset "matrix" begin + x0 = [1.0 2.0; 3.0 4.0] + result = vf(2.0, x0, 0.0) + Test.@test result == 2 .* x0 + end + end + + Test.@testset "Autonomous NonFixed ignores t, uses v" begin + vf = Data.VectorField((x, v) -> x .+ v; is_autonomous=true, is_variable=true) + + Test.@testset "scalar" begin + Test.@test vf(0.0, 3.0, 0.5) == 3.5 + end + + Test.@testset "vector" begin + Test.@test vf(0.0, [1.0, 2.0], 0.5) == [1.5, 2.5] + end + + Test.@testset "matrix" begin + x0 = [1.0 2.0; 3.0 4.0] + result = vf(0.0, x0, 0.5) + Test.@test result == x0 .+ 0.5 + end + end + end + + # ==================================================================== + # UNIT TESTS - InPlace Call Signatures + # ==================================================================== + + Test.@testset "InPlace Call Signatures" begin + Test.@testset "Autonomous Fixed - (dx, x)" begin + f(dx, x) = dx .= -x + vf = Data.VectorField(f; is_autonomous=true, is_variable=false, is_inplace=true) + + Test.@testset "scalar" begin + dx = [0.0] + x = 3.0 + vf(dx, x) + Test.@test dx[1] == -3.0 + end + + Test.@testset "vector" begin + dx = [0.0, 0.0] + x = [1.0, 2.0] + vf(dx, x) + Test.@test dx == [-1.0, -2.0] + end + end + + Test.@testset "NonAutonomous Fixed - (dx, t, x)" begin + f(dx, t, x) = dx .= t .* x + vf = Data.VectorField(f; is_autonomous=false, is_variable=false, is_inplace=true) + + Test.@testset "scalar" begin + dx = [0.0] + vf(dx, 2.0, 3.0) + Test.@test dx[1] == 6.0 + end + + Test.@testset "vector" begin + dx = [0.0, 0.0] + vf(dx, 2.0, [1.0, 2.0]) + Test.@test dx == [2.0, 4.0] + end + end + + Test.@testset "Autonomous NonFixed - (dx, x, v)" begin + f(dx, x, v) = dx .= x .+ v + vf = Data.VectorField(f; is_autonomous=true, is_variable=true, is_inplace=true) + + Test.@testset "scalar" begin + dx = [0.0] + vf(dx, 3.0, 0.5) + Test.@test dx[1] == 3.5 + end + + Test.@testset "vector" begin + dx = [0.0, 0.0] + vf(dx, [1.0, 2.0], 0.5) + Test.@test dx == [1.5, 2.5] + end + end + + Test.@testset "NonAutonomous NonFixed - (dx, t, x, v)" begin + f(dx, t, x, v) = dx .= t .* x .+ v + vf = Data.VectorField(f; is_autonomous=false, is_variable=true, is_inplace=true) + + Test.@testset "scalar" begin + dx = [0.0] + vf(dx, 2.0, 3.0, 0.5) + Test.@test dx[1] == 6.5 + end + + Test.@testset "vector" begin + dx = [0.0, 0.0] + vf(dx, 2.0, [1.0, 2.0], 0.5) + Test.@test dx == [2.5, 4.5] + end + end + end + + # ==================================================================== + # UNIT TESTS - Uniform InPlace Call Signature + # ==================================================================== + + Test.@testset "Uniform InPlace Signature" begin + Test.@testset "Autonomous Fixed InPlace uniform" begin + f(dx, x) = dx .= -x + vf = Data.VectorField(f; is_autonomous=true, is_variable=false, is_inplace=true) + + dx = [0.0, 0.0] + vf(dx, 0.0, [1.0, 2.0], 0.0) + Test.@test dx == [-1.0, -2.0] + end + + Test.@testset "NonAutonomous Fixed InPlace uniform" begin + f(dx, t, x) = dx .= t .* x + vf = Data.VectorField(f; is_autonomous=false, is_variable=false, is_inplace=true) + + dx = [0.0, 0.0] + vf(dx, 2.0, [1.0, 2.0], 0.0) + Test.@test dx == [2.0, 4.0] + end + + Test.@testset "Autonomous NonFixed InPlace uniform" begin + f(dx, x, v) = dx .= x .+ v + vf = Data.VectorField(f; is_autonomous=true, is_variable=true, is_inplace=true) + + dx = [0.0, 0.0] + vf(dx, 0.0, [1.0, 2.0], 0.5) + Test.@test dx == [1.5, 2.5] + end + + Test.@testset "NonAutonomous NonFixed InPlace uniform" begin + # natural signature (dx, t, x, v) coincides with the uniform one + f(dx, t, x, v) = dx .= t .* x .+ v + vf = Data.VectorField(f; is_autonomous=false, is_variable=true, is_inplace=true) + + dx = [0.0, 0.0] + vf(dx, 2.0, [1.0, 2.0], 0.5) + Test.@test dx == [2.5, 4.5] + end + end + + # ==================================================================== + # UNIT TESTS - Typed constructor (trait types passed positionally) + # ==================================================================== + + Test.@testset "Typed constructor" begin + vf = Data.VectorField(x -> -x, Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace) + Test.@test vf isa Data.VectorField + Test.@test Traits.time_dependence(vf) === Traits.Autonomous + Test.@test Traits.variable_dependence(vf) === Traits.Fixed + Test.@test Traits.mutability(vf) === Traits.OutOfPlace + Test.@test vf(3.0) == -3.0 + + vf_ip = Data.VectorField( + (dx, x) -> (dx .= -x), Traits.Autonomous, Traits.Fixed, Traits.InPlace + ) + Test.@test Traits.mutability(vf_ip) === Traits.InPlace + end + + Test.@testset "Common Trait Predicates" begin + vf_aut = Data.VectorField(x -> x; is_autonomous=true, is_variable=false) + vf_nonaut = Data.VectorField((t, x) -> t .* x; is_autonomous=false, is_variable=false) + vf_fixed = Data.VectorField(x -> x; is_autonomous=true, is_variable=false) + vf_nonfixed = Data.VectorField((x, v) -> x .+ v; is_autonomous=true, is_variable=true) + + Test.@testset "is_autonomous / is_nonautonomous" begin + Test.@test Traits.is_autonomous(vf_aut) === true + Test.@test Traits.is_nonautonomous(vf_aut) === false + Test.@test Traits.is_autonomous(vf_nonaut) === false + Test.@test Traits.is_nonautonomous(vf_nonaut) === true + end + + Test.@testset "is_variable / is_nonvariable" begin + Test.@test Traits.is_variable(vf_fixed) === false + Test.@test Traits.is_nonvariable(vf_fixed) === true + Test.@test Traits.is_variable(vf_nonfixed) === true + Test.@test Traits.is_nonvariable(vf_nonfixed) === false + end + end + + # ==================================================================== + # UNIT TESTS - Show Methods + # ==================================================================== + + Test.@testset "Show Methods" begin + vf = Data.VectorField(x -> x; is_autonomous=true, is_variable=false) + + Test.@testset "Base.show (compact)" begin + io = IOBuffer() + show(io, vf) + str = String(take!(io)) + Test.@test occursin("VectorField", str) + Test.@test occursin("autonomous", str) + Test.@test occursin("fixed (no variable)", str) + Test.@test occursin("out-of-place", str) + Test.@test occursin("natural call", str) + Test.@test occursin("uniform call", str) + end + + Test.@testset "Base.show (text/plain)" begin + io = IOBuffer() + show(io, MIME("text/plain"), vf) + str = String(take!(io)) + Test.@test occursin("VectorField", str) + Test.@test occursin("autonomous", str) + Test.@test occursin("fixed (no variable)", str) + end + end + + # ==================================================================== + # UNIT TESTS - Subtyping + # ==================================================================== + + Test.@testset "Subtyping" begin + Test.@testset "VectorField is an AbstractVectorField" begin + vf = Data.VectorField(x -> -x; is_autonomous=true, is_variable=false) + Test.@test vf isa Data.AbstractVectorField + end + end + + # ==================================================================== + # UNIT TESTS - Exports Verification + # ==================================================================== + + Test.@testset "Exports Verification" begin + Test.@testset "Exported types" begin + Test.@test isdefined(Data, :VectorField) + end + end + + # ==================================================================== + # UNIT TESTS - Explicit is_inplace parameter + # ==================================================================== + + Test.@testset "Explicit is_inplace parameter" begin + Test.@testset "is_inplace=true creates InPlace VectorField" begin + # Define an out-of-place function but force InPlace + f(x) = -x + vf = Data.VectorField(f; is_inplace=true) + Test.@test Traits.mutability(vf) === Traits.InPlace + end + + Test.@testset "is_inplace=false creates OutOfPlace VectorField" begin + # Define an in-place function but force OutOfPlace + f(x) = -x + vf = Data.VectorField(f; is_inplace=false) + Test.@test Traits.mutability(vf) === Traits.OutOfPlace + end + end + + # ==================================================================== + # UNIT TESTS - PreconditionError for multiple methods + # ==================================================================== + + Test.@testset "PreconditionError for multiple methods" begin + Test.@testset "Throws PreconditionError when is_inplace is not specified" begin + Test.@test_throws Exceptions.PreconditionError Data.VectorField(_multi_method_f) + end + + Test.@testset "No error when is_inplace is explicitly specified" begin + vf = Data.VectorField(_multi_method_f; is_inplace=false) + Test.@test Traits.mutability(vf) === Traits.OutOfPlace + end + end + + # ==================================================================== + # UNIT TESTS - Internal Helpers + # ==================================================================== + + Test.@testset "Internal Helpers" begin + Test.@testset "_oop_arity_vf" begin + Test.@test Data._oop_arity_vf(Traits.Autonomous, Traits.Fixed) == 1 + Test.@test Data._oop_arity_vf(Traits.NonAutonomous, Traits.Fixed) == 2 + Test.@test Data._oop_arity_vf(Traits.Autonomous, Traits.NonFixed) == 2 + Test.@test Data._oop_arity_vf(Traits.NonAutonomous, Traits.NonFixed) == 3 + end + + Test.@testset "_natural_sig_vf helpers" begin + Test.@test Data._natural_sig_vf(Traits.Autonomous, Traits.Fixed, Traits.OutOfPlace) == "f(x)" + Test.@test Data._natural_sig_vf(Traits.NonAutonomous, Traits.Fixed, Traits.OutOfPlace) == "f(t, x)" + Test.@test Data._natural_sig_vf(Traits.Autonomous, Traits.Fixed, Traits.InPlace) == "f(dx, x)" + Test.@test Data._uniform_sig_vf(Traits.OutOfPlace) == "f(t, x, v)" + Test.@test Data._uniform_sig_vf(Traits.InPlace) == "f(dx, t, x, v)" + end + + Test.@testset "_detect_mutability_vf invalid arity" begin + Test.@test_throws Exceptions.IncorrectArgument Data._detect_mutability_vf(_bad_arity_f, Traits.Autonomous, Traits.Fixed) + end + end + end +end + +end # module + +test_vector_field() = TestVectorField.test_vector_field() diff --git a/test/suite/descriptions/test_catalog.jl b/test/suite/descriptions/test_catalog.jl index cb52b011..e0dc5cf2 100644 --- a/test/suite/descriptions/test_catalog.jl +++ b/test/suite/descriptions/test_catalog.jl @@ -4,8 +4,8 @@ using Test: Test import CTBase.Descriptions import CTBase.Exceptions -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_catalog() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "Catalog Operations" begin diff --git a/test/suite/descriptions/test_complete.jl b/test/suite/descriptions/test_complete.jl index f7118e5c..f52d3302 100644 --- a/test/suite/descriptions/test_complete.jl +++ b/test/suite/descriptions/test_complete.jl @@ -4,8 +4,8 @@ using Test: Test import CTBase.Descriptions import CTBase.Exceptions -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_complete() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "Complete Descriptions" begin diff --git a/test/suite/descriptions/test_description_types.jl b/test/suite/descriptions/test_description_types.jl index 2505823c..5cc0da7e 100644 --- a/test/suite/descriptions/test_description_types.jl +++ b/test/suite/descriptions/test_description_types.jl @@ -3,8 +3,8 @@ module TestDescriptionTypes using Test: Test import CTBase.Descriptions -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_description_types() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "Description Types" begin diff --git a/test/suite/descriptions/test_display_description.jl b/test/suite/descriptions/test_display_description.jl index be40573e..e58c13d3 100644 --- a/test/suite/descriptions/test_display_description.jl +++ b/test/suite/descriptions/test_display_description.jl @@ -3,8 +3,8 @@ module TestDisplayDescription using Test: Test import CTBase.Descriptions: Descriptions -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_display_description() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "Description Display" begin diff --git a/test/suite/descriptions/test_integration.jl b/test/suite/descriptions/test_integration.jl index 85dad7bf..6787c59d 100644 --- a/test/suite/descriptions/test_integration.jl +++ b/test/suite/descriptions/test_integration.jl @@ -6,8 +6,8 @@ import CTBase.DevTools: DevTools import CTBase.Exceptions: Exceptions import CTBase.Unicode: Unicode -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true # TOP-LEVEL: Fake type for extension testing struct DummyDocRefTag <: DevTools.AbstractDocumenterReferenceTag end diff --git a/test/suite/descriptions/test_remove.jl b/test/suite/descriptions/test_remove.jl index 86b29a7e..f50d6fed 100644 --- a/test/suite/descriptions/test_remove.jl +++ b/test/suite/descriptions/test_remove.jl @@ -3,8 +3,8 @@ module TestRemove using Test: Test import CTBase.Descriptions -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_remove() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "Remove Symbols" begin diff --git a/test/suite/descriptions/test_similarity.jl b/test/suite/descriptions/test_similarity.jl index 83b2fde4..cb88055d 100644 --- a/test/suite/descriptions/test_similarity.jl +++ b/test/suite/descriptions/test_similarity.jl @@ -3,8 +3,8 @@ module TestSimilarity using Test: Test import CTBase.Descriptions -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_similarity() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "Similarity Utilities" begin diff --git a/test/suite/exceptions/test_exception_display.jl b/test/suite/exceptions/test_exception_display.jl index d5874dec..177f4427 100644 --- a/test/suite/exceptions/test_exception_display.jl +++ b/test/suite/exceptions/test_exception_display.jl @@ -3,8 +3,8 @@ module TestExceptionDisplay using Test: Test import CTBase.Exceptions -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true """ Tests for exception display functions (display.jl) diff --git a/test/suite/exceptions/test_exception_types.jl b/test/suite/exceptions/test_exception_types.jl index 6046f557..134b2908 100644 --- a/test/suite/exceptions/test_exception_types.jl +++ b/test/suite/exceptions/test_exception_types.jl @@ -3,8 +3,8 @@ module TestExceptionTypes using Test: Test import CTBase.Exceptions -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true """ Tests for exception type definitions (types.jl) diff --git a/test/suite/exceptions/test_exceptions.jl b/test/suite/exceptions/test_exceptions.jl index 852782c9..2bacec41 100644 --- a/test/suite/exceptions/test_exceptions.jl +++ b/test/suite/exceptions/test_exceptions.jl @@ -3,8 +3,8 @@ module TestExceptions using Test: Test import CTBase.Exceptions -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_exceptions() diff --git a/test/suite/exceptions/test_extension_error.jl b/test/suite/exceptions/test_extension_error.jl index 23b7fc01..9ededa88 100644 --- a/test/suite/exceptions/test_extension_error.jl +++ b/test/suite/exceptions/test_extension_error.jl @@ -3,8 +3,8 @@ module TestExtensionError using Test: Test import CTBase.Exceptions -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_extension_error() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "ExtensionError Contract Implementation" begin diff --git a/test/suite/extensions/test_coverage_helpers.jl b/test/suite/extensions/test_coverage_helpers.jl index 521992ca..3281863d 100644 --- a/test/suite/extensions/test_coverage_helpers.jl +++ b/test/suite/extensions/test_coverage_helpers.jl @@ -6,8 +6,8 @@ using Coverage: Coverage const CP = Base.get_extension(CTBase, :CoveragePostprocessing) -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_coverage_helpers() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "_reset_coverage_dir" begin diff --git a/test/suite/extensions/test_coverage_post_process.jl b/test/suite/extensions/test_coverage_post_process.jl index ab2e7f9d..e54ea943 100644 --- a/test/suite/extensions/test_coverage_post_process.jl +++ b/test/suite/extensions/test_coverage_post_process.jl @@ -6,8 +6,8 @@ using CTBase: CTBase import CTBase.DevTools import CTBase.Exceptions -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true # TOP-LEVEL: Fake type for stub testing struct DummyCoverageTag <: DevTools.AbstractCoveragePostprocessingTag end diff --git a/test/suite/extensions/test_documenter_reference.jl b/test/suite/extensions/test_documenter_reference.jl index ddca97e1..ca8dfe94 100644 --- a/test/suite/extensions/test_documenter_reference.jl +++ b/test/suite/extensions/test_documenter_reference.jl @@ -5,8 +5,8 @@ using CTBase: CTBase import CTBase.DevTools: DevTools using Documenter: Documenter -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true const DocumenterReference = Base.get_extension(CTBase, :DocumenterReference) const DR = DocumenterReference diff --git a/test/suite/extensions/test_documenter_reference_api.jl b/test/suite/extensions/test_documenter_reference_api.jl index bcd06752..e7ce8b6f 100644 --- a/test/suite/extensions/test_documenter_reference_api.jl +++ b/test/suite/extensions/test_documenter_reference_api.jl @@ -9,8 +9,8 @@ using Documenter: Documenter const DocumenterReference = Base.get_extension(CTBase, :DocumenterReference) const DR = DocumenterReference -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true # Test module for API tests module DocumenterReferenceAPITestMod diff --git a/test/suite/extensions/test_documenter_reference_config_helpers.jl b/test/suite/extensions/test_documenter_reference_config_helpers.jl index 9f3d1d30..80c0959e 100644 --- a/test/suite/extensions/test_documenter_reference_config_helpers.jl +++ b/test/suite/extensions/test_documenter_reference_config_helpers.jl @@ -7,8 +7,8 @@ using Documenter: Documenter const DocumenterReference = Base.get_extension(CTBase, :DocumenterReference) const DR = DocumenterReference -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true # Test module for config helpers module DocumenterReferenceConfigTestMod diff --git a/test/suite/extensions/test_documenter_reference_page_building.jl b/test/suite/extensions/test_documenter_reference_page_building.jl index a6445935..be8b1721 100644 --- a/test/suite/extensions/test_documenter_reference_page_building.jl +++ b/test/suite/extensions/test_documenter_reference_page_building.jl @@ -7,8 +7,8 @@ using Documenter: Documenter const DocumenterReference = Base.get_extension(CTBase, :DocumenterReference) const DR = DocumenterReference -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true # Test module for page building module DocumenterReferencePageTestMod diff --git a/test/suite/extensions/test_documenter_reference_pipeline.jl b/test/suite/extensions/test_documenter_reference_pipeline.jl index eb8cb6b2..9c70fc89 100644 --- a/test/suite/extensions/test_documenter_reference_pipeline.jl +++ b/test/suite/extensions/test_documenter_reference_pipeline.jl @@ -8,8 +8,8 @@ using Documenter: Documenter const DocumenterReference = Base.get_extension(CTBase, :DocumenterReference) const DR = DocumenterReference -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true # Test module for pipeline tests module DocumenterReferencePipelineTestMod diff --git a/test/suite/extensions/test_documenter_reference_symbol_helpers.jl b/test/suite/extensions/test_documenter_reference_symbol_helpers.jl index 7c450d6c..d5855a32 100644 --- a/test/suite/extensions/test_documenter_reference_symbol_helpers.jl +++ b/test/suite/extensions/test_documenter_reference_symbol_helpers.jl @@ -7,8 +7,8 @@ using Documenter: Documenter const DocumenterReference = Base.get_extension(CTBase, :DocumenterReference) const DR = DocumenterReference -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true # Test module for symbol helpers module DocumenterReferenceSymbolTestMod diff --git a/test/suite/extensions/test_documenter_reference_type_formatting.jl b/test/suite/extensions/test_documenter_reference_type_formatting.jl index dd5fbe93..43e6a0ca 100644 --- a/test/suite/extensions/test_documenter_reference_type_formatting.jl +++ b/test/suite/extensions/test_documenter_reference_type_formatting.jl @@ -7,8 +7,8 @@ using Documenter: Documenter const DocumenterReference = Base.get_extension(CTBase, :DocumenterReference) const DR = DocumenterReference -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true # Test module for type formatting module DRTypeFormatTestMod diff --git a/test/suite/extensions/test_documenter_reference_types.jl b/test/suite/extensions/test_documenter_reference_types.jl index 4d913a27..36bd847b 100644 --- a/test/suite/extensions/test_documenter_reference_types.jl +++ b/test/suite/extensions/test_documenter_reference_types.jl @@ -7,8 +7,8 @@ using Documenter: Documenter const DocumenterReference = Base.get_extension(CTBase, :DocumenterReference) const DR = DocumenterReference -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_documenter_reference_types() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "DocType enum" begin diff --git a/test/suite/extensions/test_extension_stubs.jl b/test/suite/extensions/test_extension_stubs.jl index 3b5636ee..eb18da85 100644 --- a/test/suite/extensions/test_extension_stubs.jl +++ b/test/suite/extensions/test_extension_stubs.jl @@ -4,8 +4,8 @@ using Test: Test import CTBase.DevTools import CTBase.Exceptions -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true # Fake tag types for extension stub testing (testing-creation.md §6). # These subtype the abstract tags but are unknown to any extension, diff --git a/test/suite/extensions/test_testrunner.jl b/test/suite/extensions/test_testrunner.jl index aa3c160c..472412ee 100644 --- a/test/suite/extensions/test_testrunner.jl +++ b/test/suite/extensions/test_testrunner.jl @@ -7,8 +7,8 @@ import CTBase.Exceptions const TestRunner = Base.get_extension(CTBase, :TestRunner) -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true struct DummyTestRunnerTag <: DevTools.AbstractTestRunnerTag end diff --git a/test/suite/extensions/test_testrunner_arg_parsing.jl b/test/suite/extensions/test_testrunner_arg_parsing.jl index bf168167..e1d5563e 100644 --- a/test/suite/extensions/test_testrunner_arg_parsing.jl +++ b/test/suite/extensions/test_testrunner_arg_parsing.jl @@ -6,8 +6,8 @@ import CTBase.Exceptions const TestRunner = Base.get_extension(CTBase, :TestRunner) -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_testrunner_arg_parsing() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "_parse_test_args" begin diff --git a/test/suite/extensions/test_testrunner_execution.jl b/test/suite/extensions/test_testrunner_execution.jl index 8be95545..d19b8419 100644 --- a/test/suite/extensions/test_testrunner_execution.jl +++ b/test/suite/extensions/test_testrunner_execution.jl @@ -6,8 +6,8 @@ import CTBase.Exceptions const TestRunner = Base.get_extension(CTBase, :TestRunner) -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_testrunner_execution() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "_run_single_test" begin diff --git a/test/suite/extensions/test_testrunner_progress.jl b/test/suite/extensions/test_testrunner_progress.jl index 81dcee0e..9a3a0336 100644 --- a/test/suite/extensions/test_testrunner_progress.jl +++ b/test/suite/extensions/test_testrunner_progress.jl @@ -5,8 +5,8 @@ using CTBase: CTBase const TestRunner = Base.get_extension(CTBase, :TestRunner) -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_testrunner_progress() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "Progress display" begin diff --git a/test/suite/extensions/test_testrunner_selection.jl b/test/suite/extensions/test_testrunner_selection.jl index a0709138..9ef7f1fb 100644 --- a/test/suite/extensions/test_testrunner_selection.jl +++ b/test/suite/extensions/test_testrunner_selection.jl @@ -5,8 +5,8 @@ using CTBase: CTBase const TestRunner = Base.get_extension(CTBase, :TestRunner) -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_testrunner_selection() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "_select_tests" begin diff --git a/test/suite/extensions/test_testrunner_types.jl b/test/suite/extensions/test_testrunner_types.jl index 3bb36422..962aec56 100644 --- a/test/suite/extensions/test_testrunner_types.jl +++ b/test/suite/extensions/test_testrunner_types.jl @@ -6,8 +6,8 @@ import CTBase.DevTools const TestRunner = Base.get_extension(CTBase, :TestRunner) -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_testrunner_types() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "TestRunInfo" begin diff --git a/test/suite/interpolation/test_interpolation.jl b/test/suite/interpolation/test_interpolation.jl index 7c466b3e..c81c3581 100644 --- a/test/suite/interpolation/test_interpolation.jl +++ b/test/suite/interpolation/test_interpolation.jl @@ -3,8 +3,8 @@ module TestInterpolation using Test: Test import CTBase.Interpolation -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_interpolation() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "Interpolation" begin diff --git a/test/suite/meta/test_code_quality.jl b/test/suite/meta/test_code_quality.jl index ed4d7c6c..ffd80d67 100644 --- a/test/suite/meta/test_code_quality.jl +++ b/test/suite/meta/test_code_quality.jl @@ -4,8 +4,8 @@ using Test: Test using Aqua: Aqua using CTBase: CTBase -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_code_quality() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "Code quality" begin diff --git a/test/suite/options/test_options.jl b/test/suite/options/test_options.jl index ae4696be..ecf071ba 100644 --- a/test/suite/options/test_options.jl +++ b/test/suite/options/test_options.jl @@ -8,6 +8,7 @@ using CTBase.Options # For testing exported symbols const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true + const CurrentModule = TestOptions """ diff --git a/test/suite/options/test_options_value.jl b/test/suite/options/test_options_value.jl index 15e53253..12f05d68 100644 --- a/test/suite/options/test_options_value.jl +++ b/test/suite/options/test_options_value.jl @@ -4,6 +4,7 @@ using Test: Test import CTBase.Exceptions using CTBase: CTBase import CTBase.Options + const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true diff --git a/test/suite/traits/test_abstract_traits.jl b/test/suite/traits/test_abstract_traits.jl index aba1d0d1..796f747f 100644 --- a/test/suite/traits/test_abstract_traits.jl +++ b/test/suite/traits/test_abstract_traits.jl @@ -3,8 +3,8 @@ module TestAbstractTraits import Test import CTBase.Traits -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_abstract_traits() Test.@testset "Abstract Traits Tests" verbose=VERBOSE showtiming=SHOWTIMING begin diff --git a/test/suite/traits/test_ad.jl b/test/suite/traits/test_ad.jl index 39fdc4a4..7aa2b998 100644 --- a/test/suite/traits/test_ad.jl +++ b/test/suite/traits/test_ad.jl @@ -3,8 +3,8 @@ module TestAD import Test import CTBase.Traits -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_ad() Test.@testset "AD Trait Tests" verbose=VERBOSE showtiming=SHOWTIMING begin diff --git a/test/suite/traits/test_dynamics.jl b/test/suite/traits/test_dynamics.jl index c2e463d4..3968303e 100644 --- a/test/suite/traits/test_dynamics.jl +++ b/test/suite/traits/test_dynamics.jl @@ -3,8 +3,8 @@ module TestDynamics import Test import CTBase.Traits -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_dynamics() Test.@testset "Dynamics Trait Tests" verbose=VERBOSE showtiming=SHOWTIMING begin diff --git a/test/suite/traits/test_mode.jl b/test/suite/traits/test_mode.jl index d8d7ce1d..dd179ae8 100644 --- a/test/suite/traits/test_mode.jl +++ b/test/suite/traits/test_mode.jl @@ -3,8 +3,8 @@ module TestMode import Test import CTBase.Traits -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_mode() Test.@testset "Mode Trait Tests" verbose=VERBOSE showtiming=SHOWTIMING begin diff --git a/test/suite/traits/test_mutability.jl b/test/suite/traits/test_mutability.jl index 3c9350b1..fcaeacc2 100644 --- a/test/suite/traits/test_mutability.jl +++ b/test/suite/traits/test_mutability.jl @@ -4,8 +4,8 @@ import Test import CTBase.Exceptions import CTBase.Traits -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true # ============================================================================== # Fake types for contract testing diff --git a/test/suite/traits/test_time_dependence.jl b/test/suite/traits/test_time_dependence.jl index 801b09a5..389f4598 100644 --- a/test/suite/traits/test_time_dependence.jl +++ b/test/suite/traits/test_time_dependence.jl @@ -4,8 +4,8 @@ import Test import CTBase.Exceptions import CTBase.Traits -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true # ============================================================================== # Fake types for contract testing diff --git a/test/suite/traits/test_traits_module.jl b/test/suite/traits/test_traits_module.jl index 13098529..db9a95f1 100644 --- a/test/suite/traits/test_traits_module.jl +++ b/test/suite/traits/test_traits_module.jl @@ -24,8 +24,8 @@ import CTBase import CTBase.Traits using CTBase.Traits # For testing exported symbols -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true const CurrentModule = TestTraitsModule diff --git a/test/suite/traits/test_variable_costate.jl b/test/suite/traits/test_variable_costate.jl index c85a0a0e..0b6f476e 100644 --- a/test/suite/traits/test_variable_costate.jl +++ b/test/suite/traits/test_variable_costate.jl @@ -3,8 +3,8 @@ module TestVariableCostate import Test import CTBase.Traits -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_variable_costate() Test.@testset "Variable Costate Trait Tests" verbose=VERBOSE showtiming=SHOWTIMING begin diff --git a/test/suite/traits/test_variable_dependence.jl b/test/suite/traits/test_variable_dependence.jl index c9aff8d8..28920524 100644 --- a/test/suite/traits/test_variable_dependence.jl +++ b/test/suite/traits/test_variable_dependence.jl @@ -4,8 +4,8 @@ import Test import CTBase.Exceptions import CTBase.Traits -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true # ============================================================================== # Fake types for contract testing diff --git a/test/suite/unicode/test_subscripts.jl b/test/suite/unicode/test_subscripts.jl index 11e4d271..aabb8977 100644 --- a/test/suite/unicode/test_subscripts.jl +++ b/test/suite/unicode/test_subscripts.jl @@ -4,8 +4,8 @@ using Test: Test import CTBase.Exceptions: Exceptions import CTBase.Unicode: Unicode -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_subscripts() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "ctindice (single subscript char)" begin diff --git a/test/suite/unicode/test_superscripts.jl b/test/suite/unicode/test_superscripts.jl index 85e45f69..3d059ff1 100644 --- a/test/suite/unicode/test_superscripts.jl +++ b/test/suite/unicode/test_superscripts.jl @@ -4,8 +4,8 @@ using Test: Test import CTBase.Exceptions: Exceptions import CTBase.Unicode: Unicode -const VERBOSE = isdefined(Main, :TestOptions) ? Main.TestOptions.VERBOSE : true -const SHOWTIMING = isdefined(Main, :TestOptions) ? Main.TestOptions.SHOWTIMING : true +const VERBOSE = isdefined(Main, :TestData) ? Main.TestData.VERBOSE : true +const SHOWTIMING = isdefined(Main, :TestData) ? Main.TestData.SHOWTIMING : true function test_superscripts() Test.@testset verbose = VERBOSE showtiming = SHOWTIMING "ctupperscript (single superscript char)" begin