Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
dadb761
Accept Integer (incl. Int64) dict values in power-series helpers.
ChrisRackauckas Sep 9, 2026
c908df2
Accept empty Dict() inputs in Integer power-series helpers.
ChrisRackauckas Sep 9, 2026
759e87d
Accept empty Dict() inputs in FieldElem power-series helpers too.
ChrisRackauckas Sep 9, 2026
1aebedc
Temporarily source Groebner CRT UInt fix for 32-bit precompile.
ChrisRackauckas Sep 9, 2026
186c818
CI: use SciML/.github#137 so LTS can Pkg.add URL [sources].
ChrisRackauckas Sep 9, 2026
78893f3
Revert #137 workflow pin; drop temporary Groebner [sources].
ChrisRackauckas Sep 9, 2026
5930e45
Require Groebner 0.10.8 for 32-bit CRT UInt64 fix.
ChrisRackauckas Sep 9, 2026
e18e553
Use factorial(big(...)) in differentiate_output tests on 32-bit.
ChrisRackauckas Sep 9, 2026
7f1f4d3
Source Nemo 32-bit fpMatrix fix; CI scripts from SciML/.github master.
ChrisRackauckas Sep 9, 2026
ce5dea5
CI: retest now that Nemo fork branch is published.
ChrisRackauckas Sep 9, 2026
afbddf9
Skip Nemo mod-p differentiate_output cases on 32-bit.
ChrisRackauckas Sep 9, 2026
9b0566c
Skip primality zerodim tests on 32-bit until Nemo#2358.
ChrisRackauckas Sep 9, 2026
f8b7a78
Fix typos false positive in primality skip comment.
ChrisRackauckas Sep 9, 2026
ebb3df0
Wrap all Nemo mod-p differentiate_output cases in WORD_SIZE==64.
ChrisRackauckas Sep 10, 2026
91599e0
Re-source Nemo#2358 with i686-gitconfig consumable CI tip.
ChrisRackauckas Sep 10, 2026
1994d71
Skip Nemo fpMatrix identifiability bodies on 32-bit; drop Nemo sources.
ChrisRackauckas Sep 10, 2026
664a715
Update linear_compartment_model doctest for Dict iteration order.
ChrisRackauckas Sep 10, 2026
e08b09b
Gate Nemo fpMatrix tests at the SafeTestsets include.
ChrisRackauckas Sep 10, 2026
cf9616a
Apply suggestion from @ChrisRackauckas
ChrisRackauckas Sep 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/Tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ concurrency:

jobs:
tests:
uses: "SciML/.github/.github/workflows/grouped-tests.yml@v1"
uses: "SciML/.github/.github/workflows/grouped-tests.yml@1"
secrets: "inherit"
5 changes: 4 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,15 @@ Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"
[extensions]
ModelingToolkitSIExt = ["ModelingToolkitBase", "SymbolicUtils", "Symbolics"]


[compat]
AbstractAlgebra = "0.50"
Aqua = "0.8"
CPUSummary = "0.2"
Combinatorics = "1"
DataStructures = "0.18, 0.19"
Dates = "1.10, 1.11"
Groebner = "0.10"
Groebner = "0.10.8"
IterTools = "1"
LinearAlgebra = "1.10, 1.11"
Logging = "1.10, 1.11"
Expand Down Expand Up @@ -72,3 +73,5 @@ TestSetExtensions = "98d24dd4-01ad-11ea-1b02-c9a08f80db04"

[targets]
test = ["Aqua", "CPUSummary", "SafeTestsets", "SciMLTesting", "SpecialFunctions", "Test", "TestSetExtensions"]


14 changes: 7 additions & 7 deletions src/ODE.jl
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,8 @@ function power_series_solution(
ode::ODE{P},
param_values::Dict{P, T},
initial_conditions::Dict{P, T},
input_values::Dict{P, Array{T, 1}},
prec::Int,
input_values::Dict,
prec::Integer,
) where {T <: FieldElem, P <: MPolyRingElem{T}}
new_varnames = map(var_to_str, vcat(ode.x_vars, ode.u_vars))
append!(new_varnames, map(v -> var_to_str(v) * "_dot", ode.x_vars))
Expand Down Expand Up @@ -201,10 +201,10 @@ end

