Repository: JuliaDiff/FiniteDifferences.jl
Pull request #43 (2019-08-31 18:24:16)

Relax type constraints to allow complex jvp
This is needed for tests in JuliaDiff/ChainRules.jl#95.

codecov-io: # [Codecov](https://codecov.io/gh/JuliaDiff/FiniteDifferences.jl/pull/43?src=pr&el=h1) Report
> Merging [#43](https://codecov.io/gh/JuliaDiff/FiniteDifferences.jl/pull/43?src=pr&el=desc) into [master](https://codecov.io/gh/JuliaDiff/FiniteDifferences.jl/commit/287a26186bc58cfba3f7d5a977bbb759b74cf871?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/JuliaDiff/FiniteDifferences.jl/pull/43/graphs/tree.svg?width=650&token=fIoJjgefQ8&height=150&src=pr)](https://codecov.io/gh/JuliaDiff/FiniteDifferences.jl/pull/43?src=pr&el=tree)

```diff
@@          Coverage Diff          @@
##           master    #43   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files           4      4           
  Lines         128    128           
=====================================
  Hits          128    128
```


| [Impacted Files](https://codecov.io/gh/JuliaDiff/FiniteDifferences.jl/pull/43?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/grad.jl](https://codecov.io/gh/JuliaDiff/FiniteDifferences.jl/pull/43/diff?src=pr&el=tree#diff-c3JjL2dyYWQuamw=) | `100% <100%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/JuliaDiff/FiniteDifferences.jl/pull/43?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/JuliaDiff/FiniteDifferences.jl/pull/43?src=pr&el=footer). Last update [287a261...ae34d43](https://codecov.io/gh/JuliaDiff/FiniteDifferences.jl/pull/43?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

codecov-io: # [Codecov](https://codecov.io/gh/JuliaDiff/FiniteDifferences.jl/pull/43?src=pr&el=h1) Report
> Merging [#43](https://codecov.io/gh/JuliaDiff/FiniteDifferences.jl/pull/43?src=pr&el=desc) into [master](https://codecov.io/gh/JuliaDiff/FiniteDifferences.jl/commit/287a26186bc58cfba3f7d5a977bbb759b74cf871?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/JuliaDiff/FiniteDifferences.jl/pull/43/graphs/tree.svg?width=650&token=fIoJjgefQ8&height=150&src=pr)](https://codecov.io/gh/JuliaDiff/FiniteDifferences.jl/pull/43?src=pr&el=tree)

```diff
@@          Coverage Diff          @@
##           master    #43   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files           4      4           
  Lines         128    128           
=====================================
  Hits          128    128
```


| [Impacted Files](https://codecov.io/gh/JuliaDiff/FiniteDifferences.jl/pull/43?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/grad.jl](https://codecov.io/gh/JuliaDiff/FiniteDifferences.jl/pull/43/diff?src=pr&el=tree#diff-c3JjL2dyYWQuamw=) | `100% <100%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/JuliaDiff/FiniteDifferences.jl/pull/43?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/JuliaDiff/FiniteDifferences.jl/pull/43?src=pr&el=footer). Last update [287a261...ae34d43](https://codecov.io/gh/JuliaDiff/FiniteDifferences.jl/pull/43?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

willtebbutt: Thanks for this @simeonschaub. Would you mind adding some tests involving complex numbers to `to_vec` tests? It might be overkill, but better safe than sorry.
simeonschaub: @willtebbutt Definitely not overkill, since I discovered that `to_vec` for `Adjoint` wasn't defined correctly for complex arrays.

Patch:
diff --git a/.travis.yml b/.travis.yml
index 1190f24..bb669f1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,14 +3,18 @@ language: julia
 os:
   - linux
   - osx
+  - windows
 julia:
-  - 0.7
   - 1.0
+  - 1.1
+  - 1.2
+  - 1.3
   - nightly
 notifications:
   email: false
 matrix:
   allow_failures:
+    - julia: 1.3
     - julia: nightly
   fast_finish: true
 after_success:
diff --git a/Project.toml b/Project.toml
index bb67502..a318b4c 100644
--- a/Project.toml
+++ b/Project.toml
@@ -7,7 +7,7 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
 Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
 
 [compat]
-julia = "0.7, 1.0"
+julia = "1"
 
 [extras]
 Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
diff --git a/README.md b/README.md
index 93e8f20..7707ad1 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,6 @@
 # FiniteDifferences.jl: Finite Difference Methods
 
 [![Build Status](https://travis-ci.org/JuliaDiff/FiniteDifferences.jl.svg?branch=master)](https://travis-ci.org/JuliaDiff/FiniteDifferences.jl)
-[![Windows Build status](https://ci.appveyor.com/api/projects/status/g0gun5dxbkt631am/branch/master?svg=true)](https://ci.appveyor.com/project/JuliaDiff/FiniteDifferences-jl/branch/master)
 [![codecov.io](https://codecov.io/github/JuliaDiff/FiniteDifferences.jl/coverage.svg?branch=master)](https://codecov.io/github/JuliaDiff/FiniteDifferences.jl?branch=master)
 [![Latest Docs](https://img.shields.io/badge/docs-latest-blue.svg)](https://juliadiff.github.io/FiniteDifferences.jl/latest/)
 
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index 4f179d3..0000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,41 +0,0 @@
-environment:
-  matrix:
-  - julia_version: 0.7
-  - julia_version: 1
-  - julia_version: nightly
-
-platform:
-  - x86 # 32-bit
-  - x64 # 64-bit
-
-matrix:
-  allow_failures:
-  - julia_version: nightly
-
-branches:
-  only:
-    - master
-    - /release-.*/
-
-notifications:
-  - provider: Email
-    on_build_success: false
-    on_build_failure: false
-    on_build_status_changed: false
-
-install:
-  - ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/JuliaCI/Appveyor.jl/version-1/bin/install.ps1"))
-
-build_script:
-  - echo "%JL_BUILD_SCRIPT%"
-  - C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%"
-
-test_script:
-  - echo "%JL_TEST_SCRIPT%"
-  - C:\julia\bin\julia -e "%JL_TEST_SCRIPT%"
-
-# # Uncomment to support code coverage upload. Should only be enabled for packages
-# # which would have coverage gaps without running on Windows
-# on_success:
-#   - echo "%JL_CODECOV_SCRIPT%"
-#   - C:\julia\bin\julia -e "%JL_CODECOV_SCRIPT%"
diff --git a/src/grad.jl b/src/grad.jl
index 29615a7..3ccf072 100644
--- a/src/grad.jl
+++ b/src/grad.jl
@@ -1,11 +1,11 @@
 export grad, jacobian, jvp, j′vp, to_vec
 
 """
-    grad(fdm, f, x::AbstractVector)
+    grad(fdm, f, x::Vector{<:Number})
 
 Approximate the gradient of `f` at `x` using `fdm`. Assumes that `f(x)` is scalar.
 """
-function grad(fdm, f, x::Vector{T}) where T<:Real
+function grad(fdm, f, x::Vector{T}) where T<:Number
     v, dx, tmp = fill(zero(T), size(x)), similar(x), similar(x)
     for n in eachindex(x)
         v[n] = one(T)
@@ -21,34 +21,34 @@ function grad(fdm, f, x::Vector{T}) where T<:Real
 end
 
 """
-    jacobian(fdm, f, x::AbstractVector{<:Real}, D::Int)
-    jacobian(fdm, f, x::AbstractVector{<:Real})
+    jacobian(fdm, f, x::Vector{<:Number}, D::Int)
+    jacobian(fdm, f, x::Vector{<:Number})
 
 Approximate the Jacobian of `f` at `x` using `fdm`. `f(x)` must be a length `D` vector. If
 `D` is not provided, then `f(x)` is computed once to determine the output size.
 """
-function jacobian(fdm, f, x::Vector{T}, D::Int) where {T<:Real}
+function jacobian(fdm, f, x::Vector{T}, D::Int) where {T<:Number}
     J = Matrix{T}(undef, D, length(x))
     for d in 1:D
         J[d, :] = grad(fdm, x->f(x)[d], x)
     end
     return J
 end
-jacobian(fdm, f, x::Vector{<:Real}) = jacobian(fdm, f, x, length(f(x)))
+jacobian(fdm, f, x::Vector{<:Number}) = jacobian(fdm, f, x, length(f(x)))
 
 """
-    _jvp(fdm, f, x::Vector{<:Real}, ẋ::AbstractVector{<:Real})
+    _jvp(fdm, f, x::Vector{<:Number}, ẋ::AbstractVector{<:Number})
 
 Convenience function to compute `jacobian(f, x) * ẋ`.
 """
-_jvp(fdm, f, x::Vector{<:Real}, ẋ::AV{<:Real}) = fdm(ε -> f(x .+ ε .* ẋ), zero(eltype(x)))
+_jvp(fdm, f, x::Vector{<:Number}, ẋ::AV{<:Number}) = fdm(ε -> f(x .+ ε .* ẋ), zero(eltype(x)))
 
 """
-    _j′vp(fdm, f, ȳ::AbstractVector{<:Real}, x::Vector{<:Real})
+    _j′vp(fdm, f, ȳ::AbstractVector{<:Number}, x::Vector{<:Number})
 
-Convenience function to compute `jacobian(f, x)' * ȳ`.
+Convenience function to compute `transpose(jacobian(f, x)) * ȳ`.
 """
-_j′vp(fdm, f, ȳ::AV{<:Real}, x::Vector{<:Real}) = jacobian(fdm, f, x, length(ȳ))' * ȳ
+_j′vp(fdm, f, ȳ::AV{<:Number}, x::Vector{<:Number}) = transpose(jacobian(fdm, f, x, length(ȳ))) * ȳ
 
 """
     jvp(fdm, f, x, ẋ)
@@ -83,10 +83,10 @@ j′vp(fdm, f, ȳ, xs...) = j′vp(fdm, xs->f(xs...), ȳ, xs)
 
 Transform `x` into a `Vector`, and return a closure which inverts the transformation.
 """
-to_vec(x::Real) = ([x], first)
+to_vec(x::Number) = ([x], first)
 
 # Vectors
-to_vec(x::Vector{<:Real}) = (x, identity)
+to_vec(x::Vector{<:Number}) = (x, identity)
 function to_vec(x::Vector)
     x_vecs_and_backs = map(to_vec, x)
     x_vecs, backs = first.(x_vecs_and_backs), last.(x_vecs_and_backs)
@@ -97,7 +97,7 @@ function to_vec(x::Vector)
 end
 
 # Arrays
-to_vec(x::Array{<:Real}) = vec(x), x_vec->reshape(x_vec, size(x))
+to_vec(x::Array{<:Number}) = vec(x), x_vec->reshape(x_vec, size(x))
 function to_vec(x::Array)
     x_vec, back = to_vec(reshape(x, :))
     return x_vec, x_vec->reshape(back(x_vec), size(x))
@@ -111,9 +111,11 @@ end
 to_vec(x::Symmetric) = vec(Matrix(x)), x_vec->Symmetric(reshape(x_vec, size(x)))
 to_vec(X::Diagonal) = vec(Matrix(X)), x_vec->Diagonal(reshape(x_vec, size(X)...))
 
-function to_vec(X::T) where T<:Union{Adjoint,Transpose}
-    U = T.name.wrapper
-    return vec(Matrix(X)), x_vec->U(permutedims(reshape(x_vec, size(X))))
+function to_vec(X::Transpose)
+    return vec(Matrix(X)), x_vec->Transpose(permutedims(reshape(x_vec, size(X))))
+end
+function to_vec(X::Adjoint)
+    return vec(Matrix(X)), x_vec->Adjoint(conj!(permutedims(reshape(x_vec, size(X)))))
 end
 
 # Non-array data structures


Test patch:
diff --git a/test/grad.jl b/test/grad.jl
index 00c4ea4..6c452a4 100644
--- a/test/grad.jl
+++ b/test/grad.jl
@@ -16,9 +16,9 @@ Base.length(x::DummyType) = size(x.X, 1)
 
 @testset "grad" begin
 
-    @testset "grad" begin
+    @testset "grad(::$T)" for T in (Float64, ComplexF64)
         rng, fdm = MersenneTwister(123456), central_fdm(5, 1)
-        x = randn(rng, 2)
+        x = randn(rng, T, 2)
         xc = copy(x)
         @test grad(fdm, x->sin(x[1]) + cos(x[2]), x) ≈ [cos(x[1]), -sin(x[2])]
         @test xc == x
@@ -29,13 +29,13 @@ Base.length(x::DummyType) = size(x.X, 1)
         @test jacobian(fdm, f, x, length(ȳ)) ≈ J_exact
         @test jacobian(fdm, f, x) == jacobian(fdm, f, x, length(ȳ))
         @test _jvp(fdm, f, x, ẋ) ≈ J_exact * ẋ
-        @test _j′vp(fdm, f, ȳ, x) ≈ J_exact' * ȳ
+        @test _j′vp(fdm, f, ȳ, x) ≈ transpose(J_exact) * ȳ
         @test xc == x
     end
 
-    @testset "jacobian / _jvp / _j′vp" begin
+    @testset "jacobian / _jvp / _j′vp (::$T)" for T in (Float64, ComplexF64)
         rng, P, Q, fdm = MersenneTwister(123456), 3, 2, central_fdm(5, 1)
-        ȳ, A, x, ẋ = randn(rng, P), randn(rng, P, Q), randn(rng, Q), randn(rng, Q)
+        ȳ, A, x, ẋ = randn(rng, T, P), randn(rng, T, P, Q), randn(rng, T, Q), randn(rng, T, Q)
         Ac = copy(A)
 
         check_jac_and_jvp_and_j′vp(fdm, x->A * x, ȳ, x, ẋ, A)
@@ -51,45 +51,54 @@ Base.length(x::DummyType) = size(x.X, 1)
         return nothing
     end
 
-    @testset "to_vec" begin
-        test_to_vec(1.0)
-        test_to_vec(1)
-        test_to_vec(randn(3))
-        test_to_vec(randn(5, 11))
-        test_to_vec(randn(13, 17, 19))
-        test_to_vec(randn(13, 0, 19))
-        test_to_vec([1.0, randn(2), randn(1), 2.0])
-        test_to_vec([randn(5, 4, 3), (5, 4, 3), 2.0])
-        test_to_vec(reshape([1.0, randn(5, 4, 3), randn(4, 3), 2.0], 2, 2))
-        test_to_vec(UpperTriangular(randn(13, 13)))
-        test_to_vec(Symmetric(randn(11, 11)))
-        test_to_vec(Diagonal(randn(7)))
-        test_to_vec(DummyType(randn(2, 9)))
-
-        @testset "$T" for T in (Adjoint, Transpose)
-            test_to_vec(T(randn(4, 4)))
-            test_to_vec(T(randn(6)))
-            test_to_vec(T(randn(2, 5)))
+    @testset "to_vec(::$T)" for T in (Float64, ComplexF64)
+        if T == Float64
+            test_to_vec(1.0)
+            test_to_vec(1)
+        else
+            test_to_vec(.7 + .8im)
+            test_to_vec(1 + 2im)
         end
-
+        test_to_vec(randn(T, 3))
+        test_to_vec(randn(T, 5, 11))
+        test_to_vec(randn(T, 13, 17, 19))
+        test_to_vec(randn(T, 13, 0, 19))
+        test_to_vec([1.0, randn(T, 2), randn(T, 1), 2.0])
+        test_to_vec([randn(T, 5, 4, 3), (5, 4, 3), 2.0])
+        test_to_vec(reshape([1.0, randn(T, 5, 4, 3), randn(T, 4, 3), 2.0], 2, 2))
+        test_to_vec(UpperTriangular(randn(T, 13, 13)))
+        test_to_vec(Symmetric(randn(T, 11, 11)))
+        test_to_vec(Diagonal(randn(T, 7)))
+        test_to_vec(DummyType(randn(T, 2, 9)))
+    
+        @testset "$Op" for Op in (Adjoint, Transpose)
+            test_to_vec(Op(randn(T, 4, 4)))
+            test_to_vec(Op(randn(T, 6)))
+            test_to_vec(Op(randn(T, 2, 5)))
+        end
+    
         @testset "Tuples" begin
             test_to_vec((5, 4))
-            test_to_vec((5, randn(5)))
-            test_to_vec((randn(4), randn(4, 3, 2), 1))
-            test_to_vec((5, randn(4, 3, 2), UpperTriangular(randn(4, 4)), 2.5))
-            test_to_vec(((6, 5), 3, randn(3, 2, 0, 1)))
-            test_to_vec((DummyType(randn(2, 7)), DummyType(randn(3, 9))))
-            test_to_vec((DummyType(randn(3, 2)), randn(11, 8)))
+            test_to_vec((5, randn(T, 5)))
+            test_to_vec((randn(T, 4), randn(T, 4, 3, 2), 1))
+            test_to_vec((5, randn(T, 4, 3, 2), UpperTriangular(randn(T, 4, 4)), 2.5))
+            test_to_vec(((6, 5), 3, randn(T, 3, 2, 0, 1)))
+            test_to_vec((DummyType(randn(T, 2, 7)), DummyType(randn(T, 3, 9))))
+            test_to_vec((DummyType(randn(T, 3, 2)), randn(T, 11, 8)))
         end
         @testset "Dictionary" begin
-            test_to_vec(Dict(:a=>5, :b=>randn(10, 11), :c=>(5, 4, 3)))
+            if T == Float64
+                test_to_vec(Dict(:a=>5, :b=>randn(10, 11), :c=>(5, 4, 3)))
+            else
+                test_to_vec(Dict(:a=>3 + 2im, :b=>randn(T, 10, 11), :c=>(5+im, 2-im, 1+im)))
+            end
         end
     end
 
-    @testset "jvp" begin
+    @testset "jvp(::$T)" for T in (Float64, ComplexF64)
         rng, N, M, fdm = MersenneTwister(123456), 2, 3, central_fdm(5, 1)
-        x, y = randn(rng, N), randn(rng, M)
-        ẋ, ẏ = randn(rng, N), randn(rng, M)
+        x, y = randn(rng, T, N), randn(rng, T, M)
+        ẋ, ẏ = randn(rng, T, N), randn(rng, T, M)
         xy, ẋẏ = vcat(x, y), vcat(ẋ, ẏ)
         ż_manual = _jvp(fdm, (xy)->sum(sin, xy), xy, ẋẏ)[1]
         ż_auto = jvp(fdm, x->sum(sin, x[1]) + sum(sin, x[2]), ((x, y), (ẋ, ẏ)))
@@ -98,10 +107,10 @@ Base.length(x::DummyType) = size(x.X, 1)
         @test ż_manual ≈ ż_multi
     end
 
-    @testset "j′vp" begin
+    @testset "j′vp(::$T)" for T in (Float64, ComplexF64)
         rng, N, M, fdm = MersenneTwister(123456), 2, 3, central_fdm(5, 1)
-        x, y = randn(rng, N), randn(rng, M)
-        z̄ = randn(rng, N + M)
+        x, y = randn(rng, T, N), randn(rng, T, M)
+        z̄ = randn(rng, T, N + M)
         xy = vcat(x, y)
         x̄ȳ_manual = j′vp(fdm, xy->sin.(xy), z̄, xy)
         x̄ȳ_auto = j′vp(fdm, x->sin.(vcat(x[1], x[2])), z̄, (x, y))