Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions ext/CTSolversCUDA.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
"""
CTSolversCUDA Extension

Extension providing CUDA backend functionality for Exa modeler.
Implements GPU-specific backend defaults and consistency validation.
Extension providing CUDA functionality for CTSolvers:
- `Modelers.Exa` GPU backend defaults and backend consistency validation;
- `Integrators.SciML{P}` initial-condition/device consistency validation.
"""

module CTSolversCUDA

import CTSolvers.Modelers
import CTSolvers.Integrators
import CTBase.Strategies
using CUDA: CUDA
using DocStringExtensions: DocStringExtensions
Expand Down Expand Up @@ -91,4 +93,41 @@ function Modelers.__consistent_backend(::Type{Strategies.GPU}, backend::CUDA.CUD
return true
end

# ============================================================================
# SciML integrator — initial-condition / device consistency
# ============================================================================

"""
$(DocStringExtensions.TYPEDSIGNATURES)

A device `CuArray` initial condition is inconsistent with a `SciML{CPU}` integrator.

Overrides the `true`-returning stub in `CTSolvers.Integrators`. Other host-array cases fall
through to the default (returns `true`).
"""
function Integrators.__consistent_initial_condition(::Type{Strategies.CPU}, u0::CUDA.AnyCuArray)
return false
end

"""
$(DocStringExtensions.TYPEDSIGNATURES)

A host `Array` initial condition is inconsistent with a `SciML{GPU}` integrator (the state
must live on the device).

Overrides the `true`-returning stub in `CTSolvers.Integrators`.
"""
function Integrators.__consistent_initial_condition(::Type{Strategies.GPU}, u0::Array)
return false
end

"""
$(DocStringExtensions.TYPEDSIGNATURES)

A device `CuArray` initial condition is consistent with a `SciML{GPU}` integrator.
"""
function Integrators.__consistent_initial_condition(::Type{Strategies.GPU}, u0::CUDA.AnyCuArray)
return true
end

end # module CTSolversCUDA
19 changes: 13 additions & 6 deletions ext/CTSolversSciMLIntegrator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,18 @@ end
"""
$(TYPEDSIGNATURES)

Return metadata defining `Integrators.SciML` options and their specifications.
Return metadata defining `Integrators.SciML{P}` options and their specifications.

The `internalnorm` option defaults to `real_norm`, which extracts the primal (Float64)
part of ForwardDiff dual numbers to ensure grid invariance (IND) when ForwardDiff is loaded.

The metadata is specialized on the execution device `P` (`CPU`/`GPU`); the option set is
currently identical for both — `P` marks the seam where GPU-specific defaults/validators land
as they are discovered. The bare `metadata(SciML)` (core) delegates here through `SciML{CPU}`.
"""
function Strategies.metadata(::Type{Integrators.SciML})
function Strategies.metadata(
::Type{Integrators.SciML{P}}
) where {P<:Union{Strategies.CPU,Strategies.GPU}}
return Strategies.StrategyMetadata(
Strategies.OptionDefinition(;
name=:alg,
Expand Down Expand Up @@ -297,6 +303,7 @@ cached dictionaries `options_point` (`:auto` → `false`) and `options_trajector

# Arguments
- `::Type{Integrators.SciMLTag}`: The SciML integrator tag type.
- `::Type{P}`: The execution device parameter (`CPU`/`GPU`); threaded into the built `SciML{P}`.
- `mode::Symbol`: Validation mode for strategy options (`:strict` or `:permissive`).
- `kwargs...`: User-provided option values. Explicit `true`/`false` override `:auto` resolution.

Expand All @@ -310,9 +317,9 @@ cached dictionaries `options_point` (`:auto` → `false`) and `options_trajector
See also: [`CTSolvers.Integrators.SciML`](@ref).
"""
function Integrators.build_sciml_integrator(
::Type{Integrators.SciMLTag}; mode::Symbol=:strict, kwargs...
)
opts = Strategies.build_strategy_options(Integrators.SciML; mode=mode, kwargs...)
::Type{Integrators.SciMLTag}, ::Type{P}; mode::Symbol=:strict, kwargs...
) where {P<:Strategies.AbstractStrategyParameter}
opts = Strategies.build_strategy_options(Integrators.SciML{P}; mode=mode, kwargs...)
raw = Strategies.options_dict(opts)

# Check if algorithm is missing and raise PreconditionError
Expand Down Expand Up @@ -342,7 +349,7 @@ function Integrators.build_sciml_integrator(
get(options_trajectory, key, :auto) === :auto && (options_trajectory[key] = true)
end

return Integrators.SciML{typeof(opts),typeof(options_point),typeof(options_trajectory)}(
return Integrators.SciML{P,typeof(opts),typeof(options_point),typeof(options_trajectory)}(
opts, options_point, options_trajectory
)
end
Expand Down
120 changes: 109 additions & 11 deletions src/Integrators/sciml.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ and solve) is provided by the `CTSolversSciMLIntegrator` package extension; this
file declares the type and **stubs** that throw `ExtensionError` until the
extension is loaded.

Parameterized on the execution device `P`:
- `SciML{CPU}`: CPU execution (default);
- `SciML{GPU}`: GPU execution (state on device arrays, e.g. `CuArray`).

`SciML(...)` builds a `SciML{CPU}` — the device parameterization is fully backward
compatible with existing call sites.

To activate the extension, load any of:
- `using OrdinaryDiffEqTsit5` (minimal)
- `using OrdinaryDiffEq`
Expand All @@ -53,7 +60,7 @@ To activate the extension, load any of:

$(TYPEDFIELDS)
"""
struct SciML{O<:Strategies.StrategyOptions,OP<:Dict{Symbol,Any},OT<:Dict{Symbol,Any}} <:
struct SciML{P<:Union{CPU,GPU},O<:Strategies.StrategyOptions,OP<:Dict{Symbol,Any},OT<:Dict{Symbol,Any}} <:
AbstractSciMLIntegrator
"Validated option bundle."
options::O
Expand All @@ -77,17 +84,45 @@ Strategies.id(::Type{<:SciML}) = :sciml
"""
$(TYPEDSIGNATURES)

Return the execution parameter type of the `SciML` integrator.
Return the execution parameter of the non-parameterized `SciML` type: `nothing`.

The bare `SciML` (device unspecified) resolves to `nothing`, matching the general
`AbstractStrategy` contract and preserving backward compatibility for consumers that
register `SciML` without a parameter (e.g. CTFlows' flow registry) and query
`parameter` on the bare type. A concrete `SciML{P}` resolves to `P` via the more
specific method below.

See also: [`CTSolvers.Integrators.SciML`](@ref), [`CTBase.Strategies.default_parameter`](@extref)
"""
Strategies.parameter(::Type{<:SciML}) = nothing

"""
$(TYPEDSIGNATURES)

Return the execution parameter type of a parameterized `SciML{P}` integrator.

Returns `nothing` because `SciML` is a non-parameterized strategy: it has no
execution parameter and does not require one.
Extracts the type parameter `P` from `SciML{P}`, which can be either `CPU` or `GPU`
since `SciML` supports both execution devices. More specific than the bare
`parameter(::Type{<:SciML})` above, so it wins for a concrete `SciML{P}`.

# Returns
- `Nothing`: `SciML` is not parameterized.
- `Type{<:Union{CPU,GPU}}`: the execution parameter type.

See also: [`CTSolvers.Integrators.SciML`](@ref)
See also: [`CTSolvers.Integrators.SciML`](@ref), [`CTBase.Strategies.CPU`](@extref), [`CTBase.Strategies.GPU`](@extref)
"""
Strategies.parameter(::Type{<:SciML}) = nothing
Strategies.parameter(::Type{<:SciML{P}}) where {P<:Union{CPU,GPU}} = P

"""
$(TYPEDSIGNATURES)

Return the default execution parameter for `SciML` when none is specified.

Returns `CPU`, so `SciML(...)` builds a `SciML{CPU}` and every existing call site is
unaffected by the device parameterization.

See also: [`CTSolvers.Integrators.SciML`](@ref), [`CTBase.Strategies.CPU`](@extref)
"""
Strategies.default_parameter(::Type{<:SciML}) = CPU

"""
$(TYPEDSIGNATURES)
Expand Down Expand Up @@ -129,8 +164,8 @@ options_trajectory(integ::SciML) = integ.options_trajectory
"""
$(TYPEDSIGNATURES)

Construct a `SciML` integrator. Delegates to `build_sciml_integrator`, which
is overridden by the `CTSolversSciMLIntegrator` package extension.
Construct a `SciML{CPU}` integrator (the default device). Equivalent to
`SciML{CPU}(...)`; delegates through [`CTBase.Strategies.default_parameter`](@extref).

# Arguments
- `mode::Symbol=:strict`: Validation mode (`:strict` or `:permissive`).
Expand All @@ -142,7 +177,28 @@ is overridden by the `CTSolversSciMLIntegrator` package extension.
See also: [`CTSolvers.Integrators.SciML`](@ref), [`CTSolvers.Integrators.build_sciml_integrator`](@ref).
"""
function SciML(; mode::Symbol=:strict, kwargs...)
return build_sciml_integrator(SciMLTag; mode=mode, kwargs...)
P = Strategies.default_parameter(SciML)
return SciML{P}(; mode=mode, kwargs...)
end

"""
$(TYPEDSIGNATURES)

Construct a parameterized `SciML{P}` integrator for the execution device `P`
(`CPU` or `GPU`). Delegates to `build_sciml_integrator`, which is overridden by the
`CTSolversSciMLIntegrator` package extension.

# Arguments
- `mode::Symbol=:strict`: Validation mode (`:strict` or `:permissive`).
- `kwargs...`: Options forwarded to the integrator builder (see extension documentation).

# Throws
- `CTBase.Exceptions.ExtensionError`: If the `CTSolversSciMLIntegrator` extension is not loaded.

See also: [`CTSolvers.Integrators.SciML`](@ref), [`CTSolvers.Integrators.build_sciml_integrator`](@ref).
"""
function SciML{P}(; mode::Symbol=:strict, kwargs...) where {P<:AbstractStrategyParameter}
return build_sciml_integrator(SciMLTag, P; mode=mode, kwargs...)
end

"""
Expand All @@ -152,7 +208,9 @@ Stub builder for `SciML`. The real implementation is provided by
`CTSolversSciMLIntegrator`; this stub throws `ExtensionError` until the extension
is loaded.
"""
function build_sciml_integrator(::Type{<:Core.AbstractTag}; kwargs...)
function build_sciml_integrator(
::Type{<:Core.AbstractTag}, ::Type{<:AbstractStrategyParameter}; kwargs...
)
return throw(
Exceptions.ExtensionError(
:OrdinaryDiffEqTsit5;
Expand Down Expand Up @@ -188,6 +246,21 @@ end
"""
$(TYPEDSIGNATURES)

Fallback for the non-parameterized `SciML` type that delegates to `SciML{CPU}`.

Preserves backward compatibility for `metadata(SciML)` once the extension defines only
the parameterized `metadata(SciML{P})`. Delegates through
[`CTBase.Strategies.default_parameter`](@extref).

See also: [`CTSolvers.Integrators.SciML`](@ref), [`CTBase.Strategies.StrategyMetadata`](@extref).
"""
function Strategies.metadata(::Type{SciML})
return Strategies.metadata(SciML{Strategies.default_parameter(SciML)})
end

"""
$(TYPEDSIGNATURES)

Return the default SciML ODE algorithm for the given tag type.

This stub returns `missing` for the abstract tag type. The actual implementation
Expand All @@ -201,3 +274,28 @@ See also: [`CTSolvers.Integrators.SciML`](@ref), [`CTSolvers.Integrators.Tsit5Ta
function __default_sciml_algorithm(::Type{<:Core.AbstractTag})
return missing
end

"""
$(TYPEDSIGNATURES)

Check whether an initial condition `u0` is consistent with the execution parameter `P`
of a `SciML{P}` integrator.

Mirrors `Modelers.__consistent_backend`: the default returns `true` (all combinations
allowed); the `CTSolversCUDA` extension adds the device-aware methods that flag a `CuArray`
`u0` under `SciML{CPU}`, or a host `Array` `u0` under `SciML{GPU}`. The seam is defined here,
next to the integrator; the consuming package (e.g. CTFlows) calls it where a concrete `u0`
is available (problem construction / solve), since `SciML` does not receive `u0` at build time.

# Arguments
- `parameter_type::Type{<:AbstractStrategyParameter}`: `CPU` or `GPU`.
- `u0`: The initial condition array to check.

# Returns
- `Bool`: `true` if consistent, `false` otherwise.

See also: [`CTSolvers.Integrators.SciML`](@ref), [`CTBase.Strategies.CPU`](@extref), [`CTBase.Strategies.GPU`](@extref).
"""
function __consistent_initial_condition(::Type{<:AbstractStrategyParameter}, u0)
return true
end
5 changes: 3 additions & 2 deletions test/suite/integrators/test_integrator_stubs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module TestIntegratorExtensionStubs
using Test: Test
import CTBase.Core
import CTBase.Exceptions
import CTBase.Strategies
import CTSolvers.Integrators
using CommonSolve: CommonSolve

Expand Down Expand Up @@ -34,12 +35,12 @@ function test_integrator_stubs()

Test.@testset "build_sciml_integrator stub" begin
Test.@test_throws Exceptions.ExtensionError Integrators.build_sciml_integrator(
DummyTag
DummyTag, Strategies.CPU
)

err = nothing
try
Integrators.build_sciml_integrator(DummyTag)
Integrators.build_sciml_integrator(DummyTag, Strategies.CPU)
catch e
err = e
end
Expand Down
Loading