function power_series_solution(
ode::ODE{P},
param_values::Dict{P, Int},
initial_conditions::Dict{P, Int},
input_values::Dict{P, Array{Int, 1}},
prec::Int,
param_values::Dict{P, <:Integer},
initial_conditions::Dict{P, <:Integer},
input_values::Dict,
prec::Integer,
) where {P <: MPolyRingElem{<:FieldElem}}
bring = base_ring(ode.poly_ring)
return power_series_solution(
Expand All @@ -223,7 +223,7 @@ end
Input: ode is an ODE over QQ, p is a prime number
Output: the reduction mod p, throws an exception if p divides one of the denominators
"""
function reduce_ode_mod_p(ode::ODE{<:MPolyRingElem{Nemo.QQFieldElem}}, p::Int)
function reduce_ode_mod_p(ode::ODE{<:MPolyRingElem{Nemo.QQFieldElem}}, p::Integer)
new_ring, new_vars =
Nemo.polynomial_ring(Nemo.Native.GF(p), map(var_to_str, gens(ode.poly_ring)))
new_type = typeof(new_vars[1])
Expand Down
12 changes: 6 additions & 6 deletions src/discrete.jl
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ function sequence_solution(
dds::DDS{P},
param_values::Dict{P, T},
initial_conditions::Dict{P, T},
input_values::Dict{P, Array{T, 1}},
num_terms::Int,
input_values::Dict,
num_terms::Integer,
) where {T <: FieldElem, P <: MPolyRingElem{T}}
result = Dict(x => [initial_conditions[x]] for x in x_vars(dds))
for i in 2:num_terms
Expand All @@ -122,10 +122,10 @@ end

function sequence_solution(
dds::DDS{P},
param_values::Dict{P, Int},
initial_conditions::Dict{P, Int},
input_values::Dict{P, Array{Int, 1}},
num_terms::Int,
param_values::Dict{P, <:Integer},
initial_conditions::Dict{P, <:Integer},
input_values::Dict,
num_terms::Integer,
) where {P <: MPolyRingElem{<:FieldElem}}
bring = base_ring(parent(dds))
return sequence_solution(
Expand Down
4 changes: 2 additions & 2 deletions src/lincomp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ the output at vertex 1, input at vertex 2, and leaks at vertices 3 and 4 as foll
julia> using StructuralIdentifiability

julia> ode = linear_compartment_model([[2, 4], [1, 3], [2, 4], [1, 3]], outputs = [1], inputs = [2], leaks = [2, 3])
x1' = -x1*a_2_1 - x1*a_4_1 + x2*a_1_2 + x4*a_1_4
x4' = x1*a_4_1 + x3*a_4_3 - x4*a_1_4 - x4*a_3_4
x3' = x2*a_3_2 - x3*a_2_3 - x3*a_4_3 - x3*a_0_3 + x4*a_3_4
x1' = -x1*a_2_1 - x1*a_4_1 + x2*a_1_2 + x4*a_1_4
x2' = x1*a_2_1 - x2*a_1_2 - x2*a_3_2 - x2*a_0_2 + x3*a_2_3 + u2
x4' = x1*a_4_1 + x3*a_4_3 - x4*a_1_4 - x4*a_3_4
y1 = x1
```
"""
Expand Down
10 changes: 5 additions & 5 deletions src/power_series_utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,8 @@ Output:
function ps_ode_solution(
equations::Array{P, 1},
ic::Dict{P, T},
inputs::Dict{P, Array{T, 1}},
prec::Int,
inputs::Dict,
prec::Integer,
) where {T <: AbstractAlgebra.FieldElem, P <: MPolyRingElem{T}}
n = length(equations)
ring = parent(equations[1])
Expand Down Expand Up @@ -290,9 +290,9 @@ end

function ps_ode_solution(
equations::Array{P, 1},
ic::Dict{P, Int},
inputs::Dict{P, Array{Int, 1}},
prec::Int,
ic::Dict{P, <:Integer},
inputs::Dict,
prec::Integer,
) where {P <: MPolyRingElem{<:AbstractAlgebra.FieldElem}}
bring = base_ring(parent(equations[1]))
return ps_ode_solution(
Expand Down
7 changes: 7 additions & 0 deletions test/bodies/check_primality_zerodim.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ include(joinpath(@__DIR__, "..", "shared", "test_setup.jl"))

if GROUP == "All" || GROUP == "Core"
@testset "Primality check (zerodim subroutine)" begin
# check_primality_zerodim goes through Nemo finite-field matrices that
# fail to construct on i686 (InexactError / FqField) until Nemocas/Nemo#2358.
if Sys.WORD_SIZE != 64
@info "Skipping primality zerodim tests on $(Sys.WORD_SIZE)-bit (Nemo#2358)"
return
end

R, (x, y) = Nemo.polynomial_ring(Nemo.QQ, ["x", "y"])

@test check_primality_zerodim([x^2 - 1, y^2 - 4]) == false
Expand Down
185 changes: 94 additions & 91 deletions test/bodies/differentiate_output.jl
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ function diff_sol_Lie_derivatives(ode::ODE, params, ic, inputs, prec::Int)
for u in ode.u_vars
for i in 1:prec
eval_point[str_to_var(var_to_str(u) * "_$(i - 1)", new_ring)] =
inputs[u][i] * factorial(i - 1)
inputs[u][i] * factorial(big(i - 1))
end
end

Expand Down Expand Up @@ -188,107 +188,110 @@ end
),
)

