Skip to content

Commit 73eabbd

Browse files
committed
some sparsity utilities
1 parent e01a2b1 commit 73eabbd

File tree

4 files changed

+71
-1
lines changed

4 files changed

+71
-1
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "NonconvexCore"
22
uuid = "035190e5-69f1-488f-aaab-becca2889735"
33
authors = ["Mohamed Tarek <[email protected]> and contributors"]
4-
version = "1.0.6"
4+
version = "1.0.7"
55

66
[deps]
77
ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"

src/NonconvexCore.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ include("utilities/params.jl")
4646
include("functions/functions.jl")
4747
include("functions/value_jacobian.jl")
4848
include("functions/counting_function.jl")
49+
include("functions/sparse.jl")
4950
include("common.jl")
5051
include("utilities/callbacks.jl")
5152
include("utilities/convergence.jl")

src/functions/sparse.jl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# workaround Zygote.jacobian and ForwardDiff.jacobian not supporting sparse jacobians
2+
3+
function sparse_jacobian(f, x)
4+
val, pb = Zygote.pullback(f, x)
5+
M, N = length(val), length(x)
6+
T = eltype(val)
7+
return copy(mapreduce(hcat, 1:M, init = spzeros(T, N, 0)) do i
8+
pb(I(M)[:, i])[1]
9+
end')
10+
end
11+
function sparse_hessian(f, x)
12+
return sparse_fd_jacobian(x -> Zygote.gradient(f, x)[1], x)
13+
end
14+
15+
function sparse_fd_jacobian(f, x)
16+
pf = pushforward_function(f, x)
17+
M, N = length(f(x)), length(x)
18+
init = pf(I(M)[:, 1])[1]
19+
M = length(init)
20+
return mapreduce(hcat, 2:N; init) do i
21+
pf(I(M)[:, i])[1]
22+
end
23+
end
24+
25+
# from AbstractDifferentiation
26+
function pushforward_function(f, xs...)
27+
return function pushforward(vs)
28+
if length(xs) == 1
29+
v = vs isa Tuple ? only(vs) : vs
30+
return (ForwardDiff.derivative(h -> f(step_toward(xs[1], v, h)), 0),)
31+
else
32+
return ForwardDiff.derivative(h -> f(step_toward.(xs, vs, h)...), 0)
33+
end
34+
end
35+
end
36+
37+
@inline step_toward(x::Number, v::Number, h) = x + h * v
38+
# support arrays and tuples
39+
@noinline step_toward(x, v, h) = x .+ h .* v

src/models/moi.jl

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,15 @@ nvalues(::Nothing) = 0
222222
nvalues(J::Matrix) = length(J)
223223
nvalues(H::LowerTriangular{<:Real, <:Matrix}) = (size(H, 1) + 1) * size(H, 1) ÷ 2
224224
nvalues(H::SparseMatrixCSC) = length(H.nzval)
225+
function nvalues(HL::LowerTriangular{<:Real, <:SparseMatrixCSC})
226+
nvalues = 0
227+
H = HL.data
228+
for col in 1:length(H.colptr)-1
229+
indices = [i for i in H.colptr[col]:H.colptr[col+1]-1 if H.rowval[i] >= col]
230+
nvalues += length(indices)
231+
end
232+
return nvalues
233+
end
225234

226235
# Implement these for sparse matrices
227236
function fill_indices!(rows, cols, J0::Matrix; offset = 0, row_offset = 0)
@@ -252,6 +261,17 @@ function fill_indices!(rows, cols, HL::SparseMatrixCSC; offset = 0, row_offset =
252261
end
253262
return rows, cols
254263
end
264+
function fill_indices!(rows, cols, HL::LowerTriangular{<:Real, <:SparseMatrixCSC}; offset = 0, row_offset = 0)
265+
H = HL.data
266+
for col in 1:length(H.colptr)-1
267+
indices = [i for i in H.colptr[col]:H.colptr[col+1]-1 if H.rowval[i] >= col]
268+
nvars = length(indices)
269+
cols[offset + 1 : offset + nvars] .= col
270+
rows[offset + 1 : offset + nvars] = row_offset .+ H.rowval[indices]
271+
offset += nvars
272+
end
273+
return rows, cols
274+
end
255275

256276
function add_values!(values, J::Matrix; offset = 0)
257277
nvars = length(J)
@@ -271,6 +291,16 @@ function add_values!(values, HL::SparseMatrixCSC; factor = 1, offset = 0)
271291
values[offset+1:offset+nvars] .= HL.nzval .* factor
272292
return values
273293
end
294+
function add_values!(values, HL::LowerTriangular{<:Real, <:SparseMatrixCSC}; factor = 1, offset = 0)
295+
H = HL.data
296+
for col in 1:length(H.colptr)-1
297+
indices = [i for i in H.colptr[col]:H.colptr[col+1]-1 if H.rowval[i] >= col]
298+
nvars = length(indices)
299+
values[offset + 1 : offset + nvars] .= H.nzval[indices] .* factor
300+
offset += nvars
301+
end
302+
return values
303+
end
274304

275305
_dot(f, x, y) = dot(f(x), y)
276306
_dot(::Nothing, ::Any, ::Any) = 0.0

0 commit comments

Comments
 (0)