-
Notifications
You must be signed in to change notification settings - Fork 34
/
Copy pathutils.jl
140 lines (111 loc) · 4.12 KB
/
utils.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# `permutedims` seems to work better with AD (cf. KernelFunctions.jl)
aT_b(a::AbstractVector{<:Real}, b::AbstractMatrix{<:Real}) = permutedims(a) * b
# `permutedims` can't be used here since scalar output is desired
aT_b(a::AbstractVector{<:Real}, b::AbstractVector{<:Real}) = dot(a, b)
# flatten arrays with fallback for scalars
_vec(x::AbstractArray{<:Real}) = vec(x)
_vec(x::Real) = x
# # Because `ReverseDiff` does not play well with structural matrices.
lower_triangular(A::AbstractMatrix) = convert(typeof(A), LowerTriangular(A))
upper_triangular(A::AbstractMatrix) = convert(typeof(A), UpperTriangular(A))
pd_from_lower(X) = LowerTriangular(X) * LowerTriangular(X)'
pd_from_upper(X) = UpperTriangular(X)' * UpperTriangular(X)
cholesky_factor(X::AbstractMatrix) = cholesky_factor(cholesky(Hermitian(X)))
cholesky_factor(X::Cholesky) = X.U
cholesky_factor(X::UpperTriangular) = X
cholesky_factor(X::LowerTriangular) = X
"""
triu_mask(X::AbstractMatrix, k::Int)
Return a mask for elements of `X` above the `k`th diagonal.
"""
function triu_mask(X::AbstractMatrix, k::Int)
# Ensure that we're working with a square matrix.
LinearAlgebra.checksquare(X)
# Using `similar` allows us to respect device of array, etc., e.g. `CuArray`.
m = similar(X, Bool)
return triu!(fill!(m, true), k)
end
ChainRulesCore.@non_differentiable triu_mask(X::AbstractMatrix, k::Int)
_triu_to_vec(X::AbstractMatrix{<:Real}, k::Int) = X[triu_mask(X, k)]
function update_triu_from_vec!(
vals::AbstractVector{<:Real}, k::Int, X::AbstractMatrix{<:Real}
)
# Ensure that we're working with one-based indexing.
# `triu` requires this too.
LinearAlgebra.require_one_based_indexing(X)
# Set the values.
idx = 1
m, n = size(X)
for j in 1:n
for i in 1:min(j - k, m)
X[i, j] = vals[idx]
idx += 1
end
end
return X
end
function update_triu_from_vec(vals::AbstractVector{<:Real}, k::Int, dim::Int)
X = similar(vals, dim, dim)
# TODO: Do we need this?
fill!(X, 0)
return update_triu_from_vec!(vals, k, X)
end
function ChainRulesCore.rrule(
::typeof(update_triu_from_vec), x::AbstractVector{<:Real}, k::Int, dim::Int
)
function update_triu_from_vec_pullback(ΔX)
return (
ChainRulesCore.NoTangent(),
_triu_to_vec(ChainRulesCore.unthunk(ΔX), k),
ChainRulesCore.NoTangent(),
ChainRulesCore.NoTangent(),
)
end
return update_triu_from_vec(x, k, dim), update_triu_from_vec_pullback
end
# n * (n - 1) / 2 = d
# ⟺ n^2 - n - 2d = 0
# ⟹ n = (1 + sqrt(1 + 8d)) / 2
_triu1_dim_from_length(d) = (1 + isqrt(1 + 8d)) ÷ 2
"""
triu1_to_vec(X::AbstractMatrix{<:Real})
Extracts elements from upper triangle of `X` with offset `1` and returns them as a vector.
"""
triu1_to_vec(X::AbstractMatrix) = _triu_to_vec(X, 1)
inverse(::typeof(triu1_to_vec)) = vec_to_triu1
"""
vec_to_triu1(x::AbstractVector{<:Real})
Constructs a matrix from a vector `x` by filling the upper triangle with offset `1`.
"""
function vec_to_triu1(x::AbstractVector)
n = _triu1_dim_from_length(length(x))
X = update_triu_from_vec(x, 1, n)
return upper_triangular(X)
end
inverse(::typeof(vec_to_triu1)) = triu1_to_vec
function vec_to_triu1_row_index(idx)
# Assumes that vector was saved in a column-major order
# and that vector is one-based indexed.
M = _triu1_dim_from_length(idx - 1)
return idx - (M * (M - 1) ÷ 2)
end
# Triangular matrix with diagonals.
# (n^2 + n) / 2 = d
# ⟺ n² + n - 2d = 0
# ⟺ n = (-1 + sqrt(1 + 8d)) / 2
_triu_dim_from_length(d) = (-1 + isqrt(1 + 8 * d)) ÷ 2
"""
triu_to_vec(X::AbstractMatrix{<:Real})
Extracts elements from upper triangle of `X` and returns them as a vector.
"""
triu_to_vec(X::AbstractMatrix) = _triu_to_vec(X, 0)
"""
vec_to_triu(x::AbstractVector{<:Real})
Constructs a matrix from a vector `x` by filling the upper triangle.
"""
function vec_to_triu(x::AbstractVector)
n = _triu_dim_from_length(length(x))
X = update_triu_from_vec(x, 0, n)
return upper_triangular(X)
end
inverse(::typeof(vec_to_triu)) = triu_to_vec