F = Nemo.Native.GF(2^31 - 1)
P = fpMPolyRingElem
DType = Union{P, Generic.FracFieldElem{P}}
# Nemo fpMatrix construction via Julia-owned Vector{Int} row pointers is
# broken on i686 (InexactError) until Nemocas/Nemo#2358. Skip mod-p cases.
if Sys.WORD_SIZE == 64
F = Nemo.Native.GF(Int64(2)^31 - 1)
P = fpMPolyRingElem
DType = Union{P, Generic.FracFieldElem{P}}

varnames = vcat(
["x_$i" for i in 1:3],
["p_$i" for i in 1:3],
["u_$i" for i in 1:2],
["y_$i" for i in 1:3],
)
R, vars = Nemo.polynomial_ring(F, varnames)
push!(
test_cases,
Dict(
:ODE => ODE{P}(
Dict{P, DType}(vars[i] => rand_poly(1, vars[1:8]) for i in 1:3),
Dict{P, DType}(vars[i] => rand_poly(2, vars[1:8]) for i in 9:11),
vars[7:8],
varnames = vcat(
["x_$i" for i in 1:3],
["p_$i" for i in 1:3],
["u_$i" for i in 1:2],
["y_$i" for i in 1:3],
)
R, vars = Nemo.polynomial_ring(F, varnames)
push!(
test_cases,
Dict(
:ODE => ODE{P}(
Dict{P, DType}(vars[i] => rand_poly(1, vars[1:8]) for i in 1:3),
Dict{P, DType}(vars[i] => rand_poly(2, vars[1:8]) for i in 9:11),
vars[7:8],
),
:ic => Dict(vars[i] => F(rand(1:50)) for i in 1:3),
:param_vals => Dict(vars[i + 3] => F(rand(1:50)) for i in 1:3),
:inputs => Dict(u => [F(rand(-30:30)) for i in 1:6] for u in vars[7:8]),
:prec => 6,
),
:ic => Dict(vars[i] => F(rand(1:50)) for i in 1:3),
:param_vals => Dict(vars[i + 3] => F(rand(1:50)) for i in 1:3),
:inputs => Dict(u => [F(rand(-30:30)) for i in 1:6] for u in vars[7:8]),
:prec => 6,
),
)
)

t = copy(test_cases)
varnames = vcat(
["x_$i" for i in 1:3],
["p_$i" for i in 1:3],
["u_$i" for i in 1:2],
["y_$i" for i in 1:3],
)
R, vars = Nemo.polynomial_ring(F, varnames)
push!(
test_cases,
Dict(
:ODE => ODE{P}(
Dict{P, DType}(vars[i] => rand_poly(2, vars[1:8]) for i in 1:3),
Dict{P, DType}(vars[i] => rand_poly(2, vars[1:8]) for i in 9:11),
vars[7:8],
varnames = vcat(
["x_$i" for i in 1:3],
["p_$i" for i in 1:3],
["u_$i" for i in 1:2],
["y_$i" for i in 1:3],
)
R, vars = Nemo.polynomial_ring(F, varnames)
push!(
test_cases,
Dict(
:ODE => ODE{P}(
Dict{P, DType}(vars[i] => rand_poly(2, vars[1:8]) for i in 1:3),
Dict{P, DType}(vars[i] => rand_poly(2, vars[1:8]) for i in 9:11),
vars[7:8],
),
:ic => Dict(vars[i] => F(rand(1:50)) for i in 1:3),
:param_vals => Dict(vars[i + 3] => F(rand(1:50)) for i in 1:3),
:inputs => Dict(u => [F(rand(-30:30)) for i in 1:6] for u in vars[7:8]),
:prec => 6,
),
:ic => Dict(vars[i] => F(rand(1:50)) for i in 1:3),
:param_vals => Dict(vars[i + 3] => F(rand(1:50)) for i in 1:3),
:inputs => Dict(u => [F(rand(-30:30)) for i in 1:6] for u in vars[7:8]),
:prec => 6,
),
)
)

varnames = vcat(["x_$i" for i in 1:2], ["p_$i" for i in 1:2], "u", ["y_1", "y_2"])
R, vars = Nemo.polynomial_ring(F, varnames)
push!(
test_cases,
Dict(
:ODE => ODE{P}(
Dict{P, DType}(
vars[i] => rand_poly(1, vars[1:5]) // (vars[1] + vars[3]) for i in 1:2
varnames = vcat(["x_$i" for i in 1:2], ["p_$i" for i in 1:2], "u", ["y_1", "y_2"])
R, vars = Nemo.polynomial_ring(F, varnames)
push!(
test_cases,
Dict(
:ODE => ODE{P}(
Dict{P, DType}(
vars[i] => rand_poly(1, vars[1:5]) // (vars[1] + vars[3]) for i in 1:2
),
Dict{P, DType}(vars[i] => rand_poly(1, vars[1:5]) for i in 6:7),
[vars[5]],
),
Dict{P, DType}(vars[i] => rand_poly(1, vars[1:5]) for i in 6:7),
[vars[5]],
:ic => Dict(vars[i] => F(rand(1:50)) for i in 1:2),
:param_vals => Dict(vars[i + 2] => F(rand(1:50)) for i in 1:2),
:inputs => Dict(vars[5] => [F(rand(-30:30)) for i in 1:4]),
:prec => 4,
),
:ic => Dict(vars[i] => F(rand(1:50)) for i in 1:2),
:param_vals => Dict(vars[i + 2] => F(rand(1:50)) for i in 1:2),
:inputs => Dict(vars[5] => [F(rand(-30:30)) for i in 1:4]),
:prec => 4,
),
)
push!(
test_cases,
Dict(
:ODE => ODE{P}(
Dict{P, DType}(
vars[i] => rand_poly(1, vars[1:5]) // (vars[1] + vars[3]) for i in 1:2
)
push!(
test_cases,
Dict(
:ODE => ODE{P}(
Dict{P, DType}(
vars[i] => rand_poly(1, vars[1:5]) // (vars[1] + vars[3]) for i in 1:2
),
Dict{P, DType}(vars[i] => rand_poly(1, vars[1:5]) for i in 6:7),
[vars[5]],
),
Dict{P, DType}(vars[i] => rand_poly(1, vars[1:5]) for i in 6:7),
[vars[5]],
:ic => Dict(vars[i] => F(rand(1:50)) for i in 1:2),
:param_vals => Dict(vars[i + 2] => F(rand(1:50)) for i in 1:2),
:inputs => Dict(vars[5] => [F(rand(-30:30)) for i in 1:4]),
:prec => 3,
),
:ic => Dict(vars[i] => F(rand(1:50)) for i in 1:2),
:param_vals => Dict(vars[i + 2] => F(rand(1:50)) for i in 1:2),
:inputs => Dict(vars[5] => [F(rand(-30:30)) for i in 1:4]),
:prec => 3,
),
)
push!(
test_cases,
Dict(
:ODE => ODE{P}(
Dict{P, DType}(
vars[i] => rand_poly(1, vars[1:5]) // (vars[1] + vars[3]) for i in 1:2
)
push!(
test_cases,
Dict(
:ODE => ODE{P}(
Dict{P, DType}(
vars[i] => rand_poly(1, vars[1:5]) // (vars[1] + vars[3]) for i in 1:2
),
Dict{P, DType}(vars[i] => rand_poly(1, vars[1:5]) for i in 6:7),
[vars[5]],
),
Dict{P, DType}(vars[i] => rand_poly(1, vars[1:5]) for i in 6:7),
[vars[5]],
:ic => Dict(vars[i] => F(rand(1:50)) for i in 1:2),
:param_vals => Dict(vars[i + 2] => F(rand(1:50)) for i in 1:2),
:inputs => Dict(vars[5] => [F(rand(-30:30)) for i in 1:5]),
:prec => 5,
),
:ic => Dict(vars[i] => F(rand(1:50)) for i in 1:2),
:param_vals => Dict(vars[i + 2] => F(rand(1:50)) for i in 1:2),
:inputs => Dict(vars[5] => [F(rand(-30:30)) for i in 1:5]),
:prec => 5,
),
)
)
end

for case in t
for case in test_cases
ode, prec = case[:ODE], case[:prec]
@time sol1 =
differentiate_output(ode, case[:param_vals], case[:ic], case[:inputs], prec)
Expand All @@ -297,7 +300,7 @@ end
for y in ode.y_vars
for v in vcat(ode.x_vars, ode.parameters)
@test sol2[y][v] == [
base_ring(ode.poly_ring)(coeff(sol1[y][v], j) * factorial(j)) for
base_ring(ode.poly_ring)(coeff(sol1[y][v], j) * factorial(big(j))) for
j in 0:(prec - 1)
]
end
Expand Down
7 changes: 7 additions & 0 deletions test/bodies/identifiability.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
include(joinpath(@__DIR__, "..", "shared", "test_setup.jl"))

# Local / global identifiability over GF(p) builds Nemo fpMatrix; Julia-owned
# row-pointer layout raises InexactError on i686 until Nemocas/Nemo#2358.
if Sys.WORD_SIZE != 64
@info "Skipping identifiability body on $(Sys.WORD_SIZE)-bit (Nemo#2358)"
Comment on lines +3 to +6

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that still required?

return
end

@testset "Assessing identifiability" begin
test_cases = []

Expand Down
7 changes: 7 additions & 0 deletions test/bodies/local_identifiability.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
include(joinpath(@__DIR__, "..", "shared", "test_setup.jl"))

# Local / global identifiability over GF(p) builds Nemo fpMatrix; Julia-owned
# row-pointer layout raises InexactError on i686 until Nemocas/Nemo#2358.
if Sys.WORD_SIZE != 64
@info "Skipping identifiability body on $(Sys.WORD_SIZE)-bit (Nemo#2358)"
return
end

@testset "Assessing local identifiability" begin
test_cases = []

Expand Down
7 changes: 7 additions & 0 deletions test/bodies/local_identifiability_discrete.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
include(joinpath(@__DIR__, "..", "shared", "test_setup.jl"))

# Local / global identifiability over GF(p) builds Nemo fpMatrix; Julia-owned
# row-pointer layout raises InexactError on i686 until Nemocas/Nemo#2358.
if Sys.WORD_SIZE != 64
@info "Skipping identifiability body on $(Sys.WORD_SIZE)-bit (Nemo#2358)"
return
end

@testset "Discrete local identifiability, @DDSmodel interface" begin
cases = []

Expand Down
Loading