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
Expand Up @@ -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

Expand Down
54 changes: 34 additions & 20 deletions stdlib/LinearAlgebra/src/generic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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))
stevengj marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Up @@ -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
Expand Down Expand Up @@ -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

stevengj marked this conversation as resolved.
Show resolved Hide resolved
@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))
Expand Down