diff --git a/lib/OrdinaryDiffEqTaylorSeries/LICENSE.md b/lib/OrdinaryDiffEqTaylorSeries/LICENSE.md new file mode 100644 index 0000000000..4a7df96ac5 --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/LICENSE.md @@ -0,0 +1,24 @@ +The OrdinaryDiffEq.jl package is licensed under the MIT "Expat" License: + +> Copyright (c) 2016-2020: ChrisRackauckas, Yingbo Ma, Julia Computing Inc, and +> other contributors: +> +> https://github.com/SciML/OrdinaryDiffEq.jl/graphs/contributors +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all +> copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +> SOFTWARE. diff --git a/lib/OrdinaryDiffEqTaylorSeries/Project.toml b/lib/OrdinaryDiffEqTaylorSeries/Project.toml new file mode 100644 index 0000000000..06506be4c8 --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/Project.toml @@ -0,0 +1,65 @@ +name = "OrdinaryDiffEqTaylorSeries" +uuid = "9c7f1690-dd92-42a3-8318-297ee24d8d39" +authors = ["ParamThakkar123 "] +version = "1.1.0" + +[deps] +DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e" +FastBroadcast = "7034ab61-46d4-4ed7-9d0f-46aef9175898" +GLPK = "60bf3e95-4087-53dc-ae20-288a0d20c6a6" +Hungarian = "e91730f6-4275-51fb-a7a0-7064cfbd3b39" +JuMP = "4076af6c-e467-56ae-b986-b466b2749572" +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +MuladdMacro = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221" +NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56" +OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" +OrdinaryDiffEqCore = "bbf590c4-e513-4bbe-9b18-05decba2e5d8" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a" +Preferences = "21216c6a-2e73-6563-6e65-726566657250" +RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" +Reexport = "189a3867-3050-52da-a836-e630ba90ab69" +SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" +Static = "aedffcd0-7271-4cad-89d0-dc628f76c6d3" +SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b" +Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" +TaylorDiff = "b36ab563-344f-407b-a36a-4f200bebf99c" +TruncatedStacktraces = "781d530d-4396-4725-bb49-402e4bee1e77" + +[compat] +DiffEqBase = "6.152.2" +DiffEqDevTools = "2.44.4" +FastBroadcast = "0.3.5" +GLPK = "1.2.1" +Hungarian = "0.7.0" +JuMP = "1.24.0" +LinearAlgebra = "<0.0.1, 1" +MuladdMacro = "0.2.4" +NLsolve = "4.5.1" +OrdinaryDiffEq = "6.91.0" +OrdinaryDiffEqCore = "1.1" +Plots = "1.40.9" +PrecompileTools = "1.2.1" +Preferences = "1.4.3" +Random = "<0.0.1, 1" +RecursiveArrayTools = "3.27.0" +Reexport = "1.2.2" +SafeTestsets = "0.1.0" +SciMLBase = "2.72.2" +Static = "1.1.1" +SymbolicUtils = "3.15.0" +Symbolics = "6.28.0" +TaylorDiff = "0.3.1" +Test = "<0.0.1, 1" +TruncatedStacktraces = "1.4.0" +julia = "1.10" + +[extras] +DiffEqDevTools = "f3b72e0c-5b89-59e1-b016-84e28bfd966d" +ODEProblemLibrary = "fdc4e326-1af4-4b90-96e7-779fcce2daa5" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["DiffEqDevTools", "Random", "SafeTestsets", "Test", "ODEProblemLibrary"] diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/DAETs_symbolics.jl b/lib/OrdinaryDiffEqTaylorSeries/src/DAETs_symbolics.jl new file mode 100644 index 0000000000..dbe7cb44d0 --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/src/DAETs_symbolics.jl @@ -0,0 +1,223 @@ +module DAETS_utils +using Symbolics +using SymbolicUtils +using Hungarian +using JuMP +using GLPK +using OrdinaryDiffEq: ODEFunction, DAEFunction +using SciMLBase +using LinearAlgebra: UniformScaling + +export signature_matrix, highest_value_transversal, find_offsets, system_jacobian +""" + signature_matrix(eqs, vars, t_ind) -> Matrix{Float64} + +Construct the signature matrix Σ for the system of equations `eqs` with respect to +the variables `vars`. Each entry Σ[i,j] is the (highest) derivative order of `vars[j]` +appearing in eqs[i], or -Inf if it does not appear. + +`eqs` can be a vector of symbolic expressions, an ODEFunction/DAEFunction object, or a function. +`vars` can be a vector of variables or a NullParameters object. +""" +function signature_matrix(eqs, vars, t_ind) + # For non-symbolic inputs, just return a default signature matrix + if !(vars isa Vector) || !(eqs isa Vector) + # Try to determine the system size + n = 1 + + # If eqs is an ODEFunction, try to get size from u_prototype + if eqs isa ODEFunction && hasproperty(eqs, :u_prototype) && !isnothing(eqs.u_prototype) + n = length(eqs.u_prototype) + end + ######################################################### + # TODO: This is a placeholder ########################### + ######################################################### + Σ = zeros(n, n) + for i in 1:n + for j in 1:n + Σ[i,j] = i == j ? 1.0 : 0.0 + end + end + return Σ + end + + # Original implementation for symbolic expressions (probably do not need this but keeping it here for now) + n_eqs = length(eqs) + n_vars = length(vars) + Σ = fill(-Inf, n_eqs, n_vars) # Initialize with -Inf + + for i in 1:n_eqs + for j in 1:n_vars + # Check for each variable in the equation what the highest derivative order is + order = max_derivative_order(eqs[i], vars[j], t_ind) + Σ[i,j] = order + end + end + return Σ +end +""" + max_derivative_order(ex, var, t_ind) + +Returns the highest derivative order of `var` that appears in the symbolic expression `ex`, +using `t_ind` as the independent variable (e.g., time). If `var` does not appear, returns -Inf. +""" +function max_derivative_order(ex, var, t_ind) + # This is also for symbolic expressions. (probably do not need this but keeping it here for now) + # Base case: if ex is exactly var(t_ind), order is 0. + if isequal(ex, var(t_ind)) + return 0 + end + + # If it's a number or unrelated symbol ignore + if ex isa Number || (ex isa Symbol && ex != var) + return -Inf + end + + # Check if ex is a derivative expression. + if iscall(ex) && operation(ex) isa Symbolics.Differential + inner = arguments(ex)[1] # Function being differentiated. + # Recurse + sub_order = max_derivative_order(inner, var, t_ind) + return sub_order == -Inf ? -Inf : 1 + sub_order + end + + # For composite expressions (e.g., sums, products), traverse arguments + if iscall(ex) + best = -Inf + for arg in arguments(ex) + # Recursively check the order of each component + best = max(best, max_derivative_order(arg, var, t_ind)) + end + return best + end + return -Inf +end + +""" + highest_value_transversal(Σ) -> (Vector{Tuple{Int, Int}}, Float64) + +Finds the highest value transversal (HVT) of the signature matrix `Σ` using the Hungarian algorithm. +Returns the transversal as a vector of (row, column) indices and its value. +""" +function highest_value_transversal(Σ::Matrix{Float64}) + n = size(Σ, 1) + + # The Hungarian algorithm minimizes so multiply by -1 to max + cost_matrix = -Σ + assignment = hungarian(cost_matrix)[1] + # Extract transversal and its value. + transversal = [(i, assignment[i]) for i in 1:n] + value = sum(Σ[i, assignment[i]] for i in 1:n) + + return transversal, value +end + + +""" + find_offsets(Σ::Matrix{Float64}, T::Vector{Tuple{Int, Int}}) -> (Vector{Int}, Vector{Int}) + +Finds the canonical offsets `c` and `d` for the signature matrix `Σ` and the highest value transversal `T`. +Returns the vectors `c` and `d`. +""" +function find_offsets(Σ::Matrix{Float64}, T::Vector{Tuple{Int, Int}}) + n = size(Σ, 1) + # Create JuMP model with GLPK solver + model = Model(GLPK.Optimizer) + # Define variables for c and d (offsets) + @variable(model, c[1:n] >= 0, Int) + @variable(model, d[1:n] >= 0, Int) + + # Add constraints for all i, j: d[j] - c[i] >= Σ[i, j] + for i in 1:n + for j in 1:n + if Σ[i, j] != -Inf + @constraint(model, d[j] - c[i] >= Σ[i, j]) + end + end + end + + # Add constraints for equality over transversal + for (i, j) in T + @constraint(model, d[j] - c[i] == Σ[i, j]) + end + + # min sum c and d finds the "canonical" offsets + @objective(model, Min, sum(c) + sum(d)) + optimize!(model) + c_values = value.(c) + d_values = value.(d) + return c_values, d_values +end + + +""" + system_jacobian(eqs, vars, t_ind, c, d, Σ) -> Matrix + +Constructs the System Jacobian matrix J for the system of equations `eqs` with respect to the variables `vars`. +The offsets `c` and `d` and the signature matrix `Σ` are used to determine the structure of the Jacobian. + +Handles both symbolic expressions and ODE/DAE function objects. +""" +function system_jacobian( + eqs, + vars, + t_ind, + c::Vector{Int}, + d::Vector{Int}, + Σ::Matrix{Float64} +) + # Try to create numerical Jacobian + if !(vars isa Vector{<:SymbolicUtils.BasicSymbolic}) || !(eqs isa Vector{<:SymbolicUtils.BasicSymbolic}) + # Get the size from the signature matrix + n = size(Σ, 1) + + # For ODEFunction, we can try to use its jacobian if available + if eqs isa ODEFunction && hasproperty(eqs, :jac) && !isnothing(eqs.jac) + return eqs.jac + else + # Create a default jacobian based on the signature matrix + ######################################################### + # TODO: This is a placeholder. Fix Jacobian Calculation # + ######################################################### + J = zeros(n, n) + for i in 1:n + for j in 1:n + if d[j] - c[i] == Σ[i, j] + J[i, j] = 1.0 # Non-zero entry where the signature matrix indicates + else + J[i, j] = 0.0 + end + end + end + return J + end + end + + # The original implementation for symbolics. + n = length(eqs) + J = zeros(Symbolics.Num, n, n) + + for i in 1:n + for j in 1:n + if d[j] - c[i] == Σ[i, j] + f_i = eqs[i] + x_j = vars[j] + σ_ij = Int(Σ[i, j]) + # Compute the σ_ij-th derivative of x_j + x_j_deriv = x_j(t_ind) + for _ in 1:σ_ij + x_j_deriv = Differential(t_ind)(x_j_deriv) + end + # Compute the partial derivative ∂f_i / ∂x_j^(σ_ij) + J[i, j] = expand_derivatives(Differential(x_j_deriv)(f_i)) + else + # Set J[i, j] = 0 if d[j] - c[i] != Σ[i, j] + J[i, j] = 0 + end + end + end + + return J +end + +end \ No newline at end of file diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl b/lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl new file mode 100644 index 0000000000..399a354c58 --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl @@ -0,0 +1,73 @@ +module OrdinaryDiffEqTaylorSeries + +# Had to add this to make the tests work. There is probably a better way to do this. +include("DAETS_utils.jl") +using .DAETS_utils: signature_matrix, max_derivative_order, highest_value_transversal, + find_offsets, system_jacobian + +export signature_matrix, max_derivative_order, highest_value_transversal, + find_offsets, system_jacobian + +import OrdinaryDiffEqCore: alg_order, alg_stability_size, explicit_rk_docstring, + OrdinaryDiffEqAdaptiveAlgorithm, OrdinaryDiffEqMutableCache, + alg_cache, + OrdinaryDiffEqConstantCache, @fold, trivial_limiter!, + constvalue, @unpack, perform_step!, calculate_residuals, @cache, + calculate_residuals!, _ode_interpolant, _ode_interpolant!, + CompiledFloats, @OnDemandTableauExtract, initialize!, + perform_step!, OrdinaryDiffEqAlgorithm, + DAEAlgorithm, + CompositeAlgorithm, _ode_addsteps!, copyat_or_push!, + AutoAlgSwitch, get_fsalfirstlast, + full_cache, DerivativeOrderNotPossibleError +import Static: False +import MuladdMacro: @muladd +import FastBroadcast: @.. +import RecursiveArrayTools: recursivefill!, recursive_unitless_bottom_eltype +import LinearAlgebra: norm, ldiv!, lu, cond +using TruncatedStacktraces +using TaylorDiff +import DiffEqBase: @def +import OrdinaryDiffEqCore + +using Reexport +@reexport using DiffEqBase + +include("DAETS_symbolics.jl") + +include("algorithms.jl") +include("alg_utils.jl") +include("TaylorSeries_caches.jl") +include("interp_func.jl") +# include("interpolants.jl") +include("TaylorSeries_perform_step.jl") +include("initialize_dae.jl") + +import PrecompileTools +import Preferences + +PrecompileTools.@compile_workload begin + lorenz = OrdinaryDiffEqCore.lorenz + lorenz_oop = OrdinaryDiffEqCore.lorenz_oop + solver_list = [ExplicitTaylor2()] + prob_list = [] + + if Preferences.@load_preference("PrecompileNoSpecialize", false) + push!(prob_list, + ODEProblem{true, SciMLBase.NoSpecialize}(lorenz, [1.0; 0.0; 0.0], (0.0, 1.0))) + push!(prob_list, + ODEProblem{true, SciMLBase.NoSpecialize}(lorenz, [1.0; 0.0; 0.0], (0.0, 1.0), + Float64[])) + end + + for prob in prob_list, solver in solver_list + solve(prob, solver)(5.0) + end + + prob_list = nothing + solver_list = nothing +end + +export ExplicitTaylor2, ExplicitTaylor, DAETS + +end diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl new file mode 100644 index 0000000000..570dc41f31 --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl @@ -0,0 +1,244 @@ +# ------------------------------------------------------------------------------ +# Caches for Explicit Taylor Methods +# ------------------------------------------------------------------------------ +@cache struct ExplicitTaylor2Cache{ + uType, rateType, uNoUnitsType, StageLimiter, StepLimiter, + Thread} <: OrdinaryDiffEqMutableCache + u::uType + uprev::uType + k1::rateType + k2::rateType + k3::rateType + utilde::uType + tmp::uType + atmp::uNoUnitsType + stage_limiter!::StageLimiter + step_limiter!::StepLimiter + thread::Thread +end + +function alg_cache(alg::ExplicitTaylor2, u, rate_prototype, ::Type{uEltypeNoUnits}, + ::Type{uBottomEltypeNoUnits}, ::Type{tTypeNoUnits}, uprev, uprev2, f, t, + dt, reltol, p, calck, + ::Val{true}) where {uEltypeNoUnits, uBottomEltypeNoUnits, tTypeNoUnits} + k1 = zero(rate_prototype) + k2 = zero(rate_prototype) + k3 = zero(rate_prototype) + utilde = zero(u) + atmp = similar(u, uEltypeNoUnits) + recursivefill!(atmp, false) + tmp = zero(u) + ExplicitTaylor2Cache(u, uprev, k1, k2, k3, utilde, tmp, atmp, + alg.stage_limiter!, alg.step_limiter!, alg.thread) +end + +struct ExplicitTaylor2ConstantCache <: OrdinaryDiffEqConstantCache end + +function alg_cache(alg::ExplicitTaylor2, u, rate_prototype, ::Type{uEltypeNoUnits}, + ::Type{uBottomEltypeNoUnits}, ::Type{tTypeNoUnits}, uprev, uprev2, f, t, + dt, reltol, p, calck, + ::Val{false}) where {uEltypeNoUnits, uBottomEltypeNoUnits, tTypeNoUnits} + ExplicitTaylor2ConstantCache() +end + +# FSAL currently not used, providing dummy implementation to satisfy the interface +get_fsalfirstlast(cache::ExplicitTaylor2Cache, u) = (cache.k1, cache.k1) + +@cache struct ExplicitTaylorCache{ + P, uType, rateType, StageLimiter, StepLimiter, + Thread} <: OrdinaryDiffEqMutableCache + order::Val{P} + u::uType + uprev::uType + us::NTuple{P, uType} + ks::NTuple{P, rateType} + stage_limiter!::StageLimiter + step_limiter!::StepLimiter + thread::Thread +end + +function alg_cache(alg::ExplicitTaylor{P}, u, rate_prototype, ::Type{uEltypeNoUnits}, + ::Type{uBottomEltypeNoUnits}, ::Type{tTypeNoUnits}, uprev, uprev2, f, t, + dt, reltol, p, calck, + ::Val{true}) where {P, uEltypeNoUnits, uBottomEltypeNoUnits, tTypeNoUnits} + ks = ntuple(_ -> zero(rate_prototype), Val(P)) + us = ntuple(_ -> zero(u), Val(P)) + ExplicitTaylorCache(Val(P), u, uprev, us, ks, alg.stage_limiter!, alg.step_limiter!, alg.thread) +end + +struct ExplicitTaylorConstantCache{P} <: OrdinaryDiffEqConstantCache end + +function alg_cache(alg::ExplicitTaylor{P}, u, rate_prototype, ::Type{uEltypeNoUnits}, + ::Type{uBottomEltypeNoUnits}, ::Type{tTypeNoUnits}, uprev, uprev2, f, t, + dt, reltol, p, calck, + ::Val{false}) where {P, uEltypeNoUnits, uBottomEltypeNoUnits, tTypeNoUnits} + ExplicitTaylorConstantCache{P}() +end + +# FSAL currently not used, providing dummy implementation to satisfy the interface +get_fsalfirstlast(cache::ExplicitTaylorCache, u) = (cache.ks[1], cache.ks[1]) + + +# ------------------------------------------------------------------------------ +# DAETS Caches +# ------------------------------------------------------------------------------ + + +@cache mutable struct DAETSCache{ + uType, rateType, uNoUnitsType, tTypeNoUnits, StageLimiter, StepLimiter, + Thread, MatType} <: OrdinaryDiffEqMutableCache + u::uType + uprev::uType + k1::rateType + utilde::uType + tmp::uType + atmp::uType + stage_limiter!::StageLimiter + step_limiter!::StepLimiter + thread::Thread + + # DAE-specific + diff_indices::Vector{Int} # Indices for differential vs algebraic variables (0=diff, 1=alg) + jacobian::MatType # System Jacobian from Pantelides algorithm or something else + taylor_coeffs::Vector{uType} # Vector of TCs + error_estimate::tTypeNoUnits # Current error estimate + u_unprojected::uType # Unprojected solution + residual::uType # Residual for projection + correction::uType # Correction for projection +end + +function alg_cache(alg::DAETS, u, rate_prototype, uEltypeNoUnits, uBottomEltypeNoUnits, + tTypeNoUnits, uprev, uprev2, f, t, dt, reltol, p, calck, ::Type{Val{iip}}) where iip + + # Create standard cache components + tmp = similar(u) + atmp = similar(u, uEltypeNoUnits isa Type ? uEltypeNoUnits : eltype(uEltypeNoUnits)) + utilde = similar(u) + k1 = zero(rate_prototype) + + # Initialize the DAE-specific components + n = length(u) + diff_indices = zeros(Int, n) + for i in 1:n + diff_indices[i] = i <= div(n, 2) ? 0 : 1 + end + + jacobian = Matrix{Float64}(I, n, n) + taylor_coeffs = [similar(u) for _ in 1:10] + u_unprojected = similar(u) + residual = similar(u) + correction = similar(u) + + # Use a concrete type for error_estimate + error_estimate = tTypeNoUnits isa Type ? zero(tTypeNoUnits) : 0.0 + + # Create the cache + return DAETSCache( + u, uprev, k1, utilde, tmp, atmp, + alg.stage_limiter!, alg.step_limiter!, alg.thread, + diff_indices, jacobian, taylor_coeffs, error_estimate, + u_unprojected, residual, correction + ) +end + +get_fsalfirstlast(cache::DAETSCache, u) = (cache.k1, cache.k1) + + +# ------------------------------------------------------------------------------ +# DAE non-symbolic system jacobian. +# ------------------------------------------------------------------------------ + + +""" + build_residual_vector(dae_f, blocks, p, t) + +Build the residual vector from the DAE's perspective. +In a high-index system, we might have constraints for each derivative level. +Here is a minimal example for an index-1 DAE, +F(du, u, p, t)=0 => residual dimension = n + +For a higher-order approach, you'd add additional constraints for d2u, etc. +""" +function build_residual_vector(dae_f, blocks, p, t) + du = blocks[2] # the first derivative block + u = blocks[1] + n = length(u) + r = similar(u) # residual + + # Suppose dae_f(du, u, p, t) sets the residual to 0 if consistent: + r .= dae_f(du, u, p, t) # dimension n + + # somehow need to add constrained for higher derivative if/when they exist + + return r +end + +""" + find_first_nonsingular_jacobian!(integrator, dae_f, u0, p, t0; max_order=5, cond_threshold=1e8) + +Uses ForwardDiff to find the first non-singular Jacobian for a DAE + F(du, u, p, t) = 0 +by successively adding higher derivatives (u', u'', etc.) if needed. +Returns the (order, jacobian) and stores `jacobian` in the integrator's field. +""" +function find_first_nonsingular_jacobian!( + integrator, + dae_f::Function, + u0::AbstractVector{<:Number}, + p, + t0::Real; + max_order::Int=5, + cond_threshold::Real=1e8 +) + for order in 0:max_order + # Build initial guess x0 = [u, 0, 0, ...] + x0 = build_initial_guess!(u0, order) + + # Forms the residual (F=0) with up to order-th derivatives + function res_wrapper(x::AbstractVector{<:Real}) + blocks = unpack_state_derivatives(x, u0, order) + return build_residual_vector(dae_f, blocks, p, t0) + end + + # Use ForwardDiff to compute Jacobian of res + J = ForwardDiff.jacobian(res_wrapper, x0) + + if cond(J) < cond_threshold + @info "Found well-conditioned system at order=$order" + integrator.system_jacobian = J + return (order, J) + end + end + + error("Could not find a non-singular Jacobian up to order=$max_order") +end + +""" + build_initial_guess!(u0, order) + +Creates a vector [u0, 0, 0, ...] with space for state and derivatives up to order. + +Returns vector of length (order+1)length(u0). +""" +function build_initial_guess!(u0, order) + n = length(u0) + x0 = zeros(eltype(u0), (order+1)*n) + x0[1:n] .= u0 + return x0 +end + +""" + unpack_state_derivatives(x, u0, order) + +Given x = [u, du, d2u, ...], returns them as a tuple of vectors, blocks[i] being the i-th derivative. +""" +function unpack_state_derivatives(x, u0, order) + n = length(u0) + blocks = Vector{Vector{eltype(u0)}}(undef, order+1) + for k in 0:order + starti = k*n + 1 + endi = (k+1)*n + blocks[k+1] = @view x[starti:endi] + end + return blocks +end \ No newline at end of file diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl new file mode 100644 index 0000000000..45adf11f63 --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl @@ -0,0 +1,484 @@ +using TaylorDiff: TaylorDiff, extract_derivative, extract_derivative! +using LinearAlgebra +using NLsolve + +@inline make_taylor(all::Vararg{X, P}) where {P, X <: AbstractArray} = TaylorArray(Base.first(all), Base.tail(all)) +@inline make_taylor(all::Vararg{X, P}) where {P, X} = TaylorScalar(all) + +function initialize!(integrator, cache::ExplicitTaylor2ConstantCache) + integrator.kshortsize = 3 + integrator.k = typeof(integrator.k)(undef, integrator.kshortsize) +end + +@muladd function perform_step!(integrator, cache::ExplicitTaylor2ConstantCache, repeat_step = false) + @unpack t, dt, uprev, u, f, p = integrator + k1 = f(uprev, p, t) + u1 = make_taylor(uprev, k1) + t1 = TaylorScalar{1}(t, one(t)) + k2 = f(u1, p, t1).partials[1] + u = @.. uprev + dt * k1 + dt^2 / 2 * k2 + OrdinaryDiffEqCore.increment_nf!(integrator.stats, 3) + integrator.k[1] = k1 + integrator.k[2] = k2 + integrator.u = u +end + +function initialize!(integrator, cache::ExplicitTaylor2Cache) + integrator.kshortsize = 3 + resize!(integrator.k, integrator.kshortsize) + # Setup k pointers + integrator.k[1] = cache.k1 + integrator.k[2] = cache.k2 + integrator.k[3] = cache.k3 + return nothing +end + +@muladd function perform_step!(integrator, cache::ExplicitTaylor2Cache, repeat_step = false) + @unpack t, dt, uprev, u, f, p = integrator + @unpack k1, k2, k3, utilde, tmp = cache + + # The following code is written to be fully non-allocating + f(k1, uprev, p, t) + u1 = make_taylor(uprev, k1) + t1 = TaylorScalar{1}(t, one(t)) + out1 = make_taylor(k1, k2) + f(out1, u1, p, t1) + @.. u = uprev + dt * k1 + dt^2 / 2 * k2 + OrdinaryDiffEqCore.increment_nf!(integrator.stats, 3) + return nothing +end + +function initialize!(integrator, cache::ExplicitTaylorConstantCache{P}) where P + integrator.kshortsize = P + integrator.k = typeof(integrator.k)(undef, P) +end + +@muladd function perform_step!(integrator, cache::ExplicitTaylorConstantCache{P}, repeat_step = false) where P + @unpack t, dt, uprev, u, f, p = integrator + us = typeof(u)[] + integrator.k[1] = f(uprev, p, t) + push!(us, integrator.k[1]) + u = @.. uprev + dt * us[1] + dti = dt + for i in 1:P-1 + ui = make_taylor(uprev, us...) + ti = TaylorScalar{i}(t, one(t)) + integrator.k[i + 1] = f(ui, p, ti).partials[i] + push!(us, integrator.k[i + 1] / (i + 1)) + dti *= dt + u += dti * us[i + 1] + end + OrdinaryDiffEqCore.increment_nf!(integrator.stats, P) + integrator.u = u +end + +function initialize!(integrator, cache::ExplicitTaylorCache{P}) where P + integrator.kshortsize = P + resize!(integrator.k, P) + # Setup k pointers + for (i, k) in enumerate(cache.ks) + integrator.k[i] = k + end + return nothing +end + +@muladd function perform_step!(integrator, cache::ExplicitTaylorCache{P}, repeat_step = false) where P + @unpack t, dt, uprev, u, f, p = integrator + @unpack ks, us = cache + + # The following code is written to be fully non-allocating + f(ks[1], uprev, p, t) + @.. us[1] .= ks[1] + @.. u = uprev + dt * us[1] + dti = dt + for i in 1:P-1 + ui = make_taylor(uprev, us[1:i]...) + ti = TaylorScalar{i}(t, one(t)) + outi = make_taylor(ks[1:i+1]...) + f(outi, ui, p, ti) + us[i + 1] .= ks[i + 1] / (i + 1) + dti *= dt + @.. u += dti * us[i + 1] + end + OrdinaryDiffEqCore.increment_nf!(integrator.stats, 3) + return nothing +end + +# ------------------------------------------------------------------------------ +# Differential Algebriac Equation Taylor Series +# ------------------------------------------------------------------------------ + +function initialize!(integrator, cache::DAETSCache) + resize!(cache.xTS, length(integrator.u)) + fill!(cache.xTS, 0.0) + cache.xtrial = zero(integrator.u) # Projected solution + cache.htrial = integrator.dt #Step size + cache.e = 0.0 # Error estimate + cache.tmp = zero(integrator.u) # Temp storage for intermediate calculations + cache.atmp = zero(integrator.u) # Temp storage for error compute + f = integrator.f + vars = integrator.p + t = integrator.t + + #Preprocessing + cache.Σ = signature_matrix(f, vars, t) + T, _ = highest_value_transversal(cache.Σ) + cache.c, cache.d = find_offsets(cache.Σ, T) + cache.J = system_jacobian(f, vars, t, cache.c, cache.d, cache.Σ) + return nothing +end + +# Below are more helper functions for DAETS. Couldn't get these to work with the tests in a different file so I put them here. Probably should be moved to DAETS_utils.jl somehow. +function compute_taylor_coefficients!(integrator, cache) + @unpack t, dt, uprev, u, f, p = integrator + @unpack Σ, c, d, J, xTS, xtrial, htrial, e, tmp = cache + + order = 3 # Set some default order + if hasproperty(integrator.alg, :order) + order = integrator.alg.order + end + + # Debug information + println("Computing Taylor coefficients for order p = ", order) + println("Current time t = ", t) + println("Current step size dt = ", dt) + + # Initialize array for Taylor coefficients + n = length(u) + xcur = zeros(n) # First-order coefficients + + if f isa ODEFunction + f(tmp, uprev, p, t) # Evaluate f at current state + xcur .= tmp # First coefficient is the derivative + ######################################################### + # TODO: Add higher-order coefficients ################### + ######################################################### + # Store the coefficients in the cache + cache.xcur = xcur + + println("Computed first-order coefficients: ", xcur) + return xcur + else + try + for i in 1:n + xcur[i] = TaylorDiff.extract_derivative(f, u, t, dt, i) + println("Coefficient for variable ", i, ": ", xcur[i]) + end + cache.xcur = xcur + return xcur + catch e + # Use finite differences? + println("Warning: Using finite differences for derivatives") + f(tmp, uprev, p, t) # Evaluate f at current state + xcur .= tmp # First coefficient is the derivative + cache.xcur = xcur + return xcur + end + end +end + +function sum_taylor_series!(integrator, cache) + @unpack t, dt, uprev, u, f, p = integrator + @unpack Σ, c, d, J, xTS, xtrial, htrial, e, tmp, xcur = cache + + order = 3 + if hasproperty(integrator.alg, :order) + order = integrator.alg.order + end + + println("Summing Taylor series for order p = ", order) + println("Trial step size htrial = ", htrial) + + # Evaluate the Taylor series at trial time + n = length(u) + for i in 1:n + xTS[i] = 0.0 + end + ets = 0.0 # Error estimate + + if xcur isa Vector{<:Number} + # First-order Taylor approximation: x(t+h) ≈ x(t) + h*x'(t) + for i in 1:n + xTS[i] = uprev[i] + htrial * xcur[i] + end + # Error estimate based on truncation error + ets = norm(xcur) * htrial^2 / 2 + println("Using first-order Taylor approximation") + else + # For higher-order Taylor series (when xcur contains higher derivatives) + # This would be used if xcur was a vector of Taylor series or similar + println("Warning: Higher-order Taylor series not fully implemented") + # Fallback to first-order approximation + for i in 1:n + xTS[i] = uprev[i] + htrial * xcur[i] + end + ets = norm(xcur) * htrial^2 / 2 + end + + println("Taylor series approximation: ", xTS) + println("Error estimate: ", ets) + + # Update the cache + cache.xTS .= xTS + + return xTS, ets +end + +using LinearAlgebra + +function project!(xtrial, xTS, J) + println("Projecting solution onto constraints") + println("Unprojected solution xTS = ", xTS) + println("System Jacobian J = ", J) + + ######################################################### + # TODO: This is a placeholder ########################### + ######################################################### + xtrial .= xTS + + # In a real implementation we need something like: + # try + # xtrial .= J \ xTS + # catch + # # Fallback if the system is singular + # xtrial .= xTS + # end + + println("Projected solution xtrial = ", xtrial) + return xtrial +end + +function compute_error!(e_out, xtrial, xTS, ets) + e = ets + norm(xtrial - xTS) # Combine Taylor series error and projection error + e_out = e + println("Combined error estimate: ", e) + return e +end + +@muladd function perform_step!(integrator, cache::DAETSCache, repeat_step = false) + @unpack t, dt, uprev, u, f, p = integrator + @unpack k1, k2, k3, utilde, tmp, diff_indices, jacobian, taylor_coeffs, error_estimate, + u_unprojected, residual, correction = cache + + # ===== Stepping Algorithm ===== + + # Step 1: Setup current step parameters + tcur = t + dt + htrial = dt + + # Limit the order + order = min(integrator.opts.maxiters, 20) + + # Step 2: ComputeTCs - Compute Taylor coefficients to required order + duprev = tmp + if integrator.iter == 0 && !isempty(integrator.sol.duals) + duprev .= integrator.sol.duals[1] + else + # Use NLsolve for consistent derivatives + function F!(res, x) + f(res, x, uprev, p, t) + end + + result = nlsolve(F!, zeros(length(uprev)), + method=:newton, autodiff=:forward, + ftol=1e-10, iterations=10) + + if converged(result) + duprev .= result.zero + else + error("Failed to find consistent initial derivatives.") + end + end + + # Might need to update the Jacobian here... + compute_taylor_coefficients!(taylor_coeffs, f, uprev, duprev, p, t, htrial, order, jacobian) + + # Step 3: SumTS - Compute unprojected Taylor series solution + evaluate_taylor_series!(u_unprojected, taylor_coeffs, htrial, order) + + # Step 4: Project - Project the Taylor series solution onto the constraints + projection_success = project_solution!(u, u_unprojected, f, p, tcur, jacobian) + + # Check for projection + if !projection_success + @warn "Projection failure in DAE solution" + integrator.dt = 0.5 * dt # Reduce step size and try again + return nothing + end + + # Step 5: Compute error estimate + compute_error_estimate!(error_estimate, u, u_unprojected, taylor_coeffs, order, htrial) + + # Add check to see if we should accept step...? TODO: Check if this is already done by the integrator. + + OrdinaryDiffEqCore.increment_nf!(integrator.stats, order+1) + return nothing +end + +## Helper Functions +function compute_taylor_coefficients!(taylor_coeffs, f, u0, du0, p, t, h, order, jacobian) + if length(taylor_coeffs) < order + 1 + resize!(taylor_coeffs, order + 1) + for i in eachindex(taylor_coeffs) + if i > length(taylor_coeffs) || taylor_coeffs[i] === nothing + taylor_coeffs[i] = similar(u0) + end + end + end + taylor_coeffs[1] .= u0 + taylor_coeffs[2] .= du0 + + n = length(u0) + + # Extract differential vs. algebraic information from the problem structure + # This should ideally come from the cache... : TODO: Add preprocessing to cache. Check how DAEProblem does it. + diff_indices = zeros(Int, n) + + # Try to extract differential/algebraic indices from the problem + if hasfield(typeof(f), :sys) && applicable(hasfield, typeof(f.sys), :differential_vars) + differential_vars = f.sys.differential_vars + for i in 1:n + diff_indices[i] = differential_vars[i] ? 0 : 1 + end + elseif hasfield(typeof(p), :differential_vars) + differential_vars = p.differential_vars + for i in 1:n + diff_indices[i] = differential_vars[i] ? 0 : 1 + end + elseif hasfield(typeof(f), :differential_vars) + differential_vars = f.differential_vars + for i in 1:n + diff_indices[i] = differential_vars[i] ? 0 : 1 + end + else + for i in 1:n + diff_indices[i] = i <= div(n, 2) ? 0 : 1 + end + end + + # Use TaylorDiff for higher-order coefficients + for k in 3:order+1 + u_taylor_values = [taylor_coeffs[i] for i in 1:k-1] + u_taylor = make_taylor(u_taylor_values...) + t_taylor = TaylorScalar{k-2}(t, one(t)) + solve_for_coefficient!(taylor_coeffs[k], jacobian, f, u_taylor, p, t_taylor, k-1, diff_indices) + taylor_coeffs[k] ./= factorial(k-1) + end +end + +function solve_for_coefficient!(coeff, jacobian, f, u_taylor, p, t_taylor, order, diff_indices) + n = length(coeff) + rhs = zeros(n) + + du_taylor_values = Vector{typeof(u_taylor.value)}(undef, length(u_taylor.partials) + 1) + du_taylor_values[1] = zeros(n) + + # Higher-order terms are adjusted based on differential/algebraic structure + for i in 1:length(u_taylor.partials) + du_taylor_values[i+1] = similar(u_taylor.value) + for j in 1:n + if diff_indices[j] == 0 # Diff eqn. + if i < length(u_taylor.partials) + du_taylor_values[i+1][j] = u_taylor.partials[i][j] * factorial(i) + else + du_taylor_values[i+1][j] = 0.0 + end + else # Alg eqn. + # For algebraic constraints, higher derivatives are 0 + du_taylor_values[i+1][j] = 0.0 + end + end + end + + du_taylor = make_taylor(du_taylor_values...) + res = similar(u_taylor.value) + # Use TaylorDiff to automatically compute all needed derivatives. TODO: Check if we need to do this in place or out of place. + if applicable(f, res, du_taylor, u_taylor, p, t_taylor) + # In-place + f(res, du_taylor, u_taylor, p, t_taylor) + else + # Out-of-place + res = f(du_taylor, u_taylor, p, t_taylor) + end + + # Extract the appropriate derivatives for the RHS of our linear system + if hasfield(typeof(res), :partials) && !isempty(res.partials) + for i in 1:n + if diff_indices[i] == 0 # Diff eqn. + # For differential equations, use the order-th derivative + if order < length(res.partials) + rhs[i] = -res.partials[order][i] + end + else # Alg eqn. + # For algebraic constraints, use -1 order + if order-1 < length(res.partials) + rhs[i] = -res.partials[order-1][i] + end + end + end + else + @warn "No partials found in result. Cannot compute higher-order coefficients." + fill!(coeff, 0.0) + return + end + + # Solve the linear system using the provided Jacobian + if isnothing(jacobian) || size(jacobian, 1) != n + @warn "No valid Jacobian available for coefficient calculation." + fill!(coeff, 0.0) + return + end + + try + # J * coeff = rhs + ldiv!(coeff, lu(jacobian), rhs) + catch e + error("Failed to solve for coefficient: $(e)") + end +end + +function evaluate_taylor_series!(u_next, taylor_coeffs, h, order) + u_next .= taylor_coeffs[1] + for i in 1:min(order, length(taylor_coeffs)-1) + u_next .+= h^i .* taylor_coeffs[i+1] + end +end + +function project_solution!(u_projected, u_unprojected, f, p, t, jacobian) + u_projected .= u_unprojected # Start with unprojected solution as initial guess + + # Define residual function for NLsolve + function F!(res, x) + f(res, zeros(length(x)), x, p, t) # evaluate with zero derivatives + end + + # Use NLsolve with Newton's method + result = nlsolve(F!, u_unprojected, # Start from unprojected solution + method=:newton, + autodiff=:forward, + ftol=1e-10, + iterations=10) + + if converged(result) + u_projected .= result.zero + return true + else + # If NLsolve fails, projection failed + @warn "NLsolve failed to project solution onto constraint manifold" + return false + end +end + +# Compute error estimate +function compute_error_estimate!(error_estimate, u_projected, u_unprojected, taylor_coeffs, order, dt) + max_order = length(taylor_coeffs) - 1 + order = min(order, max_order) + + truncation_error = 0.0 + if order+1 <= length(taylor_coeffs) + truncation_error = norm(taylor_coeffs[order+1] * dt^order / factorial(order)) + end + projection_error = norm(u_projected - u_unprojected) + error_estimate = truncation_error + projection_error + return error_estimate +end \ No newline at end of file diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/alg_utils.jl b/lib/OrdinaryDiffEqTaylorSeries/src/alg_utils.jl new file mode 100644 index 0000000000..645c74bc83 --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/src/alg_utils.jl @@ -0,0 +1,8 @@ +alg_order(alg::ExplicitTaylor2) = 2 +alg_stability_size(alg::ExplicitTaylor2) = 1 + +alg_order(alg::ExplicitTaylor{P}) where P = P +alg_stability_size(alg::ExplicitTaylor) = 1 + +alg_order(alg::DAETS) = 2 +alg_stability_size(alg::DAETS) = 1 diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/algorithms.jl b/lib/OrdinaryDiffEqTaylorSeries/src/algorithms.jl new file mode 100644 index 0000000000..dabbdc7a67 --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/src/algorithms.jl @@ -0,0 +1,43 @@ +@doc explicit_rk_docstring( + "A second-order explicit Taylor series method.", + "ExplicitTaylor2") +Base.@kwdef struct ExplicitTaylor2{StageLimiter, StepLimiter, Thread} <: + OrdinaryDiffEqAlgorithm + stage_limiter!::StageLimiter = trivial_limiter! + step_limiter!::StepLimiter = trivial_limiter! + thread::Thread = False() +end +TruncatedStacktraces.@truncate_stacktrace ExplicitTaylor2 3 +# for backwards compatibility +function ExplicitTaylor2(stage_limiter!, step_limiter! = trivial_limiter!) + ExplicitTaylor2(stage_limiter!, step_limiter!, False()) +end + +@doc explicit_rk_docstring( + "An arbitrary-order explicit Taylor series method.", + "ExplicitTaylor2") +Base.@kwdef struct ExplicitTaylor{P, StageLimiter, StepLimiter, Thread} <: + OrdinaryDiffEqAlgorithm + order::Val{P} = Val{1}() + stage_limiter!::StageLimiter = trivial_limiter! + step_limiter!::StepLimiter = trivial_limiter! + thread::Thread = False() +end + +struct DAETS{CS, AD, FDT, ST, CJ, StageLimiter, StepLimiter, Thread} <: DAEAlgorithm{CS, AD, FDT, ST, CJ} + order::CS + adaptive::AD + fdtype::FDT + calck::CJ + tableau::ST + stage_limiter!::StageLimiter + step_limiter!::StepLimiter + thread::Thread +end + +function DAETS(; order = 5, adaptive = false, fdtype = Val(:central), + calck = Val(true), tableau = nothing, + stage_limiter! = trivial_limiter!, step_limiter! = trivial_limiter!, + thread = False()) + DAETS(order, adaptive, fdtype, calck, tableau, stage_limiter!, step_limiter!, thread) +end diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/initialize_dae.jl b/lib/OrdinaryDiffEqTaylorSeries/src/initialize_dae.jl new file mode 100644 index 0000000000..06c1c18fd8 --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/src/initialize_dae.jl @@ -0,0 +1,21 @@ +# Initialize DAE for DAETS algorithm +function OrdinaryDiffEqCore._initialize_dae!(integrator::OrdinaryDiffEqCore.ODEIntegrator{<:DAETS}, + prob::DAEProblem, + alg::OrdinaryDiffEqCore.DefaultInit, + ::Val{true}) + @unpack f, u0, du0, p, tspan = prob + t0 = first(tspan) + + #integrator + integrator.u = u0 + integrator.uprev = copy(u0) + integrator.t = t0 + + #cache + initialize!(integrator, integrator.cache) + integrator.k[1] .= du0 + + # Step: run structural analysis or find jacobian or something TODO: fix this + # Σ, modified_eqs = structural_analysis(f, u0, tspan, p) + return nothing +end \ No newline at end of file diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/interp_func.jl b/lib/OrdinaryDiffEqTaylorSeries/src/interp_func.jl new file mode 100644 index 0000000000..a712cfa1be --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/src/interp_func.jl @@ -0,0 +1,7 @@ +function DiffEqBase.interp_summary(::Type{cacheType}, + dense::Bool) where { + cacheType <: + Union{ExplicitTaylor2Cache, ExplicitTaylor2ConstantCache, DAETSCache +}} + dense ? "specialized 4th order \"free\" interpolation" : "1st order linear" +end diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/interpolants.jl b/lib/OrdinaryDiffEqTaylorSeries/src/interpolants.jl new file mode 100644 index 0000000000..c6e93e36f2 --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/src/interpolants.jl @@ -0,0 +1,352 @@ +RK_WITH_SPECIAL_INTERPOLATIONS = Union{Tsit5ConstantCache, Tsit5Cache} + +function _ode_interpolant(Θ, dt, y₀, y₁, k, + cache::RK_WITH_SPECIAL_INTERPOLATIONS, + idxs, T::Type{Val{D}}, differential_vars) where {D} + throw(DerivativeOrderNotPossibleError()) +end + +function _ode_interpolant!(out, Θ, dt, y₀, y₁, k, + cache::RK_WITH_SPECIAL_INTERPOLATIONS, + idxs, T::Type{Val{D}}, differential_vars) where {D} + throw(DerivativeOrderNotPossibleError()) +end + +""" +Runge–Kutta pairs of order 5(4) satisfying only the first column +simplifying assumption + +Ch. Tsitouras +""" +@def tsit5unpack begin + var"#T#" = constvalue(recursive_unitless_bottom_eltype(y₁)) + r11, r12, r13, r14, r22, r23, r24, r32, r33, r34, r42, r43, r44, r52, r53, r54, r62, r63, r64, r72, r73, r74 = Tsit5Interp(var"#T#") +end + +@def tsit5pre0 begin + @tsit5unpack + Θ² = Θ * Θ + b1Θ = Θ * @evalpoly(Θ, r11, r12, r13, r14) + b2Θ = Θ² * @evalpoly(Θ, r22, r23, r24) + b3Θ = Θ² * @evalpoly(Θ, r32, r33, r34) + b4Θ = Θ² * @evalpoly(Θ, r42, r43, r44) + b5Θ = Θ² * @evalpoly(Θ, r52, r53, r54) + b6Θ = Θ² * @evalpoly(Θ, r62, r63, r64) + b7Θ = Θ² * @evalpoly(Θ, r72, r73, r74) +end + +@muladd function _ode_interpolant(Θ, dt, y₀, y₁, k, cache::Tsit5ConstantCache, + idxs::Nothing, T::Type{Val{0}}, differential_vars::Nothing) + @tsit5pre0 + #@.. broadcast=false y₀ + dt*(k[1]*b1Θ + k[2]*b2Θ + k[3]*b3Θ + k[4]*b4Θ + k[5]*b5Θ + k[6]*b6Θ + k[7]*b7Θ) + return @inbounds y₀ + + dt * (k[1] * b1Θ + k[2] * b2Θ + k[3] * b3Θ + k[4] * b4Θ + + k[5] * b5Θ + k[6] * b6Θ + k[7] * b7Θ) +end + +@muladd function _ode_interpolant(Θ, dt, y₀, y₁, k, cache::Tsit5Cache, idxs::Nothing, + T::Type{Val{0}}, differential_vars::Nothing) + @tsit5pre0 + return @inbounds @.. broadcast=false y₀+dt * (k[1] * b1Θ + k[2] * b2Θ + k[3] * b3Θ + + k[4] * b4Θ + + k[5] * b5Θ + k[6] * b6Θ + k[7] * b7Θ) +end + +@muladd function _ode_interpolant(Θ, dt, y₀, y₁, k, + cache::Union{Tsit5ConstantCache, Tsit5Cache}, idxs, + T::Type{Val{0}}, differential_vars::Nothing) + @tsit5pre0 + return y₀[idxs] + + dt * (k[1][idxs] * b1Θ + k[2][idxs] * b2Θ + k[3][idxs] * b3Θ + + k[4][idxs] * b4Θ + k[5][idxs] * b5Θ + k[6][idxs] * b6Θ + k[7][idxs] * b7Θ) +end + +@muladd function _ode_interpolant!(out, Θ, dt, y₀, y₁, k, + cache::Union{Tsit5ConstantCache, Tsit5Cache}, + idxs::Nothing, T::Type{Val{0}}, differential_vars::Nothing) + @tsit5pre0 + @inbounds @.. broadcast=false out=y₀ + + dt * + (k[1] * b1Θ + k[2] * b2Θ + k[3] * b3Θ + k[4] * b4Θ + + k[5] * b5Θ + k[6] * b6Θ + k[7] * b7Θ) + out +end + +@muladd function _ode_interpolant!(out::Array, Θ, dt, y₀, y₁, k, + cache::Union{Tsit5ConstantCache, Tsit5Cache}, + idxs::Nothing, T::Type{Val{0}}, differential_vars::Nothing) + @tsit5pre0 + @inbounds @simd ivdep for i in eachindex(out) + out[i] = y₀[i] + + dt * (k[1][i] * b1Θ + k[2][i] * b2Θ + k[3][i] * b3Θ + k[4][i] * b4Θ + + k[5][i] * b5Θ + k[6][i] * b6Θ + k[7][i] * b7Θ) + end + out +end + +@muladd function _ode_interpolant!(out, Θ, dt, y₀, y₁, k, + cache::Union{Tsit5ConstantCache, Tsit5Cache}, idxs, + T::Type{Val{0}}, differential_vars::Nothing) + @tsit5pre0 + @views @.. broadcast=false out=y₀[idxs] + + dt * + (k[1][idxs] * b1Θ + k[2][idxs] * b2Θ + k[3][idxs] * b3Θ + + k[4][idxs] * b4Θ + k[5][idxs] * b5Θ + k[6][idxs] * b6Θ + + k[7][idxs] * b7Θ) + #@inbounds for (j,i) in enumerate(idxs) + # out[j] = y₀[i] + dt*(k[1][i]*b1Θ + k[2][i]*b2Θ + k[3][i]*b3Θ + k[4][i]*b4Θ + k[5][i]*b5Θ + k[6][i]*b6Θ + k[7][i]*b7Θ) + #end + out +end + +@muladd function _ode_interpolant!(out::Array, Θ, dt, y₀, y₁, k, + cache::Union{Tsit5ConstantCache, Tsit5Cache}, idxs, + T::Type{Val{0}}, differential_vars::Nothing) + @tsit5pre0 + @inbounds for (j, i) in enumerate(idxs) + out[j] = y₀[i] + + dt * (k[1][i] * b1Θ + k[2][i] * b2Θ + k[3][i] * b3Θ + k[4][i] * b4Θ + + k[5][i] * b5Θ + k[6][i] * b6Θ + k[7][i] * b7Θ) + end + out +end + +@def tsit5pre1 begin + @tsit5unpack + b1Θdiff = @evalpoly(Θ, r11, 2*r12, 3*r13, 4*r14) + b2Θdiff = Θ * @evalpoly(Θ, 2*r22, 3*r23, 4*r24) + b3Θdiff = Θ * @evalpoly(Θ, 2*r32, 3*r33, 4*r34) + b4Θdiff = Θ * @evalpoly(Θ, 2*r42, 3*r43, 4*r44) + b5Θdiff = Θ * @evalpoly(Θ, 2*r52, 3*r53, 4*r54) + b6Θdiff = Θ * @evalpoly(Θ, 2*r62, 3*r63, 4*r64) + b7Θdiff = Θ * @evalpoly(Θ, 2*r72, 3*r73, 4*r74) +end + +@muladd function _ode_interpolant(Θ, dt, y₀, y₁, k, cache::Tsit5ConstantCache, + idxs::Nothing, T::Type{Val{1}}, differential_vars::Nothing) + @tsit5pre1 + # return @.. broadcast=false k[1]*b1Θdiff + k[2]*b2Θdiff + k[3]*b3Θdiff + k[4]*b4Θdiff + k[5]*b5Θdiff + k[6]*b6Θdiff + k[7]*b7Θdiff + return @inbounds k[1] * b1Θdiff + k[2] * b2Θdiff + k[3] * b3Θdiff + k[4] * b4Θdiff + + k[5] * b5Θdiff + k[6] * b6Θdiff + k[7] * b7Θdiff +end + +@muladd function _ode_interpolant(Θ, dt, y₀, y₁, k, cache::Tsit5Cache, idxs::Nothing, + T::Type{Val{1}}, differential_vars::Nothing) + @tsit5pre1 + return @inbounds @.. broadcast=false k[1]*b1Θdiff+k[2]*b2Θdiff+k[3]*b3Θdiff+ + k[4]*b4Θdiff+k[5]*b5Θdiff+k[6]*b6Θdiff+k[7]*b7Θdiff +end + +@muladd function _ode_interpolant(Θ, dt, y₀, y₁, k, + cache::Union{Tsit5ConstantCache, Tsit5Cache}, idxs, + T::Type{Val{1}}, differential_vars::Nothing) + @tsit5pre1 + # return @.. broadcast=false k[1][idxs]*b1Θdiff + k[2][idxs]*b2Θdiff + k[3][idxs]*b3Θdiff + k[4][idxs]*b4Θdiff + k[5][idxs]*b5Θdiff + k[6][idxs]*b6Θdiff + k[7][idxs]*b7Θdiff + return k[1][idxs] * b1Θdiff + k[2][idxs] * b2Θdiff + k[3][idxs] * b3Θdiff + + k[4][idxs] * b4Θdiff + k[5][idxs] * b5Θdiff + k[6][idxs] * b6Θdiff + + k[7][idxs] * b7Θdiff +end + +@muladd function _ode_interpolant!(out, Θ, dt, y₀, y₁, k, + cache::Union{Tsit5ConstantCache, Tsit5Cache}, + idxs::Nothing, T::Type{Val{1}}, differential_vars::Nothing) + @tsit5pre1 + @inbounds @.. broadcast=false out=k[1] * b1Θdiff + k[2] * b2Θdiff + k[3] * b3Θdiff + + k[4] * b4Θdiff + k[5] * b5Θdiff + k[6] * b6Θdiff + + k[7] * b7Θdiff + #@inbounds for i in eachindex(out) + # out[i] = k[1][i]*b1Θdiff + k[2][i]*b2Θdiff + k[3][i]*b3Θdiff + k[4][i]*b4Θdiff + k[5][i]*b5Θdiff + k[6][i]*b6Θdiff + k[7][i]*b7Θdiff + #end + out +end + +@muladd function _ode_interpolant!(out, Θ, dt, y₀, y₁, k, + cache::Union{Tsit5ConstantCache, Tsit5Cache}, idxs, + T::Type{Val{1}}, differential_vars::Nothing) + @tsit5pre1 + @views @.. broadcast=false out=k[1][idxs] * b1Θdiff + k[2][idxs] * b2Θdiff + + k[3][idxs] * b3Θdiff + k[4][idxs] * b4Θdiff + + k[5][idxs] * b5Θdiff + k[6][idxs] * b6Θdiff + + k[7][idxs] * b7Θdiff + #@inbounds for (j,i) in enumerate(idxs) + # out[j] = k[1][i]*b1Θdiff + k[2][i]*b2Θdiff + k[3][i]*b3Θdiff + k[4][i]*b4Θdiff + k[5][i]*b5Θdiff + k[6][i]*b6Θdiff + k[7][i]*b7Θdiff + #end + out +end + +@def tsit5pre2 begin + @tsit5unpack + b1Θdiff2 = @evalpoly(Θ, 2*r12, 6*r13, 12*r14) + b2Θdiff2 = @evalpoly(Θ, 2*r22, 6*r23, 12*r24) + b3Θdiff2 = @evalpoly(Θ, 2*r32, 6*r33, 12*r34) + b4Θdiff2 = @evalpoly(Θ, 2*r42, 6*r43, 12*r44) + b5Θdiff2 = @evalpoly(Θ, 2*r52, 6*r53, 12*r54) + b6Θdiff2 = @evalpoly(Θ, 2*r62, 6*r63, 12*r64) + b7Θdiff2 = @evalpoly(Θ, 2*r72, 6*r73, 12*r74) + invdt = inv(dt) +end + +@muladd function _ode_interpolant(Θ, dt, y₀, y₁, k, + cache::Union{Tsit5ConstantCache, Tsit5Cache}, + idxs::Nothing, T::Type{Val{2}}, differential_vars::Nothing) + @tsit5pre2 + # return @.. broadcast=false k[1]*b1Θdiff2 + k[2]*b2Θdiff2 + k[3]*b3Θdiff2 + k[4]*b4Θdiff2 + k[5]*b5Θdiff2 + k[6]*b6Θdiff2 + k[7]*b7Θdiff2 + return @inbounds (k[1] * b1Θdiff2 + k[2] * b2Θdiff2 + k[3] * b3Θdiff2 + + k[4] * b4Θdiff2 + + k[5] * b5Θdiff2 + k[6] * b6Θdiff2 + k[7] * b7Θdiff2) * invdt +end + +@muladd function _ode_interpolant(Θ, dt, y₀, y₁, k, + cache::Union{Tsit5ConstantCache, Tsit5Cache}, idxs, + T::Type{Val{2}}, differential_vars::Nothing) + @tsit5pre2 + # return @.. broadcast=false k[1][idxs]*b1Θdiff2 + k[2][idxs]*b2Θdiff2 + k[3][idxs]*b3Θdiff2 + k[4][idxs]*b4Θdiff2 + k[5][idxs]*b5Θdiff2 + k[6][idxs]*b6Θdiff2 + k[7][idxs]*b7Θdiff2 + return (k[1][idxs] * b1Θdiff2 + k[2][idxs] * b2Θdiff2 + k[3][idxs] * b3Θdiff2 + + k[4][idxs] * b4Θdiff2 + k[5][idxs] * b5Θdiff2 + k[6][idxs] * b6Θdiff2 + + k[7][idxs] * b7Θdiff2) * invdt +end + +@muladd function _ode_interpolant!(out, Θ, dt, y₀, y₁, k, + cache::Union{Tsit5ConstantCache, Tsit5Cache}, + idxs::Nothing, T::Type{Val{2}}, differential_vars::Nothing) + @tsit5pre2 + @inbounds @.. broadcast=false out=(k[1] * b1Θdiff2 + k[2] * b2Θdiff2 + k[3] * b3Θdiff2 + + k[4] * b4Θdiff2 + k[5] * b5Θdiff2 + k[6] * b6Θdiff2 + + k[7] * b7Θdiff2) * invdt + #@inbounds for i in eachindex(out) + # out[i] = (k[1][i]*b1Θdiff2 + k[2][i]*b2Θdiff2 + k[3][i]*b3Θdiff2 + k[4][i]*b4Θdiff2 + k[5][i]*b5Θdiff2 + k[6][i]*b6Θdiff2 + k[7][i]*b7Θdiff2)*invdt + #end + out +end + +@muladd function _ode_interpolant!(out, Θ, dt, y₀, y₁, k, + cache::Union{Tsit5ConstantCache, Tsit5Cache}, idxs, + T::Type{Val{2}}, differential_vars::Nothing) + @tsit5pre2 + @views @.. broadcast=false out=(k[1][idxs] * b1Θdiff2 + k[2][idxs] * b2Θdiff2 + + k[3][idxs] * b3Θdiff2 + k[4][idxs] * b4Θdiff2 + + k[5][idxs] * b5Θdiff2 + k[6][idxs] * b6Θdiff2 + + k[7][idxs] * b7Θdiff2) * invdt + #@inbounds for (j,i) in enumerate(idxs) + # out[j] = (k[1][i]*b1Θdiff2 + k[2][i]*b2Θdiff2 + k[3][i]*b3Θdiff2 + k[4][i]*b4Θdiff2 + k[5][i]*b5Θdiff2 + k[6][i]*b6Θdiff2 + k[7][i]*b7Θdiff2)*invdt + #end + out +end + +@def tsit5pre3 begin + @tsit5unpack + b1Θdiff3 = @evalpoly(Θ, 6*r13, 24*r14) + b2Θdiff3 = @evalpoly(Θ, 6*r23, 24*r24) + b3Θdiff3 = @evalpoly(Θ, 6*r33, 24*r34) + b4Θdiff3 = @evalpoly(Θ, 6*r43, 24*r44) + b5Θdiff3 = @evalpoly(Θ, 6*r53, 24*r54) + b6Θdiff3 = @evalpoly(Θ, 6*r63, 24*r64) + b7Θdiff3 = @evalpoly(Θ, 6*r73, 24*r74) + invdt2 = inv(dt)^2 +end + +@muladd function _ode_interpolant(Θ, dt, y₀, y₁, k, + cache::Union{Tsit5ConstantCache, Tsit5Cache}, + idxs::Nothing, T::Type{Val{3}}, differential_vars::Nothing) + @tsit5pre3 + # return @.. broadcast=false k[1]*b1Θdiff3 + k[2]*b2Θdiff3 + k[3]*b3Θdiff3 + k[4]*b4Θdiff3 + k[5]*b5Θdiff3 + k[6]*b6Θdiff3 + k[7]*b7Θdiff3 + return @inbounds (k[1] * b1Θdiff3 + k[2] * b2Θdiff3 + k[3] * b3Θdiff3 + + k[4] * b4Θdiff3 + + k[5] * b5Θdiff3 + k[6] * b6Θdiff3 + k[7] * b7Θdiff3) * invdt2 +end + +@muladd function _ode_interpolant(Θ, dt, y₀, y₁, k, + cache::Union{Tsit5ConstantCache, Tsit5Cache}, idxs, + T::Type{Val{3}}, differential_vars::Nothing) + @tsit5pre3 + # return @.. broadcast=false k[1][idxs]*b1Θdiff3 + k[2][idxs]*b2Θdiff3 + k[3][idxs]*b3Θdiff3 + k[4][idxs]*b4Θdiff3 + k[5][idxs]*b5Θdiff3 + k[6][idxs]*b6Θdiff3 + k[7][idxs]*b7Θdiff3 + return (k[1][idxs] * b1Θdiff3 + k[2][idxs] * b2Θdiff3 + k[3][idxs] * b3Θdiff3 + + k[4][idxs] * b4Θdiff3 + k[5][idxs] * b5Θdiff3 + k[6][idxs] * b6Θdiff3 + + k[7][idxs] * b7Θdiff3) * invdt2 +end + +@muladd function _ode_interpolant!(out, Θ, dt, y₀, y₁, k, + cache::Union{Tsit5ConstantCache, Tsit5Cache}, + idxs::Nothing, T::Type{Val{3}}, differential_vars::Nothing) + @tsit5pre3 + @inbounds @.. broadcast=false out=(k[1] * b1Θdiff3 + k[2] * b2Θdiff3 + k[3] * b3Θdiff3 + + k[4] * b4Θdiff3 + k[5] * b5Θdiff3 + k[6] * b6Θdiff3 + + k[7] * b7Θdiff3) * invdt2 + #@inbounds for i in eachindex(out) + # out[i] = (k[1][i]*b1Θdiff3 + k[2][i]*b2Θdiff3 + k[3][i]*b3Θdiff3 + k[4][i]*b4Θdiff3 + k[5][i]*b5Θdiff3 + k[6][i]*b6Θdiff3 + k[7][i]*b7Θdiff3)*invdt2 + #end + out +end + +@muladd function _ode_interpolant!(out, Θ, dt, y₀, y₁, k, + cache::Union{Tsit5ConstantCache, Tsit5Cache}, idxs, + T::Type{Val{3}}, differential_vars::Nothing) + @tsit5pre3 + @views @.. broadcast=false out=(k[1][idxs] * b1Θdiff3 + k[2][idxs] * b2Θdiff3 + + k[3][idxs] * b3Θdiff3 + k[4][idxs] * b4Θdiff3 + + k[5][idxs] * b5Θdiff3 + k[6][idxs] * b6Θdiff3 + + k[7][idxs] * b7Θdiff3) * invdt2 + #@inbounds for (j,i) in enumerate(idxs) + # out[j] = (k[1][i]*b1Θdiff3 + k[2][i]*b2Θdiff3 + k[3][i]*b3Θdiff3 + k[4][i]*b4Θdiff3 + k[5][i]*b5Θdiff3 + k[6][i]*b6Θdiff3 + k[7][i]*b7Θdiff3)*invdt2 + #end + out +end + +@def tsit5pre4 begin + @tsit5unpack + b1Θdiff4 = 24 * r14 + b2Θdiff4 = 24 * r24 + b3Θdiff4 = 24 * r34 + b4Θdiff4 = 24 * r44 + b5Θdiff4 = 24 * r54 + b6Θdiff4 = 24 * r64 + b7Θdiff4 = 24 * r74 + invdt3 = inv(dt)^3 +end + +@muladd function _ode_interpolant(Θ, dt, y₀, y₁, k, + cache::Union{Tsit5ConstantCache, Tsit5Cache}, + idxs::Nothing, T::Type{Val{4}}, differential_vars::Nothing) + @tsit5pre4 + # return @.. broadcast=false k[1]*b1Θdiff4 + k[2]*b2Θdiff4 + k[3]*b3Θdiff4 + k[4]*b4Θdiff4 + k[5]*b5Θdiff4 + k[6]*b6Θdiff4 + k[7]*b7Θdiff4 + return @inbounds (k[1] * b1Θdiff4 + k[2] * b2Θdiff4 + k[3] * b3Θdiff4 + + k[4] * b4Θdiff4 + + k[5] * b5Θdiff4 + k[6] * b6Θdiff4 + k[7] * b7Θdiff4) * invdt3 +end + +@muladd function _ode_interpolant(Θ, dt, y₀, y₁, k, + cache::Union{Tsit5ConstantCache, Tsit5Cache}, idxs, + T::Type{Val{4}}, differential_vars::Nothing) + @tsit5pre4 + # return @.. broadcast=false k[1][idxs]*b1Θdiff4 + k[2][idxs]*b2Θdiff4 + k[3][idxs]*b3Θdiff4 + k[4][idxs]*b4Θdiff4 + k[5][idxs]*b5Θdiff4 + k[6][idxs]*b6Θdiff4 + k[7][idxs]*b7Θdiff4 + return (k[1][idxs] * b1Θdiff4 + k[2][idxs] * b2Θdiff4 + k[3][idxs] * b3Θdiff4 + + k[4][idxs] * b4Θdiff4 + k[5][idxs] * b5Θdiff4 + k[6][idxs] * b6Θdiff4 + + k[7][idxs] * b7Θdiff4) * invdt3 +end + +@muladd function _ode_interpolant!(out, Θ, dt, y₀, y₁, k, + cache::Union{Tsit5ConstantCache, Tsit5Cache}, + idxs::Nothing, T::Type{Val{4}}, differential_vars::Nothing) + @tsit5pre4 + @inbounds @.. broadcast=false out=(k[1] * b1Θdiff4 + k[2] * b2Θdiff4 + k[3] * b3Θdiff4 + + k[4] * b4Θdiff4 + k[5] * b5Θdiff4 + k[6] * b6Θdiff4 + + k[7] * b7Θdiff4) * invdt3 + #@inbounds for i in eachindex(out) + # out[i] = (k[1][i]*b1Θdiff4 + k[2][i]*b2Θdiff4 + k[3][i]*b3Θdiff4 + k[4][i]*b4Θdiff4 + k[5][i]*b5Θdiff4 + k[6][i]*b6Θdiff4 + k[7][i]*b7Θdiff4)*invdt3 + #end + out +end + +@muladd function _ode_interpolant!(out, Θ, dt, y₀, y₁, k, + cache::Union{Tsit5ConstantCache, Tsit5Cache}, idxs, + T::Type{Val{4}}, differential_vars::Nothing) + @tsit5pre4 + @views @.. broadcast=false out=(k[1][idxs] * b1Θdiff4 + k[2][idxs] * b2Θdiff4 + + k[3][idxs] * b3Θdiff4 + k[4][idxs] * b4Θdiff4 + + k[5][idxs] * b5Θdiff4 + k[6][idxs] * b6Θdiff4 + + k[7][idxs] * b7Θdiff4) * invdt3 + #@inbounds for (j,i) in enumerate(idxs) + # out[j] = (k[1][i]*b1Θdiff4 + k[2][i]*b2Θdiff4 + k[3][i]*b3Θdiff4 + k[4][i]*b4Θdiff4 + k[5][i]*b5Θdiff4 + k[6][i]*b6Θdiff4 + k[7][i]*b7Θdiff4)*invdt3 + #end + out +end diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/tsit_tableaus.jl b/lib/OrdinaryDiffEqTaylorSeries/src/tsit_tableaus.jl new file mode 100644 index 0000000000..8d972ce7d6 --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/src/tsit_tableaus.jl @@ -0,0 +1,257 @@ +# struct Tsit5ConstantCache <: OrdinaryDiffEqConstantCache end +# struct Tsit5ConstantCacheActual{T, T2} +# c1::T2 +# c2::T2 +# c3::T2 +# c4::T2 +# c5::T2 +# c6::T2 +# a21::T +# a31::T +# a32::T +# a41::T +# a42::T +# a43::T +# a51::T +# a52::T +# a53::T +# a54::T +# a61::T +# a62::T +# a63::T +# a64::T +# a65::T +# a71::T +# a72::T +# a73::T +# a74::T +# a75::T +# a76::T +# btilde1::T +# btilde2::T +# btilde3::T +# btilde4::T +# btilde5::T +# btilde6::T +# btilde7::T +# end + +# @fold function Tsit5ConstantCacheActual(::Type{T}, +# ::Type{T2}) where {T <: CompiledFloats, +# T2 <: CompiledFloats} +# c1 = convert(T2, 0.161) +# c2 = convert(T2, 0.327) +# c3 = convert(T2, 0.9) +# c4 = convert(T2, 0.9800255409045097) +# c5 = convert(T2, 1) +# c6 = convert(T2, 1) +# a21 = convert(T, 0.161) +# a31 = convert(T, -0.008480655492356989) +# a32 = convert(T, 0.335480655492357) +# a41 = convert(T, 2.8971530571054935) +# a42 = convert(T, -6.359448489975075) +# a43 = convert(T, 4.3622954328695815) +# a51 = convert(T, 5.325864828439257) +# a52 = convert(T, -11.748883564062828) +# a53 = convert(T, 7.4955393428898365) +# a54 = convert(T, -0.09249506636175525) +# a61 = convert(T, 5.86145544294642) +# a62 = convert(T, -12.92096931784711) +# a63 = convert(T, 8.159367898576159) +# a64 = convert(T, -0.071584973281401) +# a65 = convert(T, -0.028269050394068383) +# a71 = convert(T, 0.09646076681806523) +# a72 = convert(T, 0.01) +# a73 = convert(T, 0.4798896504144996) +# a74 = convert(T, 1.379008574103742) +# a75 = convert(T, -3.290069515436081) +# a76 = convert(T, 2.324710524099774) +# # b1 = convert(T,0.09468075576583945) +# # b2 = convert(T,0.009183565540343254) +# # b3 = convert(T,0.4877705284247616) +# # b4 = convert(T,1.234297566930479) +# # b5 = convert(T,-2.7077123499835256) +# # b6 = convert(T,1.866628418170587) +# # b7 = convert(T,0.015151515151515152) +# btilde1 = convert(T, -0.00178001105222577714) +# btilde2 = convert(T, -0.0008164344596567469) +# btilde3 = convert(T, 0.007880878010261995) +# btilde4 = convert(T, -0.1447110071732629) +# btilde5 = convert(T, 0.5823571654525552) +# btilde6 = convert(T, -0.45808210592918697) +# btilde7 = convert(T, 0.015151515151515152) + +# Tsit5ConstantCacheActual( +# c1, c2, c3, c4, c5, c6, a21, a31, a32, a41, a42, a43, a51, a52, +# a53, +# a54, a61, a62, a63, a64, a65, a71, a72, a73, a74, a75, a76, +# btilde1, +# btilde2, btilde3, btilde4, btilde5, btilde6, btilde7) +# end + +# @fold function Tsit5ConstantCacheActual(::Type{T}, ::Type{T2}) where {T, T2} +# c1 = convert(T2, 161 // 1000) +# c2 = convert(T2, 327 // 1000) +# c3 = convert(T2, 9 // 10) +# c4 = convert(T2, +# big".9800255409045096857298102862870245954942137979563024768854764293221195950761080302604") +# c5 = convert(T2, 1) +# c6 = convert(T2, 1) +# a21 = convert(T, 161 // 1000) +# a31 = convert(T, +# big"-.8480655492356988544426874250230774675121177393430391537369234245294192976164141156943e-2") +# a32 = convert(T, +# big".3354806554923569885444268742502307746751211773934303915373692342452941929761641411569") +# a41 = convert(T, +# big"2.897153057105493432130432594192938764924887287701866490314866693455023795137503079289") +# a42 = convert(T, +# big"-6.359448489975074843148159912383825625952700647415626703305928850207288721235210244366") +# a43 = convert(T, +# big"4.362295432869581411017727318190886861027813359713760212991062156752264926097707165077") +# a51 = convert(T, +# big"5.325864828439256604428877920840511317836476253097040101202360397727981648835607691791") +# a52 = convert(T, +# big"-11.74888356406282787774717033978577296188744178259862899288666928009020615663593781589") +# a53 = convert(T, +# big"7.495539342889836208304604784564358155658679161518186721010132816213648793440552049753") +# a54 = convert(T, +# big"-.9249506636175524925650207933207191611349983406029535244034750452930469056411389539635e-1") +# a61 = convert(T, +# big"5.861455442946420028659251486982647890394337666164814434818157239052507339770711679748") +# a62 = convert(T, +# big"-12.92096931784710929170611868178335939541780751955743459166312250439928519268343184452") +# a63 = convert(T, +# big"8.159367898576158643180400794539253485181918321135053305748355423955009222648673734986") +# a64 = convert(T, +# big"-.7158497328140099722453054252582973869127213147363544882721139659546372402303777878835e-1") +# a65 = convert(T, +# big"-.2826905039406838290900305721271224146717633626879770007617876201276764571291579142206e-1") +# a71 = convert(T, +# big".9646076681806522951816731316512876333711995238157997181903319145764851595234062815396e-1") +# a72 = convert(T, 1 // 100) +# a73 = convert(T, +# big".4798896504144995747752495322905965199130404621990332488332634944254542060153074523509") +# a74 = convert(T, +# big"1.379008574103741893192274821856872770756462643091360525934940067397245698027561293331") +# a75 = convert(T, +# big"-3.290069515436080679901047585711363850115683290894936158531296799594813811049925401677") +# a76 = convert(T, +# big"2.324710524099773982415355918398765796109060233222962411944060046314465391054716027841") +# # b1 = convert(T,big".9468075576583945807478876255758922856117527357724631226139574065785592789071067303271e-1") +# # b2 = convert(T,big".9183565540343253096776363936645313759813746240984095238905939532922955247253608687270e-2") +# # b3 = convert(T,big".4877705284247615707855642599631228241516691959761363774365216240304071651579571959813") +# # b4 = convert(T,big"1.234297566930478985655109673884237654035539930748192848315425833500484878378061439761") +# # b5 = convert(T,big"-2.707712349983525454881109975059321670689605166938197378763992255714444407154902012702") +# # b6 = convert(T,big"1.866628418170587035753719399566211498666255505244122593996591602841258328965767580089") +# # b7 = convert(T,1//66) +# btilde1 = convert(T, +# big"-1.780011052225771443378550607539534775944678804333659557637450799792588061629796e-03") +# btilde2 = convert(T, +# big"-8.164344596567469032236360633546862401862537590159047610940604670770447527463931e-04") +# btilde3 = convert(T, +# big"7.880878010261996010314727672526304238628733777103128603258129604952959142646516e-03") +# btilde4 = convert(T, +# big"-1.44711007173262907537165147972635116720922712343167677619514233896760819649515e-01") +# btilde5 = convert(T, +# big"5.823571654525552250199376106520421794260781239567387797673045438803694038950012e-01") +# btilde6 = convert(T, +# big"-4.580821059291869466616365188325542974428047279788398179474684434732070620889539e-01") +# btilde7 = convert(T, 1 // 66) + +# Tsit5ConstantCacheActual( +# c1, c2, c3, c4, c5, c6, a21, a31, a32, a41, a42, a43, a51, a52, +# a53, +# a54, a61, a62, a63, a64, a65, a71, a72, a73, a74, a75, a76, +# btilde1, +# btilde2, btilde3, btilde4, btilde5, btilde6, btilde7) +# end + +# """ +# Coefficients for the polynomial +# bᵢΘ = ri1*Θ + ri2*Θ^2 + ri3*Θ^3 + ... + +# These are the coefficients of the expanded form of the polynomials from + +# Runge–Kutta pairs of order 5(4) satisfying only the first column +# simplifying assumption + +# Ch. Tsitouras +# """ +# @fold function Tsit5Interp(::Type{T}) where {T <: CompiledFloats} +# r11 = convert(T, 1.0) +# r12 = convert(T, -2.763706197274826) +# r13 = convert(T, 2.9132554618219126) +# r14 = convert(T, -1.0530884977290216) + +# r22 = convert(T, 0.13169999999999998) +# r23 = convert(T, -0.2234) +# r24 = convert(T, 0.1017) + +# r32 = convert(T, 3.9302962368947516) +# r33 = convert(T, -5.941033872131505) +# r34 = convert(T, 2.490627285651253) + +# r42 = convert(T, -12.411077166933676) +# r43 = convert(T, 30.33818863028232) +# r44 = convert(T, -16.548102889244902) + +# r52 = convert(T, 37.50931341651104) +# r53 = convert(T, -88.1789048947664) +# r54 = convert(T, 47.37952196281928) + +# r62 = convert(T, -27.896526289197286) +# r63 = convert(T, 65.09189467479366) +# r64 = convert(T, -34.87065786149661) + +# r72 = convert(T, 1.5) +# r73 = convert(T, -4) +# r74 = convert(T, 2.5) + +# return r11, r12, r13, r14, r22, r23, r24, r32, r33, r34, r42, r43, r44, r52, r53, r54, +# r62, r63, r64, r72, r73, r74 +# end + +# """ +# Coefficients for the polynomial +# bᵢΘ = ri1*Θ + ri2*Θ^2 + ri3*Θ^3 + ... + +# These are the coefficients of the expanded form of the polynomials from + +# Runge–Kutta pairs of order 5(4) satisfying only the first column +# simplifying assumption + +# Ch. Tsitouras +# """ +# @fold function Tsit5Interp(::Type{T}) where {T} +# r11 = convert(T, big"0.999999999999999974283372471559910888475488471328") +# r12 = convert(T, big"-2.763706197274825911336735930481400260916070804192") +# r13 = convert(T, big"2.91325546182191274375068099306808") +# r14 = convert(T, -1.0530884977290216) + +# r22 = convert(T, big"0.13169999999999999727") +# r23 = convert(T, big"-0.22339999999999999818") +# r24 = convert(T, 0.1017) + +# r32 = convert(T, big"3.93029623689475152850687446709813398") +# r33 = convert(T, big"-5.94103387213150473470249202589458001") +# r34 = convert(T, big"2.490627285651252793") + +# r42 = convert(T, big"-12.411077166933676983734381540685453484102414134010752") +# r43 = convert(T, big"30.3381886302823215981729903691836576") +# r44 = convert(T, big"-16.54810288924490272") + +# r52 = convert(T, big"37.50931341651103919496903965334519631242339792120440212") +# r53 = convert(T, big"-88.1789048947664011014276693541209817") +# r54 = convert(T, big"47.37952196281928122") + +# r62 = convert(T, big"-27.896526289197287805948263144598643896") +# r63 = convert(T, big"65.09189467479367152629021928716553658") +# r64 = convert(T, big"-34.87065786149660974") + +# r72 = convert(T, 1.5) +# r73 = convert(T, -4) +# r74 = convert(T, 2.5) + +# return r11, r12, r13, r14, r22, r23, r24, r32, r33, r34, r42, r43, r44, r52, r53, r54, +# r62, r63, r64, r72, r73, r74 +# end diff --git a/lib/OrdinaryDiffEqTaylorSeries/test/direct_test.jl b/lib/OrdinaryDiffEqTaylorSeries/test/direct_test.jl new file mode 100644 index 0000000000..6a07cd835c --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/test/direct_test.jl @@ -0,0 +1,30 @@ +using OrdinaryDiffEqTaylorSeries +using Test + +# Define a simple linear DAE system +function linear_dae!(res, du, u, p, t) + # x' = y + # 0 = x - t + res[1] = du[1] - u[2] # Differential equation + res[2] = u[1] - t # Algebraic constraint + return nothing +end + +# Initial conditions (consistent with constraints) +u0 = [0.0, 1.0] # [x(0), y(0)] +du0 = [1.0, 0.0] # [x'(0), y'(0)] +tspan = (0.0, 5.0) +p = nothing + +# Create problem and solve +prob = DAEProblem(linear_dae!, du0, u0, tspan, p) +sol = solve(prob, DAETS(), dt=0.1) + +# Check solution against analytical solution: x(t) = t, y(t) = 1 +for i in 1:length(sol.t) + t = sol.t[i] + @test isapprox(sol[1,i], t, rtol=1e-2) # x(t) = t + @test isapprox(sol[2,i], 1.0, rtol=1e-2) # y(t) = 1 +end + +println("Test completed successfully!") \ No newline at end of file diff --git a/lib/OrdinaryDiffEqTaylorSeries/test/harmonic_oscillator_comparison.png b/lib/OrdinaryDiffEqTaylorSeries/test/harmonic_oscillator_comparison.png new file mode 100644 index 0000000000..3e3da69866 Binary files /dev/null and b/lib/OrdinaryDiffEqTaylorSeries/test/harmonic_oscillator_comparison.png differ diff --git a/lib/OrdinaryDiffEqTaylorSeries/test/linear_growth_comparison.png b/lib/OrdinaryDiffEqTaylorSeries/test/linear_growth_comparison.png new file mode 100644 index 0000000000..f0c57ca5b7 Binary files /dev/null and b/lib/OrdinaryDiffEqTaylorSeries/test/linear_growth_comparison.png differ diff --git a/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl b/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl new file mode 100644 index 0000000000..e269e247a0 --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl @@ -0,0 +1,343 @@ +using OrdinaryDiffEqTaylorSeries +using Test +using Symbolics +import OrdinaryDiffEqCore: alg_order, alg_stability_size, explicit_rk_docstring, + OrdinaryDiffEqAdaptiveAlgorithm, OrdinaryDiffEqMutableCache, + OrdinaryDiffEqConstantCache, @fold, trivial_limiter!, + constvalue, @unpack, perform_step!, calculate_residuals, @cache, + calculate_residuals!, _ode_interpolant, _ode_interpolant!, + CompiledFloats, @OnDemandTableauExtract, initialize!, + perform_step!, OrdinaryDiffEqAlgorithm, + DAEAlgorithm, + CompositeAlgorithm, _ode_addsteps!, copyat_or_push!, + AutoAlgSwitch, get_fsalfirstlast, + full_cache, DerivativeOrderNotPossibleError + +using LinearAlgebra +using Plots +# using DifferentialEquations +# f(u, p, t) = cos(u) +# f!(du, u, p, t) = du .= cos.(u) + +# u0 = 0.0 +# u0! = [0.0] +# prob = ODEProblem{false, SciMLBase.NoSpecialize}(f, u0, (0.0, 10.0)) +# prob! = ODEProblem{true, SciMLBase.NoSpecialize}(f!, u0!, (0.0, 10.0)) +# sol = solve(prob, ExplicitTaylor2(), dt=0.01) +# sol! = solve(prob!, ExplicitTaylor2(), dt=0.01) +# # sol = solve(prob, DAETS(), dt=0.01) +# sol = solve(prob, ExplicitTaylor(order=Val(2)), dt=0.01) +# sol! = solve(prob!, ExplicitTaylor(order=Val(2)), dt=0.01) + +# println("DONE with ODE tests") +include(joinpath(@__DIR__, "../src/DAETS_symbolics.jl")) +include(joinpath(@__DIR__, "../src/TaylorSeries_caches.jl")) +# println("Starting tests on DAETS") + +# # --- Test Cases --- # +# @testset "Signature Matrix & Derivative Order Tests" begin +# @syms t x(t) y(t) +# # # Test max_derivative_order +# # The variable itself. +# order1 = max_derivative_order(x(t), x, t) +# @test order1 == 0 + +# # A multiplication expression: 7*x(t) +# order2 = max_derivative_order(7*x(t), x, t) +# @test order2 == 0 + +# # First derivative: x'(t) +# dx = Differential(t)(x(t)) +# # println(typeof(dx)) +# order3 = max_derivative_order(dx, x, t) +# @test order3 == 1 + +# # Second derivative: x''(t) +# d2x = Differential(t)(dx) +# order4 = max_derivative_order(d2x, x, t) +# @test order4 == 2 + +# # Expression that does not contain x(t): y(t) +# order5 = max_derivative_order(y(t), x, t) +# @test order5 == -Inf + +# # Test signature_matrix: +# # Equation 1: f₁ = x'(t) + sin(x(t)) = 0 +# # Equation 2: f₂ = y''(t) - x(t) = 0 +# eq1 = Differential(t)(x(t)) + sin(x(t)) +# eq2 = Differential(t)(Differential(t)(y(t))) - x(t) +# eqs = [eq1, eq2] +# # pass functions x and y (which take input t) +# vars = [x, y] + +# Σ = signature_matrix(eqs, vars, t) +# # First equation: +# # Column 1: x(t) appears as x'(t) ⟹ order 1. +# # Column 2: no y(t) ⟹ -Inf. +# @test Σ[1, 1] == 1 +# @test Σ[1, 2] == -Inf + +# # Second equation: +# # Column 1: x(t) appears as x(t) ⟹ order 0. +# # Column 2: y(t) appears as y''(t) ⟹ order 2. +# @test Σ[2, 1] == 0 +# @test Σ[2, 2] == 2 +# end + +# println("DONE Signature Matrix Tests") + +# @testset "Highest Value Transversal Tests" begin +# @syms t x(t) y(t) + +# # Same Equation +# eq1 = Differential(t)(x(t)) + sin(x(t)) +# eq2 = Differential(t)(Differential(t)(y(t))) - x(t) +# eqs = [eq1, eq2] +# vars = [x, y] +# Σ = signature_matrix(eqs, vars, t) + +# # Expected signature matrix: +# # [ 1 -Inf +# # 0 2 ] +# @test Σ == [1 -Inf; 0 2] +# # Test HVT +# transversal, value = highest_value_transversal(Σ) + +# # Expected transversal: [(1, 1), (2, 2)] (x'(t) in eq1 and y''(t) in eq2) +# # Expected value: 1 + 2 = 3 TODO: Should probably add a check in case the value is infinite. In this case we say that the system is "ill-posed". +# @test transversal == [(1, 1), (2, 2)] +# @test value == 3 +# end +# @testset "Highest Value Transversal Tests for 3x3 System" begin +# @syms t x(t) y(t) z(t) + +# # Equation 1: f₁ = x''(t) + y(t) = 0 +# # Equation 2: f₂ = y'(t) + z(t) = 0 +# # Equation 3: f₃ = z''(t) + x(t) = 0 +# eq1 = Differential(t)(Differential(t)(x(t))) + y(t) +# eq2 = Differential(t)(y(t)) + z(t) +# eq3 = Differential(t)(Differential(t)(z(t))) + x(t) +# eqs = [eq1, eq2, eq3] +# vars = [x, y, z] +# Σ = signature_matrix(eqs, vars, t) + +# # Expected signature matrix: +# # [ 2 0 -Inf +# # -Inf 1 0 +# # 0 -Inf 2 ] +# @test Σ == [2 0 -Inf; -Inf 1 0; 0 -Inf 2] +# # Test HVT +# transversal, value = highest_value_transversal(Σ) + +# # Expected transversal: [(1, 1), (2, 2), (3, 3)] (x''(t) in eq1, y'(t) in eq2, z''(t) in eq3) +# # Expected value: 2 + 1 + 2 = 5 +# @test transversal == [(1, 1), (2, 2), (3, 3)] +# @test value == 5 +# end +# println("DONE Highest Value Transversal Tests") + +# @testset "Find Offsets Tests for 2x2 System" begin +# @syms t x(t) y(t) + +# # Same Equation as 2x2 System +# eq1 = Differential(t)(x(t)) + sin(x(t)) +# eq2 = Differential(t)(Differential(t)(y(t))) - x(t) +# eqs = [eq1, eq2] +# vars = [x, y] +# Σ = signature_matrix(eqs, vars, t) + +# # Find the highest value transversal +# transversal, value = highest_value_transversal(Σ) + +# # Find the offsets +# c, d = find_offsets(Σ, transversal) + +# # Expected offsets (canonical): +# # c = [0, 0] +# # d = [1, 2] +# @test c == [0, 0] +# @test d == [1, 2] +# end +# @testset "Find Offsets Tests for 3x3 System" begin +# @syms t x(t) y(t) z(t) + +# # Same Equation as 3x3 System +# eq1 = Differential(t)(Differential(t)(x(t))) + y(t) +# eq2 = Differential(t)(y(t)) + z(t) +# eq3 = Differential(t)(Differential(t)(z(t))) + x(t) +# eqs = [eq1, eq2, eq3] +# vars = [x, y, z] +# Σ = signature_matrix(eqs, vars, t) +# transversal, value = highest_value_transversal(Σ) + +# # Test Offsets +# c, d = find_offsets(Σ, transversal) + +# # Expected offsets (canonical): +# # c = [0, 0, 0] +# # d = [2, 1, 2] +# @test c == [0, 0, 0] +# @test d == [2, 1, 2] +# end + +# println("DONE Find Offsets Tests") + +# @testset "System Jacobian Tests for 2x2 System" begin +# @syms t x(t) y(t) + +# # Same 2x2 +# eq1 = Differential(t)(x(t)) + sin(x(t)) +# eq2 = Differential(t)(Differential(t)(y(t))) - x(t) +# eqs = [eq1, eq2] +# vars = [x, y] +# Σ = signature_matrix(eqs, vars, t) +# transversal, value = highest_value_transversal(Σ) +# c, d = find_offsets(Σ, transversal) + +# # Convert c and d to Vector{Int} +# c = Int.(c) +# d = Int.(d) + +# # Test Jacobian +# J = system_jacobian(eqs, vars, t, c, d, Σ) + +# # Expected Jacobian: +# # [ 1 0 +# # 0 1 ] +# @test isequal(J[1, 1], 1) +# @test isequal(J[1, 2], 0) +# @test isequal(J[2, 1], 0) +# @test isequal(J[2, 2], 1) +# end +# @testset "System Jacobian Tests for 3x3 System" begin +# @syms t x(t) y(t) z(t) + +# # Same 3x3 +# eq1 = Differential(t)(Differential(t)(x(t))) + y(t) +# eq2 = Differential(t)(y(t)) + z(t) +# eq3 = Differential(t)(Differential(t)(z(t))) + x(t) +# eqs = [eq1, eq2, eq3] +# vars = [x, y, z] +# Σ = signature_matrix(eqs, vars, t) +# transversal, value = highest_value_transversal(Σ) +# c, d = find_offsets(Σ, transversal) + +# # Convert c and d to Vector{Int} +# c = Int.(c) +# d = Int.(d) + +# # Test Jacobian +# J = system_jacobian(eqs, vars, t, c, d, Σ) + +# # Expected Jacobian: +# # [1 0 0 +# # 0 1 1 +# # 0 0 1] +# @test isequal(J[1, 1], 1) +# @test isequal(J[1, 2], 0) +# @test isequal(J[1, 3], 0) +# @test isequal(J[2, 1], 0) +# @test isequal(J[2, 2], 1) +# @test isequal(J[2, 3], 0) +# @test isequal(J[3, 1], 0) +# @test isequal(J[3, 2], 0) +# @test isequal(J[3, 3], 1) +# end +# println("DONE System Jacobian Tests") + +# @testset "System Jacobian Tests for Simple Pendulum" begin +# @syms t x(t) y(t) λ(t) G L + +# # Equations +# f = Differential(t)(Differential(t)(x(t))) + x(t) * λ(t) +# g = Differential(t)(Differential(t)(y(t))) + y(t) * λ(t) - G +# h = x(t)^2 + y(t)^2 - L^2 +# eqs = [f, g, h] +# vars = [x, y, λ] + +# # Construct the signature matrix +# Σ = signature_matrix(eqs, vars, t) + +# # Find the highest value transversal +# transversal, value = highest_value_transversal(Σ) + +# # Find the offsets +# c, d = find_offsets(Σ, transversal) + +# # Convert c and d to Vector{Int} +# c = Int.(c) +# d = Int.(d) + +# # Construct the system Jacobian +# J = system_jacobian(eqs, vars, t, c, d, Σ) + +# # Expected Jacobian: +# # [1 0 x(t) +# # 0 1 y(t) +# # 2 2 0] +# @test isequal(J[1, 1], 1) +# @test isequal(J[1, 2], 0) +# @test isequal(J[1, 3], x(t)) +# @test isequal(J[2, 1], 0) +# @test isequal(J[2, 2], 1) +# @test isequal(J[2, 3], y(t)) +# @test isequal(J[3, 1], 2x(t)) +# @test isequal(J[3, 2], 2y(t)) +# @test isequal(J[3, 3], 0) +# # end + +@testset "Basic DAE Test" begin + # Simple index-1 DAE: + # x'(t) = y(t) + # 0 = x(t) - sin(t) + # + # Analytical solution: + # x(t) = sin(t) + # y(t) = cos(t) + + function dae_simple!(res, du, u, p, t) + x, y = u + dx, dy = du + + # Diffeq + res[1] = dx - y + # Alg eq + res[2] = x - sin(t) + + return nothing + end + + # Initial conditions + u0 = [0.0, 1.0] # [x(0), y(0)] = [sin(0), cos(0)] + du0 = [1.0, 0.0] # [x'(0), y'(0)] = [cos(0), -sin(0)] + tspan = (0.0, 2π) + prob = DAEProblem(dae_simple!, du0, u0, tspan) + sol = solve(prob, DAETS(), dt=0.1) + + # Generate analytical solution + t_analytical = 0:0.01:2π + x_analytical = sin.(t_analytical) + y_analytical = cos.(t_analytical) + # numeric solutions + t_numerical = sol.t + x_numerical = [sol[1, i] for i in 1:length(sol.t)] + y_numerical = [sol[2, i] for i in 1:length(sol.t)] + + # Plot + p1 = plot(t_analytical, x_analytical, label="x analytici", title="DAE") + plot!(p1, t_numerical, x_numerical, label="x numerical", marker=:circle, markersize=3) + + p2 = plot(t_analytical, y_analytical, label="y analytic", title="") + plot!(p2, t_numerical, y_numerical, label="y numerical", marker=:circle, markersize=3) + + plot(p1, p2, layout=(2,1), size=(800, 600)) + savefig("dae_test_1_comparison.png") + + # test that solution matches analytical solution + for i in eachindex(t_numerical) + t = t_numerical[i] + @test isapprox(x_numerical[i], sin(t), rtol=1e-2) + @test isapprox(y_numerical[i], cos(t), rtol=1e-2) + end +end +