Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
f3b9477
Created basic solvers for 1st order separable eqs and linear systems
LordOfFrogs Jun 26, 2025
e55dc34
added rationalization to inverted matrix in solve_linear_system
LordOfFrogs Jun 26, 2025
129803b
integrated into Symbolics, restructured files
LordOfFrogs Jun 27, 2025
a5b7c51
added unit tests
LordOfFrogs Jun 27, 2025
d6daa7b
added display functionality for LinearODE
LordOfFrogs Jun 27, 2025
871fff4
implemented solver for homogeneous solutions (of degree 5 or less wit…
LordOfFrogs Jun 27, 2025
b7a6c2e
added solving via integrating factor (1st order ODEs)
LordOfFrogs Jun 27, 2025
ee8bc8e
added handling of repeated characteristic roots
LordOfFrogs Jun 28, 2025
2376fa9
implemented exponential response formula / resonant response formula
LordOfFrogs Jun 28, 2025
b4b0f1e
added solving particular solutions when q(t) includes sin or cos and …
LordOfFrogs Jun 30, 2025
9292ef4
added handling of complex characteristic roots
LordOfFrogs Jun 30, 2025
1d26ec5
slight refactoring and improved documentation
LordOfFrogs Jun 30, 2025
38c59a3
added method of undetermined coefficients
LordOfFrogs Jul 1, 2025
77f3994
added expression parsing
LordOfFrogs Jul 1, 2025
c1de7c1
updated cases handled
LordOfFrogs Jul 1, 2025
11bd108
added IVP
LordOfFrogs Jul 1, 2025
4c5400e
added method to solve Clairaut's equation
LordOfFrogs Jul 3, 2025
e1a8fb0
implemented solving bernoulli equations
LordOfFrogs Jul 4, 2025
4d04477
WIP laplace
LordOfFrogs Jul 8, 2025
3c2ad4b
implemented laplace transform and wip inverse transform
LordOfFrogs Jul 16, 2025
5812be6
WIP partial fraction decomposition
LordOfFrogs Jul 17, 2025
d6000f6
working on algorithm more (possibly complete RSQDEC, but with bugs)
LordOfFrogs Jul 17, 2025
852ae5b
updated documentation and fixed test imports
LordOfFrogs Jul 18, 2025
2cb343c
Mostly working. Switched to cover-up method and solving a system of l…
LordOfFrogs Jul 22, 2025
1536770
Merge branch 'ToriDell/partialfractions' into ToriDell/laplace
LordOfFrogs Jul 23, 2025
c00baed
added unit tests
LordOfFrogs Jul 23, 2025
79a21fd
added checks to make sure expression is valid for decomposition
LordOfFrogs Jul 23, 2025
b72253c
Merge branch 'ToriDell/partialfractions' into ToriDell/laplace
LordOfFrogs Jul 23, 2025
628cbc9
added partial fractions into laplace transform, fixed various bugs ha…
LordOfFrogs Jul 23, 2025
8164a7a
started implementing laplace ode solver
LordOfFrogs Jul 25, 2025
c699bce
fix sign of terms when solving odes
LordOfFrogs Aug 5, 2025
c0589a0
minor fixes for type of _true_factors and verifying MUD solutions
LordOfFrogs Aug 5, 2025
8c2f212
working! added tests and fixed issues with laplace ode solver
LordOfFrogs Aug 6, 2025
9203e1c
switched to unicode variables to avoid collisions
LordOfFrogs Aug 8, 2025
0f000b6
(wip) adding jacobian
LordOfFrogs Aug 11, 2025
ee951aa
switched parsing logic to use linear_expansion, adding reduction of o…
LordOfFrogs Aug 11, 2025
a94a6df
reorganized some methods into new helpers file
LordOfFrogs Aug 11, 2025
e936e08
Merge branch 'ToriDell/symbolic-ode-solver' into ToriDell/laplace
LordOfFrogs Aug 18, 2025
d7eeccb
add simplification to q when parsing
LordOfFrogs Aug 18, 2025
3a53386
possible fix for SymPy dependency
LordOfFrogs Aug 18, 2025
ff69873
Merge remote-tracking branch 'origin/master' into ToriDell/symbolic-o…
LordOfFrogs Aug 18, 2025
7ba9be2
fix minor spelling errors
LordOfFrogs Aug 19, 2025
245924a
Merge branch 'ToriDell/symbolic-ode-solver' of https://github.com/Lor…
LordOfFrogs Aug 19, 2025
b8704f6
working tests for sympy
LordOfFrogs Aug 19, 2025
719cb0e
Merge branch 'ToriDell/symbolic-ode-solver' into ToriDell/laplace
LordOfFrogs Aug 20, 2025
1498a6c
fixed tests
LordOfFrogs Aug 20, 2025
9eeeafc
better codecov and minor bug fixes
LordOfFrogs Aug 20, 2025
09fbc98
Merge branch 'ToriDell/symbolic-ode-solver' into ToriDell/laplace
LordOfFrogs Aug 21, 2025
e497107
now accounts for non-one leading coefficient in denominator
LordOfFrogs Aug 21, 2025
ca21823
switched to solve_interms_ofvar instead of symbolic_solve
LordOfFrogs Aug 21, 2025
839bf66
updated docstring
LordOfFrogs Aug 21, 2025
551c9cc
Merge branch 'ToriDell/partialfractions' into ToriDell/laplace
LordOfFrogs Aug 22, 2025
930a0ab
doc strings and code cleanup
LordOfFrogs Aug 22, 2025
6bf95bb
pull out transform rules into global scope
LordOfFrogs Aug 25, 2025
39449c1
moved unwrap_der into diffeq_helpers.jl
LordOfFrogs Aug 25, 2025
e3d0811
switch to using fast_substitute when possible
LordOfFrogs Aug 25, 2025
dde1cdb
minor bug fix in transform_rules
LordOfFrogs Aug 25, 2025
11f8f29
more detailed comments
LordOfFrogs Aug 25, 2025
6317eb3
added laplace examples to docstrings and added to ode.md docs page
LordOfFrogs Aug 25, 2025
389b328
created partial fractions docs
LordOfFrogs Aug 25, 2025
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 docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ makedocs(
"Algebra" => [
"manual/solver.md",
"manual/groebner.md",
"manual/partial_fractions.md",
],

"Calculus" => [
Expand Down
25 changes: 23 additions & 2 deletions docs/src/manual/ode.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,32 @@ The analytical solution can be investigated symbolically using `observed(sys)`.

## Symbolically Solving ODEs

Currently there is no native symbolic ODE solver. Though there are bindings to SymPy

!!! note
This area is currently under heavy development. More solvers will be available in the near future.

```@docs
Symbolics.LinearODE
```

```@docs
Symbolics.symbolic_solve_ode
```

### Continuous Dynamical Systems
```@docs
Symbolics.solve_linear_system
```

### Laplace Transform

The Laplace transform can be used to solve ODEs by transforming the whole equation, solving algebraically, then applying the inverse transform. The Laplace transform and inverse transform functionality is currently based on a rule table and applying linearity, so this method is limited in what expressions are able to be transformed and inverse transformed.

```@docs
Symbolics.laplace
Symbolics.inverse_laplace
Symbolics.laplace_solve_ode
```

### SymPy

```@docs
Expand Down
9 changes: 9 additions & 0 deletions docs/src/manual/partial_fractions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Partial Fraction Decomposition

Partial fraction decomposition is performed using the cover-up method. This involves "covering up" a factor in the denominator and substituting the root into the remaining expression. When the denominator can be completely factored into non-repeated linear factors, this produces the desired result. When there are repeated or irreducible quadratic factors, it produces terms with unknown coefficients in the numerator that is solved as a system of equations.

It is often used when solving integrals or performing an inverse Laplace transform (see [`inverse_laplace`](@ref)).

```docs
Symbolics.partial_frac_decomposition
```
10 changes: 10 additions & 0 deletions src/Symbolics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ include("operators.jl")
include("limits.jl")
export limit

include("partialfractions.jl")
export partial_frac_decomposition

# Hacks to make wrappers "nicer"
const NumberTypes = Union{AbstractFloat,Integer,Complex{<:AbstractFloat},Complex{<:Integer}}
(::Type{T})(x::SymbolicUtils.Symbolic) where {T<:NumberTypes} = throw(ArgumentError("Cannot convert Sym to $T since Sym is symbolic and $T is concrete. Use `substitute` to replace the symbolic unwraps."))
Expand Down Expand Up @@ -220,6 +223,13 @@ include("solver/main.jl")
include("solver/special_cases.jl")
export symbolic_solve

# Diff Eq Solver
include("diffeqs/diffeqs.jl")
include("diffeqs/systems.jl")
include("diffeqs/laplace.jl")
include("diffeqs/diffeq_helpers.jl")
export LinearODE, IVP, symbolic_solve_ode, solve_linear_system, solve_IVP, laplace, inverse_laplace, laplace_solve_ode

# Sympy Functions

"""
Expand Down
113 changes: 113 additions & 0 deletions src/diffeqs/diffeq_helpers.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# recursively find highest derivative order in `expr`
function _get_der_order(expr, x, t)
if !hasderiv(unwrap(expr))
return 0
end

if length(terms(expr)) > 1
return maximum(_get_der_order.(terms(expr), Ref(x), Ref(t)))
end

if length(factors(expr)) > 1
return maximum(_get_der_order.(factors(expr), Ref(x), Ref(t)))
end

return _get_der_order(fast_substitute(expr, Dict(Differential(t)(x) => x)), x, t) + 1
end

"""
unwrap_der(expr, Dt)

Helper function to unwrap derivatives of `f(t)` in `expr` with respect to the differential operator `Dt = Differential(t)`. Returns a tuple `(n, base_expr)`, where `n` is the order of the derivative and `base_expr` is the expression with the derivatives removed. If `expr` does not contain `f(t)` or its derivatives, returns `(0, expr)`.
"""
function unwrap_der(expr, Dt)
reduce_rule = @rule Dt(~x) => ~x

if reduce_rule(expr) === nothing
return 0, expr
end

order, expr = unwrap_der(reduce_rule(expr), Dt)
return order + 1, expr
end

# takes into account fractions
function _true_factors(expr)
facs = factors(expr)
true_facs::Vector{Real} = []
frac_rule = @rule (~x)/(~y) => [~x, 1/~y]
for fac in facs
frac = frac_rule(fac)
if frac !== nothing && !isequal(frac[1], 1)
append!(true_facs, _true_factors(frac[1]))
append!(true_facs, _true_factors(frac[2]))
else
push!(true_facs, fac)
end
end

return convert(Vector{Num}, true_facs)
end

"""
reduce_order(eq, x, t, ys)

Reduce order of an ODE by substituting variables for derivatives to form a system of first order ODEs
"""
function reduce_order(eq, x, t, ys)
Dt = Differential(t)
n = _get_der_order(eq, x, t)
@assert n >= 1 "ODE must have at least one derivative"

# reduction of order
y_sub = Dict([[(Dt^i)(x) => ys[i+1] for i=0:n-1]; (Dt^n)(x) => variable(:𝒴)])
eq = fast_substitute(eq, y_sub)

# isolate (Dt^n)(x)
f = symbolic_linear_solve(eq, variable(:𝒴), check=false)
@assert f !== nothing "Failed to isolate highest order derivative term"
f = f[1]
system = [ys[2:n]; f]

return system
end

function unreduce_order(expr, x, t, ys)
Dt = Differential(t)
rev_y_sub = Dict(ys[i] => (Dt^(i-1))(x) for i in eachindex(ys))

return fast_substitute(expr, rev_y_sub)
end

function is_solution(solution, eq::Equation, x, t)
is_solution(solution, eq.lhs - eq.rhs, x, t)
end

function is_solution(solution, eq::LinearODE)
is_solution(solution, get_expression(eq), eq.x, eq.t)
end

function is_solution(solution, eq, x, t)
if solution === nothing
return false
end

expr = substitute(eq, Dict(x => solution))
expr = expand(expand_derivatives(expr))
return isequal(expr, 0)
end

function _parse_trig(expr, t)
parse_sin = Symbolics.Chain([(@rule sin(t) => 1), (@rule sin(~x * t) => ~x)])
parse_cos = Symbolics.Chain([(@rule cos(t) => 1), (@rule cos(~x * t) => ~x)])

if !isequal(parse_sin(expr), expr)
return parse_sin(expr), true
end

if !isequal(parse_cos(expr), expr)
return parse_cos(expr), false
end

return nothing
end
Loading
Loading