From 4f502f53f667661a997fc135846a4294bad5423d Mon Sep 17 00:00:00 2001 From: jchuharski Date: Sun, 9 Feb 2025 09:13:25 -0500 Subject: [PATCH 1/8] first commit --- lib/OrdinaryDiffEqTaylorSeries/LICENSE.md | 24 ++ lib/OrdinaryDiffEqTaylorSeries/Project.toml | 46 +++ .../src/OrdinaryDiffEqTaylorSeries.jl | 61 +++ .../src/TaylorSeries_caches.jl | 45 +++ .../src/TaylorSeries_perform_step.jl | 39 ++ .../src/alg_utils.jl | 3 + .../src/algorithms.jl | 12 + .../src/interp_func.jl | 7 + .../src/interpolants.jl | 352 ++++++++++++++++++ .../src/tsit_tableaus.jl | 257 +++++++++++++ .../test/runtests.jl | 1 + 11 files changed, 847 insertions(+) create mode 100644 lib/OrdinaryDiffEqTaylorSeries/LICENSE.md create mode 100644 lib/OrdinaryDiffEqTaylorSeries/Project.toml create mode 100644 lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl create mode 100644 lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl create mode 100644 lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl create mode 100644 lib/OrdinaryDiffEqTaylorSeries/src/alg_utils.jl create mode 100644 lib/OrdinaryDiffEqTaylorSeries/src/algorithms.jl create mode 100644 lib/OrdinaryDiffEqTaylorSeries/src/interp_func.jl create mode 100644 lib/OrdinaryDiffEqTaylorSeries/src/interpolants.jl create mode 100644 lib/OrdinaryDiffEqTaylorSeries/src/tsit_tableaus.jl create mode 100644 lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl 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..9cb9f88b0f --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/Project.toml @@ -0,0 +1,46 @@ +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" +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +MuladdMacro = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221" +OrdinaryDiffEqCore = "bbf590c4-e513-4bbe-9b18-05decba2e5d8" +PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a" +Preferences = "21216c6a-2e73-6563-6e65-726566657250" +RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" +Reexport = "189a3867-3050-52da-a836-e630ba90ab69" +Static = "aedffcd0-7271-4cad-89d0-dc628f76c6d3" +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" +LinearAlgebra = "<0.0.1, 1" +MuladdMacro = "0.2.4" +OrdinaryDiffEqCore = "1.1" +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" +Static = "1.1.1" +TaylorDiff = "0.3.1" +Test = "<0.0.1, 1" +TruncatedStacktraces = "1.4.0" +julia = "1.10" + +[extras] +DiffEqDevTools = "f3b72e0c-5b89-59e1-b016-84e28bfd966d" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["DiffEqDevTools", "Random", "SafeTestsets", "Test"] diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl b/lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl new file mode 100644 index 0000000000..d41472af21 --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl @@ -0,0 +1,61 @@ +module OrdinaryDiffEqTaylorSeries + +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, + 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 +using TruncatedStacktraces +using TaylorDiff +import DiffEqBase: @def +import OrdinaryDiffEqCore + +using Reexport +@reexport using DiffEqBase + +include("algorithms.jl") +include("alg_utils.jl") +include("TaylorSeries_caches.jl") +include("interp_func.jl") +# include("interpolants.jl") +include("TaylorSeries_perform_step.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 + +end diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl new file mode 100644 index 0000000000..f9f72bd194 --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl @@ -0,0 +1,45 @@ +@cache struct ExplicitTaylor2Cache{uType, rateType, uNoUnitsType, StageLimiter, StepLimiter, + Thread} <: OrdinaryDiffEqMutableCache + u::uType + uprev::uType + k1::rateType + k2::rateType + k3::rateType + # k4::rateType + # k5::rateType + # k6::rateType + # k7::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) + # k4 = zero(rate_prototype) + # k5 = zero(rate_prototype) + # k6 = zero(rate_prototype) + # k7 = 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 +get_fsalfirstlast(cache::ExplicitTaylor2Cache, u) = (nothing, nothing) \ 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..f0d091d6c4 --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl @@ -0,0 +1,39 @@ + +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) + k2 = TaylorDiff.derivative(_u -> f(_u, p, t), uprev, k1, Val(1)) + k3 = TaylorDiff.derivative(_t -> f(uprev, p, _t), t, Val(1)) + u = @.. uprev + dt * k1 + dt^2 / 2 * (k2 + k3) + OrdinaryDiffEqCore.increment_nf!(integrator.stats, 3) + integrator.k[1] = k1 + integrator.k[2] = k2 + integrator.k[3] = k3 + integrator.u = u +end + +function initialize!(integrator, cache::ExplicitTaylor2Cache) + integrator.kshortsize = 7 + 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 + + f(k1, uprev, p, t) + TaylorDiff.derivative!(k2, (_y, _u) -> f(_y, _u, p, t), tmp, uprev, k1, Val(1)) + TaylorDiff.derivative!(k3, (_y, _t) -> f(_y, uprev, p, _t), tmp, t, one(t), Val(1)) + @.. u = uprev + dt * k1 + dt^2 / 2 * (k2 + k3) + OrdinaryDiffEqCore.increment_nf!(integrator.stats, 3) +end diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/alg_utils.jl b/lib/OrdinaryDiffEqTaylorSeries/src/alg_utils.jl new file mode 100644 index 0000000000..5e94004c60 --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/src/alg_utils.jl @@ -0,0 +1,3 @@ +alg_order(alg::ExplicitTaylor2) = 2 + +alg_stability_size(alg::ExplicitTaylor2) = 1 diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/algorithms.jl b/lib/OrdinaryDiffEqTaylorSeries/src/algorithms.jl new file mode 100644 index 0000000000..3f4fb19fc5 --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/src/algorithms.jl @@ -0,0 +1,12 @@ + +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 diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/interp_func.jl b/lib/OrdinaryDiffEqTaylorSeries/src/interp_func.jl new file mode 100644 index 0000000000..7e598a1b63 --- /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 +}} + 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..6fea90c0fa --- /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/runtests.jl b/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl @@ -0,0 +1 @@ + From 11430555f0ec017c32b410cea9c0504b590e84a6 Mon Sep 17 00:00:00 2001 From: Songchen Tan Date: Mon, 10 Feb 2025 18:09:14 -0500 Subject: [PATCH 2/8] Fix in-place mode and add tests --- lib/OrdinaryDiffEqTaylorSeries/Project.toml | 2 ++ .../src/TaylorSeries_caches.jl | 3 ++- .../src/TaylorSeries_perform_step.jl | 5 +++-- lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl | 12 ++++++++++++ 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/OrdinaryDiffEqTaylorSeries/Project.toml b/lib/OrdinaryDiffEqTaylorSeries/Project.toml index 9cb9f88b0f..c418bc8077 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/Project.toml +++ b/lib/OrdinaryDiffEqTaylorSeries/Project.toml @@ -13,6 +13,7 @@ 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" TaylorDiff = "b36ab563-344f-407b-a36a-4f200bebf99c" TruncatedStacktraces = "781d530d-4396-4725-bb49-402e4bee1e77" @@ -30,6 +31,7 @@ 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" TaylorDiff = "0.3.1" Test = "<0.0.1, 1" diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl index f9f72bd194..2a1d5a2124 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl @@ -42,4 +42,5 @@ function alg_cache(alg::ExplicitTaylor2, u, rate_prototype, ::Type{uEltypeNoUnit ::Val{false}) where {uEltypeNoUnits, uBottomEltypeNoUnits, tTypeNoUnits} ExplicitTaylor2ConstantCache() end -get_fsalfirstlast(cache::ExplicitTaylor2Cache, u) = (nothing, nothing) \ No newline at end of file +# FSAL currently not used, providing dummy implementation to satisfy the interface +get_fsalfirstlast(cache::ExplicitTaylor2Cache, u) = (cache.k1, cache.k1) diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl index f0d091d6c4..68e3a3f725 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl @@ -18,7 +18,7 @@ end end function initialize!(integrator, cache::ExplicitTaylor2Cache) - integrator.kshortsize = 7 + integrator.kshortsize = 3 resize!(integrator.k, integrator.kshortsize) # Setup k pointers integrator.k[1] = cache.k1 @@ -30,10 +30,11 @@ 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 - + f(k1, uprev, p, t) TaylorDiff.derivative!(k2, (_y, _u) -> f(_y, _u, p, t), tmp, uprev, k1, Val(1)) TaylorDiff.derivative!(k3, (_y, _t) -> f(_y, uprev, p, _t), tmp, t, one(t), Val(1)) @.. u = uprev + dt * k1 + dt^2 / 2 * (k2 + k3) OrdinaryDiffEqCore.increment_nf!(integrator.stats, 3) + return nothing end diff --git a/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl b/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl index 8b13789179..4d2b497595 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl @@ -1 +1,13 @@ +using OrdinaryDiffEqTaylorSeries +f(u, p, t) = cos(u) +f!(du, u, p, t) = du .= cos.(u) +odef = ODEFunction{false, SciMLBase.NoSpecialize}(f) +odef! = ODEFunction{true, SciMLBase.NoSpecialize}(f!) + +u0 = 0.0 +u0! = [0.0] +prob = ODEProblem(odef, u0, (0.0, 10.0)) +prob! = ODEProblem(odef!, u0!, (0.0, 10.0)) +sol = solve(prob, ExplicitTaylor2(), dt=0.01) +sol! = solve(prob!, ExplicitTaylor2(), dt=0.01) From 287c0ae6e7a5de23adbc3b386e71c7e4dd0360fd Mon Sep 17 00:00:00 2001 From: Songchen Tan Date: Tue, 11 Feb 2025 16:31:49 -0500 Subject: [PATCH 3/8] Add generic taylor --- .../src/OrdinaryDiffEqTaylorSeries.jl | 2 +- .../src/TaylorSeries_caches.jl | 48 +++++++++--- .../src/TaylorSeries_perform_step.jl | 77 +++++++++++++++++-- .../src/alg_utils.jl | 4 +- .../src/algorithms.jl | 8 ++ .../test/runtests.jl | 8 +- 6 files changed, 124 insertions(+), 23 deletions(-) diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl b/lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl index d41472af21..bb5eb5c7be 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl @@ -56,6 +56,6 @@ PrecompileTools.@compile_workload begin solver_list = nothing end -export ExplicitTaylor2 +export ExplicitTaylor2, ExplicitTaylor end diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl index 2a1d5a2124..9867ea39d3 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl @@ -1,14 +1,11 @@ -@cache struct ExplicitTaylor2Cache{uType, rateType, uNoUnitsType, StageLimiter, StepLimiter, +@cache struct ExplicitTaylor2Cache{ + uType, rateType, uNoUnitsType, StageLimiter, StepLimiter, Thread} <: OrdinaryDiffEqMutableCache u::uType uprev::uType k1::rateType k2::rateType k3::rateType - # k4::rateType - # k5::rateType - # k6::rateType - # k7::rateType utilde::uType tmp::uType atmp::uNoUnitsType @@ -24,10 +21,6 @@ function alg_cache(alg::ExplicitTaylor2, u, rate_prototype, ::Type{uEltypeNoUnit k1 = zero(rate_prototype) k2 = zero(rate_prototype) k3 = zero(rate_prototype) - # k4 = zero(rate_prototype) - # k5 = zero(rate_prototype) - # k6 = zero(rate_prototype) - # k7 = zero(rate_prototype) utilde = zero(u) atmp = similar(u, uEltypeNoUnits) recursivefill!(atmp, false) @@ -40,7 +33,42 @@ function alg_cache(alg::ExplicitTaylor2, u, rate_prototype, ::Type{uEltypeNoUnit ::Type{uBottomEltypeNoUnits}, ::Type{tTypeNoUnits}, uprev, uprev2, f, t, dt, reltol, p, calck, ::Val{false}) where {uEltypeNoUnits, uBottomEltypeNoUnits, tTypeNoUnits} - ExplicitTaylor2ConstantCache() + 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: normalized derivatives for f, starting from 0 + ks = ntuple(i -> zero(rate_prototype), Val(P)) + # us: normalized derivatives for u, starting from 1 + us = ntuple(i -> 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]) diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl index 68e3a3f725..e2e556580e 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl @@ -1,3 +1,7 @@ +using TaylorDiff: TaylorDiff, extract_derivative, extract_derivative! + +@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 @@ -7,13 +11,13 @@ end @muladd function perform_step!(integrator, cache::ExplicitTaylor2ConstantCache, repeat_step = false) @unpack t, dt, uprev, u, f, p = integrator k1 = f(uprev, p, t) - k2 = TaylorDiff.derivative(_u -> f(_u, p, t), uprev, k1, Val(1)) - k3 = TaylorDiff.derivative(_t -> f(uprev, p, _t), t, Val(1)) - u = @.. uprev + dt * k1 + dt^2 / 2 * (k2 + k3) + 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.k[3] = k3 integrator.u = u end @@ -31,10 +35,69 @@ end @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) - TaylorDiff.derivative!(k2, (_y, _u) -> f(_y, _u, p, t), tmp, uprev, k1, Val(1)) - TaylorDiff.derivative!(k3, (_y, _t) -> f(_y, uprev, p, _t), tmp, t, one(t), Val(1)) - @.. u = uprev + dt * k1 + dt^2 / 2 * (k2 + k3) + 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 diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/alg_utils.jl b/lib/OrdinaryDiffEqTaylorSeries/src/alg_utils.jl index 5e94004c60..3e83c42d4a 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/alg_utils.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/alg_utils.jl @@ -1,3 +1,5 @@ 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 diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/algorithms.jl b/lib/OrdinaryDiffEqTaylorSeries/src/algorithms.jl index 3f4fb19fc5..fbfa7b70a7 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/algorithms.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/algorithms.jl @@ -10,3 +10,11 @@ TruncatedStacktraces.@truncate_stacktrace ExplicitTaylor2 3 function ExplicitTaylor2(stage_limiter!, step_limiter! = trivial_limiter!) ExplicitTaylor2(stage_limiter!, step_limiter!, False()) end + +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 diff --git a/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl b/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl index 4d2b497595..7841961cf0 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl @@ -2,12 +2,12 @@ using OrdinaryDiffEqTaylorSeries f(u, p, t) = cos(u) f!(du, u, p, t) = du .= cos.(u) -odef = ODEFunction{false, SciMLBase.NoSpecialize}(f) -odef! = ODEFunction{true, SciMLBase.NoSpecialize}(f!) u0 = 0.0 u0! = [0.0] -prob = ODEProblem(odef, u0, (0.0, 10.0)) -prob! = ODEProblem(odef!, u0!, (0.0, 10.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, ExplicitTaylor(order=Val(2)), dt=0.01) +sol! = solve(prob!, ExplicitTaylor(order=Val(2)), dt=0.01) From 19632bd54747520d5beb47f6700485f1c8838f2c Mon Sep 17 00:00:00 2001 From: jchuharski Date: Sun, 23 Feb 2025 20:36:28 -0500 Subject: [PATCH 4/8] utils and util_tests --- lib/OrdinaryDiffEqTaylorSeries/Project.toml | 10 + .../src/DAETS_utils.jl | 166 +++++++ .../src/OrdinaryDiffEqTaylorSeries.jl | 4 +- .../src/TaylorSeries_caches.jl | 27 + .../src/TaylorSeries_perform_step.jl | 26 + .../src/alg_utils.jl | 3 + .../src/algorithms.jl | 12 + .../src/interp_func.jl | 2 +- .../src/tsit_tableaus.jl | 462 +++++++++--------- .../test/runtests.jl | 281 +++++++++++ 10 files changed, 760 insertions(+), 233 deletions(-) create mode 100644 lib/OrdinaryDiffEqTaylorSeries/src/DAETS_utils.jl diff --git a/lib/OrdinaryDiffEqTaylorSeries/Project.toml b/lib/OrdinaryDiffEqTaylorSeries/Project.toml index c418bc8077..473a6e0335 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/Project.toml +++ b/lib/OrdinaryDiffEqTaylorSeries/Project.toml @@ -6,6 +6,9 @@ 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" OrdinaryDiffEqCore = "bbf590c4-e513-4bbe-9b18-05decba2e5d8" @@ -15,6 +18,8 @@ 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" @@ -22,6 +27,9 @@ TruncatedStacktraces = "781d530d-4396-4725-bb49-402e4bee1e77" 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" OrdinaryDiffEqCore = "1.1" @@ -33,6 +41,8 @@ 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" diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/DAETS_utils.jl b/lib/OrdinaryDiffEqTaylorSeries/src/DAETS_utils.jl new file mode 100644 index 0000000000..7d921b1f6d --- /dev/null +++ b/lib/OrdinaryDiffEqTaylorSeries/src/DAETS_utils.jl @@ -0,0 +1,166 @@ +using Symbolics +using SymbolicUtils +using Hungarian +using JuMP +using GLPK + +export compute_signature_matrix, compute_hvt, compute_offsets, compute_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. +""" +function signature_matrix(eqs::Vector, vars::Vector, t_ind) + # eqs[i] is something like f_i(x₁(t), x₂'(t), x₃''(t), ...) + n_eqs = length(eqs) + n_vars = length(vars) + Σ = fill(-Inf, n_eqs, n_vars) # Initialize with -Inf + + for i in 1:n_eqs + # For each equation + 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) + # 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 an unrelated symbol we 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] # The function being differentiated. + # Recursively check the order of the inner expression. + sub_order = max_derivative_order(inner, var, t_ind) + # If the inner expression is not related to var, return -Inf otherwise add 1 to derivative order. + return sub_order == -Inf ? -Inf : 1 + sub_order + end + + # For composite expressions (e.g., sums, products), traverse their 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 + @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 to find 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::Vector{SymbolicUtils.BasicSymbolic{Number}}, vars::Vector{SymbolicUtils.BasicSymbolic{SymbolicUtils.FnType{Tuple{Number}, Number, Nothing}}}, t_ind::SymbolicUtils.BasicSymbolic{Number}, c::Vector{Int}, d::Vector{Int}, Σ::Matrix{Float64}) -> Matrix{SymbolicUtils.BasicSymbolic{Number}} + +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. +""" +function system_jacobian( + eqs::Vector{SymbolicUtils.BasicSymbolic{Number}}, + vars::Vector{SymbolicUtils.BasicSymbolic{SymbolicUtils.FnType{Tuple{Number}, Number, Nothing}}}, + t_ind::SymbolicUtils.BasicSymbolic{Number}, + c::Vector{Int}, + d::Vector{Int}, + Σ::Matrix{Float64} +) + 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 \ No newline at end of file diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl b/lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl index bb5eb5c7be..4b8758a52c 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl @@ -24,6 +24,8 @@ import OrdinaryDiffEqCore using Reexport @reexport using DiffEqBase +include("DAETS_utils.jl") + include("algorithms.jl") include("alg_utils.jl") include("TaylorSeries_caches.jl") @@ -56,6 +58,6 @@ PrecompileTools.@compile_workload begin solver_list = nothing end -export ExplicitTaylor2, ExplicitTaylor +export ExplicitTaylor2, ExplicitTaylor, DAETS end diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl index 9867ea39d3..2c8beb34a6 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl @@ -72,3 +72,30 @@ end # FSAL currently not used, providing dummy implementation to satisfy the interface get_fsalfirstlast(cache::ExplicitTaylorCache, u) = (cache.ks[1], cache.ks[1]) + + +# Differential Algebriac Equation Taylor Series + +@cache struct DAETSCache{ + 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 +get_fsalfirstlast(cache::DAETSCache, u) = (cache.k1, cache.k1) + +function alg_cache(alg::DAETS, 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 \ No newline at end of file diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl index e2e556580e..d26a363075 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl @@ -101,3 +101,29 @@ end OrdinaryDiffEqCore.increment_nf!(integrator.stats, 3) return nothing end + +# Differential Algebriac Equation Taylor Series +function initialize!(integrator, cache::DAETSCache) + 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::DAETSCache, 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 \ No newline at end of file diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/alg_utils.jl b/lib/OrdinaryDiffEqTaylorSeries/src/alg_utils.jl index 3e83c42d4a..645c74bc83 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/alg_utils.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/alg_utils.jl @@ -3,3 +3,6 @@ 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 index fbfa7b70a7..13e06a2028 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/algorithms.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/algorithms.jl @@ -18,3 +18,15 @@ Base.@kwdef struct ExplicitTaylor{P, StageLimiter, StepLimiter, Thread} <: step_limiter!::StepLimiter = trivial_limiter! thread::Thread = False() end + +Base.@kwdef struct DAETS{StageLimiter, StepLimiter, Thread} <: + OrdinaryDiffEqAlgorithm + stage_limiter!::StageLimiter = trivial_limiter! + step_limiter!::StepLimiter = trivial_limiter! + thread::Thread = False() +end +TruncatedStacktraces.@truncate_stacktrace DAETS 3 +# for backwards compatibility +function DAETS(stage_limiter!, step_limiter! = trivial_limiter!) + DAETS(stage_limiter!, step_limiter!, False()) +end \ No newline at end of file diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/interp_func.jl b/lib/OrdinaryDiffEqTaylorSeries/src/interp_func.jl index 7e598a1b63..a712cfa1be 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/interp_func.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/interp_func.jl @@ -1,7 +1,7 @@ function DiffEqBase.interp_summary(::Type{cacheType}, dense::Bool) where { cacheType <: - Union{ExplicitTaylor2Cache, ExplicitTaylor2ConstantCache + Union{ExplicitTaylor2Cache, ExplicitTaylor2ConstantCache, DAETSCache }} dense ? "specialized 4th order \"free\" interpolation" : "1st order linear" end diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/tsit_tableaus.jl b/lib/OrdinaryDiffEqTaylorSeries/src/tsit_tableaus.jl index 6fea90c0fa..8d972ce7d6 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/tsit_tableaus.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/tsit_tableaus.jl @@ -1,257 +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 +# 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) +# @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 +# 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) +# @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 +# 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 + ... +# """ +# Coefficients for the polynomial +# bᵢΘ = ri1*Θ + ri2*Θ^2 + ri3*Θ^3 + ... -These are the coefficients of the expanded form of the polynomials from +# 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 +# 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) +# 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) +# 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) +# 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) +# 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) +# 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) +# 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) +# 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 +# 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 + ... +# """ +# Coefficients for the polynomial +# bᵢΘ = ri1*Θ + ri2*Θ^2 + ri3*Θ^3 + ... -These are the coefficients of the expanded form of the polynomials from +# 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 +# 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) +# 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) +# 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") +# 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") +# 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") +# 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") +# 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) +# 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 +# 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/runtests.jl b/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl index 7841961cf0..339f41f9fa 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl @@ -1,4 +1,6 @@ using OrdinaryDiffEqTaylorSeries +using Test +using Symbolics f(u, p, t) = cos(u) f!(du, u, p, t) = du .= cos.(u) @@ -9,5 +11,284 @@ 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_utils.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 "compute_taylor_coefficients! Tests" begin +# @syms t x(t) y(t) +# @syms G L + +# # Define a simple ODE system: x'(t) = y(t), y'(t) = -x(t) +# f = [Differential(t)(x(t)) - y(t), Differential(t)(y(t)) + x(t)] +# vars = [x, y] + +# # Create a mock integrator and cache +# integrator = (u = [1.0, 0.0], t = 0.0, dt = 0.1, f = f, p = nothing) +# cache = (Σ = nothing, c = nothing, d = nothing, J = nothing, xTS = nothing, xtrial = nothing, htrial = 0.1, e = 0.0, tmp = nothing) + +# # Compute Taylor coefficients +# xcur = compute_taylor_coefficients!(integrator, cache) + +# # Expected Taylor coefficients for x(t) and y(t) at t = 0: +# # x(t) = 1 - t^2/2 + t^4/24 - ... +# # y(t) = t - t^3/6 + t^5/120 - ... +# @test xcur[1] ≈ 1.0 # x(0) +# @test xcur[2] ≈ 0.0 # y(0) +# end \ No newline at end of file From 5d4c27ad02c29bb026ec7ac068821f4bbbb67cc5 Mon Sep 17 00:00:00 2001 From: Songchen Tan Date: Wed, 26 Feb 2025 12:41:35 -0500 Subject: [PATCH 5/8] Improve tests and docs --- lib/OrdinaryDiffEqTaylorSeries/Project.toml | 3 +- .../src/algorithms.jl | 9 +++-- .../test/runtests.jl | 36 ++++++++++++------- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/lib/OrdinaryDiffEqTaylorSeries/Project.toml b/lib/OrdinaryDiffEqTaylorSeries/Project.toml index 473a6e0335..30fb15e9c9 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/Project.toml +++ b/lib/OrdinaryDiffEqTaylorSeries/Project.toml @@ -50,9 +50,10 @@ 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"] +test = ["DiffEqDevTools", "Random", "SafeTestsets", "Test", "ODEProblemLibrary"] diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/algorithms.jl b/lib/OrdinaryDiffEqTaylorSeries/src/algorithms.jl index 13e06a2028..949ac9742f 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/algorithms.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/algorithms.jl @@ -1,4 +1,6 @@ - +@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! @@ -11,6 +13,9 @@ 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}() @@ -29,4 +34,4 @@ TruncatedStacktraces.@truncate_stacktrace DAETS 3 # for backwards compatibility function DAETS(stage_limiter!, step_limiter! = trivial_limiter!) DAETS(stage_limiter!, step_limiter!, False()) -end \ No newline at end of file +end diff --git a/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl b/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl index 339f41f9fa..1cd205847a 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl @@ -1,19 +1,29 @@ -using OrdinaryDiffEqTaylorSeries +using OrdinaryDiffEqTaylorSeries, ODEProblemLibrary, DiffEqDevTools using Test using Symbolics -f(u, p, t) = cos(u) -f!(du, u, p, t) = du .= cos.(u) +@testset "Taylor2 Convergence Tests" begin + # Test convergence + dts = 2. .^ (-8:-4) + testTol = 0.2 + sim = test_convergence(dts, prob_ode_linear, ExplicitTaylor2()) + @test sim.𝒪est[:final]≈2 atol=testTol + sim = test_convergence(dts, prob_ode_2Dlinear, ExplicitTaylor2()) + @test sim.𝒪est[:final]≈2 atol=testTol +end -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) +@testset "TaylorN Convergence Tests" begin + # Test convergence + dts = 2. .^ (-8:-4) + testTol = 0.2 + for N in 3:4 + alg = ExplicitTaylor(order=Val(N)) + sim = test_convergence(dts, prob_ode_linear, alg) + @test sim.𝒪est[:final]≈N atol=testTol + sim = test_convergence(dts, prob_ode_2Dlinear, alg) + @test sim.𝒪est[:final]≈N atol=testTol + end +end println("DONE with ODE tests") include(joinpath(@__DIR__, "../src/DAETS_utils.jl")) @@ -291,4 +301,4 @@ end # # y(t) = t - t^3/6 + t^5/120 - ... # @test xcur[1] ≈ 1.0 # x(0) # @test xcur[2] ≈ 0.0 # y(0) -# end \ No newline at end of file +# end From 0bb068a46db6628e7c4df8d9e80fcf32f0c82327 Mon Sep 17 00:00:00 2001 From: jchuharski Date: Thu, 27 Feb 2025 13:41:35 -0500 Subject: [PATCH 6/8] first order stepper --- lib/OrdinaryDiffEqTaylorSeries/Project.toml | 2 + .../src/DAETS_utils.jl | 91 ++++++-- .../src/OrdinaryDiffEqTaylorSeries.jl | 10 +- .../src/TaylorSeries_caches.jl | 64 +++++- .../src/TaylorSeries_perform_step.jl | 197 ++++++++++++++++-- .../test/runtests.jl | 58 +++--- 6 files changed, 344 insertions(+), 78 deletions(-) diff --git a/lib/OrdinaryDiffEqTaylorSeries/Project.toml b/lib/OrdinaryDiffEqTaylorSeries/Project.toml index 30fb15e9c9..2d055c5d94 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/Project.toml +++ b/lib/OrdinaryDiffEqTaylorSeries/Project.toml @@ -11,6 +11,7 @@ Hungarian = "e91730f6-4275-51fb-a7a0-7064cfbd3b39" JuMP = "4076af6c-e467-56ae-b986-b466b2749572" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MuladdMacro = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221" +OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" OrdinaryDiffEqCore = "bbf590c4-e513-4bbe-9b18-05decba2e5d8" PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a" Preferences = "21216c6a-2e73-6563-6e65-726566657250" @@ -32,6 +33,7 @@ Hungarian = "0.7.0" JuMP = "1.24.0" LinearAlgebra = "<0.0.1, 1" MuladdMacro = "0.2.4" +OrdinaryDiffEq = "6.91.0" OrdinaryDiffEqCore = "1.1" PrecompileTools = "1.2.1" Preferences = "1.4.3" diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/DAETS_utils.jl b/lib/OrdinaryDiffEqTaylorSeries/src/DAETS_utils.jl index 7d921b1f6d..dbe7cb44d0 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/DAETS_utils.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/DAETS_utils.jl @@ -1,25 +1,52 @@ +module DAETS_utils using Symbolics using SymbolicUtils using Hungarian using JuMP using GLPK +using OrdinaryDiffEq: ODEFunction, DAEFunction +using SciMLBase +using LinearAlgebra: UniformScaling -export compute_signature_matrix, compute_hvt, compute_offsets, compute_jacobian +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::Vector, vars::Vector, t_ind) - # eqs[i] is something like f_i(x₁(t), x₂'(t), x₃''(t), ...) +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 each equation 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) @@ -35,26 +62,26 @@ Returns the highest derivative order of `var` that appears in the symbolic expre 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 an unrelated symbol we ignore + # 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] # The function being differentiated. - # Recursively check the order of the inner expression. + inner = arguments(ex)[1] # Function being differentiated. + # Recurse sub_order = max_derivative_order(inner, var, t_ind) - # If the inner expression is not related to var, return -Inf otherwise add 1 to derivative order. return sub_order == -Inf ? -Inf : 1 + sub_order end - # For composite expressions (e.g., sums, products), traverse their arguments. + # For composite expressions (e.g., sums, products), traverse arguments if iscall(ex) best = -Inf for arg in arguments(ex) @@ -94,11 +121,9 @@ 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 + # Define variables for c and d (offsets) @variable(model, c[1:n] >= 0, Int) @variable(model, d[1:n] >= 0, Int) @@ -116,7 +141,7 @@ function find_offsets(Σ::Matrix{Float64}, T::Vector{Tuple{Int, Int}}) @constraint(model, d[j] - c[i] == Σ[i, j]) end - # min sum c and d to find canonical offsets + # min sum c and d finds the "canonical" offsets @objective(model, Min, sum(c) + sum(d)) optimize!(model) c_values = value.(c) @@ -126,19 +151,49 @@ end """ - system_jacobian(eqs::Vector{SymbolicUtils.BasicSymbolic{Number}}, vars::Vector{SymbolicUtils.BasicSymbolic{SymbolicUtils.FnType{Tuple{Number}, Number, Nothing}}}, t_ind::SymbolicUtils.BasicSymbolic{Number}, c::Vector{Int}, d::Vector{Int}, Σ::Matrix{Float64}) -> Matrix{SymbolicUtils.BasicSymbolic{Number}} + 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::Vector{SymbolicUtils.BasicSymbolic{Number}}, - vars::Vector{SymbolicUtils.BasicSymbolic{SymbolicUtils.FnType{Tuple{Number}, Number, Nothing}}}, - t_ind::SymbolicUtils.BasicSymbolic{Number}, + 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) @@ -163,4 +218,6 @@ function system_jacobian( 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 index 4b8758a52c..a94a64b9ae 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl @@ -1,5 +1,13 @@ 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, @@ -24,8 +32,6 @@ import OrdinaryDiffEqCore using Reexport @reexport using DiffEqBase -include("DAETS_utils.jl") - include("algorithms.jl") include("alg_utils.jl") include("TaylorSeries_caches.jl") diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl index 2c8beb34a6..e171a1125f 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl @@ -75,10 +75,9 @@ get_fsalfirstlast(cache::ExplicitTaylorCache, u) = (cache.ks[1], cache.ks[1]) # Differential Algebriac Equation Taylor Series - -@cache struct DAETSCache{ - uType, rateType, uNoUnitsType, StageLimiter, StepLimiter, - Thread} <: OrdinaryDiffEqMutableCache +@cache mutable struct DAETSCache{ + uType, rateType, uNoUnitsType, tTypeNoUnits, StageLimiter, StepLimiter, + Thread, MatType} <: OrdinaryDiffEqMutableCache u::uType uprev::uType k1::rateType @@ -86,16 +85,61 @@ get_fsalfirstlast(cache::ExplicitTaylorCache, u) = (cache.ks[1], cache.ks[1]) k3::rateType utilde::uType tmp::uType - atmp::uNoUnitsType + atmp::uType stage_limiter!::StageLimiter step_limiter!::StepLimiter thread::Thread + xcur::uType + xTS::uType + xtrial::uType + htrial::tTypeNoUnits + e::tTypeNoUnits + Σ::MatType # Signature matrix + c::Vector{Int} # Offsets for equations + d::Vector{Int} # Offsets for variables + J::MatType # Jacobian end -get_fsalfirstlast(cache::DAETSCache, u) = (cache.k1, cache.k1) function alg_cache(alg::DAETS, 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 \ No newline at end of file + dt, reltol, p, calck, ::Val{true}) where {uEltypeNoUnits, uBottomEltypeNoUnits, tTypeNoUnits} + xcur = similar(u) + xTS = similar(u) + xtrial = similar(u) + htrial = dt + e = zero(tTypeNoUnits) + tmp = similar(u) + atmp = similar(u) + utilde = similar(u) + + k1 = zero(rate_prototype) + k2 = zero(rate_prototype) + k3 = zero(rate_prototype) + + fill!(xcur, 0.0) + fill!(xTS, 0.0) + fill!(xtrial, 0.0) + fill!(tmp, 0.0) + fill!(atmp, 0.0) + fill!(utilde, 0.0) + + # DAETS specific fields + n = length(u) + Σ = zeros(n, n) # Empty signature matrix + c = zeros(Int, n) # Equation offsets + d = zeros(Int, n) # Variable offsets + J = zeros(n, n) # Jacobian + + # Return cache + return DAETSCache{ + typeof(u), typeof(rate_prototype), typeof(atmp), tTypeNoUnits, + typeof(alg.stage_limiter!), typeof(alg.step_limiter!), typeof(alg.thread), + typeof(Σ) + }( + u, uprev, k1, k2, k3, utilde, tmp, atmp, + alg.stage_limiter!, alg.step_limiter!, alg.thread, + xcur, xTS, xtrial, htrial, e, + Σ, c, d, J + ) +end +get_fsalfirstlast(cache::DAETSCache, u) = (cache.k1, cache.k1) \ No newline at end of file diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl index d26a363075..e77b6f3909 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl @@ -102,28 +102,193 @@ end return nothing end -# Differential Algebriac Equation Taylor Series +# DAETS function initialize!(integrator, cache::DAETSCache) - 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 + 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 = cache + @unpack Σ, c, d, J, xTS, xtrial, htrial, e, tmp, xcur = cache + + ttrial = t + htrial + compute_taylor_coefficients!(integrator, cache) + xTS, ets = sum_taylor_series!(integrator, cache) + project!(cache.xtrial, cache.xTS, cache.J) + err = compute_error!(cache.e, cache.xtrial, cache.xTS, ets) + tol = 1e-6 # Set some tolerance + if hasproperty(integrator, :opts) && hasproperty(integrator.opts, :reltol) + tol = integrator.opts.reltol + end + + println("Using tolerance: ", tol) + println("Current error: ", err) + if err <= tol + println("Step accepted") + integrator.u .= cache.xtrial + integrator.t = ttrial + if hasproperty(integrator, :tprev) + integrator.tprev = t + end + if hasproperty(integrator, :tcur) + integrator.tcur = ttrial + end + if hasproperty(integrator, :uprev) + integrator.uprev .= u + end + integrator.dt = cache.htrial + else + println("Step rejected, adjusting step size") + # Temporary step size adjust + new_htrial = htrial * 0.5 + cache.htrial = max(new_htrial, 1e-10) + end + if hasproperty(integrator, :stats) + OrdinaryDiffEqCore.increment_nf!(integrator.stats, 3) + end - # 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 \ No newline at end of file diff --git a/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl b/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl index 1cd205847a..189757fecc 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/test/runtests.jl @@ -26,7 +26,7 @@ end end println("DONE with ODE tests") -include(joinpath(@__DIR__, "../src/DAETS_utils.jl")) +# include(joinpath(@__DIR__, "../src/DAETS_utils.jl")) # include(joinpath(@__DIR__, "../src/TaylorSeries_caches.jl")) println("Starting tests on DAETS") @@ -244,27 +244,17 @@ println("DONE System Jacobian Tests") @testset "System Jacobian Tests for Simple Pendulum" begin @syms t x(t) y(t) λ(t) G L - # Equations + # Pendulum 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: @@ -281,24 +271,26 @@ println("DONE System Jacobian Tests") @test isequal(J[3, 2], 2y(t)) @test isequal(J[3, 3], 0) end -# @testset "compute_taylor_coefficients! Tests" begin -# @syms t x(t) y(t) -# @syms G L - -# # Define a simple ODE system: x'(t) = y(t), y'(t) = -x(t) -# f = [Differential(t)(x(t)) - y(t), Differential(t)(y(t)) + x(t)] -# vars = [x, y] - -# # Create a mock integrator and cache -# integrator = (u = [1.0, 0.0], t = 0.0, dt = 0.1, f = f, p = nothing) -# cache = (Σ = nothing, c = nothing, d = nothing, J = nothing, xTS = nothing, xtrial = nothing, htrial = 0.1, e = 0.0, tmp = nothing) - -# # Compute Taylor coefficients -# xcur = compute_taylor_coefficients!(integrator, cache) - -# # Expected Taylor coefficients for x(t) and y(t) at t = 0: -# # x(t) = 1 - t^2/2 + t^4/24 - ... -# # y(t) = t - t^3/6 + t^5/120 - ... -# @test xcur[1] ≈ 1.0 # x(0) -# @test xcur[2] ≈ 0.0 # y(0) -# end + +using OrdinaryDiffEq +using Test +function exponential_decay!(du, u, p, t) + du[1] = -u[1] # du/dt = -u +end + +# Initial condition +u0 = [1.0] # u(0) = 1 +tspan = (0.0, 1.0) # Time span from 0 to 1 +prob = ODEProblem(exponential_decay!, u0, tspan) +dt = 0.01 # Fixed timestep +sol = solve(prob, DAETS(), dt=dt) +exact_solution(t) = exp(-t) + +# Test accuracy +for t in sol.t + u_num = sol(t)[1] + u_exact = exact_solution(t) + @test abs(u_num - u_exact) < .5 # Verify accuracy (up to first order) +end + +println("ODE test passed!") \ No newline at end of file From 43d548f8e44a8a9f064d0de5249f71da7fdd82a2 Mon Sep 17 00:00:00 2001 From: jchuharski Date: Wed, 19 Mar 2025 13:43:40 -0400 Subject: [PATCH 7/8] updates --- lib/OrdinaryDiffEqTaylorSeries/Project.toml | 4 + .../{DAETS_utils.jl => DAETs_symbolics.jl} | 0 .../src/OrdinaryDiffEqTaylorSeries.jl | 6 +- .../src/TaylorSeries_caches.jl | 175 ++++- .../src/TaylorSeries_perform_step.jl | 236 ++++++- .../src/algorithms.jl | 25 +- .../src/initialize_dae.jl | 21 + .../test/direct_test.jl | 30 + .../test/harmonic_oscillator_comparison.png | Bin 0 -> 44024 bytes .../test/linear_growth_comparison.png | Bin 0 -> 19213 bytes .../test/runtests.jl | 611 ++++++++++-------- path/to/runtests.jl | 27 + 12 files changed, 817 insertions(+), 318 deletions(-) rename lib/OrdinaryDiffEqTaylorSeries/src/{DAETS_utils.jl => DAETs_symbolics.jl} (100%) create mode 100644 lib/OrdinaryDiffEqTaylorSeries/src/initialize_dae.jl create mode 100644 lib/OrdinaryDiffEqTaylorSeries/test/direct_test.jl create mode 100644 lib/OrdinaryDiffEqTaylorSeries/test/harmonic_oscillator_comparison.png create mode 100644 lib/OrdinaryDiffEqTaylorSeries/test/linear_growth_comparison.png create mode 100644 path/to/runtests.jl diff --git a/lib/OrdinaryDiffEqTaylorSeries/Project.toml b/lib/OrdinaryDiffEqTaylorSeries/Project.toml index 473a6e0335..edce87e898 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/Project.toml +++ b/lib/OrdinaryDiffEqTaylorSeries/Project.toml @@ -11,7 +11,9 @@ 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" 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" @@ -32,7 +34,9 @@ Hungarian = "0.7.0" JuMP = "1.24.0" LinearAlgebra = "<0.0.1, 1" MuladdMacro = "0.2.4" +NLsolve = "4.5.1" OrdinaryDiffEqCore = "1.1" +Plots = "1.40.9" PrecompileTools = "1.2.1" Preferences = "1.4.3" Random = "<0.0.1, 1" diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/DAETS_utils.jl b/lib/OrdinaryDiffEqTaylorSeries/src/DAETs_symbolics.jl similarity index 100% rename from lib/OrdinaryDiffEqTaylorSeries/src/DAETS_utils.jl rename to lib/OrdinaryDiffEqTaylorSeries/src/DAETs_symbolics.jl diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl b/lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl index 4b8758a52c..01d21fddff 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/OrdinaryDiffEqTaylorSeries.jl @@ -8,6 +8,7 @@ import OrdinaryDiffEqCore: alg_order, alg_stability_size, explicit_rk_docstring, 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 @@ -15,7 +16,7 @@ import Static: False import MuladdMacro: @muladd import FastBroadcast: @.. import RecursiveArrayTools: recursivefill!, recursive_unitless_bottom_eltype -import LinearAlgebra: norm +import LinearAlgebra: norm, ldiv!, lu, cond using TruncatedStacktraces using TaylorDiff import DiffEqBase: @def @@ -24,7 +25,7 @@ import OrdinaryDiffEqCore using Reexport @reexport using DiffEqBase -include("DAETS_utils.jl") +include("DAETS_symbolics.jl") include("algorithms.jl") include("alg_utils.jl") @@ -32,6 +33,7 @@ include("TaylorSeries_caches.jl") include("interp_func.jl") # include("interpolants.jl") include("TaylorSeries_perform_step.jl") +include("initialize_dae.jl") import PrecompileTools import Preferences diff --git a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl index 2c8beb34a6..570dc41f31 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_caches.jl @@ -1,3 +1,6 @@ +# ------------------------------------------------------------------------------ +# Caches for Explicit Taylor Methods +# ------------------------------------------------------------------------------ @cache struct ExplicitTaylor2Cache{ uType, rateType, uNoUnitsType, StageLimiter, StepLimiter, Thread} <: OrdinaryDiffEqMutableCache @@ -28,13 +31,16 @@ function alg_cache(alg::ExplicitTaylor2, u, rate_prototype, ::Type{uEltypeNoUnit 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) @@ -55,14 +61,13 @@ function alg_cache(alg::ExplicitTaylor{P}, u, rate_prototype, ::Type{uEltypeNoUn ::Type{uBottomEltypeNoUnits}, ::Type{tTypeNoUnits}, uprev, uprev2, f, t, dt, reltol, p, calck, ::Val{true}) where {P, uEltypeNoUnits, uBottomEltypeNoUnits, tTypeNoUnits} - # ks: normalized derivatives for f, starting from 0 - ks = ntuple(i -> zero(rate_prototype), Val(P)) - # us: normalized derivatives for u, starting from 1 - us = ntuple(i -> zero(u), Val(P)) + 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, @@ -74,28 +79,166 @@ end get_fsalfirstlast(cache::ExplicitTaylorCache, u) = (cache.ks[1], cache.ks[1]) -# Differential Algebriac Equation Taylor Series +# ------------------------------------------------------------------------------ +# DAETS Caches +# ------------------------------------------------------------------------------ -@cache struct DAETSCache{ - uType, rateType, uNoUnitsType, StageLimiter, StepLimiter, - Thread} <: OrdinaryDiffEqMutableCache + +@cache mutable struct DAETSCache{ + uType, rateType, uNoUnitsType, tTypeNoUnits, StageLimiter, StepLimiter, + Thread, MatType} <: OrdinaryDiffEqMutableCache u::uType uprev::uType k1::rateType - k2::rateType - k3::rateType utilde::uType tmp::uType - atmp::uNoUnitsType + 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) -function alg_cache(alg::DAETS, u, rate_prototype, ::Type{uEltypeNoUnits}, - ::Type{uBottomEltypeNoUnits}, ::Type{tTypeNoUnits}, uprev, uprev2, f, t, - dt, reltol, p, calck, - ::Val{false}) where {uEltypeNoUnits, uBottomEltypeNoUnits, tTypeNoUnits} -ExplicitTaylor2ConstantCache() + +# ------------------------------------------------------------------------------ +# 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 index d26a363075..7d4e832b8f 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/TaylorSeries_perform_step.jl @@ -1,4 +1,6 @@ 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) @@ -102,7 +104,10 @@ end return nothing end +# ------------------------------------------------------------------------------ # Differential Algebriac Equation Taylor Series +# ------------------------------------------------------------------------------ + function initialize!(integrator, cache::DAETSCache) integrator.kshortsize = 3 resize!(integrator.k, integrator.kshortsize) @@ -115,15 +120,228 @@ 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 = cache + @unpack k1, k2, k3, utilde, tmp, diff_indices, jacobian, taylor_coeffs, error_estimate, + u_unprojected, residual, correction = 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) + # ===== 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/algorithms.jl b/lib/OrdinaryDiffEqTaylorSeries/src/algorithms.jl index 13e06a2028..9cad78cf63 100644 --- a/lib/OrdinaryDiffEqTaylorSeries/src/algorithms.jl +++ b/lib/OrdinaryDiffEqTaylorSeries/src/algorithms.jl @@ -1,4 +1,3 @@ - Base.@kwdef struct ExplicitTaylor2{StageLimiter, StepLimiter, Thread} <: OrdinaryDiffEqAlgorithm stage_limiter!::StageLimiter = trivial_limiter! @@ -19,14 +18,20 @@ Base.@kwdef struct ExplicitTaylor{P, StageLimiter, StepLimiter, Thread} <: thread::Thread = False() end -Base.@kwdef struct DAETS{StageLimiter, StepLimiter, Thread} <: - OrdinaryDiffEqAlgorithm - stage_limiter!::StageLimiter = trivial_limiter! - step_limiter!::StepLimiter = trivial_limiter! - thread::Thread = False() +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 -TruncatedStacktraces.@truncate_stacktrace DAETS 3 -# for backwards compatibility -function DAETS(stage_limiter!, step_limiter! = trivial_limiter!) - DAETS(stage_limiter!, step_limiter!, False()) + +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 \ No newline at end of file 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/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 0000000000000000000000000000000000000000..3e3da69866f3a3dc42769c19d4e1da82b68ff7e7 GIT binary patch literal 44024 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 Date: Wed, 19 Mar 2025 14:02:08 -0400 Subject: [PATCH 8/8] didn't add to gitignore --- path/to/runtests.jl | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 path/to/runtests.jl diff --git a/path/to/runtests.jl b/path/to/runtests.jl deleted file mode 100644 index fc882a0f78..0000000000 --- a/path/to/runtests.jl +++ /dev/null @@ -1,27 +0,0 @@ -using Test -using OrdinaryDiffEqTaylorSeries: build_index_reduced_system - -# A small dummy DAE function: -function dummy_dae(du, x, p, t) - # Not doing actual computations - just a placeholder - return du .+ x -end - -# A mock-up compute_system_jacobian for testing: -function compute_system_jacobian(f, taylor_u, p, t0, order) - # Return a trivial 2x2 identity matrix so we can confirm the function gets called. - return [1.0 0.0; 0.0 1.0] -end - -@testset "DAETS build_index_reduced_system test" begin - # Let's pick a small initial condition - u0 = [1.0, 2.0] - p = nothing - t0 = 0.0 - - # We call our function under test: - order, jac = build_index_reduced_system(dummy_dae, u0, p, t0; max_order=5) - - @test order == 0 # Because our identity matrix is already nonsingular - @test jac == [1.0 0.0; 0.0 1.0] -end \ No newline at end of file