Mercurial > repos > public > sbplib_julia
comparison src/LazyTensors/lazy_tensor_operations.jl @ 995:1ba8a398af9c refactor/lazy_tensors
Rename types
author | Jonatan Werpers <jonatan@werpers.com> |
---|---|
date | Fri, 18 Mar 2022 21:14:47 +0100 |
parents | 55ab7801c45f |
children | 20c376dffe84 |
comparison
equal
deleted
inserted
replaced
994:55ab7801c45f | 995:1ba8a398af9c |
---|---|
1 # TBD: Is there a good way to split this file? | 1 # TBD: Is there a good way to split this file? |
2 | 2 |
3 """ | 3 """ |
4 LazyTensorMappingApplication{T,R,D} <: LazyArray{T,R} | 4 LazyTensorApplication{T,R,D} <: LazyArray{T,R} |
5 | 5 |
6 Struct for lazy application of a TensorMapping. Created using `*`. | 6 Struct for lazy application of a LazyTensor. Created using `*`. |
7 | 7 |
8 Allows the result of a `TensorMapping` applied to a vector to be treated as an `AbstractArray`. | 8 Allows the result of a `LazyTensor` applied to a vector to be treated as an `AbstractArray`. |
9 With a mapping `m` and a vector `v` the LazyTensorMappingApplication object can be created by `m*v`. | 9 With a mapping `m` and a vector `v` the LazyTensorApplication object can be created by `m*v`. |
10 The actual result will be calcualted when indexing into `m*v`. | 10 The actual result will be calcualted when indexing into `m*v`. |
11 """ | 11 """ |
12 struct LazyTensorMappingApplication{T,R,D, TM<:TensorMapping{<:Any,R,D}, AA<:AbstractArray{<:Any,D}} <: LazyArray{T,R} | 12 struct LazyTensorApplication{T,R,D, TM<:LazyTensor{<:Any,R,D}, AA<:AbstractArray{<:Any,D}} <: LazyArray{T,R} |
13 t::TM | 13 t::TM |
14 o::AA | 14 o::AA |
15 | 15 |
16 function LazyTensorMappingApplication(t::TensorMapping{<:Any,R,D}, o::AbstractArray{<:Any,D}) where {R,D} | 16 function LazyTensorApplication(t::LazyTensor{<:Any,R,D}, o::AbstractArray{<:Any,D}) where {R,D} |
17 I = ntuple(i->1, range_dim(t)) | 17 I = ntuple(i->1, range_dim(t)) |
18 T = typeof(apply(t,o,I...)) | 18 T = typeof(apply(t,o,I...)) |
19 return new{T,R,D,typeof(t), typeof(o)}(t,o) | 19 return new{T,R,D,typeof(t), typeof(o)}(t,o) |
20 end | 20 end |
21 end | 21 end |
22 # TODO: Do boundschecking on creation! | 22 # TODO: Do boundschecking on creation! |
23 | 23 |
24 Base.getindex(ta::LazyTensorMappingApplication{T,R}, I::Vararg{Any,R}) where {T,R} = apply(ta.t, ta.o, I...) | 24 Base.getindex(ta::LazyTensorApplication{T,R}, I::Vararg{Any,R}) where {T,R} = apply(ta.t, ta.o, I...) |
25 Base.getindex(ta::LazyTensorMappingApplication{T,1}, I::CartesianIndex{1}) where {T} = apply(ta.t, ta.o, I.I...) # Would otherwise be caught in the previous method. | 25 Base.getindex(ta::LazyTensorApplication{T,1}, I::CartesianIndex{1}) where {T} = apply(ta.t, ta.o, I.I...) # Would otherwise be caught in the previous method. |
26 Base.size(ta::LazyTensorMappingApplication) = range_size(ta.t) | 26 Base.size(ta::LazyTensorApplication) = range_size(ta.t) |
27 # TODO: What else is needed to implement the AbstractArray interface? | 27 # TODO: What else is needed to implement the AbstractArray interface? |
28 | 28 |
29 Base.:*(a::TensorMapping, v::AbstractArray) = LazyTensorMappingApplication(a,v) | 29 Base.:*(a::LazyTensor, v::AbstractArray) = LazyTensorApplication(a,v) |
30 Base.:*(a::TensorMapping, b::TensorMapping) = throw(MethodError(Base.:*,(a,b))) | 30 Base.:*(a::LazyTensor, b::LazyTensor) = throw(MethodError(Base.:*,(a,b))) |
31 Base.:*(a::TensorMapping, args::Union{TensorMapping, AbstractArray}...) = foldr(*,(a,args...)) | 31 Base.:*(a::LazyTensor, args::Union{LazyTensor, AbstractArray}...) = foldr(*,(a,args...)) |
32 | 32 |
33 # # We need the associativity to be a→b→c = a→(b→c), which is the case for '→' | 33 # # We need the associativity to be a→b→c = a→(b→c), which is the case for '→' |
34 # # Should we overload some other infix binary opesrator? | 34 # # Should we overload some other infix binary opesrator? |
35 # →(tm::TensorMapping{T,R,D}, o::AbstractArray{T,D}) where {T,R,D} = LazyTensorMappingApplication(tm,o) | 35 # →(tm::LazyTensor{T,R,D}, o::AbstractArray{T,D}) where {T,R,D} = LazyTensorApplication(tm,o) |
36 # TODO: We need to be really careful about good error messages. | 36 # TODO: We need to be really careful about good error messages. |
37 # For example what happens if you try to multiply LazyTensorMappingApplication with a TensorMapping(wrong order)? | 37 # For example what happens if you try to multiply LazyTensorApplication with a LazyTensor(wrong order)? |
38 | 38 |
39 """ | 39 """ |
40 LazyTensorMappingTranspose{T,R,D} <: TensorMapping{T,D,R} | 40 LazyTensorTranspose{T,R,D} <: LazyTensor{T,D,R} |
41 | 41 |
42 Struct for lazy transpose of a TensorMapping. | 42 Struct for lazy transpose of a LazyTensor. |
43 | 43 |
44 If a mapping implements the the `apply_transpose` method this allows working with | 44 If a mapping implements the the `apply_transpose` method this allows working with |
45 the transpose of mapping `m` by using `m'`. `m'` will work as a regular TensorMapping lazily calling | 45 the transpose of mapping `m` by using `m'`. `m'` will work as a regular LazyTensor lazily calling |
46 the appropriate methods of `m`. | 46 the appropriate methods of `m`. |
47 """ | 47 """ |
48 struct LazyTensorMappingTranspose{T,R,D, TM<:TensorMapping{T,R,D}} <: TensorMapping{T,D,R} | 48 struct LazyTensorTranspose{T,R,D, TM<:LazyTensor{T,R,D}} <: LazyTensor{T,D,R} |
49 tm::TM | 49 tm::TM |
50 end | 50 end |
51 | 51 |
52 # # TBD: Should this be implemented on a type by type basis or through a trait to provide earlier errors? | 52 # # TBD: Should this be implemented on a type by type basis or through a trait to provide earlier errors? |
53 # Jonatan 2020-09-25: Is the problem that you can take the transpose of any TensorMapping even if it doesn't implement `apply_transpose`? | 53 # Jonatan 2020-09-25: Is the problem that you can take the transpose of any LazyTensor even if it doesn't implement `apply_transpose`? |
54 Base.adjoint(tm::TensorMapping) = LazyTensorMappingTranspose(tm) | 54 Base.adjoint(tm::LazyTensor) = LazyTensorTranspose(tm) |
55 Base.adjoint(tmt::LazyTensorMappingTranspose) = tmt.tm | 55 Base.adjoint(tmt::LazyTensorTranspose) = tmt.tm |
56 | 56 |
57 apply(tmt::LazyTensorMappingTranspose{T,R,D}, v::AbstractArray{<:Any,R}, I::Vararg{Any,D}) where {T,R,D} = apply_transpose(tmt.tm, v, I...) | 57 apply(tmt::LazyTensorTranspose{T,R,D}, v::AbstractArray{<:Any,R}, I::Vararg{Any,D}) where {T,R,D} = apply_transpose(tmt.tm, v, I...) |
58 apply_transpose(tmt::LazyTensorMappingTranspose{T,R,D}, v::AbstractArray{<:Any,D}, I::Vararg{Any,R}) where {T,R,D} = apply(tmt.tm, v, I...) | 58 apply_transpose(tmt::LazyTensorTranspose{T,R,D}, v::AbstractArray{<:Any,D}, I::Vararg{Any,R}) where {T,R,D} = apply(tmt.tm, v, I...) |
59 | 59 |
60 range_size(tmt::LazyTensorMappingTranspose) = domain_size(tmt.tm) | 60 range_size(tmt::LazyTensorTranspose) = domain_size(tmt.tm) |
61 domain_size(tmt::LazyTensorMappingTranspose) = range_size(tmt.tm) | 61 domain_size(tmt::LazyTensorTranspose) = range_size(tmt.tm) |
62 | 62 |
63 | 63 |
64 struct LazyTensorMappingBinaryOperation{Op,T,R,D,T1<:TensorMapping{T,R,D},T2<:TensorMapping{T,R,D}} <: TensorMapping{T,D,R} | 64 struct LazyLazyTensorBinaryOperation{Op,T,R,D,T1<:LazyTensor{T,R,D},T2<:LazyTensor{T,R,D}} <: LazyTensor{T,D,R} |
65 tm1::T1 | 65 tm1::T1 |
66 tm2::T2 | 66 tm2::T2 |
67 | 67 |
68 @inline function LazyTensorMappingBinaryOperation{Op,T,R,D}(tm1::T1,tm2::T2) where {Op,T,R,D, T1<:TensorMapping{T,R,D},T2<:TensorMapping{T,R,D}} | 68 @inline function LazyLazyTensorBinaryOperation{Op,T,R,D}(tm1::T1,tm2::T2) where {Op,T,R,D, T1<:LazyTensor{T,R,D},T2<:LazyTensor{T,R,D}} |
69 return new{Op,T,R,D,T1,T2}(tm1,tm2) | 69 return new{Op,T,R,D,T1,T2}(tm1,tm2) |
70 end | 70 end |
71 end | 71 end |
72 # TODO: Boundschecking in constructor. | 72 # TODO: Boundschecking in constructor. |
73 | 73 |
74 apply(tmBinOp::LazyTensorMappingBinaryOperation{:+,T,R,D}, v::AbstractArray{<:Any,D}, I::Vararg{Any,R}) where {T,R,D} = apply(tmBinOp.tm1, v, I...) + apply(tmBinOp.tm2, v, I...) | 74 apply(tmBinOp::LazyLazyTensorBinaryOperation{:+,T,R,D}, v::AbstractArray{<:Any,D}, I::Vararg{Any,R}) where {T,R,D} = apply(tmBinOp.tm1, v, I...) + apply(tmBinOp.tm2, v, I...) |
75 apply(tmBinOp::LazyTensorMappingBinaryOperation{:-,T,R,D}, v::AbstractArray{<:Any,D}, I::Vararg{Any,R}) where {T,R,D} = apply(tmBinOp.tm1, v, I...) - apply(tmBinOp.tm2, v, I...) | 75 apply(tmBinOp::LazyLazyTensorBinaryOperation{:-,T,R,D}, v::AbstractArray{<:Any,D}, I::Vararg{Any,R}) where {T,R,D} = apply(tmBinOp.tm1, v, I...) - apply(tmBinOp.tm2, v, I...) |
76 | 76 |
77 range_size(tmBinOp::LazyTensorMappingBinaryOperation) = range_size(tmBinOp.tm1) | 77 range_size(tmBinOp::LazyLazyTensorBinaryOperation) = range_size(tmBinOp.tm1) |
78 domain_size(tmBinOp::LazyTensorMappingBinaryOperation) = domain_size(tmBinOp.tm1) | 78 domain_size(tmBinOp::LazyLazyTensorBinaryOperation) = domain_size(tmBinOp.tm1) |
79 | 79 |
80 Base.:+(tm1::TensorMapping{T,R,D}, tm2::TensorMapping{T,R,D}) where {T,R,D} = LazyTensorMappingBinaryOperation{:+,T,R,D}(tm1,tm2) | 80 Base.:+(tm1::LazyTensor{T,R,D}, tm2::LazyTensor{T,R,D}) where {T,R,D} = LazyLazyTensorBinaryOperation{:+,T,R,D}(tm1,tm2) |
81 Base.:-(tm1::TensorMapping{T,R,D}, tm2::TensorMapping{T,R,D}) where {T,R,D} = LazyTensorMappingBinaryOperation{:-,T,R,D}(tm1,tm2) | 81 Base.:-(tm1::LazyTensor{T,R,D}, tm2::LazyTensor{T,R,D}) where {T,R,D} = LazyLazyTensorBinaryOperation{:-,T,R,D}(tm1,tm2) |
82 | 82 |
83 """ | 83 """ |
84 TensorMappingComposition{T,R,K,D} | 84 LazyTensorComposition{T,R,K,D} |
85 | 85 |
86 Lazily compose two `TensorMapping`s, so that they can be handled as a single `TensorMapping`. | 86 Lazily compose two `LazyTensor`s, so that they can be handled as a single `LazyTensor`. |
87 """ | 87 """ |
88 struct TensorMappingComposition{T,R,K,D, TM1<:TensorMapping{T,R,K}, TM2<:TensorMapping{T,K,D}} <: TensorMapping{T,R,D} | 88 struct LazyTensorComposition{T,R,K,D, TM1<:LazyTensor{T,R,K}, TM2<:LazyTensor{T,K,D}} <: LazyTensor{T,R,D} |
89 t1::TM1 | 89 t1::TM1 |
90 t2::TM2 | 90 t2::TM2 |
91 | 91 |
92 @inline function TensorMappingComposition(t1::TensorMapping{T,R,K}, t2::TensorMapping{T,K,D}) where {T,R,K,D} | 92 @inline function LazyTensorComposition(t1::LazyTensor{T,R,K}, t2::LazyTensor{T,K,D}) where {T,R,K,D} |
93 @boundscheck check_domain_size(t1, range_size(t2)) | 93 @boundscheck check_domain_size(t1, range_size(t2)) |
94 return new{T,R,K,D, typeof(t1), typeof(t2)}(t1,t2) | 94 return new{T,R,K,D, typeof(t1), typeof(t2)}(t1,t2) |
95 end | 95 end |
96 end | 96 end |
97 | 97 |
98 range_size(tm::TensorMappingComposition) = range_size(tm.t1) | 98 range_size(tm::LazyTensorComposition) = range_size(tm.t1) |
99 domain_size(tm::TensorMappingComposition) = domain_size(tm.t2) | 99 domain_size(tm::LazyTensorComposition) = domain_size(tm.t2) |
100 | 100 |
101 function apply(c::TensorMappingComposition{T,R,K,D}, v::AbstractArray{<:Any,D}, I::Vararg{Any,R}) where {T,R,K,D} | 101 function apply(c::LazyTensorComposition{T,R,K,D}, v::AbstractArray{<:Any,D}, I::Vararg{Any,R}) where {T,R,K,D} |
102 apply(c.t1, c.t2*v, I...) | 102 apply(c.t1, c.t2*v, I...) |
103 end | 103 end |
104 | 104 |
105 function apply_transpose(c::TensorMappingComposition{T,R,K,D}, v::AbstractArray{<:Any,R}, I::Vararg{Any,D}) where {T,R,K,D} | 105 function apply_transpose(c::LazyTensorComposition{T,R,K,D}, v::AbstractArray{<:Any,R}, I::Vararg{Any,D}) where {T,R,K,D} |
106 apply_transpose(c.t2, c.t1'*v, I...) | 106 apply_transpose(c.t2, c.t1'*v, I...) |
107 end | 107 end |
108 | 108 |
109 Base.@propagate_inbounds Base.:∘(s::TensorMapping, t::TensorMapping) = TensorMappingComposition(s,t) | 109 Base.@propagate_inbounds Base.:∘(s::LazyTensor, t::LazyTensor) = LazyTensorComposition(s,t) |
110 | 110 |
111 """ | 111 """ |
112 LazyLinearMap{T,R,D,...}(A, range_indicies, domain_indicies) | 112 LazyLinearMap{T,R,D,...}(A, range_indicies, domain_indicies) |
113 | 113 |
114 TensorMapping defined by the AbstractArray A. `range_indicies` and `domain_indicies` define which indicies of A should | 114 LazyTensor defined by the AbstractArray A. `range_indicies` and `domain_indicies` define which indicies of A should |
115 be considerd the range and domain of the TensorMapping. Each set of indices must be ordered in ascending order. | 115 be considerd the range and domain of the LazyTensor. Each set of indices must be ordered in ascending order. |
116 | 116 |
117 For instance, if A is a m x n matrix, and range_size = (1,), domain_size = (2,), then the LazyLinearMap performs the | 117 For instance, if A is a m x n matrix, and range_size = (1,), domain_size = (2,), then the LazyLinearMap performs the |
118 standard matrix-vector product on vectors of size n. | 118 standard matrix-vector product on vectors of size n. |
119 """ | 119 """ |
120 struct LazyLinearMap{T,R,D, RD, AA<:AbstractArray{T,RD}} <: TensorMapping{T,R,D} | 120 struct LazyLinearMap{T,R,D, RD, AA<:AbstractArray{T,RD}} <: LazyTensor{T,R,D} |
121 A::AA | 121 A::AA |
122 range_indicies::NTuple{R,Int} | 122 range_indicies::NTuple{R,Int} |
123 domain_indicies::NTuple{D,Int} | 123 domain_indicies::NTuple{D,Int} |
124 | 124 |
125 function LazyLinearMap(A::AA, range_indicies::NTuple{R,Int}, domain_indicies::NTuple{D,Int}) where {T,R,D, RD, AA<:AbstractArray{T,RD}} | 125 function LazyLinearMap(A::AA, range_indicies::NTuple{R,Int}, domain_indicies::NTuple{D,Int}) where {T,R,D, RD, AA<:AbstractArray{T,RD}} |
147 apply(LazyLinearMap(llm.A, llm.domain_indicies, llm.range_indicies), v, I...) | 147 apply(LazyLinearMap(llm.A, llm.domain_indicies, llm.range_indicies), v, I...) |
148 end | 148 end |
149 | 149 |
150 | 150 |
151 """ | 151 """ |
152 IdentityMapping{T,D} <: TensorMapping{T,D,D} | 152 IdentityTensor{T,D} <: LazyTensor{T,D,D} |
153 | 153 |
154 The lazy identity TensorMapping for a given size. Usefull for building up higher dimensional tensor mappings from lower | 154 The lazy identity LazyTensor for a given size. Usefull for building up higher dimensional tensor mappings from lower |
155 dimensional ones through outer products. Also used in the Implementation for InflatedTensorMapping. | 155 dimensional ones through outer products. Also used in the Implementation for InflatedLazyTensor. |
156 """ | 156 """ |
157 struct IdentityMapping{T,D} <: TensorMapping{T,D,D} | 157 struct IdentityTensor{T,D} <: LazyTensor{T,D,D} |
158 size::NTuple{D,Int} | 158 size::NTuple{D,Int} |
159 end | 159 end |
160 | 160 |
161 IdentityMapping{T}(size::NTuple{D,Int}) where {T,D} = IdentityMapping{T,D}(size) | 161 IdentityTensor{T}(size::NTuple{D,Int}) where {T,D} = IdentityTensor{T,D}(size) |
162 IdentityMapping{T}(size::Vararg{Int,D}) where {T,D} = IdentityMapping{T,D}(size) | 162 IdentityTensor{T}(size::Vararg{Int,D}) where {T,D} = IdentityTensor{T,D}(size) |
163 IdentityMapping(size::Vararg{Int,D}) where D = IdentityMapping{Float64,D}(size) | 163 IdentityTensor(size::Vararg{Int,D}) where D = IdentityTensor{Float64,D}(size) |
164 | 164 |
165 range_size(tmi::IdentityMapping) = tmi.size | 165 range_size(tmi::IdentityTensor) = tmi.size |
166 domain_size(tmi::IdentityMapping) = tmi.size | 166 domain_size(tmi::IdentityTensor) = tmi.size |
167 | 167 |
168 apply(tmi::IdentityMapping{T,D}, v::AbstractArray{<:Any,D}, I::Vararg{Any,D}) where {T,D} = v[I...] | 168 apply(tmi::IdentityTensor{T,D}, v::AbstractArray{<:Any,D}, I::Vararg{Any,D}) where {T,D} = v[I...] |
169 apply_transpose(tmi::IdentityMapping{T,D}, v::AbstractArray{<:Any,D}, I::Vararg{Any,D}) where {T,D} = v[I...] | 169 apply_transpose(tmi::IdentityTensor{T,D}, v::AbstractArray{<:Any,D}, I::Vararg{Any,D}) where {T,D} = v[I...] |
170 | 170 |
171 """ | 171 """ |
172 Base.:∘(tm, tmi) | 172 Base.:∘(tm, tmi) |
173 Base.:∘(tmi, tm) | 173 Base.:∘(tmi, tm) |
174 | 174 |
175 Composes a `Tensormapping` `tm` with an `IdentityMapping` `tmi`, by returning `tm` | 175 Composes a `Tensormapping` `tm` with an `IdentityTensor` `tmi`, by returning `tm` |
176 """ | 176 """ |
177 @inline function Base.:∘(tm::TensorMapping{T,R,D}, tmi::IdentityMapping{T,D}) where {T,R,D} | 177 @inline function Base.:∘(tm::LazyTensor{T,R,D}, tmi::IdentityTensor{T,D}) where {T,R,D} |
178 @boundscheck check_domain_size(tm, range_size(tmi)) | 178 @boundscheck check_domain_size(tm, range_size(tmi)) |
179 return tm | 179 return tm |
180 end | 180 end |
181 | 181 |
182 @inline function Base.:∘(tmi::IdentityMapping{T,R}, tm::TensorMapping{T,R,D}) where {T,R,D} | 182 @inline function Base.:∘(tmi::IdentityTensor{T,R}, tm::LazyTensor{T,R,D}) where {T,R,D} |
183 @boundscheck check_domain_size(tmi, range_size(tm)) | 183 @boundscheck check_domain_size(tmi, range_size(tm)) |
184 return tm | 184 return tm |
185 end | 185 end |
186 # Specialization for the case where tm is an IdentityMapping. Required to resolve ambiguity. | 186 # Specialization for the case where tm is an IdentityTensor. Required to resolve ambiguity. |
187 @inline function Base.:∘(tm::IdentityMapping{T,D}, tmi::IdentityMapping{T,D}) where {T,D} | 187 @inline function Base.:∘(tm::IdentityTensor{T,D}, tmi::IdentityTensor{T,D}) where {T,D} |
188 @boundscheck check_domain_size(tm, range_size(tmi)) | 188 @boundscheck check_domain_size(tm, range_size(tmi)) |
189 return tmi | 189 return tmi |
190 end | 190 end |
191 # TODO: Implement the above as TensorMappingComposition instead | 191 # TODO: Implement the above as LazyTensorComposition instead |
192 # TODO: Move the operator definitions to one place | 192 # TODO: Move the operator definitions to one place |
193 | 193 |
194 """ | 194 """ |
195 ScalingTensor{T,D} <: TensorMapping{T,D,D} | 195 ScalingTensor{T,D} <: LazyTensor{T,D,D} |
196 | 196 |
197 A lazy tensor that scales its input with `λ`. | 197 A lazy tensor that scales its input with `λ`. |
198 """ | 198 """ |
199 struct ScalingTensor{T,D} <: TensorMapping{T,D,D} | 199 struct ScalingTensor{T,D} <: LazyTensor{T,D,D} |
200 λ::T | 200 λ::T |
201 size::NTuple{D,Int} | 201 size::NTuple{D,Int} |
202 end | 202 end |
203 | 203 |
204 LazyTensors.apply(tm::ScalingTensor{T,D}, v::AbstractArray{<:Any,D}, I::Vararg{Any,D}) where {T,D} = tm.λ*v[I...] | 204 LazyTensors.apply(tm::ScalingTensor{T,D}, v::AbstractArray{<:Any,D}, I::Vararg{Any,D}) where {T,D} = tm.λ*v[I...] |
209 | 209 |
210 # TODO: Rename everything with mapping | 210 # TODO: Rename everything with mapping |
211 # TODO: Remove ScalingOperator from tests | 211 # TODO: Remove ScalingOperator from tests |
212 | 212 |
213 """ | 213 """ |
214 InflatedTensorMapping{T,R,D} <: TensorMapping{T,R,D} | 214 InflatedLazyTensor{T,R,D} <: LazyTensor{T,R,D} |
215 | 215 |
216 An inflated `TensorMapping` with dimensions added before and afer its actual dimensions. | 216 An inflated `LazyTensor` with dimensions added before and afer its actual dimensions. |
217 """ | 217 """ |
218 struct InflatedTensorMapping{T,R,D,D_before,R_middle,D_middle,D_after, TM<:TensorMapping{T,R_middle,D_middle}} <: TensorMapping{T,R,D} | 218 struct InflatedLazyTensor{T,R,D,D_before,R_middle,D_middle,D_after, TM<:LazyTensor{T,R_middle,D_middle}} <: LazyTensor{T,R,D} |
219 before::IdentityMapping{T,D_before} | 219 before::IdentityTensor{T,D_before} |
220 tm::TM | 220 tm::TM |
221 after::IdentityMapping{T,D_after} | 221 after::IdentityTensor{T,D_after} |
222 | 222 |
223 function InflatedTensorMapping(before, tm::TensorMapping{T}, after) where T | 223 function InflatedLazyTensor(before, tm::LazyTensor{T}, after) where T |
224 R_before = range_dim(before) | 224 R_before = range_dim(before) |
225 R_middle = range_dim(tm) | 225 R_middle = range_dim(tm) |
226 R_after = range_dim(after) | 226 R_after = range_dim(after) |
227 R = R_before+R_middle+R_after | 227 R = R_before+R_middle+R_after |
228 | 228 |
232 D = D_before+D_middle+D_after | 232 D = D_before+D_middle+D_after |
233 return new{T,R,D,D_before,R_middle,D_middle,D_after, typeof(tm)}(before, tm, after) | 233 return new{T,R,D,D_before,R_middle,D_middle,D_after, typeof(tm)}(before, tm, after) |
234 end | 234 end |
235 end | 235 end |
236 """ | 236 """ |
237 InflatedTensorMapping(before, tm, after) | 237 InflatedLazyTensor(before, tm, after) |
238 InflatedTensorMapping(before,tm) | 238 InflatedLazyTensor(before,tm) |
239 InflatedTensorMapping(tm,after) | 239 InflatedLazyTensor(tm,after) |
240 | 240 |
241 The outer product of `before`, `tm` and `after`, where `before` and `after` are `IdentityMapping`s. | 241 The outer product of `before`, `tm` and `after`, where `before` and `after` are `IdentityTensor`s. |
242 | 242 |
243 If one of `before` or `after` is left out, a 0-dimensional `IdentityMapping` is used as the default value. | 243 If one of `before` or `after` is left out, a 0-dimensional `IdentityTensor` is used as the default value. |
244 | 244 |
245 If `tm` already is an `InflatedTensorMapping`, `before` and `after` will be extended instead of | 245 If `tm` already is an `InflatedLazyTensor`, `before` and `after` will be extended instead of |
246 creating a nested `InflatedTensorMapping`. | 246 creating a nested `InflatedLazyTensor`. |
247 """ | 247 """ |
248 InflatedTensorMapping(::IdentityMapping, ::TensorMapping, ::IdentityMapping) | 248 InflatedLazyTensor(::IdentityTensor, ::LazyTensor, ::IdentityTensor) |
249 | 249 |
250 function InflatedTensorMapping(before, itm::InflatedTensorMapping, after) | 250 function InflatedLazyTensor(before, itm::InflatedLazyTensor, after) |
251 return InflatedTensorMapping( | 251 return InflatedLazyTensor( |
252 IdentityMapping(before.size..., itm.before.size...), | 252 IdentityTensor(before.size..., itm.before.size...), |
253 itm.tm, | 253 itm.tm, |
254 IdentityMapping(itm.after.size..., after.size...), | 254 IdentityTensor(itm.after.size..., after.size...), |
255 ) | 255 ) |
256 end | 256 end |
257 | 257 |
258 InflatedTensorMapping(before::IdentityMapping, tm::TensorMapping{T}) where T = InflatedTensorMapping(before,tm,IdentityMapping{T}()) | 258 InflatedLazyTensor(before::IdentityTensor, tm::LazyTensor{T}) where T = InflatedLazyTensor(before,tm,IdentityTensor{T}()) |
259 InflatedTensorMapping(tm::TensorMapping{T}, after::IdentityMapping) where T = InflatedTensorMapping(IdentityMapping{T}(),tm,after) | 259 InflatedLazyTensor(tm::LazyTensor{T}, after::IdentityTensor) where T = InflatedLazyTensor(IdentityTensor{T}(),tm,after) |
260 # Resolve ambiguity between the two previous methods | 260 # Resolve ambiguity between the two previous methods |
261 InflatedTensorMapping(I1::IdentityMapping{T}, I2::IdentityMapping{T}) where T = InflatedTensorMapping(I1,I2,IdentityMapping{T}()) | 261 InflatedLazyTensor(I1::IdentityTensor{T}, I2::IdentityTensor{T}) where T = InflatedLazyTensor(I1,I2,IdentityTensor{T}()) |
262 | 262 |
263 # TODO: Implement some pretty printing in terms of ⊗. E.g InflatedTensorMapping(I(3),B,I(2)) -> I(3)⊗B⊗I(2) | 263 # TODO: Implement some pretty printing in terms of ⊗. E.g InflatedLazyTensor(I(3),B,I(2)) -> I(3)⊗B⊗I(2) |
264 | 264 |
265 function range_size(itm::InflatedTensorMapping) | 265 function range_size(itm::InflatedLazyTensor) |
266 return flatten_tuple( | 266 return flatten_tuple( |
267 range_size(itm.before), | 267 range_size(itm.before), |
268 range_size(itm.tm), | 268 range_size(itm.tm), |
269 range_size(itm.after), | 269 range_size(itm.after), |
270 ) | 270 ) |
271 end | 271 end |
272 | 272 |
273 function domain_size(itm::InflatedTensorMapping) | 273 function domain_size(itm::InflatedLazyTensor) |
274 return flatten_tuple( | 274 return flatten_tuple( |
275 domain_size(itm.before), | 275 domain_size(itm.before), |
276 domain_size(itm.tm), | 276 domain_size(itm.tm), |
277 domain_size(itm.after), | 277 domain_size(itm.after), |
278 ) | 278 ) |
279 end | 279 end |
280 | 280 |
281 function apply(itm::InflatedTensorMapping{T,R,D}, v::AbstractArray{<:Any,D}, I::Vararg{Any,R}) where {T,R,D} | 281 function apply(itm::InflatedLazyTensor{T,R,D}, v::AbstractArray{<:Any,D}, I::Vararg{Any,R}) where {T,R,D} |
282 dim_before = range_dim(itm.before) | 282 dim_before = range_dim(itm.before) |
283 dim_domain = domain_dim(itm.tm) | 283 dim_domain = domain_dim(itm.tm) |
284 dim_range = range_dim(itm.tm) | 284 dim_range = range_dim(itm.tm) |
285 dim_after = range_dim(itm.after) | 285 dim_after = range_dim(itm.after) |
286 | 286 |
288 | 288 |
289 v_inner = view(v, view_index...) | 289 v_inner = view(v, view_index...) |
290 return apply(itm.tm, v_inner, inner_index...) | 290 return apply(itm.tm, v_inner, inner_index...) |
291 end | 291 end |
292 | 292 |
293 function apply_transpose(itm::InflatedTensorMapping{T,R,D}, v::AbstractArray{<:Any,R}, I::Vararg{Any,D}) where {T,R,D} | 293 function apply_transpose(itm::InflatedLazyTensor{T,R,D}, v::AbstractArray{<:Any,R}, I::Vararg{Any,D}) where {T,R,D} |
294 dim_before = range_dim(itm.before) | 294 dim_before = range_dim(itm.before) |
295 dim_domain = domain_dim(itm.tm) | 295 dim_domain = domain_dim(itm.tm) |
296 dim_range = range_dim(itm.tm) | 296 dim_range = range_dim(itm.tm) |
297 dim_after = range_dim(itm.after) | 297 dim_after = range_dim(itm.after) |
298 | 298 |
381 flatten_tuple(ts::Vararg) = flatten_tuple(ts) | 381 flatten_tuple(ts::Vararg) = flatten_tuple(ts) |
382 | 382 |
383 @doc raw""" | 383 @doc raw""" |
384 LazyOuterProduct(tms...) | 384 LazyOuterProduct(tms...) |
385 | 385 |
386 Creates a `TensorMappingComposition` for the outerproduct of `tms...`. | 386 Creates a `LazyTensorComposition` for the outerproduct of `tms...`. |
387 This is done by separating the outer product into regular products of outer products involving only identity mappings and one non-identity mapping. | 387 This is done by separating the outer product into regular products of outer products involving only identity mappings and one non-identity mapping. |
388 | 388 |
389 First let | 389 First let |
390 ```math | 390 ```math |
391 \begin{aligned} | 391 \begin{aligned} |
417 (A⊗B⊗C)v = [(A⊗I_{|M|}⊗I_{|P|}) [(I_{|J|}⊗B⊗I_{|P|}) [(I_{|J|}⊗I_{|N|}⊗C)v]]] | 417 (A⊗B⊗C)v = [(A⊗I_{|M|}⊗I_{|P|}) [(I_{|J|}⊗B⊗I_{|P|}) [(I_{|J|}⊗I_{|N|}⊗C)v]]] |
418 ``` | 418 ``` |
419 """ | 419 """ |
420 function LazyOuterProduct end | 420 function LazyOuterProduct end |
421 | 421 |
422 function LazyOuterProduct(tm1::TensorMapping{T}, tm2::TensorMapping{T}) where T | 422 function LazyOuterProduct(tm1::LazyTensor{T}, tm2::LazyTensor{T}) where T |
423 itm1 = InflatedTensorMapping(tm1, IdentityMapping{T}(range_size(tm2))) | 423 itm1 = InflatedLazyTensor(tm1, IdentityTensor{T}(range_size(tm2))) |
424 itm2 = InflatedTensorMapping(IdentityMapping{T}(domain_size(tm1)),tm2) | 424 itm2 = InflatedLazyTensor(IdentityTensor{T}(domain_size(tm1)),tm2) |
425 | 425 |
426 return itm1∘itm2 | 426 return itm1∘itm2 |
427 end | 427 end |
428 | 428 |
429 LazyOuterProduct(t1::IdentityMapping{T}, t2::IdentityMapping{T}) where T = IdentityMapping{T}(t1.size...,t2.size...) | 429 LazyOuterProduct(t1::IdentityTensor{T}, t2::IdentityTensor{T}) where T = IdentityTensor{T}(t1.size...,t2.size...) |
430 LazyOuterProduct(t1::TensorMapping, t2::IdentityMapping) = InflatedTensorMapping(t1, t2) | 430 LazyOuterProduct(t1::LazyTensor, t2::IdentityTensor) = InflatedLazyTensor(t1, t2) |
431 LazyOuterProduct(t1::IdentityMapping, t2::TensorMapping) = InflatedTensorMapping(t1, t2) | 431 LazyOuterProduct(t1::IdentityTensor, t2::LazyTensor) = InflatedLazyTensor(t1, t2) |
432 | 432 |
433 LazyOuterProduct(tms::Vararg{TensorMapping}) = foldl(LazyOuterProduct, tms) | 433 LazyOuterProduct(tms::Vararg{LazyTensor}) = foldl(LazyOuterProduct, tms) |
434 | 434 |
435 ⊗(a::TensorMapping, b::TensorMapping) = LazyOuterProduct(a,b) | 435 ⊗(a::LazyTensor, b::LazyTensor) = LazyOuterProduct(a,b) |
436 | 436 |
437 | 437 |
438 function check_domain_size(tm::TensorMapping, sz) | 438 function check_domain_size(tm::LazyTensor, sz) |
439 if domain_size(tm) != sz | 439 if domain_size(tm) != sz |
440 throw(SizeMismatch(tm,sz)) | 440 throw(SizeMismatch(tm,sz)) |
441 end | 441 end |
442 end | 442 end |
443 | 443 |
444 struct SizeMismatch <: Exception | 444 struct SizeMismatch <: Exception |
445 tm::TensorMapping | 445 tm::LazyTensor |
446 sz | 446 sz |
447 end | 447 end |
448 | 448 |
449 function Base.showerror(io::IO, err::SizeMismatch) | 449 function Base.showerror(io::IO, err::SizeMismatch) |
450 print(io, "SizeMismatch: ") | 450 print(io, "SizeMismatch: ") |
451 print(io, "domain size $(domain_size(err.tm)) of TensorMapping not matching size $(err.sz)") | 451 print(io, "domain size $(domain_size(err.tm)) of LazyTensor not matching size $(err.sz)") |
452 end | 452 end |