-
Notifications
You must be signed in to change notification settings - Fork 6
/
identity.jl
200 lines (153 loc) ยท 6.3 KB
/
identity.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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
"""
IdentityMultiple{T} < AbstractMatrix{T} where T
A scalar multiple of the identity matrix of given order and numeric type.
### Fields
- `M` -- uniform scaling operator of type `T`
- `n` -- size of the identity matrix
### Notes
This type can be used to create multiples of the identity of given size. Since
only the multiple and the order are stored, the allocations are minimal.
Internally, the type wraps Julia's lazy multiple of the identity operator,
`UniformScaling`. `IdentityMultiple` subtypes `AbstractMatrix`, hence it can be
used in usual matrix arithmetic and for dispatch on `AbstractMatrix`.
The difference between `UniformScaling` and `IdentityMultiple` is that while the
size of the former is generic, the size of the latter is fixed.
### Examples
Only specifying the matrix size represents an identity matrix:
```jldoctest identitymultiple
julia> using MathematicalSystems: IdentityMultiple
julia> I2 = Id(2)
IdentityMultiple{Float64} of value 1.0 and order 2
julia> I2 + I2
IdentityMultiple{Float64} of value 2.0 and order 2
julia> 4*I2
IdentityMultiple{Float64} of value 4.0 and order 2
```
The scaling (default `1.0`) can be passed as the second argument:
```jldoctest identitymultiple
julia> I2r = Id(2, 1//1)
IdentityMultiple{Rational{Int64}} of value 1//1 and order 2
julia> I2r + I2r
IdentityMultiple{Rational{Int64}} of value 2//1 and order 2
julia> 4*I2r
IdentityMultiple{Rational{Int64}} of value 4//1 and order 2
```
Alternatively, use the constructor passing the `UniformScaling` (`I`):
```jldoctest identitymultiple
julia> using LinearAlgebra
julia> I2 = IdentityMultiple(2.0*I, 2)
IdentityMultiple{Float64} of value 2.0 and order 2
julia> I2r = IdentityMultiple(2//1*I, 2)
IdentityMultiple{Rational{Int64}} of value 2//1 and order 2
```
"""
struct IdentityMultiple{T} <: AbstractMatrix{T}
M::UniformScaling{T}
n::Int
function IdentityMultiple(M::UniformScaling{T}, n::Int) where {T}
if n < 1
throw(ArgumentError("the dimension of `IdentityMultiple` cannot be negative or zero"))
end
return new{T}(M, n)
end
end
Base.IndexStyle(::Type{<:IdentityMultiple}) = IndexLinear()
Base.size(๐ผ::IdentityMultiple) = (๐ผ.n, ๐ผ.n)
function Base.getindex(๐ผ::IdentityMultiple, inds...)
any(idx -> idx > ๐ผ.n, inds) && throw(BoundsError(๐ผ, inds))
return getindex(๐ผ.M, inds...)
end
function Base.getindex(๐ผ::IdentityMultiple{T}, ind) where {T}
if 1 โค ind โค ๐ผ.n^2
return rem(ind - 1, ๐ผ.n + 1) == 0 ? ๐ผ.M.ฮป : zero(T)
else
throw(BoundsError(๐ผ, ind))
end
end
function Base.setindex!(::IdentityMultiple, ::Any, inds...)
return error("cannot store a value in an `IdentityMultiple` because this type is immutable")
end
Base.:(-)(๐ผ::IdentityMultiple) = IdentityMultiple(-๐ผ.M, ๐ผ.n)
Base.:(+)(๐ผ::IdentityMultiple, M::AbstractMatrix) = ๐ผ.M + M
Base.:(+)(M::AbstractMatrix, ๐ผ::IdentityMultiple) = M + ๐ผ.M
Base.:(*)(x::Number, ๐ผ::IdentityMultiple) = IdentityMultiple(x * ๐ผ.M, ๐ผ.n)
Base.:(*)(๐ผ::IdentityMultiple, x::Number) = IdentityMultiple(x * ๐ผ.M, ๐ผ.n)
Base.:(/)(๐ผ::IdentityMultiple, x::Number) = IdentityMultiple(๐ผ.M / x, ๐ผ.n)
function Base.:(*)(๐ผ::IdentityMultiple, v::AbstractVector)
@assert ๐ผ.n == length(v)
return ๐ผ.M.ฮป * v
end
# beside `AbstractMatrix`, we need some disambiguations with LinearAlgebra since v1.6
for M in @static VERSION < v"1.6" ? [:AbstractMatrix] :
(:AbstractMatrix, :Diagonal, :(Transpose{<:Any,<:AbstractVector}),
:(Adjoint{<:Any,<:AbstractVector}), :(LinearAlgebra.AbstractTriangular))
@eval begin
function Base.:(*)(๐ผ::IdentityMultiple, A::$M)
@assert ๐ผ.n == size(A, 1)
return ๐ผ.M.ฮป * A
end
function Base.:(*)(A::$M, ๐ผ::IdentityMultiple)
@assert size(A, 2) == ๐ผ.n
return A * ๐ผ.M.ฮป
end
end
end
# right-division
# beside `AbstractMatrix`, we need some disambiguations with LinearAlgebra since v1.6
for M in @static VERSION < v"1.6" ? [:AbstractMatrix] :
(:AbstractMatrix, :(Transpose{<:Any,<:AbstractVector}),
:(Adjoint{<:Any,<:AbstractVector}))
@eval begin
function Base.:(/)(A::$M, ๐ผ::IdentityMultiple)
@assert size(A, 2) == ๐ผ.n
return A * inv(๐ผ.M.ฮป)
end
end
end
function Base.:(+)(๐ผ1::IdentityMultiple, ๐ผ2::IdentityMultiple)
@assert ๐ผ1.n == ๐ผ2.n
return IdentityMultiple(๐ผ1.M + ๐ผ2.M, ๐ผ1.n)
end
function Base.:(-)(๐ผ1::IdentityMultiple, ๐ผ2::IdentityMultiple)
@assert ๐ผ1.n == ๐ผ2.n
return IdentityMultiple(๐ผ1.M - ๐ผ2.M, ๐ผ1.n)
end
function Base.:(*)(๐ผ1::IdentityMultiple, ๐ผ2::IdentityMultiple)
@assert ๐ผ1.n == ๐ผ2.n
return IdentityMultiple(๐ผ1.M * ๐ผ2.M, ๐ผ1.n)
end
function Base.:(*)(๐ผ::IdentityMultiple{T}, U::UniformScaling{S}) where {T<:Number,S<:Number}
return IdentityMultiple(๐ผ.M.ฮป * U, ๐ผ.n)
end
function Base.:(*)(U::UniformScaling{T}, ๐ผ::IdentityMultiple{S}) where {T<:Number,S<:Number}
return IdentityMultiple(๐ผ.M.ฮป * U, ๐ผ.n)
end
function Base.:(/)(๐ผ::IdentityMultiple{T}, U::UniformScaling{S}) where {T<:Number,S<:Number}
@assert !iszero(U.ฮป)
return IdentityMultiple(๐ผ.M * inv(U.ฮป), ๐ผ.n)
end
function Base.:(/)(U::UniformScaling{T}, ๐ผ::IdentityMultiple{S}) where {T<:Number,S<:Number}
@assert !iszero(๐ผ.M.ฮป)
return IdentityMultiple(U * inv(๐ผ.M.ฮป), ๐ผ.n)
end
function Base.show(io::IO, ::MIME"text/plain", ๐ผ::IdentityMultiple{T}) where {T}
return print(io, "$(typeof(๐ผ)) of value $(๐ผ.M.ฮป) and order $(๐ผ.n)")
end
"""
Id(n::Int, [ฮป]::Number=1.0)
Convenience constructor of an [`IdentityMultiple`](@ref).
### Input
- `n` -- dimension
- `ฮป` -- (optional; default: `1.0`) scaling factor
### Output
An `IdentityMultiple` of the given size and scaling factor.
"""
function Id(n::Int, ฮป::Number=1.0)
return IdentityMultiple(ฮป * I, n)
end
# callable identity matrix given the scaling factor and the size
IdentityMultiple(ฮป::Number, n::Int) = IdentityMultiple(ฮป * I, n)
function LinearAlgebra.Hermitian(๐ผ::IdentityMultiple)
return Hermitian(Diagonal(fill(๐ผ.M.ฮป, ๐ผ.n)))
end
Base.exp(๐ผ::IdentityMultiple) = IdentityMultiple(exp(๐ผ.M.ฮป), ๐ผ.n)