Skip to content

Commit 508d717

Browse files
authored
Merge pull request #221 from JuliaStats/dw/invwhiten_invunwhiten
Add `invwhiten` and `invunwhiten`
2 parents 7b61f73 + b4c2257 commit 508d717

File tree

10 files changed

+209
-17
lines changed

10 files changed

+209
-17
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "PDMats"
22
uuid = "90014a1f-27ba-587c-ab20-58faa44d9150"
3-
version = "0.11.34"
3+
version = "0.11.35"
44

55
[deps]
66
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

README.md

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ X_invA_Xt(a, x) # compute `x * inv(a) * x'` for a matrix `x`.
180180

181181
Xt_invA_X(a, x) # compute `x' * inv(a) * x` for a matrix `x`.
182182

183-
whiten(a, x) # whitening transform. `x` can be a vector or a matrix.
183+
whiten(a, x) # whitening transform defined by `a`. `x` can be a vector or a matrix.
184184
#
185185
# Note: If the covariance of `x` is `a`, then the
186186
# covariance of the transformed result is an identity
@@ -190,8 +190,17 @@ whiten!(a, x) # whitening transform inplace, directly updating `x`.
190190

191191
whiten!(r, a, x) # write the transformed result to `r`.
192192

193-
unwhiten(a, x) # inverse of whitening transform. `x` can be a vector or
194-
# a matrix.
193+
invwhiten(a, x) # whitening transform defined by `inv(a)`. `x` can be a vector or a matrix.
194+
#
195+
# Note: If the precision of `x` is `a`, then the
196+
# covariance of the transformed result is an identity matrix.
197+
198+
invwhiten!(a, x) # whitening transform inplace, directly updating `x`.
199+
200+
invwhiten!(r, a, x) # write the transformed result to `r`.
201+
202+
unwhiten(a, x) # inverse of whitening transform with `a`.
203+
# `x` can be a vector or a matrix.
195204
#
196205
# Note: If the covariance of `x` is an identity matrix,
197206
# then the covariance of the transformed result is `a`.
@@ -201,6 +210,18 @@ unwhiten(a, x) # inverse of whitening transform. `x` can be a vector or
201210
unwhiten!(a, x) # un-whitening transform inplace, updating `x`.
202211

