Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#34234 normalize(a) for multidimensional arrays #34239

Merged
merged 10 commits into from
Jan 7, 2020
2 changes: 1 addition & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -30,8 +30,8 @@ Standard library changes


#### LinearAlgebra

* The BLAS submodule now supports the level-2 BLAS subroutine `hpmv!` ([#34211]).
* `normalize` now supports multidimensional arrays ([#34239])

#### Markdown

54 changes: 34 additions & 20 deletions stdlib/LinearAlgebra/src/generic.jl
Original file line number Diff line number Diff line change
@@ -1582,39 +1582,39 @@ function isapprox(x::AbstractArray, y::AbstractArray;
end

"""
normalize!(v::AbstractVector, p::Real=2)
normalize!(a::AbstractArray, p::Real=2)
Normalize the vector `v` in-place so that its `p`-norm equals unity,
i.e. `norm(v, p) == 1`.
Normalize the array `a` in-place so that its `p`-norm equals unity,
i.e. `norm(a, p) == 1`.
See also [`normalize`](@ref) and [`norm`](@ref).
"""
function normalize!(v::AbstractVector, p::Real=2)
nrm = norm(v, p)
__normalize!(v, nrm)
function normalize!(a::AbstractArray, p::Real=2)
nrm = norm(a, p)
__normalize!(a, nrm)
end

@inline function __normalize!(v::AbstractVector, nrm::AbstractFloat)
@inline function __normalize!(a::AbstractArray, nrm::AbstractFloat)
# The largest positive floating point number whose inverse is less than infinity
δ = inv(prevfloat(typemax(nrm)))

if nrm δ # Safe to multiply with inverse
invnrm = inv(nrm)
rmul!(v, invnrm)
rmul!(a, invnrm)

else # scale elements to avoid overflow
εδ = eps(one(nrm))/δ
rmul!(v, εδ)
rmul!(v, inv(nrm*εδ))
rmul!(a, εδ)
rmul!(a, inv(nrm*εδ))
end

v
a
end

"""
normalize(v::AbstractVector, p::Real=2)
normalize(a::AbstractArray, p::Real=2)
Normalize the vector `v` so that its `p`-norm equals unity,
i.e. `norm(v, p) == 1`.
Normalize the array `a` so that its `p`-norm equals unity,
i.e. `norm(a, p) == 1`.
See also [`normalize!`](@ref) and [`norm`](@ref).
# Examples
@@ -1638,15 +1638,29 @@ julia> c = normalize(a, 1)
julia> norm(c, 1)
1.0
julia> a = [1 2 4 ; 1 2 4]
2×3 Array{Int64,2}:
1 2 4
1 2 4
julia> norm(a)
6.48074069840786
julia> normalize(a)
2×3 Array{Float64,2}:
0.154303 0.308607 0.617213
0.154303 0.308607 0.617213
```
"""
function normalize(v::AbstractVector, p::Real = 2)
nrm = norm(v, p)
if !isempty(v)
vv = copy_oftype(v, typeof(v[1]/nrm))
return __normalize!(vv, nrm)
function normalize(a::AbstractArray, p::Real = 2)
nrm = norm(a, p)
if !isempty(a)
aa = copy_oftype(a, typeof(first(a)/nrm))
return __normalize!(aa, nrm)
else
T = typeof(zero(eltype(v))/nrm)
T = typeof(zero(eltype(a))/nrm)
return T[]
end
end
24 changes: 24 additions & 0 deletions stdlib/LinearAlgebra/test/generic.jl
Original file line number Diff line number Diff line change
@@ -5,9 +5,14 @@ module TestGeneric
using Test, LinearAlgebra, Random

const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test")

isdefined(Main, :Quaternions) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "Quaternions.jl"))
using .Main.Quaternions

isdefined(Main, :OffsetArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "OffsetArrays.jl"))
using .Main.OffsetArrays


Random.seed!(123)

n = 5 # should be odd
@@ -248,6 +253,25 @@ end
end
end

@testset "normalize for multidimensional arrays" begin

for arr in (
fill(10.0, ()), # 0 dim
[1.0], # 1 dim
[1.0 2.0 3.0; 4.0 5.0 6.0], # 2-dim
rand(1,2,3), # higher dims
rand(1,2,3,4),
OffsetArray([-1,0], (-2,)) # no index 1
)
@test normalize(arr) == normalize!(copy(arr))
@test size(normalize(arr)) == size(arr)
@test axes(normalize(arr)) == axes(arr)
@test vec(normalize(arr)) == normalize(vec(arr))
end

@test typeof(normalize([1 2 3; 4 5 6])) == Array{Float64,2}
end

@testset "Issue #30466" begin
@test norm([typemin(Int), typemin(Int)], Inf) == -float(typemin(Int))
@test norm([typemin(Int), typemin(Int)], 1) == -2float(typemin(Int))