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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/Test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ jobs:
- IO
- Spatial
- Extensions
- QA
uses: "SciML/.github/.github/workflows/tests.yml@v1"
with:
julia-version: "${{ matrix.version }}"
Expand Down
7 changes: 6 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
DynamicPolynomials = "7c1d4256-1411-5781-91ec-d7bc3513ac07"
DynamicQuantities = "06fc5a27-2a28-4c7c-a15d-362465fb6821"
EnumX = "4e289a0a-7415-4d19-859d-a7e5c4648b56"
ExplicitImports = "7d51a73a-1435-4ff3-83d9-f097790105c7"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
JumpProcesses = "ccbc3e58-028d-4f4c-8cd5-9ae44345cda5"
LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
Expand All @@ -24,6 +25,7 @@ RuntimeGeneratedFunctions = "7e49a35a-f44a-4d26-94aa-eba1b4ca6b47"
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5"
SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b"
Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
Expand Down Expand Up @@ -54,6 +56,7 @@ DocStringExtensions = "0.8, 0.9"
DynamicPolynomials = "0.6"
DynamicQuantities = "1"
EnumX = "1"
ExplicitImports = "1.13.1"
GraphMakie = "0.5"
Graphs = "1.4"
HomotopyContinuation = "2.9"
Expand All @@ -71,12 +74,14 @@ RuntimeGeneratedFunctions = "0.5.12"
SciMLBase = "2.84"
Setfield = "1"
StructuralIdentifiability = "0.5.11"
SymbolicIndexingInterface = "0.3"
SymbolicUtils = "3.20"
Symbolics = "6.31.1"
Unitful = "1.12.4"
julia = "1.10"

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
DataInterpolations = "82cc6244-b520-54b8-b5a6-8a565e85f1d0"
DiffEqCallbacks = "459566f4-90b8-5000-8ac3-15dfb0a30def"
DomainSets = "5b8099bc-c8ec-5219-889f-1d9e522a28bf"
Expand All @@ -100,4 +105,4 @@ StochasticDiffEq = "789caeaf-c7a9-5a7d-9973-96adeb23e2a0"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["DataInterpolations", "DiffEqCallbacks", "DomainSets", "Logging", "NonlinearSolve", "OrdinaryDiffEqBDF", "OrdinaryDiffEqDefault", "OrdinaryDiffEqRosenbrock", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqVerner", "Pkg", "Plots", "Random", "SafeTestsets", "StableRNGs", "StaticArrays", "Statistics", "SteadyStateDiffEq", "StochasticDiffEq", "Test"]
test = ["Aqua", "DataInterpolations", "DiffEqCallbacks", "DomainSets", "Logging", "NonlinearSolve", "OrdinaryDiffEqBDF", "OrdinaryDiffEqDefault", "OrdinaryDiffEqRosenbrock", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqVerner", "Pkg", "Plots", "Random", "SafeTestsets", "StableRNGs", "StaticArrays", "Statistics", "SteadyStateDiffEq", "StochasticDiffEq", "Test"]
4 changes: 3 additions & 1 deletion ext/CatalystBifurcationKitExtension.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
module CatalystBifurcationKitExtension

# Fetch packages.
using Catalyst
using Catalyst: Catalyst, ReactionSystem, NonlinearSystem, ModelingToolkit,
isautonomous, get_iv, symmap_to_varmap, conservationlaw_constants,
complete, conservationlaw_errorcheck, get_networkproperties
import BifurcationKit as BK

# Extends BifurcationProblem to work for ReactionSystem.
Expand Down
4 changes: 3 additions & 1 deletion ext/CatalystCairoMakieExtension.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
module CatalystCairoMakieExtension

# Fetch packages.
using Catalyst, CairoMakie, SparseArrays
using Catalyst: Catalyst
import Catalyst: lattice_plot, lattice_animation, lattice_kymograph,
demask_vals, extract_vals, extract_grid_axes
using CairoMakie: CairoMakie
using SparseArrays: SparseArrays

# Creates and exports utilities for plotting lattice simulations.
include("CatalystCairoMakieExtension/cairo_makie_extension_spatial_modelling.jl")
Expand Down
11 changes: 8 additions & 3 deletions ext/CatalystGraphMakieExtension.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
module CatalystGraphMakieExtension

# Fetch packages.
using Catalyst, GraphMakie, Graphs, Symbolics, SparseArrays, NetworkLayout, Makie
using Symbolics: get_variables!
import Catalyst: species_reaction_graph, incidencematgraph, lattice_plot, lattice_animation
using Catalyst: Catalyst, species_reaction_graph, incidencematgraph,
lattice_plot, lattice_animation
using GraphMakie: GraphMakie
using Graphs: Graphs
using Symbolics: Symbolics, get_variables!
using SparseArrays: SparseArrays
using NetworkLayout: NetworkLayout
using Makie: Makie

# Creates and exports graph plotting functions.
include("CatalystGraphMakieExtension/graph_makie_extension_spatial_modelling.jl")
Expand Down
9 changes: 7 additions & 2 deletions ext/CatalystHomotopyContinuationExtension.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
module CatalystHomotopyContinuationExtension

# Fetch packages.
using Catalyst
using Catalyst: Catalyst, ReactionSystem, NonlinearSystem, ModelingToolkit,
isautonomous, get_iv, complete, conservationlaw_constants,
conservedequations, species, unknowns, parameters, substitute,
symmap_to_varmap, get_networkproperties, conservationlaw_errorcheck,
hc_steady_states, Initial, equations, defaults, mm, hill
import DynamicPolynomials
import ModelingToolkit as MT
import HomotopyContinuation as HC
import Setfield: @set
import Symbolics: unwrap, wrap, Rewriters, symtype, issym, maketerm, BasicSymbolic, metadata
using Symbolics: iscall
using Symbolics: iscall, simplify, simplify_fractions, solve, arguments, operation,
sorted_arguments

# Creates and exports hc_steady_states function.
include("CatalystHomotopyContinuationExtension/homotopy_continuation_extension.jl")
Expand Down
7 changes: 5 additions & 2 deletions ext/CatalystStructuralIdentifiabilityExtension.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
module CatalystStructuralIdentifiabilityExtension

# Fetch packages.
using Catalyst
import DataStructures.OrderedDict
using Catalyst: Catalyst, ReactionSystem, ODESystem, ModelingToolkit, Equation,
complete, conservationlaw_constants, conservedequations,
species, unknowns, parameters, substitute, equations,
observed, flatten, defaults, make_si_ode
import DataStructures: OrderedDict
import StructuralIdentifiability as SI

# Creates and exports make_si_ode function.
Expand Down
64 changes: 40 additions & 24 deletions src/Catalyst.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,50 +3,66 @@ $(DocStringExtensions.README)
"""
module Catalyst

using DocStringExtensions
using SparseArrays, DiffEqBase, Reexport, Setfield, EnumX
using LaTeXStrings, Latexify, Requires
using LinearAlgebra, Combinatorics
using DocStringExtensions: DocStringExtensions, FIELDS, TYPEDEF, README
using SparseArrays: SparseArrays, AbstractSparseArray, SparseMatrixCSC,
SparseVector, findnz, issparse, nnz, nonzeros, nzrange,
rowvals, sparse, spzeros
using DiffEqBase: DiffEqBase, ODESolution, SciMLBase, deleteat!, remake
using Reexport: Reexport, @reexport
using Setfield: Setfield, @set, @set!, get
using EnumX: EnumX, @enumx
using LaTeXStrings: LaTeXStrings, LaTeXString
using Latexify: Latexify, @latexrecipe, latexraw, md
using Requires: Requires
using LinearAlgebra: LinearAlgebra, I, rank, tr, eigvals
using Combinatorics: Combinatorics, factorial
using JumpProcesses: JumpProcesses, JumpProblem,
MassActionJump, ConstantRateJump, VariableRateJump,
SpatialMassActionJump, CartesianGrid, CartesianGridRej

# ModelingToolkit imports and convenience functions we use
using ModelingToolkit
using ModelingToolkit: ModelingToolkit, @parameters, @unpack, DiscreteProblem,
Initial, JumpSystem, NonlinearProblem, NonlinearSystem,
ODEFunction, ODEProblem, ODESystem, SDEProblem,
SDESystem, SteadyStateProblem, Sym, asgraph, complete,
continuous_events, defaults, discrete_events, entry,
eqeq_dependencies, equations, has_alg_equations,
independent_variables, observed, parameters, unknowns,
variable_dependencies, D_nounits, t_nounits
const MT = ModelingToolkit
using DynamicQuantities #, Unitful # Having Unitful here as well currently gives an error.
using DynamicQuantities: DynamicQuantities, @u_str

@reexport using ModelingToolkit
using Symbolics
using LinearAlgebra
using RuntimeGeneratedFunctions
using Symbolics: Symbolics, @register_symbolic, @variables, Differential,
Equation, Num, PolyForm, SymbolicUtils, arguments,
build_function, operation, substitute, iscall, sorted_arguments
using RuntimeGeneratedFunctions: RuntimeGeneratedFunctions,
@RuntimeGeneratedFunction, drop_expr
RuntimeGeneratedFunctions.init(@__MODULE__)

import Symbolics: BasicSymbolic
using Symbolics: iscall, sorted_arguments
using ModelingToolkit: Symbolic, value, get_unknowns, get_ps, get_iv, get_systems,
get_eqs, get_defaults, toparam, get_var_to_name, get_observed,
getvar, has_iv
import SymbolicUtils: BasicSymbolic
import SymbolicUtils: Symbolic
import Symbolics: value
using ModelingToolkit: get_unknowns, get_ps, get_iv, get_systems,
get_eqs, get_defaults, get_observed, has_iv

import ModelingToolkit: get_variables, namespace_expr, namespace_equation, get_variables!,
modified_unknowns!, validate, namespace_variables,
namespace_parameters, rename, renamespace, getname, flatten,
is_alg_equation, is_diff_equation, collect_vars!,
eqtype_supports_collect_vars
import ModelingToolkit: get_variables, namespace_expr, namespace_equation,
validate, namespace_variables, flatten, is_diff_equation
import Symbolics: get_variables!
import SymbolicIndexingInterface: getname

# internal but needed ModelingToolkit functions
import ModelingToolkit: check_variables,
check_parameters, _iszero, _merge, check_units,
get_unit, check_equations, iscomplete

import Base: (==), hash, size, getindex, setindex, isless, Sort.defalg, length, show
import Base: (==), hash, size, getindex, isless, Sort.defalg, length, show, occursin
import MacroTools, Graphs
using MacroTools: striplines
import Graphs: DiGraph, SimpleGraph, SimpleDiGraph, vertices, edges, add_vertices!, nv, ne
import Graphs: DiGraph, SimpleGraph, SimpleDiGraph, edges, nv, ne
import DataStructures: OrderedDict, OrderedSet
import Parameters: @with_kw_noshow
import Symbolics: occursin, wrap
import Symbolics.RewriteHelpers: hasnode, replacenode
using Symbolics.RewriteHelpers: hasnode, replacenode
import SymbolicUtils: getmetadata, hasmetadata, setmetadata

# globals for the modulate
Expand Down Expand Up @@ -170,7 +186,7 @@ export isedgeparameter
include("spatial_reaction_systems/lattice_reaction_systems.jl")
export LatticeReactionSystem
export spatial_species, vertex_parameters, edge_parameters
export CartesianGrid, CartesianGridReJ # (Implemented in JumpProcesses)
export CartesianGrid, CartesianGridRej # (Implemented in JumpProcesses)
export has_cartesian_lattice, has_masked_lattice, has_grid_lattice, has_graph_lattice,
grid_dims, grid_size
export make_edge_p_values, make_directed_edge_values
Expand Down
144 changes: 144 additions & 0 deletions test/qa.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
using Test
using Catalyst
using Aqua
using ExplicitImports

@testset "Code quality (Aqua.jl)" begin
# Test with Aqua.jl - testing code quality
# We allow unbound type parameters in some constructors
# We allow some ambiguities that are hard to resolve without breaking changes
Aqua.test_all(Catalyst;
ambiguities = false, # TODO: Fix ambiguities in future PR
unbound_args = false, # Some constructors have unbound type parameters by design
stale_deps = false, # Some test dependencies might appear stale
deps_compat = false, # Skip - incorrectly flags stdlib packages
piracies = false # We extend some Base/MTK methods which might be detected as piracy
)

# Test individual Aqua checks that we want to enforce
@testset "Aqua selective tests" begin
Aqua.test_undefined_exports(Catalyst)
Aqua.test_project_extras(Catalyst)
# Skip deps_compat test as it incorrectly flags stdlib packages
# Aqua.test_deps_compat(Catalyst)
end
end

@testset "Explicit imports (ExplicitImports.jl)" begin
# Test that we're not relying on implicit imports
@testset "Check implicit imports" begin
# Check for implicit imports - we allow some from closely related packages
result = check_no_implicit_imports(Catalyst; skip = (Base, Core))

# If there are any implicit imports, check if they're acceptable
if !isnothing(result)
# Extract the actual implicit imports
implicit_names = []
for r in result
if hasproperty(r, :name)
push!(implicit_names, r.name)
end
end

# Allow some specific implicit imports that are intentional
allowed_implicit = [
# Add any symbols here that we intentionally want to allow as implicit
]

problematic = filter(x -> !(x in allowed_implicit), implicit_names)

if !isempty(problematic)
@warn "Implicit imports detected:" problematic
# For now, we'll allow implicit imports but track them
@test_broken isempty(problematic)
else
@test true # All implicit imports are allowed
end
else
@test true # No implicit imports found
end
end

@testset "Check stale explicit imports" begin
# Check for unused explicit imports
stale_imports = check_no_stale_explicit_imports(Catalyst; skip = (Base, Core))

if !isnothing(stale_imports)
# Check if the stale imports are in our allowed list
has_unexpected_stale = false
for (mod, imports) in stale_imports
# Some imports might be used in macros or extensions and not detected
allowed_stale = [
# These might appear stale but are actually used
:MacroTools, # Used in DSL macros
:Graphs, # Used in graph analysis
:DataStructures, # Used for OrderedDict/OrderedSet
:Parameters # Used for @with_kw_noshow
]

for imp in imports
if !(Symbol(imp) in allowed_stale)
@warn "Unexpected stale import in $mod: $imp"
has_unexpected_stale = true
end
end
end
@test !has_unexpected_stale
else
@test true # No stale imports found
end
end

@testset "Check qualified accesses are public" begin
# Check that we only use public APIs when accessing other modules
result = check_all_qualified_accesses_are_public(Catalyst)

if !isnothing(result)
# Some non-public accesses might be necessary for deep integration with ModelingToolkit
# We should document these and work to minimize them
problematic_accesses = []

for r in result
# Check if this is an acceptable non-public access
# ModelingToolkit internal functions that Catalyst needs
if !occursin("ModelingToolkit", string(r))
push!(problematic_accesses, r)
end
end

if !isempty(problematic_accesses)
@warn "Non-public qualified accesses (non-MTK):" problematic_accesses
@test_broken isempty(problematic_accesses)
else
# All non-public accesses are to ModelingToolkit internals which are acceptable
@test true
end
else
@test true # All qualified accesses are public
end
end

@testset "Analysis summary" begin
# Print a summary of the explicit imports analysis
# This helps track progress but doesn't fail tests
@info "Running explicit imports analysis for review..."

# Capture the analysis in a string to check for issues
io = IOBuffer()
print_explicit_imports(io, Catalyst; strict = false)
analysis = String(take!(io))

# Check if the analysis mentions any critical issues
has_issues = occursin("relying on implicit imports", analysis)

if has_issues
@info "Analysis found potential improvements needed"
# Don't fail, just inform
else
@info "Explicit imports analysis looks good"
end

# Always pass this test - it's just for information
@test true
end
end
5 changes: 5 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,9 @@ end
@time @safetestset "Lattice Simulation Plotting" begin include("extensions/lattice_simulation_plotting.jl") end
end

# Code quality tests (Aqua.jl and ExplicitImports.jl)
if GROUP == "All" || GROUP == "QA"
@time @safetestset "Code Quality Assurance" begin include("qa.jl") end
end

end # @time
Loading