Skip to content

Commit e0a8985

Browse files
committed
Merge pull request #13681 from JuliaLang/cjh/normalize
Implement normalize and normalize!
2 parents 4e81212 + 5df3ebd commit e0a8985

File tree

7 files changed

+162
-8
lines changed

7 files changed

+162
-8
lines changed

NEWS.md

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,28 @@ Library improvements
4545

4646
* `cov` and `cor` don't use keyword arguments anymore and are therefore now type stable ([#13465]).
4747

48-
* New method for generic QR with column pivoting ([#13480]).
48+
* Linear algebra:
4949

50-
* A new `SparseVector` type allows for one-dimensional sparse arrays. Slicing
51-
and reshaping sparse matrices now return vectors when appropriate. The
52-
`sparsevec` function returns a one-dimensional sparse vector instead of a
53-
one-column sparse matrix.
50+
* New `normalize` and `normalize!` convenience functions for normalizing
51+
vectors ([#13681]).
52+
53+
* QR
54+
55+
* New method for generic QR with column pivoting ([#13480]).
56+
57+
* New method for polar decompositions of `AbstractVector`s ([#13681]).
58+
59+
* A new `SparseVector` type allows for one-dimensional sparse arrays.
60+
Slicing and reshaping sparse matrices now return vectors when
61+
appropriate. The `sparsevec` function returns a one-dimensional sparse
62+
vector instead of a one-column sparse matrix. ([#13440])
5463

5564
Deprecated or removed
5665
---------------------
5766

58-
* The method `A_ldiv_B!(SparseMatrixCSC, StrideVecOrMat)` has been deprecated in favor
59-
of versions that require the matrix to in factored form ([#13496]).
67+
* The method `A_ldiv_B!(SparseMatrixCSC, StrideVecOrMat)` has been deprecated
68+
in favor of versions that require the matrix to be in factored form
69+
([#13496]).
6070

6171
Julia v0.4.0 Release Notes
6272
==========================

base/exports.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,8 @@ export
676676
lufact,
677677
lyap,
678678
norm,
679+
normalize,
680+
normalize!,
679681
nullspace,
680682
ordschur!,
681683
ordschur,

base/linalg.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ export
9595
lufact!,
9696
lyap,
9797
norm,
98+
normalize,
99+
normalize!,
98100
nullspace,
99101
ordschur!,
100102
ordschur,

base/linalg/generic.jl

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ scale(s::Number, X::AbstractArray) = s*X
88
# For better performance when input and output are the same array
99
# See https://github.com/JuliaLang/julia/issues/8415#issuecomment-56608729
1010
function generic_scale!(X::AbstractArray, s::Number)
11-
for I in eachindex(X)
11+
@simd for I in eachindex(X)
1212
@inbounds X[I] *= s
1313
end
1414
X
@@ -529,3 +529,71 @@ function isapprox{T<:Number,S<:Number}(x::AbstractArray{T}, y::AbstractArray{S};
529529
d = norm(x - y)
530530
return isfinite(d) ? d <= atol + rtol*max(norm(x), norm(y)) : x == y
531531
end
532+
533+
"""
534+
normalize!(v, [p=2])
535+
536+
Normalize the vector `v` in-place with respect to the `p`-norm.
537+
538+
# Inputs
539+
540+
- `v::AbstractVector` - vector to be normalized
541+
- `p::Real` - The `p`-norm to normalize with respect to. Default: 2
542+
543+
# Output
544+
545+
- `v` - A unit vector being the input vector, rescaled to have norm 1.
546+
The input vector is modified in-place.
547+
548+
# See also
549+
550+
`normalize`, `qr`
551+
552+
"""
553+
function normalize!(v::AbstractVector, p::Real=2)
554+
nrm = norm(v, p)
555+
__normalize!(v, nrm)
556+
end
557+
558+
@inline function __normalize!{T<:AbstractFloat}(v::AbstractVector{T}, nrm::T)
559+
#The largest positive floating point number whose inverse is less than
560+
#infinity
561+
const δ = inv(prevfloat(typemax(T)))
562+
563+
if nrm δ #Safe to multiply with inverse
564+
invnrm = inv(nrm)
565+
scale!(v, invnrm)
566+
567+
else #Divide by norm; slower but more correct
568+
#Note 2015-10-19: As of Julia 0.4, @simd does not vectorize floating
569+
#point division, although vectorized intrinsics like DIVPD exist. I
570+
#will leave the @simd annotation in, hoping that someone will improve
571+
#the @simd macro in the future. - cjh
572+
@inbounds @simd for i in eachindex(v)
573+
v[i] /= nrm
574+
end
575+
end
576+
577+
v
578+
end
579+
580+
"""
581+
normalize(v, [p=2])
582+
583+
Normalize the vector `v` with respect to the `p`-norm.
584+
585+
# Inputs
586+
587+
- `v::AbstractVector` - vector to be normalized
588+
- `p::Real` - The `p`-norm to normalize with respect to. Default: 2
589+
590+
# Output
591+
592+
- `v` - A unit vector being a copy of the input vector, scaled to have norm 1
593+
594+
# See also
595+
596+
`normalize!`, `qr`
597+
"""
598+
normalize(v::AbstractVector, p::Real=2) = v/norm(v, p)
599+

base/linalg/qr.jl

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,49 @@ function _qr(A::Union{Number, AbstractMatrix}, ::Type{Val{true}}; thin::Bool=tru
108108
full(getq(F), thin=thin), F[:R]::Matrix{eltype(F)}, F[:p]::Vector{BlasInt}
109109
end
110110

111+
"""
112+
qr(v::AbstractVector)
113+
114+
Computes the polar decomposition of a vector.
115+
116+
# Input
117+
- `v::AbstractVector` - vector to normalize
118+
119+
# Outputs
120+
- `w` - A unit vector in the direction of `v`
121+
- `r` - The norm of `v`
122+
123+
# See also
124+
125+
`normalize`, `normalize!`, `qr!`
126+
"""
127+
function qr(v::AbstractVector)
128+
nrm = norm(v)
129+
v/nrm, nrm
130+
end
131+
132+
"""
133+
qr!(v::AbstractVector)
134+
135+
Computes the polar decomposition of a vector.
136+
137+
# Input
138+
- `v::AbstractVector` - vector to normalize
139+
140+
# Outputs
141+
- `w` - A unit vector in the direction of `v`
142+
- `r` - The norm of `v`
143+
144+
# See also
145+
146+
`normalize`, `normalize!`, `qr`
147+
"""
148+
function qr!(v::AbstractVector)
149+
nrm = norm(v)
150+
__normalize!(v, nrm), nrm
151+
end
152+
153+
111154
convert{T}(::Type{QR{T}},A::QR) = QR(convert(AbstractMatrix{T}, A.factors), convert(Vector{T}, A.τ))
112155
convert{T}(::Type{Factorization{T}}, A::QR) = convert(QR{T}, A)
113156
convert{T}(::Type{QRCompactWY{T}},A::QRCompactWY) = QRCompactWY(convert(AbstractMatrix{T}, A.factors), convert(AbstractMatrix{T}, A.T))

test/linalg/generic.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,24 @@ let
150150
@test LinAlg.axpy!(α, x, deepcopy(y)) == x .* Matrix{Int}[α]
151151
@test LinAlg.axpy!(α, x, deepcopy(y)) != Matrix{Int}[α] .* x
152152
end
153+
154+
let
155+
v = [3.0, 4.0]
156+
@test norm(v) === 5.0
157+
w = normalize(v)
158+
@test w == [0.6, 0.8]
159+
@test norm(w) === 1.0
160+
@test norm(normalize!(v) - w, Inf) < eps()
161+
end
162+
163+
#Test potential overflow in normalize!
164+
let
165+
δ = inv(prevfloat(typemax(Float64)))
166+
v = [δ, -δ]
167+
168+
@test norm(v) === 7.866824069956793e-309
169+
w = normalize(v)
170+
@test w [1/√2, -1/√2]
171+
@test norm(w) === 1.0
172+
@test norm(normalize!(v) - w, Inf) < eps()
173+
end

test/linalg/qr.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,11 @@ let
164164
Q=full(qrfact(A)[:Q])
165165
@test vecnorm(A-Q) < eps()
166166
end
167+
168+
let
169+
debug && println("qr on AbstractVector")
170+
171+
v = [3.0, 4.0]
172+
@test qr(v) == ([0.6, 0.8], 5.0)
173+
end
174+

0 commit comments

Comments
 (0)