From deaa8313cd62a682bd5df0c54879287768644cd5 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 18 Mar 2020 09:02:55 -0500 Subject: [PATCH 1/8] =?UTF-8?q?Support=20`=E2=8A=99`=20and=20`=E2=8A=97`?= =?UTF-8?q?=20as=20elementwise-=20and=20tensor-product=20operators?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While we have broadcasting and `a*b'`, sometimes you need to pass an operator as an argument to a function. Since we already have `dot` or `⋅` for the inner product, these elementwise and tensor products fill out the space of possibilities. Note that adjoint/transpose are covectors, and so to prevent confusion calling `u ⊗ v'` is an error. --- NEWS.md | 2 + stdlib/LinearAlgebra/docs/src/index.md | 2 + stdlib/LinearAlgebra/src/LinearAlgebra.jl | 120 ++++++++++++++++++++++ stdlib/LinearAlgebra/src/dense.jl | 6 +- stdlib/LinearAlgebra/test/addmul.jl | 52 ++++++++++ 5 files changed, 179 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index 3edd1fdc46d25..7950043cb9ff7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -145,6 +145,8 @@ Standard library changes * The BLAS submodule now supports the level-2 BLAS subroutine `spmv!` ([#34320]). * The BLAS submodule now supports the level-1 BLAS subroutine `rot!` ([#35124]). * New generic `rotate!(x, y, c, s)` and `reflect!(x, y, c, s)` functions ([#35124]). +* `hadamard` or `⊙` (`\odotTAB`) can be used as an elementwise multiplication operator, + and `tensor` or `⊗` (`\otimesTAB`) as the tensor product operator ([#35150]). #### Markdown diff --git a/stdlib/LinearAlgebra/docs/src/index.md b/stdlib/LinearAlgebra/docs/src/index.md index b78ed785080e0..2dd1feb76a6f8 100644 --- a/stdlib/LinearAlgebra/docs/src/index.md +++ b/stdlib/LinearAlgebra/docs/src/index.md @@ -321,6 +321,7 @@ LinearAlgebra.PosDefException LinearAlgebra.ZeroPivotException LinearAlgebra.dot LinearAlgebra.cross +LinearAlgebra.tensor LinearAlgebra.factorize LinearAlgebra.Diagonal LinearAlgebra.Bidiagonal @@ -474,6 +475,7 @@ LinearAlgebra.lmul! LinearAlgebra.rmul! LinearAlgebra.ldiv! LinearAlgebra.rdiv! +LinearAlgebra.tensor! ``` ## BLAS functions diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index bb1dcb3c17ea7..d09800ce433bf 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -388,6 +388,126 @@ const ⋅ = dot const × = cross export ⋅, × +""" + hadamard(a, b) + a ⊙ b + +Elementwise multiplication of `a` and `b`. This yields the same result as `map(*, a, b)` +if `a` and `b` have the same axes, and throws an error otherwise. + +`⊙` can be passed as an operator to higher-order functions. + +```jldoctest +julia> a = [2, 3]; b = [5, 7]; + +julia> a ⊙ b +2-element Array{$Int,1}: + 10 + 21 + +julia> a ⊙ [5] +ERROR: DimensionMismatch("in the axes of `A` and `B` must match, got (Base.OneTo(2),) and (Base.OneTo(1),)") +[...] +``` + +!!! compat "Julia 1.5" + This function requires at least Julia 1.5. In Julia 1.0-1.4 it is available from + the `Compat` package. +""" +function hadamard(A, B) + @noinline throw_dmm(axA, axB) = throw(DimensionMismatch("in the axes of `A` and `B` must match, got $axA and $axB")) + + axA, axB = axes(A), axes(B) + axA == axB || throw_dmm(axA, axB) + return map(*, A, B) +end +const ⊙ = hadamard + +""" + hadamard!(dest, A, B) + +Similar to `hadamard(A, B)` (which can also be written `A ⊙ B`), but stores its results in +the pre-allocated array `dest`. + +!!! compat "Julia 1.5" + This function requires at least Julia 1.5. In Julia 1.0-1.4 it is available from + the `Compat` package. +""" +function hadamard!(dest::AbstractArray, A::AbstractArray, B::AbstractArray) + @noinline function throw_dmm(axA, axB, axdest) + throw(DimensionMismatch("`axes(dest) = $axdest` must be equal to `axes(A) = $axA` and `axes(B) = $axB`")) + end + + axA, axB, axdest = axes(A), axes(B), axes(dest) + ((axdest == axA) & (axdest == axB)) || throw_dmm(axA, axB, axdest) + @simd for I in CartesianIndices(axdest) + @inbounds dest[I] = A[I]*B[I] + end + return dest +end + +export ⊙, hadamard, hadamard! + +""" + tensor(A, B) + A ⊗ B + +Compute the tensor product of `A` and `B`. +If `C = A ⊗ B`, then `C[i1, ..., im, j1, ..., jn] = A[i1, ... im] * B[j1, ..., jn]`. + +```jldoctest +julia> a = [2, 3]; b = [5, 7, 11]; + +julia> a ⊗ b +2×3 Array{$Int,2}: + 10 14 22 + 15 21 33 +``` + +See also: [`kron`](@ref). + +!!! compat "Julia 1.5" + This function requires at least Julia 1.5. In Julia 1.0-1.4 it is available from + the `Compat` package. +""" +tensor(A::AbstractArray, B::AbstractArray) = [a*b for a in A, b in B] +const ⊗ = tensor + +function tensor(u::AbstractVector, v::Adjoint{T,<:AbstractVector}) where T + error("`u ⊗ v'` is not defined, perhaps you meant `u * v'` or `u * transpose(v)`") +end +function tensor(u::AbstractVector, v::Transpose{T,<:AbstractVector}) where T + error("`u ⊗ v'` is not defined, perhaps you meant `u * v'` or `u * transpose(v)`") +end + +""" + tensor!(dest, A, B) + +Similar to `tensor(A, B)` (which can also be written `A ⊗ B`), but stores its results in +the pre-allocated array `dest`. + +!!! compat "Julia 1.5" + This function requires at least Julia 1.5. In Julia 1.0-1.4 it is available from + the `Compat` package. +""" +function tensor!(dest::AbstractArray, A::AbstractArray, B::AbstractArray) + @noinline function throw_dmm(axA, axB, axdest) + throw(DimensionMismatch("`axes(dest) = $axdest` must concatenate `axes(A) = $axA` and `axes(B) = $axB`")) + end + + axA, axB, axdest = axes(A), axes(B), axes(dest) + axes(dest) == (axA..., axB...) || throw_dmm(axA, axB, axdest) + for IB in CartesianIndices(axB) + b = B[IB] + @simd for IA in CartesianIndices(axA) + @inbounds dest[IA,IB] = A[IA]*b + end + end + return dest +end + +export ⊗, tensor, tensor! + """ LinearAlgebra.peakflops(n::Integer=2000; parallel::Bool=false) diff --git a/stdlib/LinearAlgebra/src/dense.jl b/stdlib/LinearAlgebra/src/dense.jl index 5d69e48ad9e26..cf6af00481060 100644 --- a/stdlib/LinearAlgebra/src/dense.jl +++ b/stdlib/LinearAlgebra/src/dense.jl @@ -341,9 +341,9 @@ end Kronecker tensor product of two vectors or two matrices. -For vectors v and w, the Kronecker product is related to the outer product by -`kron(v,w) == vec(w*transpose(v))` or -`w*transpose(v) == reshape(kron(v,w), (length(w), length(v)))`. +For vectors v and w, the Kronecker product is related to the tensor product [`tensor`](@ref), or `⊗`, by +`kron(v,w) == vec(w ⊗ v)` or +`w ⊗ v == reshape(kron(v,w), (length(w), length(v)))`. Note how the ordering of `v` and `w` differs on the left and right of these expressions (due to column-major storage). diff --git a/stdlib/LinearAlgebra/test/addmul.jl b/stdlib/LinearAlgebra/test/addmul.jl index 42529f3f4f334..7f9ea8ffb855e 100644 --- a/stdlib/LinearAlgebra/test/addmul.jl +++ b/stdlib/LinearAlgebra/test/addmul.jl @@ -131,6 +131,58 @@ for cmat in mattypes, push!(testdata, (cmat{celt}, amat{aelt}, bmat{belt})) end +@testset "Alternative multiplication operators" begin + for T in (Int, Float32, Float64, BigFloat) + a = [T[1, 2], T[-3, 7]] + b = [T[5, 11], T[-13, 17]] + @test map(⋅, a, b) == map(dot, a, b) == [27, 158] + @test map(⊙, a, b) == map(hadamard, a, b) == [a[1].*b[1], a[2].*b[2]] + @test map(⊗, a, b) == map(tensor, a, b) == [a[1]*transpose(b[1]), a[2]*transpose(b[2])] + @test hadamard!(fill(typemax(Int), 2), T[1, 2], T[-3, 7]) == [-3, 14] + @test tensor!(fill(typemax(Int), 2, 2), T[1, 2], T[-3, 7]) == [-3 7; -6 14] + end + + @test_throws DimensionMismatch [1,2] ⊙ [3] + @test_throws DimensionMismatch hadamard!([0, 0, 0], [1,2], [-3,7]) + @test_throws DimensionMismatch hadamard!([0, 0], [1,2], [-3]) + @test_throws DimensionMismatch hadamard!([0, 0], [1], [-3,7]) + @test_throws DimensionMismatch tensor!(Matrix{Int}(undef, 2, 2), [1], [-3,7]) + @test_throws DimensionMismatch tensor!(Matrix{Int}(undef, 2, 2), [1,2], [-3]) + + u, v = [2+2im, 3+5im], [1-3im, 7+3im] + @test u ⋅ v == conj(u[1])*v[1] + conj(u[2])*v[2] + @test u ⊙ v == [u[1]*v[1], u[2]*v[2]] + @test u ⊗ v == [u[1]*v[1] u[1]*v[2]; u[2]*v[1] u[2]*v[2]] + @test hadamard(u, v) == u ⊙ v + @test tensor(u, v) == u ⊗ v + dest = similar(u) + @test hadamard!(dest, u, v) == u ⊙ v + dest = Matrix{Complex{Int}}(undef, 2, 2) + @test tensor!(dest, u, v) == u ⊗ v + + for (A, B, b) in (([1 2; 3 4], [5 6; 7 8], [5,6]), + ([1+0.8im 2+0.7im; 3+0.6im 4+0.5im], + [5+0.4im 6+0.3im; 7+0.2im 8+0.1im], + [5+0.6im,6+0.3im])) + @test A ⊗ b == cat(A*b[1], A*b[2]; dims=3) + @test A ⊗ B == cat(cat(A*B[1,1], A*B[2,1]; dims=3), + cat(A*B[1,2], A*B[2,2]; dims=3); dims=4) + end + + A, B = reshape(1:27, 3, 3, 3), reshape(1:4, 2, 2) + @test A ⊗ B == [a*b for a in A, b in B] + + # Adjoint/transpose is a dual vector, not an AbstractMatrix + v = [1,2] + @test_throws ErrorException v ⊗ v' + @test_throws ErrorException v ⊗ transpose(v) + + # Docs comparison to `kron` + v, w = [1,2,3], [5,7] + @test kron(v,w) == vec(w ⊗ v) + @test w ⊗ v == reshape(kron(v,w), (length(w), length(v))) +end + @testset "mul!(::$TC, ::$TA, ::$TB, α, β)" for (TC, TA, TB) in testdata if needsquare(TA) na1 = na2 = rand(sizecandidates) From 2ad9931bd171f4e476d71489d5fa814cc6ac0234 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sun, 26 Apr 2020 06:35:36 -0500 Subject: [PATCH 2/8] Update stdlib/LinearAlgebra/src/LinearAlgebra.jl Co-Authored-By: simeonschaub --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index d09800ce433bf..a807d5dac533d 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -415,7 +415,7 @@ ERROR: DimensionMismatch("in the axes of `A` and `B` must match, got (Base.OneTo the `Compat` package. """ function hadamard(A, B) - @noinline throw_dmm(axA, axB) = throw(DimensionMismatch("in the axes of `A` and `B` must match, got $axA and $axB")) + @noinline throw_dmm(axA, axB) = throw(DimensionMismatch("Axes of `A` and `B` must match, got $axA and $axB")) axA, axB = axes(A), axes(B) axA == axB || throw_dmm(axA, axB) From 0b94f15db7736a0f4111d454f002992323de32c7 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sun, 26 Apr 2020 07:39:50 -0500 Subject: [PATCH 3/8] Address review comments --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 25 ++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index a807d5dac533d..4e4c4e87c3070 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -414,13 +414,14 @@ ERROR: DimensionMismatch("in the axes of `A` and `B` must match, got (Base.OneTo This function requires at least Julia 1.5. In Julia 1.0-1.4 it is available from the `Compat` package. """ -function hadamard(A, B) +function hadamard(A::AbstractArray, B::AbstractArray) @noinline throw_dmm(axA, axB) = throw(DimensionMismatch("Axes of `A` and `B` must match, got $axA and $axB")) axA, axB = axes(A), axes(B) axA == axB || throw_dmm(axA, axB) - return map(*, A, B) + return map(hadamard, A, B) end +hadamard(a, b) = a * b const ⊙ = hadamard """ @@ -440,7 +441,7 @@ function hadamard!(dest::AbstractArray, A::AbstractArray, B::AbstractArray) axA, axB, axdest = axes(A), axes(B), axes(dest) ((axdest == axA) & (axdest == axB)) || throw_dmm(axA, axB, axdest) - @simd for I in CartesianIndices(axdest) + @simd for I in eachindex(dest, A, B) @inbounds dest[I] = A[I]*B[I] end return dest @@ -497,10 +498,20 @@ function tensor!(dest::AbstractArray, A::AbstractArray, B::AbstractArray) axA, axB, axdest = axes(A), axes(B), axes(dest) axes(dest) == (axA..., axB...) || throw_dmm(axA, axB, axdest) - for IB in CartesianIndices(axB) - b = B[IB] - @simd for IA in CartesianIndices(axA) - @inbounds dest[IA,IB] = A[IA]*b + if IndexStyle(dest) === IndexCartesian() + for IB in CartesianIndices(axB) + @inbounds b = B[IB] + @simd for IA in CartesianIndices(axA) + @inbounds dest[IA,IB] = A[IA]*b + end + end + else + i = firstindex(dest) + @inbounds for b in B + @simd for a in A + dest[i] = a*b + i += 1 + end end end return dest From 4d20e250e8766b08937b7b0e5ae72f810c4ac049 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 27 Apr 2020 11:09:19 -0500 Subject: [PATCH 4/8] Update stdlib/LinearAlgebra/src/LinearAlgebra.jl Co-Authored-By: simeonschaub --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 4e4c4e87c3070..926c975c887c2 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -406,7 +406,7 @@ julia> a ⊙ b 21 julia> a ⊙ [5] -ERROR: DimensionMismatch("in the axes of `A` and `B` must match, got (Base.OneTo(2),) and (Base.OneTo(1),)") +ERROR: DimensionMismatch("Axes of `A` and `B` must match, got (Base.OneTo(2),) and (Base.OneTo(1),)") [...] ``` From 9e0b9ee5d142acbd093320acb1523d5c08dbe5e6 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Tue, 28 Apr 2020 05:29:40 -0500 Subject: [PATCH 5/8] Add more tensor signatures that error for covectors --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 15 +++++++++++---- stdlib/LinearAlgebra/test/addmul.jl | 8 ++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 926c975c887c2..2e04915785d0e 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -474,11 +474,18 @@ See also: [`kron`](@ref). tensor(A::AbstractArray, B::AbstractArray) = [a*b for a in A, b in B] const ⊗ = tensor -function tensor(u::AbstractVector, v::Adjoint{T,<:AbstractVector}) where T - error("`u ⊗ v'` is not defined, perhaps you meant `u * v'` or `u * transpose(v)`") +const CovectorLike{T} = Union{Adjoint{T,<:AbstractVector},Transpose{T,<:AbstractVector}} +function tensor(u::AbstractArray, v::CovectorLike) + # If `v` is thought of as a covector, you might want this to be two-dimensional, + # but thought of as a matrix it should be three-dimensional. + # The safest is to avoid supporting it at all. See discussion in #35150. + error("`u ⊗ v` is not defined for co-vectors, perhaps you meant `*`?") end -function tensor(u::AbstractVector, v::Transpose{T,<:AbstractVector}) where T - error("`u ⊗ v'` is not defined, perhaps you meant `u * v'` or `u * transpose(v)`") +function tensor(u::CovectorLike, v::AbstractArray) + error("`u ⊗ v` is not defined for co-vectors, perhaps you meant `*`?") +end +function tensor(u::CovectorLike, v::CovectorLike) + error("`u ⊗ v` is not defined for co-vectors, perhaps you meant `*`?") end """ diff --git a/stdlib/LinearAlgebra/test/addmul.jl b/stdlib/LinearAlgebra/test/addmul.jl index 7f9ea8ffb855e..6d1681186e1dc 100644 --- a/stdlib/LinearAlgebra/test/addmul.jl +++ b/stdlib/LinearAlgebra/test/addmul.jl @@ -176,6 +176,14 @@ end v = [1,2] @test_throws ErrorException v ⊗ v' @test_throws ErrorException v ⊗ transpose(v) + @test_throws ErrorException v' ⊗ v + @test_throws ErrorException transpose(v) ⊗ v + @test_throws ErrorException v' ⊗ v' + @test_throws ErrorException transpose(v) ⊗ transpose(v) + @test_throws ErrorException v' ⊗ transpose(v) + @test_throws ErrorException transpose(v) ⊗ v' + @test_throws ErrorException A ⊗ v' + @test_throws ErrorException A ⊗ transpose(v) # Docs comparison to `kron` v, w = [1,2,3], [5,7] From 71455c2c46092416e5272f2ec407573dff4d94c5 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Tue, 28 Apr 2020 05:39:11 -0500 Subject: [PATCH 6/8] Tweak docs and error messages --- stdlib/LinearAlgebra/docs/src/index.md | 2 ++ stdlib/LinearAlgebra/src/LinearAlgebra.jl | 11 ++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/stdlib/LinearAlgebra/docs/src/index.md b/stdlib/LinearAlgebra/docs/src/index.md index 2dd1feb76a6f8..1c4ed48786a23 100644 --- a/stdlib/LinearAlgebra/docs/src/index.md +++ b/stdlib/LinearAlgebra/docs/src/index.md @@ -321,6 +321,7 @@ LinearAlgebra.PosDefException LinearAlgebra.ZeroPivotException LinearAlgebra.dot LinearAlgebra.cross +LinearAlgebra.hadamard LinearAlgebra.tensor LinearAlgebra.factorize LinearAlgebra.Diagonal @@ -475,6 +476,7 @@ LinearAlgebra.lmul! LinearAlgebra.rmul! LinearAlgebra.ldiv! LinearAlgebra.rdiv! +LinearAlgebra.hadamard! LinearAlgebra.tensor! ``` diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 2e04915785d0e..923efd0ec03bf 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -392,8 +392,9 @@ export ⋅, × hadamard(a, b) a ⊙ b -Elementwise multiplication of `a` and `b`. This yields the same result as `map(*, a, b)` -if `a` and `b` have the same axes, and throws an error otherwise. +For arrays `a` and `b`, perform elementwise multiplication. +`a` and `b` must have identical `axes`. +When either `a` or `b` is not an array, `hadamard(a, b)` defaults to `a*b`. `⊙` can be passed as an operator to higher-order functions. @@ -479,13 +480,13 @@ function tensor(u::AbstractArray, v::CovectorLike) # If `v` is thought of as a covector, you might want this to be two-dimensional, # but thought of as a matrix it should be three-dimensional. # The safest is to avoid supporting it at all. See discussion in #35150. - error("`u ⊗ v` is not defined for co-vectors, perhaps you meant `*`?") + error("`tensor` is not defined for co-vectors, perhaps you meant `*`?") end function tensor(u::CovectorLike, v::AbstractArray) - error("`u ⊗ v` is not defined for co-vectors, perhaps you meant `*`?") + error("`tensor` is not defined for co-vectors, perhaps you meant `*`?") end function tensor(u::CovectorLike, v::CovectorLike) - error("`u ⊗ v` is not defined for co-vectors, perhaps you meant `*`?") + error("`tensor` is not defined for co-vectors, perhaps you meant `*`?") end """ From bc876800bb8999528d74877bbdfbbf9a3d400930 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Tue, 28 Apr 2020 11:04:33 -0500 Subject: [PATCH 7/8] Make hadamard! recursive --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 923efd0ec03bf..04cd06da33cf5 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -443,7 +443,7 @@ function hadamard!(dest::AbstractArray, A::AbstractArray, B::AbstractArray) axA, axB, axdest = axes(A), axes(B), axes(dest) ((axdest == axA) & (axdest == axB)) || throw_dmm(axA, axB, axdest) @simd for I in eachindex(dest, A, B) - @inbounds dest[I] = A[I]*B[I] + @inbounds dest[I] = hadamard(A[I], B[I]) end return dest end From 26f0d2aa7300d40e2a12b76abfe50c2ca5c06a76 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Fri, 1 May 2020 03:59:02 -0500 Subject: [PATCH 8/8] Back out recursive hadamard --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 04cd06da33cf5..790c3960b92e9 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -394,7 +394,6 @@ export ⋅, × For arrays `a` and `b`, perform elementwise multiplication. `a` and `b` must have identical `axes`. -When either `a` or `b` is not an array, `hadamard(a, b)` defaults to `a*b`. `⊙` can be passed as an operator to higher-order functions. @@ -420,9 +419,8 @@ function hadamard(A::AbstractArray, B::AbstractArray) axA, axB = axes(A), axes(B) axA == axB || throw_dmm(axA, axB) - return map(hadamard, A, B) + return map(*, A, B) end -hadamard(a, b) = a * b const ⊙ = hadamard """ @@ -443,7 +441,7 @@ function hadamard!(dest::AbstractArray, A::AbstractArray, B::AbstractArray) axA, axB, axdest = axes(A), axes(B), axes(dest) ((axdest == axA) & (axdest == axB)) || throw_dmm(axA, axB, axdest) @simd for I in eachindex(dest, A, B) - @inbounds dest[I] = hadamard(A[I], B[I]) + @inbounds dest[I] = A[I] * B[I] end return dest end