Skip to content

Commit e82ea5a

Browse files
Merge pull request #140 from DanielVandH/master
Support for non-sparse prototypes
2 parents 0d33617 + 4b26a0e commit e82ea5a

File tree

2 files changed

+130
-50
lines changed

2 files changed

+130
-50
lines changed

src/jacobians.jl

+19
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,23 @@ function finite_difference_jacobian!(J,
321321
finite_difference_jacobian!(J, f, x, cache, cache.fx; relstep=relstep, absstep=absstep, colorvec=colorvec, sparsity=sparsity)
322322
end
323323

324+
function _findstructralnz(A::DenseMatrix)
325+
numnz = count(A .≠ 0)
326+
I = Vector{Int64}(undef, numnz)
327+
J = Vector{Int64}(undef, numnz)
328+
idx = 1
329+
for j in axes(A, 2)
330+
for i in axes(A, 1)
331+
if A[i, j] 0
332+
I[idx] = i
333+
J[idx] = j
334+
idx += 1
335+
end
336+
end
337+
end
338+
I, J
339+
end
340+
324341
function finite_difference_jacobian!(
325342
J,
326343
f,
@@ -344,6 +361,8 @@ function finite_difference_jacobian!(
344361
cols_index = nothing
345362
if _use_findstructralnz(sparsity)
346363
rows_index, cols_index = ArrayInterfaceCore.findstructralnz(sparsity)
364+
elseif sparsity isa DenseMatrix
365+
rows_index, cols_index = FiniteDiff._findstructralnz(sparsity)
347366
end
348367

349368
if sparsity !== nothing

test/coloring_tests.jl

+111-50
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
using FiniteDiff, LinearAlgebra, SparseArrays, Test, LinearAlgebra,
2-
BlockBandedMatrices, ArrayInterfaceCore, BandedMatrices,
3-
ArrayInterfaceBlockBandedMatrices
2+
BlockBandedMatrices, ArrayInterfaceCore, BandedMatrices,
3+
ArrayInterfaceBlockBandedMatrices
44

55
fcalls = 0
6-
function f(dx,x)
6+
function f(dx, x)
77
global fcalls += 1
88
for i in 2:length(x)-1
99
dx[i] = x[i-1] - 2x[i] + x[i+1]
@@ -14,98 +14,98 @@ function f(dx,x)
1414
end
1515
function oopf(x)
1616
global fcalls += 1
17-
vcat([-2x[1]+x[2]],[x[i-1]-2x[i]+x[i+1] for i in 2:length(x)-1],[x[end-1]-2x[end]])
17+
vcat([-2x[1] + x[2]], [x[i-1] - 2x[i] + x[i+1] for i in 2:length(x)-1], [x[end-1] - 2x[end]])
1818
end
1919

2020
function second_derivative_stencil(N)
21-
A = zeros(N,N)
21+
A = zeros(N, N)
2222
for i in 1:N, j in 1:N
23-
(j-i==-1 || j-i==1) && (A[i,j]=1)
24-
j-i==0 && (A[i,j]=-2)
23+
(j - i == -1 || j - i == 1) && (A[i, j] = 1)
24+
j - i == 0 && (A[i, j] = -2)
2525
end
2626
A
2727
end
2828

29-
J = FiniteDiff.finite_difference_jacobian(oopf,rand(30))
29+
J = FiniteDiff.finite_difference_jacobian(oopf, rand(30))
3030
@test J second_derivative_stencil(30)
3131
_J = sparse(J)
3232
@test fcalls == 31
3333

3434
_J2 = similar(_J)
3535
fcalls = 0
36-
FiniteDiff.finite_difference_jacobian!(_J2,f,rand(30),colorvec=repeat(1:3,10))
36+
FiniteDiff.finite_difference_jacobian!(_J2, f, rand(30), colorvec=repeat(1:3, 10))
3737
@test fcalls == 4
3838
@test _J2 _J
3939

4040
_J2 = similar(_J)
4141
fcalls = 0
42-
FiniteDiff.finite_difference_jacobian!(_J2,f,rand(30),Val{:central},colorvec=repeat(1:3,10))
42+
FiniteDiff.finite_difference_jacobian!(_J2, f, rand(30), Val{:central}, colorvec=repeat(1:3, 10))
4343
@test fcalls == 6
4444
@test _J2 _J
4545

4646
_J2 = similar(_J)
4747
fcalls = 0
48-
FiniteDiff.finite_difference_jacobian!(_J2,f,rand(30),Val{:complex},colorvec=repeat(1:3,10))
48+
FiniteDiff.finite_difference_jacobian!(_J2, f, rand(30), Val{:complex}, colorvec=repeat(1:3, 10))
4949
@test fcalls == 3
5050
@test _J2 _J
5151

5252
_J2 = similar(_J)
5353
_denseJ2 = collect(_J2)
5454
fcalls = 0
55-
FiniteDiff.finite_difference_jacobian!(_denseJ2,f,rand(30),colorvec=repeat(1:3,10),sparsity=_J2)
55+
FiniteDiff.finite_difference_jacobian!(_denseJ2, f, rand(30), colorvec=repeat(1:3, 10), sparsity=_J2)
5656
@test fcalls == 4
5757
@test sparse(_denseJ2) _J
5858

5959
_J2 = similar(_J)
6060
_denseJ2 = collect(_J2)
6161
fcalls = 0
62-
FiniteDiff.finite_difference_jacobian!(_denseJ2,f,rand(30),Val{:central},colorvec=repeat(1:3,10),sparsity=_J2)
62+
FiniteDiff.finite_difference_jacobian!(_denseJ2, f, rand(30), Val{:central}, colorvec=repeat(1:3, 10), sparsity=_J2)
6363
@test fcalls == 6
6464
@test sparse(_denseJ2) _J
6565

6666
_J2 = similar(_J)
6767
_denseJ2 = collect(_J2)
6868
fcalls = 0
69-
FiniteDiff.finite_difference_jacobian!(_denseJ2,f,rand(30),Val{:complex},colorvec=repeat(1:3,10),sparsity=_J2)
69+
FiniteDiff.finite_difference_jacobian!(_denseJ2, f, rand(30), Val{:complex}, colorvec=repeat(1:3, 10), sparsity=_J2)
7070
@test fcalls == 3
7171
@test sparse(_denseJ2) _J
7272

7373
_J2 = similar(Tridiagonal(_J))
7474
fcalls = 0
75-
FiniteDiff.finite_difference_jacobian!(_J2,f,rand(30),colorvec=repeat(1:3,10))
75+
FiniteDiff.finite_difference_jacobian!(_J2, f, rand(30), colorvec=repeat(1:3, 10))
7676
@test fcalls == 4
7777
@test _J2 _J
7878

7979
_J2 = similar(_J2)
8080
fcalls = 0
81-
FiniteDiff.finite_difference_jacobian!(_J2,f,rand(30),Val{:central},colorvec=repeat(1:3,10))
81+
FiniteDiff.finite_difference_jacobian!(_J2, f, rand(30), Val{:central}, colorvec=repeat(1:3, 10))
8282
@test fcalls == 6
8383
@test _J2 _J
8484

8585
_J2 = similar(_J2)
8686
fcalls = 0
87-
FiniteDiff.finite_difference_jacobian!(_J2,f,rand(30),Val{:complex},colorvec=repeat(1:3,10))
87+
FiniteDiff.finite_difference_jacobian!(_J2, f, rand(30), Val{:complex}, colorvec=repeat(1:3, 10))
8888
@test fcalls == 3
8989
@test _J2 _J
9090

91-
_Jb = BandedMatrices.BandedMatrix(similar(_J2),(1,1))
92-
FiniteDiff.finite_difference_jacobian!(_Jb, f, rand(30), colorvec=repeat(1:3,10))
91+
_Jb = BandedMatrices.BandedMatrix(similar(_J2), (1, 1))
92+
FiniteDiff.finite_difference_jacobian!(_Jb, f, rand(30), colorvec=repeat(1:3, 10))
9393
@test _Jb _J
9494

9595
_Jtri = Tridiagonal(similar(_J2))
96-
FiniteDiff.finite_difference_jacobian!(_Jtri, f, rand(30), colorvec=repeat(1:3,10))
96+
FiniteDiff.finite_difference_jacobian!(_Jtri, f, rand(30), colorvec=repeat(1:3, 10))
9797
@test _Jtri _J
9898

9999
#https://github.com/JuliaDiff/FiniteDiff.jl/issues/67#issuecomment-516871956
100100
function f(out, x)
101-
x = reshape(x, 100, 100)
102-
out = reshape(out, 100, 100)
103-
for i in 1:100
104-
for j in 1:100
105-
out[i, j] = x[i, j] + x[max(i -1, 1), j] + x[min(i+1, size(x, 1)), j] + x[i, max(j-1, 1)] + x[i, min(j+1, size(x, 2))]
106-
end
107-
end
108-
return vec(out)
101+
x = reshape(x, 100, 100)
102+
out = reshape(out, 100, 100)
103+
for i in 1:100
104+
for j in 1:100
105+
out[i, j] = x[i, j] + x[max(i - 1, 1), j] + x[min(i + 1, size(x, 1)), j] + x[i, max(j - 1, 1)] + x[i, min(j + 1, size(x, 2))]
106+
end
107+
end
108+
return vec(out)
109109
end
110110
x = rand(10000)
111111
Jbbb = BandedBlockBandedMatrix(Ones(10000, 10000), fill(100, 100), fill(100, 100), (1, 1), (1, 1))
@@ -114,7 +114,7 @@ colorsbbb = ArrayInterfaceCore.matrix_colors(Jbbb)
114114
FiniteDiff.finite_difference_jacobian!(Jbbb, f, x, colorvec=colorsbbb)
115115
FiniteDiff.finite_difference_jacobian!(Jsparse, f, x, colorvec=colorsbbb)
116116
@test Jbbb Jsparse
117-
Jbb = BlockBandedMatrix(similar(Jsparse),fill(100, 100), fill(100, 100),(1,1));
117+
Jbb = BlockBandedMatrix(similar(Jsparse), fill(100, 100), fill(100, 100), (1, 1));
118118
colorsbb = ArrayInterfaceCore.matrix_colors(Jbb)
119119
FiniteDiff.finite_difference_jacobian!(Jbb, f, x, colorvec=colorsbb)
120120
@test Jbb Jsparse
@@ -123,21 +123,21 @@ FiniteDiff.finite_difference_jacobian!(Jbb, f, x, colorvec=colorsbb)
123123
# Non-square Jacobian test.
124124
# The Jacobian of f_nonsquare! has size (n, 2*n).
125125
function f_nonsquare!(y, x)
126-
global fcalls += 1
127-
@assert length(x) == 2*length(y)
128-
n = length(x) ÷ 2
129-
x1 = @view x[1:n]
130-
x2 = @view x[n+1:end]
131-
132-
@. y = (x1 .- 3).^2 .+ x1.*x2 .+ (x2 .+ 4).^2 .- 3
133-
return nothing
126+
global fcalls += 1
127+
@assert length(x) == 2 * length(y)
128+
n = length(x) ÷ 2
129+
x1 = @view x[1:n]
130+
x2 = @view x[n+1:end]
131+
132+
@. y = (x1 .- 3) .^ 2 .+ x1 .* x2 .+ (x2 .+ 4) .^ 2 .- 3
133+
return nothing
134134
end
135135

136136
n = 4
137-
x0 = vcat(ones(n).*(1:n) .+ 0.5, ones(n).*(1:n) .+ 1.5)
137+
x0 = vcat(ones(n) .* (1:n) .+ 0.5, ones(n) .* (1:n) .+ 1.5)
138138
y0 = zeros(n)
139139
rows = vcat([i for i in 1:n], [i for i in 1:n])
140-
cols = vcat([i for i in 1:n], [i+n for i in 1:n])
140+
cols = vcat([i for i in 1:n], [i + n for i in 1:n])
141141
sparsity = sparse(rows, cols, ones(length(rows)))
142142
colorvec = vcat(fill(1, n), fill(2, n))
143143

@@ -146,15 +146,76 @@ FiniteDiff.finite_difference_jacobian!(J_nonsquare1, f_nonsquare!, x0)
146146

147147
J_nonsquare2 = similar(sparsity)
148148
for method in [Val(:forward), Val(:central), Val(:complex)]
149-
cache = FiniteDiff.JacobianCache(copy(x0), copy(y0), copy(y0), method; sparsity, colorvec)
150-
global fcalls = 0
151-
FiniteDiff.finite_difference_jacobian!(J_nonsquare2, f_nonsquare!, x0, cache)
152-
if method == Val(:central)
153-
@test fcalls == 2*maximum(colorvec)
154-
elseif method == Val(:complex)
155-
@test fcalls == maximum(colorvec)
156-
else
157-
@test fcalls == maximum(colorvec) + 1
158-
end
159-
@test isapprox(J_nonsquare2, J_nonsquare1; rtol=1e-6)
149+
cache = FiniteDiff.JacobianCache(copy(x0), copy(y0), copy(y0), method; sparsity, colorvec)
150+
global fcalls = 0
151+
FiniteDiff.finite_difference_jacobian!(J_nonsquare2, f_nonsquare!, x0, cache)
152+
if method == Val(:central)
153+
@test fcalls == 2 * maximum(colorvec)
154+
elseif method == Val(:complex)
155+
@test fcalls == maximum(colorvec)
156+
else
157+
@test fcalls == maximum(colorvec) + 1
158+
end
159+
@test isapprox(J_nonsquare2, J_nonsquare1; rtol=1e-6)
160+
end
161+
162+
## Non-sparse prototype
163+
# Test structralnz for dense matrix
164+
A = [[1 1; 0 1], [1 1 1], [1.0 1.0; 1.0 1.0; 1.0 1.0], [true true; true true]]
165+
for a in A
166+
rows_index, cols_index = FiniteDiff._findstructralnz(a)
167+
rows_index2, cols_index2 = ArrayInterfaceCore.findstructralnz(sparse(a))
168+
@test (rows_index, cols_index) == (rows_index2, cols_index2)
169+
end
170+
171+
# Square Jacobian
172+
function _f(dx, x)
173+
dx .= [x[1]^2 + x[2]^2, x[1] + x[2]]
174+
end
175+
J = zeros(2, 2)
176+
θ = [5.0, 3.0]
177+
y0 = [0.0, 0.0]
178+
cache = FiniteDiff.JacobianCache(copy(θ), copy(y0), copy(y0), Val(:forward); sparsity=[1 1; 1 1])
179+
FiniteDiff.finite_difference_jacobian!(J, _f, θ, cache)
180+
@test J [10 6; 1 1]
181+
182+
function _f2(dx, x)
183+
dx .= [x[1]^2 + x[2]^2, x[1]]
184+
end
185+
J = zeros(2, 2)
186+
θ = [-3.0, 2.0]
187+
y0 = [0.0, 0.0]
188+
cache = FiniteDiff.JacobianCache(copy(θ), copy(y0), copy(y0), Val(:forward); sparsity=[1 1; 1 0])
189+
FiniteDiff.finite_difference_jacobian!(J, _f2, θ, cache)
190+
@test J [-6 4; 1 0]
191+
192+
# Rectangular Jacobian
193+
function _f3(dx, x)
194+
dx .= [x[1]^2 + x[2]^2 - x[1]]
195+
end
196+
J = zeros(1, 2)
197+
θ = [-3.0, 2.0]
198+
y0 = [0.0, 0.0]
199+
cache = FiniteDiff.JacobianCache(copy(θ), copy(y0), copy(y0), Val(:forward); sparsity=[1 1])
200+
FiniteDiff.finite_difference_jacobian!(J, _f3, θ, cache)
201+
@test J [-7 4]
202+
203+
function _f4(dx, x)
204+
dx .= [x[1]^2 + x[2]^2 - x[1]; x[1]*x[2]; x[1]*x[3]; x[1]]
205+
end
206+
J = zeros(4, 3)
207+
θ = [-3.0, 2.0, 13.3]
208+
y0 = [0.0, 0.0, 0.0, 0.0]
209+
cache = FiniteDiff.JacobianCache(copy(θ), copy(y0), copy(y0), Val(:forward); sparsity=[1 1 0; 1 1 0; 1 0 1; 1 0 0])
210+
FiniteDiff.finite_difference_jacobian!(J, _f4, θ, cache)
211+
@test J [-7.0 4.0 0; 2.0 -3.0 0.0; 13.3 0.0 -3.0; 1.0 0.0 0.0]
212+
213+
function _f5(dx, x)
214+
dx .= [x[1]^2 + x[2]^2]
160215
end
216+
J = zeros(1, 2)
217+
θ = [5.0, 3.0]
218+
y0 = [0.0]
219+
cache = FiniteDiff.JacobianCache(copy(θ), copy(y0), copy(y0), Val(:forward); sparsity = [1 1])
220+
FiniteDiff.finite_difference_jacobian!(J, _f5, θ, cache)
221+
@test J [10.0 6.0]

0 commit comments

Comments
 (0)