Skip to content

Commit f2c8c73

Browse files
Merge pull request #498 from SciML/s/sparse_jacobian
Fast Jacobian Sparsity
2 parents 3ae62e8 + 2391791 commit f2c8c73

File tree

2 files changed

+72
-6
lines changed

2 files changed

+72
-6
lines changed

src/differentials.jl

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,36 @@ function expand_derivatives(O::Operation,simplify=true)
4848
isa(o, Operation) || return O
4949
isa(o.op, Variable) && return O
5050

51-
x = sum(1:length(o.args)) do i
51+
l = length(o.args)
52+
exprs = Expression[]
53+
c = 0
54+
55+
for i in 1:l
5256
t2 = expand_derivatives(D(o.args[i]),false)
5357

54-
if _iszero(t2)
55-
return t2
58+
x = if _iszero(t2)
59+
t2
5660
elseif _isone(t2)
57-
return derivative(o, i)
61+
derivative(o, i)
5862
else
59-
return derivative(o, i) * t2
63+
derivative(o, i) * t2
64+
end
65+
66+
if _iszero(x)
67+
continue
68+
elseif x isa Expression
69+
push!(exprs, x)
70+
else
71+
c += x
6072
end
6173
end
6274

63-
return simplify ? ModelingToolkit.simplify(x) : x
75+
if isempty(exprs)
76+
return c
77+
else
78+
x = Operation(+, !iszero(c) ? vcat(c, exprs) : exprs)
79+
return simplify ? ModelingToolkit.simplify(x) : x
80+
end
6481
end
6582

6683
return simplify ? ModelingToolkit.simplify(O) : O

src/direct.jl

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,55 @@ function jacobian(ops::AbstractVector{<:Expression}, vars::AbstractVector{<:Expr
2222
[expand_derivatives(Differential(v)(O),simplify) for O in ops, v in vars]
2323
end
2424

25+
"""
26+
```julia
27+
sparsejacobian(ops::AbstractVector{<:Expression}, vars::AbstractVector{<:Expression}; simplify = true)
28+
```
29+
30+
A helper function for computing the sparse Jacobian of an array of expressions with respect to
31+
an array of variable expressions.
32+
"""
33+
function sparsejacobian(ops::AbstractVector{<:Expression}, vars::AbstractVector{<:Expression}; simplify = true)
34+
I = Int[]
35+
J = Int[]
36+
du = Expression[]
37+
38+
sp = jacobian_sparsity(ops, vars)
39+
I,J,_ = findnz(sp)
40+
41+
exprs = Expression[]
42+
43+
for (i,j) in zip(I, J)
44+
push!(exprs, expand_derivatives(Differential(vars[j])(ops[i])))
45+
end
46+
sparse(I,J, exprs, length(ops), length(vars))
47+
end
48+
49+
using SymbolicUtils: @rule, Rewriters
50+
51+
function jacobian_sparsity(du, u)
52+
dict = Dict(zip(to_symbolic.(u), 1:length(u)))
53+
54+
i = Ref(1)
55+
I = Int[]
56+
J = Int[]
57+
58+
# This rewriter notes down which u's appear in a
59+
# given du (whose index is stored in the `i` Ref)
60+
r = [@rule ~x::(x->haskey(dict, x)) => begin
61+
push!(I, i[])
62+
push!(J, dict[~x])
63+
nothing
64+
end] |> Rewriters.Chain |> Rewriters.Postwalk
65+
66+
for ii = 1:length(du)
67+
i[] = ii
68+
r(to_symbolic(du[ii]))
69+
end
70+
71+
sparse(I, J, true, length(du), length(u))
72+
end
73+
2574
"""
2675
```julia
2776
hessian(O::Expression, vars::AbstractVector{<:Expression}; simplify = true)

0 commit comments

Comments
 (0)