203212
unwhiten!(r, a, x) # write the transformed result to `r`.
213+
214+
invunwhiten(a, x) # inverse of whitening transform defined by `inv(a)`.
215+
# `x` can be a vector or a matrix.
216+
#
217+
# Note: If the covariance of `x` is an identity matrix,
218+
# then the precision of the transformed result is `a`.
219+
# Note: the un-whitening transform is useful for
220+
# generating Gaussian samples.
221+
222+
invunwhiten!(a, x) # un-whitening transform inplace, updating `x`.
223+
224+
invunwhiten!(r, a, x) # write the transformed result to `r`.
204225
```
205226

206227
### Fallbacks for `AbstractArray`s

src/PDMats.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@ export
1616
dim,
1717
whiten,
1818
whiten!,
19+
invwhiten,
20+
invwhiten!,
1921
unwhiten,
2022
unwhiten!,
23+
invunwhiten,
24+
invunwhiten!,
2125
pdadd,
2226
pdadd!,
2327
quad,

src/chol.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,22 @@ dim(A::Cholesky) = LinearAlgebra.checksquare(A)
3232
# whiten
3333
whiten(A::Cholesky, x::AbstractVecOrMat) = chol_lower(A) \ x
3434
whiten!(A::Cholesky, x::AbstractVecOrMat) = ldiv!(chol_lower(A), x)
35+
invwhiten(A::Cholesky, x::AbstractVecOrMat) = chol_upper(A) * x
36+
invwhiten!(A::Cholesky, x::AbstractVecOrMat) = lmul!(chol_upper(A), x)
3537

3638
# unwhiten
3739
unwhiten(A::Cholesky, x::AbstractVecOrMat) = chol_lower(A) * x
3840
unwhiten!(A::Cholesky, x::AbstractVecOrMat) = lmul!(chol_lower(A), x)
41+
invunwhiten(A::Cholesky, x::AbstractVecOrMat) = chol_upper(A) \ x
42+
invunwhiten!(A::Cholesky, x::AbstractVecOrMat) = ldiv!(chol_upper(A), x)
3943

4044
# 3-argument whiten/unwhiten
4145
for T in (:AbstractVector, :AbstractMatrix)
4246
@eval begin
4347
whiten!(r::$T, A::Cholesky, x::$T) = whiten!(A, copyto!(r, x))
48+
invwhiten!(r::$T, A::Cholesky, x::$T) = invwhiten!(A, copyto!(r, x))
4449
unwhiten!(r::$T, A::Cholesky, x::$T) = unwhiten!(A, copyto!(r, x))
50+
invunwhiten!(r::$T, A::Cholesky, x::$T) = invunwhiten!(A, copyto!(r, x))
4551
end
4652
end
4753

src/generics.jl

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ LinearAlgebra.checksquare(a::AbstractPDMat) = size(a, 1)
3838
whiten!(a::AbstractMatrix, x::AbstractVecOrMat)
3939
whiten!(r::AbstractVecOrMat, a::AbstractPDMat, x::AbstractVecOrMat)
4040
41-
Allocating and in-place versions of the `whiten`ing transform defined by `a` applied to `x`
41+
Allocating and in-place versions of the `whiten`ing transform defined by `a` applied to `x`.
4242
4343
If the covariance of `x` is `a` then the covariance of the result will be `I`.
4444
The name `whiten` indicates that this function transforms a correlated multivariate random
@@ -66,34 +66,80 @@ julia> W * W'
6666
0.0 1.0
6767
```
6868
69-
See also [`unwhiten`](@ref).
69+
`whiten` is the inverse transformation of [`unwhiten`](@ref).
70+
71+
See also [`invwhiten`](@ref) and [`invunwhiten`](ref)
7072
"""
7173
whiten(a::AbstractMatrix{<:Real}, x::AbstractVecOrMat) = whiten(AbstractPDMat(a), x)
7274

75+
"""
76+
invwhiten(a::AbstractMatrix, x::AbstractVecOrMat)
77+
invwhiten!(a::AbstractMatrix, x::AbstractVecOrMat)
78+
invwhiten!(r::AbstractVecOrMat, a::AbstractPDMat, x::AbstractVecOrMat)
79+
80+
Allocating and in-place versions of the `whiten`ing transform defined by `inv(a)` applied to `x`.
81+
82+
If the precision of `x` is `a` then the covariance of the result will be `I`.
83+
The name `invwhiten` indicates that this function transforms a correlated multivariate random
84+
variable to "white noise".
85+
86+
`invwhiten` is the inverse transformation of [`invunwhiten`](@ref).
87+
88+
See also [`whiten`](@ref) and [`unwhiten`](@ref)
89+
"""
90+
invwhiten(a::AbstractMatrix{<:Real}, x::AbstractVecOrMat) = invwhiten(AbstractPDMat(a), x)
91+
7392
"""
7493
unwhiten(a::AbstractMatrix, x::AbstractVecOrMat)
7594
unwhiten!(a::AbstractMatrix, x::AbstractVecOrMat)
7695
unwhiten!(r::AbstractVecOrMat, a::AbstractPDMat, x::AbstractVecOrMat)
7796
78-
Allocating and in-place versions of the `unwhiten`ing transform defined by `a` applied to `x`
97+
Allocating and in-place versions of the `unwhiten`ing transform defined by `a` applied to `x`.
7998
8099
If the covariance of `x` is `I` then the covariance of the result will be `a`.
81100
The name `unwhiten` indicates that this function transforms an uncorrelated "white noise" signal
82101
into a signal with the given covariance.
83102
84103
`unwhiten` is the inverse transformation of [`whiten`](@ref).
104+
105+
See also [`invwhiten`](@ref) and [`invunwhiten`](@ref)
85106
"""
86107
unwhiten(a::AbstractMatrix{<:Real}, x::AbstractVecOrMat) = unwhiten(AbstractPDMat(a), x)
87108

109+
"""
110+
invunwhiten(a::AbstractMatrix, x::AbstractVecOrMat)
111+
invunwhiten!(a::AbstractMatrix, x::AbstractVecOrMat)
112+
invunwhiten!(r::AbstractVecOrMat, a::AbstractPDMat, x::AbstractVecOrMat)
113+
114+
Allocating and in-place versions of the `unwhiten`ing transform defined by `inv(a)` applied to `x`.
115+
116+
If the covariance of `x` is `I` then the precision of the result will be `a`.
117+
The name `invunwhiten` indicates that this function transforms an uncorrelated "white noise" signal
118+
into a signal with the given covariance.
119+
120+
`invunwhiten` is the inverse transformation of [`invwhiten`](@ref).
121+
122+
See also [`whiten`](@ref) and [`unwhiten`](@ref)
123+
"""
124+
invunwhiten(a::AbstractMatrix{<:Real}, x::AbstractVecOrMat) = invunwhiten(AbstractPDMat(a), x)
125+
88126
whiten!(a::AbstractMatrix{<:Real}, x::AbstractVecOrMat) = whiten!(x, a, x)
127+
invwhiten!(a::AbstractMatrix{<:Real}, x::AbstractVecOrMat) = invwhiten!(x, a, x)
89128
unwhiten!(a::AbstractMatrix{<:Real}, x::AbstractVecOrMat) = unwhiten!(x, a, x)
129+
invunwhiten!(a::AbstractMatrix{<:Real}, x::AbstractVecOrMat) = invunwhiten!(x, a, x)
90130

91131
function whiten!(r::AbstractVecOrMat, a::AbstractMatrix{<:Real}, x::AbstractVecOrMat)
92132
return whiten!(r, AbstractPDMat(a), x)
93133
end
134+
function invwhiten!(r::AbstractVecOrMat, a::AbstractMatrix{<:Real}, x::AbstractVecOrMat)
135+
return invwhiten!(r, AbstractPDMat(a), x)
136+
end
94137
function unwhiten!(r::AbstractVecOrMat, a::AbstractMatrix{<:Real}, x::AbstractVecOrMat)
95138
return unwhiten!(r, AbstractPDMat(a), x)
96139
end
140+
function invunwhiten!(r::AbstractVecOrMat, a::AbstractMatrix{<:Real}, x::AbstractVecOrMat)
141+
return invunwhiten!(r, AbstractPDMat(a), x)
142+
end
97143

98144
## quad
99145

src/pdiagmat.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,20 +90,24 @@ function whiten!(r::AbstractVecOrMat, a::PDiagMat, x::AbstractVecOrMat)
9090
@check_argdims a.dim == size(x, 1)
9191
return r .= x ./ sqrt.(a.diag)
9292
end
93+
invwhiten!(r::AbstractVecOrMat, a::PDiagMat, x::AbstractVecOrMat) = unwhiten!(r, a, x)
9394
function unwhiten!(r::AbstractVecOrMat, a::PDiagMat, x::AbstractVecOrMat)
9495
@check_argdims axes(r) == axes(x)
9596
@check_argdims a.dim == size(x, 1)
9697
return r .= x .* sqrt.(a.diag)
9798
end
99+
invunwhiten!(r::AbstractVecOrMat, a::PDiagMat, x::AbstractVecOrMat) = whiten!(r, a, x)
98100

99101
function whiten(a::PDiagMat, x::AbstractVecOrMat)
100102
@check_argdims a.dim == size(x, 1)
101103
return x ./ sqrt.(a.diag)
102104
end
105+
invwhiten(a::PDiagMat, x::AbstractVecOrMat) = unwhiten(a, x)
103106
function unwhiten(a::PDiagMat, x::AbstractVecOrMat)
104107
@check_argdims a.dim == size(x, 1)
105108
return x .* sqrt.(a.diag)
106109
end
110+
invunwhiten(a::PDiagMat, x::AbstractVecOrMat) = whiten(a, x)
107111

108112
### quadratic forms
109113

src/pdmat.jl

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,15 @@ function whiten!(r::AbstractVecOrMat, a::PDMat, x::AbstractVecOrMat)
131131
return ldiv!(r, chol_lower(cholesky(a)), x)
132132
end
133133
end
134+
function invwhiten!(r::AbstractVecOrMat, a::PDMat, x::AbstractVecOrMat)
135+
@check_argdims axes(r) == axes(x)
136+
@check_argdims a.dim == size(x, 1)
137+
if r === x
138+
return lmul!(chol_upper(cholesky(a)), r)
139+
else
140+
return mul!(r, chol_upper(cholesky(a)), x)
141+
end
142+
end
134143
function unwhiten!(r::AbstractVecOrMat, a::PDMat, x::AbstractVecOrMat)
135144
@check_argdims axes(r) == axes(x)
136145
@check_argdims a.dim == size(x, 1)
@@ -140,15 +149,32 @@ function unwhiten!(r::AbstractVecOrMat, a::PDMat, x::AbstractVecOrMat)
140149
return mul!(r, chol_lower(cholesky(a)), x)
141150
end
142151
end
152+
function invunwhiten!(r::AbstractVecOrMat, a::PDMat, x::AbstractVecOrMat)
153+
@check_argdims axes(r) == axes(x)
154+
@check_argdims a.dim == size(x, 1)
155+
if r === x
156+
return ldiv!(chol_upper(cholesky(a)), r)
157+
else
158+
return ldiv!(r, chol_upper(cholesky(a)), x)
159+
end
160+
end
143161

144162
function whiten(a::PDMat, x::AbstractVecOrMat)
145163
@check_argdims a.dim == size(x, 1)
146164
return chol_lower(cholesky(a)) \ x
147165
end
166+
function invwhiten(a::PDMat, x::AbstractVecOrMat)
167+
@check_argdims a.dim == size(x, 1)
168+
return chol_upper(cholesky(a)) * x
169+
end
148170
function unwhiten(a::PDMat, x::AbstractVecOrMat)
149171
@check_argdims a.dim == size(x, 1)
150172
return chol_lower(cholesky(a)) * x
151173
end
174+
function invunwhiten(a::PDMat, x::AbstractVecOrMat)
175+
@check_argdims a.dim == size(x, 1)
176+
return chol_upper(cholesky(a)) \ x
177+
end
152178

153179
## quad/invquad
154180

src/pdsparsemat.jl

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,30 +86,61 @@ function whiten!(r::AbstractVecOrMat, a::PDSparseMat, x::AbstractVecOrMat)
8686
# Can't use `ldiv!` due to missing support in SparseArrays
8787
return copyto!(r, chol_lower(a.chol) \ x)
8888
end
89+
function invwhiten!(r::AbstractVecOrMat, a::PDSparseMat, x::AbstractVecOrMat)
90+
@check_argdims axes(r) == axes(x)
91+
@check_argdims a.dim == size(x, 1)
92+
# `*` and `mul!` are not defined for `UP` factor components,
93+
# so we can't use `chol_upper(C) * x`
94+
# `sparse` is neither defined for `PtL` nor for `UP` nor for `U` factor components
95+
C = cholesky(a)
96+
PtL = sparse(C.L)[C.p, :]
97+
return copyto!(r, PtL' * x)
98+
end
8999

90100
function unwhiten!(r::AbstractVecOrMat, a::PDSparseMat, x::AbstractVecOrMat)
91101
@check_argdims axes(r) == axes(x)
92102
@check_argdims a.dim == size(x, 1)
93103
# `*` is not defined for `PtL` factor components,
94-
# so we can't use `chol_lower(a.chol) * x`
95-
C = a.chol
104+
# so we can't use `chol_lower(C) * x`
105+
# `sparse` is neither defined for `PtL` nor for `UP` nor for `U` factor components
106+
C = cholesky(a)
96107
PtL = sparse(C.L)[C.p, :]
97108
return copyto!(r, PtL * x)
98109
end
110+
function invunwhiten!(r::AbstractVecOrMat, a::PDSparseMat, x::AbstractVecOrMat)
111+
@check_argdims axes(r) == axes(x)
112+
@check_argdims a.dim == size(x, 1)
113+
# Can't use `ldiv!` due to missing support in SparseArrays
114+
return copyto!(r, chol_upper(cholesky(a)) \ x)
115+
end
99116

100117
function whiten(a::PDSparseMat, x::AbstractVecOrMat)
101118
@check_argdims a.dim == size(x, 1)
102119
return chol_lower(cholesky(a)) \ x
103120
end
121+
function invwhiten(a::PDSparseMat, x::AbstractVecOrMat)
122+
@check_argdims a.dim == size(x, 1)
123+
# `*` is not defined for `UP` factor components,
124+
# so we can't use `chol_upper(C) * x`
125+
# `sparse` is neither defined for `PtL` nor for `UP` nor for `U` factor components
126+
C = cholesky(a)
127+
PtL = sparse(C.L)[C.p, :]
128+
return PtL' * x
129+
end
104130

105131
function unwhiten(a::PDSparseMat, x::AbstractVecOrMat)
106132
@check_argdims a.dim == size(x, 1)
107133
# `*` is not defined for `PtL` factor components,
108-
# so we can't use `chol_lower(a.chol) * x`
109-
C = a.chol
134+
# so we can't use `chol_lower(C) * x`
135+
# `sparse` is neither defined for `PtL` nor for `UP` nor for `U` factor components
136+
C = cholesky(a)
110137
PtL = sparse(C.L)[C.p, :]
111138
return PtL * x
112139
end
140+
function invunwhiten(a::PDSparseMat, x::AbstractVecOrMat)
141+
@check_argdims a.dim == size(x, 1)
142+
return chol_upper(cholesky(a)) \ x
143+
end
113144

114145
### quadratic forms
115146

src/scalmat.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,21 +75,25 @@ function whiten!(r::AbstractVecOrMat, a::ScalMat, x::AbstractVecOrMat)
7575
@check_argdims a.dim == size(x, 1)
7676
return ldiv!(r, sqrt(a.value), x)
7777
end
78+
invwhiten!(r::AbstractVecOrMat, a::ScalMat, x::AbstractVecOrMat) = unwhiten!(r, a, x)
7879

7980
function unwhiten!(r::AbstractVecOrMat, a::ScalMat, x::AbstractVecOrMat)
8081
@check_argdims axes(r) == axes(x)
8182
@check_argdims a.dim == size(x, 1)
8283
return mul!(r, x, sqrt(a.value))
8384
end
85+
invunwhiten!(r::AbstractVecOrMat, a::ScalMat, x::AbstractVecOrMat) = whiten!(r, a, x)
8486

8587
function whiten(a::ScalMat, x::AbstractVecOrMat)
8688
@check_argdims a.dim == size(x, 1)
8789
return x / sqrt(a.value)
8890
end
91+
invwhiten(a::ScalMat, x::AbstractVecOrMat) = unwhiten(a, x)
8992
function unwhiten(a::ScalMat, x::AbstractVecOrMat)
9093
@check_argdims a.dim == size(x, 1)
9194
return sqrt(a.value) * x
9295
end
96+
invunwhiten(a::ScalMat, x::AbstractVecOrMat) = whiten(a, x)
9397

9498
### quadratic forms
9599

0 commit comments

Comments
 (0)