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
2 changes: 2 additions & 0 deletions src/Flows/Flows.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import ..Solutions: Solutions

include(joinpath(@__DIR__, "abstract_flow.jl"))
include(joinpath(@__DIR__, "flow.jl"))
include(joinpath(@__DIR__, "registry.jl"))
include(joinpath(@__DIR__, "flow_routing.jl"))
include(joinpath(@__DIR__, "building.jl"))
include(joinpath(@__DIR__, "calling.jl"))

Expand Down
102 changes: 102 additions & 0 deletions src/Flows/building.jl
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,105 @@ function Flow(data::Data.HamiltonianVectorField; state_dimension::Union{Int, Not
return build_flow(system, integrator)
end

"""
$(TYPEDSIGNATURES)

High-level constructor for `HamiltonianFlow` from a scalar Hamiltonian.

This constructor builds a complete Hamiltonian flow by:
1. Routing keyword options to the appropriate strategy families (backend and integrator)
2. Building a concrete AD backend and integrator from the routed options
3. Building a `HamiltonianSystem` from the Hamiltonian and backend
4. Combining them into a callable `HamiltonianFlow`

# Arguments
- `h::CTFlows.Data.AbstractHamiltonian`: The scalar Hamiltonian function.
- `kwargs...`: Keyword options passed to the backend and integrator strategies.
Options are automatically routed based on their names:
- Backend options (e.g., `ad_backend`, `prepare_cache`) → `:di` strategy
- Integrator options (e.g., `reltol`, `abstol`, `alg`) → `:sciml` strategy

# Returns
- `CTFlows.Flows.HamiltonianFlow`: The complete Hamiltonian flow ready for integration.

# Throws
- [`CTBase.Exceptions.IncorrectArgument`](@extref): If an option is unknown, ambiguous,
or routed to the wrong strategy.
- [`CTBase.Exceptions.ExtensionError`](@extref): If the `CTFlowsSciML` extension is not loaded
(required for `:sciml` strategy metadata).

# Example
```julia
using CTFlows.Data, CTFlows.Flows

h = Data.Hamiltonian((t, x, p, v) -> 0.5 * (x[1]^2 + p[1]^2); is_autonomous=true, is_variable=false)
flow = Flows.Flow(h; reltol=1e-8, ad_backend=ADTypes.AutoForwardDiff())
# flow isa CTFlows.Flows.HamiltonianFlow
```

# Notes
- The state dimension is inferred from the Hamiltonian's signature.
- Use the `state_dimension` argument overload if explicit dimension is needed.
- Requires the `CTFlowsSciML` extension to be loaded for integrator options.

See also: [`CTFlows.Flows.HamiltonianFlow`](@ref), [`CTFlows.Systems.build_system`](@ref),
[`_route_flow_options`](@ref), [`_build_flow_components`](@ref)
"""
function Flow(h::Data.AbstractHamiltonian; kwargs...)
routed = _route_flow_options(kwargs)
components = _build_flow_components(routed)
sys = Systems.build_system(h, components.backend)
return build_flow(sys, components.integrator)
end

"""
$(TYPEDSIGNATURES)

High-level constructor for `HamiltonianFlow` from a scalar Hamiltonian with explicit state dimension.

This constructor builds a complete Hamiltonian flow by:
1. Routing keyword options to the appropriate strategy families (backend and integrator)
2. Building a concrete AD backend and integrator from the routed options
3. Building a `HamiltonianSystem` from the Hamiltonian and backend with explicit state dimension
4. Combining them into a callable `HamiltonianFlow`

# Arguments
- `h::CTFlows.Data.AbstractHamiltonian`: The scalar Hamiltonian function.
- `state_dimension::Int`: The state dimension (number of state variables, not including costates).
- `kwargs...`: Keyword options passed to the backend and integrator strategies.
Options are automatically routed based on their names:
- Backend options (e.g., `ad_backend`, `prepare_cache`) → `:di` strategy
- Integrator options (e.g., `reltol`, `abstol`, `alg`) → `:sciml` strategy

# Returns
- `CTFlows.Flows.HamiltonianFlow`: The complete Hamiltonian flow ready for integration.

# Throws
- [`CTBase.Exceptions.IncorrectArgument`](@extref): If an option is unknown, ambiguous,
or routed to the wrong strategy.
- [`CTBase.Exceptions.ExtensionError`](@extref): If the `CTFlowsSciML` extension is not loaded
(required for `:sciml` strategy metadata).

# Example
```julia
using CTFlows.Data, CTFlows.Flows

h = Data.Hamiltonian((t, x, p, v) -> 0.5 * (x[1]^2 + p[1]^2); is_autonomous=true, is_variable=false)
flow = Flows.Flow(h, 1; reltol=1e-8, ad_backend=ADTypes.AutoForwardDiff())
# flow isa CTFlows.Flows.HamiltonianFlow
```

# Notes
- Use this overload when the state dimension cannot be inferred from the Hamiltonian's signature.
- Requires the `CTFlowsSciML` extension to be loaded for integrator options.

See also: [`CTFlows.Flows.HamiltonianFlow`](@ref), [`CTFlows.Systems.build_system`](@ref),
[`Flow(h::AbstractHamiltonian; kwargs...)`](@ref), [`_route_flow_options`](@ref)
"""
function Flow(h::Data.AbstractHamiltonian, state_dimension::Int; kwargs...)
routed = _route_flow_options(kwargs)
components = _build_flow_components(routed)
sys = Systems.build_system(h, components.backend; state_dimension=state_dimension)
return build_flow(sys, components.integrator)
end

119 changes: 119 additions & 0 deletions src/Flows/flow_routing.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
"""
$(TYPEDSIGNATURES)

Return the strategy families used for option routing in flow construction.

The returned `NamedTuple` maps family names to their abstract types, as expected
by [`CTSolvers.Orchestration.route_all_options`](@extref).

# Returns
- `NamedTuple`: `(backend, integrator)` mapped to their abstract types

# Example
```julia
# Get the strategy families for flow construction
fam = Flows._flow_families()
# Returns: (backend = CTFlows.Differentiation.AbstractADBackend, integrator = CTFlows.Integrators.AbstractIntegrator)
```

See also: [`_route_flow_options`](@ref), [`flow_registry`](@ref)
"""
function _flow_families()
return (
backend = Differentiation.AbstractADBackend,
integrator = Integrators.AbstractIntegrator,
)
end

const _FLOW_DESCRIPTION = (:di, :sciml)

"""
$(TYPEDSIGNATURES)

Route all keyword options to the appropriate strategy families for flow construction.

This function wraps [`CTSolvers.Orchestration.route_all_options`](@extref) with the
families specific to CTFlows flow construction. Options are routed to either the
backend family (`:di`) or the integrator family (`:sciml`).

# Arguments
- `kwargs`: All keyword arguments from the user's `Flow` call (strategy options only,
no action-level options).

# Returns
- `NamedTuple` with fields:
- `action`: action-level options (always empty for flows)
- `strategies`: `NamedTuple` with `backend` and `integrator` sub-tuples

# Throws
- [`CTBase.Exceptions.IncorrectArgument`](@extref): If an option is unknown, ambiguous,
or routed to the wrong strategy.

# Example
```julia
# Route options to backend and integrator
routed = Flows._route_flow_options((; reltol=1e-8, ad_backend=ADTypes.AutoForwardDiff()))
# routed.strategies.integrator contains (reltol = 1e-8,)
# routed.strategies.backend contains (ad_backend = AutoForwardDiff(),)
```

# Notes
- This function uses `:description` source mode for user-friendly error messages.
- No action-level options are defined for flows (empty `OptionDefinition` array).

See also: [`_flow_families`](@ref), [`_build_flow_components`](@ref),
[`CTSolvers.Orchestration.route_all_options`](@extref)
"""
function _route_flow_options(kwargs)
return CTSolvers.Orchestration.route_all_options(
_FLOW_DESCRIPTION,
_flow_families(),
CTSolvers.Options.OptionDefinition[],
(; kwargs...),
flow_registry();
source_mode = :description,
)
end

"""
$(TYPEDSIGNATURES)

Build concrete strategy instances from routed options.

Each strategy is constructed via
[`CTSolvers.Orchestration.build_strategy_from_resolved`](@extref) using the options
that were routed to its family by [`_route_flow_options`](@ref).

# Arguments
- `routed`: Result of [`_route_flow_options`](@ref) containing routed option values.

# Returns
- `NamedTuple{(:backend, :integrator)}`: Concrete strategy instances.

# Example
```julia
# Build concrete strategies from routed options
routed = Flows._route_flow_options((; reltol=1e-8))
components = Flows._build_flow_components(routed)
# components.backend isa CTFlows.Differentiation.DifferentiationInterface
# components.integrator isa CTFlows.Integrators.SciML
```

See also: [`_route_flow_options`](@ref), [`flow_registry`](@ref),
[`CTSolvers.Orchestration.build_strategy_from_resolved`](@extref)
"""
function _build_flow_components(routed)
families = _flow_families()
resolved = CTSolvers.Orchestration.resolve_method(
_FLOW_DESCRIPTION, families, flow_registry()
)
backend = CTSolvers.Orchestration.build_strategy_from_resolved(
resolved, :backend, families, flow_registry();
routed.strategies.backend...
)
integrator = CTSolvers.Orchestration.build_strategy_from_resolved(
resolved, :integrator, families, flow_registry();
routed.strategies.integrator...
)
return (backend = backend, integrator = integrator)
end
28 changes: 28 additions & 0 deletions src/Flows/registry.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const _FLOW_REGISTRY = CTSolvers.Strategies.create_registry(
Differentiation.AbstractADBackend => (Differentiation.DifferentiationInterface,),
Integrators.AbstractIntegrator => (Integrators.SciML,),
)

"""
$(TYPEDSIGNATURES)

Return the strategy registry for flow construction.

The registry maps abstract strategy families to their concrete implementations
for automatic differentiation backends and ODE integrators.

# Returns
- `CTSolvers.Strategies.StrategyRegistry`: Registry with `:di` (DifferentiationInterface)
and `:sciml` (SciML) strategies registered.

# Notes
- This registry is used by [`_route_flow_options`](@ref) to resolve and build
concrete strategy instances from keyword arguments.
- The registry is precomputed and cached in `_FLOW_REGISTRY` for performance.

See also: [`_route_flow_options`](@ref), [`_build_flow_components`](@ref),
[`CTSolvers.Strategies.create_registry`](@extref)
"""
function flow_registry()
return _FLOW_REGISTRY
end
Loading
Loading