1
1
"""
2
2
AbstractBoundaryConditions
3
3
4
- Boundary conditions for the QTT to use. Use `OpenBoundaryCondtions`` for open
5
- boundaries and `PeriodicBoundaryConditions` for periodic ones.
4
+ Boundary conditions for the QTT to use. Use `OpenBoundaryCondtions`` for open boundaries and `PeriodicBoundaryConditions` for periodic ones.
6
5
"""
7
6
abstract type AbstractBoundaryConditions end
8
7
@@ -35,23 +34,22 @@ Construct and return ITensor matrix product state for the affine transformation
35
34
| | | | | |
36
35
x[1,1] ... x[1,N] x[2,1] ... x[2,N] x[R,1] ... x[R,N]
37
36
37
+ ## Arguments
38
38
39
- Arguments
40
- ---------
41
- - `y`: An `R × M` matrix of ITensor indices, where `y[r,m]` corresponds to
42
- the `r`-th length scale of the `m`-th output variable.
43
- - `x`: An `R × N` matrix of ITensor indices, where `x[r,n]` corresponds to
44
- the `r`-th length scale of the `n`-th input variable.
45
- - `A`: An `M × N` rational matrix representing the linear transformation.
46
- - `b`: An `M` reational vector representing the translation.
47
- - `boundary`: boundary conditions (defaults to `PeriodicBoundaryConditions()`)
39
+ - `y`: An `R × M` matrix of ITensor indices, where `y[r,m]` corresponds to
40
+ the `r`-th length scale of the `m`-th output variable.
41
+ - `x`: An `R × N` matrix of ITensor indices, where `x[r,n]` corresponds to
42
+ the `r`-th length scale of the `n`-th input variable.
43
+ - `A`: An `M × N` rational matrix representing the linear transformation.
44
+ - `b`: An `M` reational vector representing the translation.
45
+ - `boundary`: boundary conditions (defaults to `PeriodicBoundaryConditions()`)
48
46
"""
49
47
function affine_transform_mpo (
50
- y:: AbstractMatrix{<:Index} , x:: AbstractMatrix{<:Index} ,
51
- A:: AbstractMatrix{<:Union{Integer,Rational}} ,
52
- b:: AbstractVector{<:Union{Integer,Rational}} ,
53
- boundary:: AbstractBoundaryConditions = PeriodicBoundaryConditions ()
54
- ):: MPO
48
+ y:: AbstractMatrix{<:Index} , x:: AbstractMatrix{<:Index} ,
49
+ A:: AbstractMatrix{<:Union{Integer,Rational}} ,
50
+ b:: AbstractVector{<:Union{Integer,Rational}} ,
51
+ boundary:: AbstractBoundaryConditions = PeriodicBoundaryConditions ()
52
+ ):: MPO
55
53
R = size (y, 1 )
56
54
M, N = size (A)
57
55
size (x) == (R, N) ||
@@ -65,24 +63,24 @@ function affine_transform_mpo(
65
63
tensors = affine_transform_tensors (R, A, b, boundary)
66
64
67
65
# Create the links
68
- link = [Index (size (tensors[r], 2 ), tags= " link $r " ) for r in 1 : R - 1 ]
66
+ link = [Index (size (tensors[r], 2 ); tags= " link $r " ) for r in 1 : (R - 1 ) ]
69
67
70
68
# Fill the MPO, taking care to not include auxiliary links at the edges
71
69
mpo = MPO (R)
72
70
spin_dims = ntuple (_ -> 2 , M + N)
73
71
if R == 1
74
72
mpo[1 ] = ITensor (reshape (tensors[1 ], spin_dims... ),
75
- (y[1 ,:]. .. , x[1 ,:]. .. ))
73
+ (y[1 , :]. .. , x[1 , :]. .. ))
76
74
elseif R > 1
77
75
mpo[1 ] = ITensor (reshape (tensors[1 ], size (tensors[1 ], 2 ), spin_dims... ),
78
- (link[1 ], y[1 ,:]. .. , x[1 ,:]. .. ))
79
- for r in 2 : R - 1
76
+ (link[1 ], y[1 , :]. .. , x[1 , :]. .. ))
77
+ for r in 2 : (R - 1 )
80
78
newshape = size (tensors[r])[1 : 2 ]. .. , spin_dims...
81
79
mpo[r] = ITensor (reshape (tensors[r], newshape),
82
- (link[r- 1 ], link[r], y[r,:]. .. , x[r,:]. .. ))
80
+ (link[r - 1 ], link[r], y[r, :]. .. , x[r, :]. .. ))
83
81
end
84
82
mpo[R] = ITensor (reshape (tensors[R], size (tensors[R], 1 ), spin_dims... ),
85
- (link[R- 1 ], y[R,:]. .. , x[R,:]. .. ))
83
+ (link[R - 1 ], y[R, :]. .. , x[R, :]. .. ))
86
84
end
87
85
return mpo
88
86
end
@@ -95,51 +93,79 @@ operator corresponding to one of affine transformation `y = A*x + b` with
95
93
rational `A` and `b`
96
94
"""
97
95
function affine_transform_tensors (
98
- R:: Integer , A:: AbstractMatrix{<:Union{Integer,Rational}} ,
99
- b:: AbstractVector{<:Union{Integer,Rational}} ,
100
- boundary:: AbstractBoundaryConditions = PeriodicBoundaryConditions ())
101
- return affine_transform_tensors (
102
- Int (R), _affine_static_args (A, b)... , boundary)
96
+ R:: Integer , A:: AbstractMatrix{<:Union{Integer,Rational}} ,
97
+ b:: AbstractVector{<:Union{Integer,Rational}} ,
98
+ boundary:: AbstractBoundaryConditions = PeriodicBoundaryConditions ())
99
+ tensors, carry = affine_transform_tensors (
100
+ Int (R), _affine_static_args (A, b)... ; boundary)
101
+ return tensors
103
102
end
104
103
105
104
function affine_transform_tensors (
106
- R:: Int , A:: SMatrix{M, N, Int} , b:: SVector{M, Int} , s:: Int ,
107
- boundary:: AbstractBoundaryConditions ) where {M, N}
105
+ R:: Int , A:: SMatrix{M,N, Int} , b:: SVector{M,Int} , s:: Int ;
106
+ boundary:: AbstractBoundaryConditions = PeriodicBoundaryConditions ()) where {M,N}
108
107
# Checks
109
108
0 <= R * max (M, N) <= 8 * sizeof (Int) ||
110
109
throw (DomainError (R, " invalid value of the length R" ))
111
110
112
111
# The output tensors are a collection of matrices, but their first two
113
112
# dimensions (links) vary
114
- tensors = Vector {Array{Bool, 4}} (undef, R)
113
+ tensors = Vector {Array{Bool,4}} (undef, R)
115
114
116
115
# The initial carry is zero
117
- carry = [zero (SVector{M, Int})]
116
+ carry = [zero (SVector{M,Int})]
118
117
for r in R: - 1 : 1
119
118
# Figure out the current bit to add from the shift term and shift
120
- # it out from the array
121
- bcurr = @. Bool (b & 1 )
119
+ bcurr = SVector {M,Int} ((copysign (b_, abs (b_)) & 1 for b_ in b))
122
120
123
121
# Get tensor.
124
122
new_carry, data = affine_transform_core (A, bcurr, s, carry)
125
123
126
124
# XXX do pruning: cut away carries that are dead ends in further
127
125
# tensors
128
126
129
- if r == 1
130
- # For the first tensor, we examine the carry to see which elements
131
- # contribute with which weight
132
- weights = map (c -> carry_weight (c, boundary), new_carry)
133
- tensors[r] = sum (data .* weights, dims= 1 )
134
- else
135
- tensors[r] = data
136
- end
127
+ # if r == 1
128
+ # For the first tensor, we examine the carry to see which elements
129
+ # contribute with which weight
130
+ # weights = map(c -> carry_weight(c, boundary), new_carry)
131
+ # tensors[r] = sum(data .* weights, dims=1)
132
+ # else
133
+ tensors[r] = data
134
+ # end
137
135
138
136
# Set carry to the next value
139
137
carry = new_carry
140
138
b = @. b >> 1
141
139
end
142
- return tensors
140
+
141
+ if boundary == OpenBoundaryConditions () && maximum (abs, b) > 0
142
+ # Extend the tensors to the left until we have no more nonzero bits in b
143
+ # This is equivalent to a larger domain.
144
+ tensors_ext = Array{Bool,4 }[]
145
+ while maximum (abs, b) > 0
146
+ bcurr = SVector {M,Int} ((copysign (b_, abs (b_)) & 1 for b_ in b))
147
+ new_carry, data = affine_transform_core (A, bcurr, s, carry; activebit= false )
148
+ pushfirst! (tensors_ext, data)
149
+
150
+ carry = new_carry
151
+ b = @. b >> 1
152
+ end
153
+
154
+ weights = map (c -> carry_weight (c, boundary), carry)
155
+ tensors_ext[1 ] = sum (tensors_ext[1 ] .* weights; dims= 1 )
156
+ _matrix (x) = reshape (x, size (x, 1 ), size (x, 2 ))
157
+ cap_matrix = reduce (* , _matrix .(tensors_ext))
158
+
159
+ tensors[1 ] = reshape (
160
+ cap_matrix * reshape (tensors[1 ], size (tensors[1 ], 1 ), :),
161
+ size (cap_matrix, 1 ), size (tensors[1 ])[2 : end ]. ..
162
+ )
163
+ else
164
+ weights = map (c -> carry_weight (c, boundary), carry)
165
+ tensors[1 ] = sum (tensors[1 ] .* weights; dims= 1 )
166
+ end
167
+
168
+ return tensors, carry
143
169
end
144
170
145
171
"""
@@ -159,9 +185,9 @@ are the binary input and output vectors, respectively, of the affine transform.
159
185
They are indexed in a "little-endian" fashion.
160
186
"""
161
187
function affine_transform_core (
162
- A:: SMatrix{M, N, Int} , b:: SVector{M, Bool } , s:: Int ,
163
- carry:: AbstractVector{SVector{M, Int}}
164
- ) where {M, N}
188
+ A:: SMatrix{M,N, Int} , b:: SVector{M,Int } , s:: Int ,
189
+ carry:: AbstractVector{SVector{M,Int}} ; activebit = true
190
+ ) where {M,N}
165
191
166
192
# Otherwise we have to reverse the indexing of x and y
167
193
M <= N ||
@@ -174,14 +200,16 @@ function affine_transform_core(
174
200
# for all "incoming" carrys c and all possible bit vectors, x ∈ {0,1}^N.
175
201
# and y ∈ {0,1}^M for some outgoing carry d, which may be negative.
176
202
# We then store this as something like out[d, c, x, y].
177
- out = Dict {SVector{M, Int}, Array{Bool, 3}} ()
203
+ out = Dict {SVector{M,Int},Array{Bool,3}} ()
178
204
sizehint! (out, length (carry))
179
205
180
- all_x = Iterators. product (ntuple (_ -> 0 : 1 , N)... )
181
- all_y = Iterators. product (ntuple (_ -> 0 : 1 , M)... )
206
+ bitrange = activebit ? range (0 , 1 ) : range (0 , 0 )
207
+ all_x = Iterators. product (ntuple (_ -> bitrange, N)... )
208
+ all_y = Iterators. product (ntuple (_ -> bitrange, M)... )
209
+
182
210
for (c_index, c) in enumerate (carry)
183
211
for (x_index, x) in enumerate (all_x)
184
- z = A * SVector {N, Bool} (x) + b + SVector {M, Int} (c)
212
+ z = A * SVector {N,Bool} (x) + b + SVector {M,Int} (c)
185
213
186
214
if isodd (s)
187
215
# if s is odd, then there is a unique y which solves satisfies
@@ -194,7 +222,8 @@ function affine_transform_core(
194
222
195
223
# Store this
196
224
d_mat = get! (out, d) do
197
- return zeros (Bool, length (carry), 1 << M, 1 << N)
225
+ return zeros (
226
+ Bool, length (carry), length (bitrange)^ M, length (bitrange)^ N)
198
227
end
199
228
@inbounds d_mat[c_index, y_index, x_index] = true
200
229
else
@@ -214,7 +243,8 @@ function affine_transform_core(
214
243
215
244
# Store this
216
245
d_mat = get! (out, d) do
217
- return zeros (Bool, length (carry), 1 << M, 1 << N)
246
+ return zeros (
247
+ Bool, length (carry), length (bitrange)^ M, length (bitrange)^ N)
218
248
end
219
249
@inbounds d_mat[c_index, y_index, x_index] = true
220
250
end
@@ -224,8 +254,10 @@ function affine_transform_core(
224
254
225
255
# We translate the dictionary into a vector of carrys (which we can then
226
256
# pass into the next iteration) and a 4-way tensor of output values.
227
- carry_out = Vector {SVector{M, Int}} (undef, length (out))
228
- value_out = Array {Bool, 4} (undef, length (out), length (carry), 1 << M, 1 << N)
257
+ carry_out = Vector {SVector{M,Int}} (undef, length (out))
258
+ # value_out = Array{Bool,4}(undef, length(out), length(carry), 1 << M, 1 << N)
259
+ value_out = Array {Bool,4} (
260
+ undef, length (out), length (carry), length (bitrange)^ M, length (bitrange)^ N)
229
261
for (p_index, p) in enumerate (pairs (out))
230
262
carry_out[p_index] = p. first
231
263
value_out[p_index, :, :, :] .= p. second
@@ -251,16 +283,16 @@ mapped to `x` and `y` as follows:
251
283
`boundary` specifies the type of boundary conditions.
252
284
"""
253
285
function affine_transform_matrix (
254
- R:: Integer , A:: AbstractMatrix{<:Union{Integer,Rational}} ,
255
- b:: AbstractVector{<:Union{Integer,Rational}} ,
256
- boundary:: AbstractBoundaryConditions = PeriodicBoundaryConditions ()
257
- )
286
+ R:: Integer , A:: AbstractMatrix{<:Union{Integer,Rational}} ,
287
+ b:: AbstractVector{<:Union{Integer,Rational}} ,
288
+ boundary:: AbstractBoundaryConditions = PeriodicBoundaryConditions ()
289
+ )
258
290
return affine_transform_matrix (Int (R), _affine_static_args (A, b)... , boundary)
259
291
end
260
292
261
293
function affine_transform_matrix (
262
- R:: Int , A:: SMatrix{M, N, Int} , b:: SVector{M, Int} ,
263
- s:: Int , boundary:: AbstractBoundaryConditions ) where {M, N}
294
+ R:: Int , A:: SMatrix{M,N, Int} , b:: SVector{M,Int} ,
295
+ s:: Int , boundary:: AbstractBoundaryConditions ) where {M,N}
264
296
# Checks
265
297
0 <= R * max (M, N) <= 8 * sizeof (Int) ||
266
298
throw (DomainError (R, " invalid value of the length R" ))
@@ -274,22 +306,22 @@ function affine_transform_matrix(
274
306
all_x = Iterators. product (ntuple (_ -> 0 : mask, N)... )
275
307
all_y = Iterators. product (ntuple (_ -> 0 : mask, M)... )
276
308
for (ix, x) in enumerate (all_x)
277
- v = A * SVector {N, Int} (x) + b
309
+ v = A * SVector {N,Int} (x) + b
278
310
for (iy, y) in enumerate (all_y)
279
- if equiv (v, s * SVector {M, Int} (y), R, boundary)
311
+ if equiv (v, s * SVector {M,Int} (y), R, boundary)
280
312
# println("$y <- $x")
281
313
push! (y_index, iy)
282
314
push! (x_index, ix)
283
315
end
284
316
end
285
317
end
286
318
values = ones (Bool, size (x_index))
287
- return sparse (y_index, x_index, values, 1 << (R* M), 1 << (R* N))
319
+ return sparse (y_index, x_index, values, 1 << (R * M), 1 << (R * N))
288
320
end
289
321
290
322
function affine_mpo_to_matrix (
291
- outsite:: AbstractMatrix{<:Index} , insite:: AbstractMatrix{<:Index} ,
292
- mpo:: MPO )
323
+ outsite:: AbstractMatrix{<:Index} , insite:: AbstractMatrix{<:Index} ,
324
+ mpo:: MPO )
293
325
prev_warn_order = ITensors. disable_warn_order ()
294
326
try
295
327
mpo_contr = reduce (* , mpo)
@@ -298,31 +330,31 @@ function affine_mpo_to_matrix(
298
330
# order (xR, ..., x1, yR, ..., y1) in order to have y = (y1 ... yR)_2
299
331
# once we reshape a column-major array and match the order of the
300
332
# variables in the full matrix.
301
- out_indices = vec (reverse (outsite, dims= 1 ))
302
- in_indices = vec (reverse (insite, dims= 1 ))
333
+ out_indices = vec (reverse (outsite; dims= 1 ))
334
+ in_indices = vec (reverse (insite; dims= 1 ))
303
335
tensor = Array (mpo_contr, out_indices... , in_indices... )
304
336
matrix = reshape (tensor,
305
- 1 << length (out_indices), 1 << length (in_indices))
337
+ 1 << length (out_indices), 1 << length (in_indices))
306
338
return matrix
307
339
finally
308
340
ITensors. set_warn_order (prev_warn_order)
309
341
end
310
342
end
311
343
312
344
function _affine_static_args (A:: AbstractMatrix{<:Union{Integer,Rational}} ,
313
- b:: AbstractVector{<:Union{Integer,Rational}} )
345
+ b:: AbstractVector{<:Union{Integer,Rational}} )
314
346
M, N = size (A)
315
347
size (b, 1 ) == M ||
316
348
throw (ArgumentError (" A and b have incompatible size" ))
317
349
318
350
# Factor out common denominator and pass
319
- denom = lcm (mapreduce (denominator, lcm, A, init= 1 ),
320
- mapreduce (denominator, lcm, b, init= 1 ))
351
+ denom = lcm (mapreduce (denominator, lcm, A; init= 1 ),
352
+ mapreduce (denominator, lcm, b; init= 1 ))
321
353
Ai = @. Int (denom * A)
322
354
bi = @. Int (denom * b)
323
355
324
356
# Construct static matrix
325
- return SMatrix {M, N, Int} (Ai), SVector {M, Int} (bi), denom
357
+ return SMatrix {M,N, Int} (Ai), SVector {M,Int} (bi), denom
326
358
end
327
359
328
360
"""
@@ -337,20 +369,20 @@ even though A is purely integer. In this case, the inverse transformation only
337
369
maps some of the points.
338
370
"""
339
371
function active_to_passive (A:: AbstractMatrix{<:Union{Rational,Integer}} ,
340
- b:: AbstractVector{<:Union{Rational,Integer}} )
372
+ b:: AbstractVector{<:Union{Rational,Integer}} )
341
373
return active_to_passive (Rational .(A), Rational .(b))
342
374
end
343
375
344
376
function active_to_passive (
345
- A:: AbstractMatrix{<:Rational} , b:: AbstractVector{<:Rational} )
377
+ A:: AbstractMatrix{<:Rational} , b:: AbstractVector{<:Rational} )
346
378
m, n = size (A)
347
379
T = [A b; zero (b)' 1 ]
348
380
349
381
# XXX : we do not support pseudo-inverses (LinearAlgebra cannot do
350
382
# this yet over the Rationals).
351
383
Tinv = inv (T)
352
384
Ainv = Tinv[1 : m, 1 : n]
353
- binv = Tinv[1 : m, n+ 1 ]
385
+ binv = Tinv[1 : m, n + 1 ]
354
386
return Ainv, binv
355
387
end
356
388
@@ -363,5 +395,5 @@ a number.
363
395
digits_to_number (v:: AbstractVector{Bool} ) = _digits_to_number (Tuple (v))
364
396
365
397
@inline _digits_to_number (v:: Tuple{} ) = 0
366
- @inline _digits_to_number (v:: Tuple{Bool, Vararg{Bool}} ) =
367
- _digits_to_number (v[ 2 : end ]) << 1 | v[1 ]
398
+ @inline _digits_to_number (v:: Tuple{Bool,Vararg{Bool}} ) = _digits_to_number (v[ 2 : end ]) << 1 |
399
+ v[1 ]
0 commit comments