Skip to content

Commit f2b37dd

Browse files
ericphansonodow
andauthored
improve performance of conv (#634)
* improve performance of `conv` * tweak * rm comment * format * Apply suggestions from code review --------- Co-authored-by: Oscar Dowson <[email protected]>
1 parent 96904be commit f2b37dd

File tree

3 files changed

+49
-10
lines changed

3 files changed

+49
-10
lines changed

src/reformulations/conv.jl

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,37 @@
33
# Use of this source code is governed by a BSD-style license that can be found
44
# in the LICENSE file or at https://opensource.org/license/bsd-2-clause
55

6+
"""
7+
conv1D_matrix(h::AbstractVector, n::Integer) -> SparseMatrixCSC
8+
9+
Create a sparse matrix `A` such that if `x` has length `n`,
10+
then we have `A * x ≈ conv1d(h, x)`.
11+
"""
12+
function conv1D_matrix(h::AbstractVector, n::Integer)
13+
m = length(h)
14+
Is = Int[]
15+
Js = Int[]
16+
Vs = eltype(h)[]
17+
sizehint!(Is, n * m)
18+
sizehint!(Js, n * m)
19+
sizehint!(Vs, n * m)
20+
# build matrix by columns
21+
for j in 1:n
22+
append!(Is, j:(j+m-1))
23+
append!(Js, (j for _ in 1:m))
24+
append!(Vs, h)
25+
end
26+
return SparseArrays.sparse(Is, Js, Vs, m + n - 1, n)
27+
end
28+
629
function conv(x::Value, y::AbstractExpr)
730
if length(x) != size(x, 1) || size(y, 2) > 1
831
error("convolution only supported between two vectors")
932
end
10-
m, n = length(x), size(y, 1)
11-
X = spzeros(eltype(x), m + n - 1, n)
12-
for i in 1:n, j in 1:m
13-
X[i+j-1, i] = x[j]
33+
if length(x) == 0
34+
throw(ArgumentError("convolution with empty vector not supported"))
1435
end
36+
X = conv1D_matrix(x, length(y))
1537
return X * y
1638
end
1739

test/test_atoms.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1968,6 +1968,10 @@ function test_conv()
19681968
ErrorException("convolution only supported between two vectors"),
19691969
conv([1, 2], Variable(2, 2)),
19701970
)
1971+
@test_throws(
1972+
ArgumentError("convolution with empty vector not supported"),
1973+
conv([], Variable(2)),
1974+
)
19711975
return
19721976
end
19731977

test/test_utilities.jl

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -660,23 +660,36 @@ function test_logsumexp_stability()
660660
return
661661
end
662662

663+
# simple 1D convolution implementation
664+
function _conv(h, x)
665+
m = length(h)
666+
n = length(x)
667+
zero_pad_x(i) = 1 <= i <= n ? x[i] : 0
668+
return [sum(h[j] * zero_pad_x(i - j + 1) for j in 1:m) for i in 1:m+n-1]
669+
end
670+
663671
function test_conv_issue_364()
664672
n = 3
665673
m = 11
666674
h = rand(m)
667675
x = rand(n)
668676
hvar = Variable(m)
669677
hvar.value = h
670-
function _conv(h, x)
671-
m = length(h)
672-
n = length(x)
673-
zero_pad_x(i) = 1 <= i <= n ? x[i] : 0
674-
return [sum(h[j] * zero_pad_x(i - j + 1) for j in 1:m) for i in 1:m+n-1]
675-
end
676678
@test evaluate(conv(hvar, x)) _conv(h, x)
677679
return
678680
end
679681

682+
function test_conv1D_matrix()
683+
for (x_len, y_len) in ((20, 5), (5, 20), (5, 5), (1, 1), (2, 3))
684+
for im1 in (im, 0), im2 in (im, 0)
685+
x = randn(x_len) + randn(x_len) * im1
686+
y = randn(y_len) + randn(y_len) * im2
687+
@test Convex.conv1D_matrix(x, length(y)) * y _conv(x, y)
688+
end
689+
end
690+
return
691+
end
692+
680693
function test_conj_issue_416()
681694
A = [1 1im; -1im 1]
682695
X = ComplexVariable(2, 2)

0 commit comments

Comments
 (0)