19
19
OpTuple = namedtuple ("OpTuple" , ["symbol" , "qn" , "factor" ])
20
20
21
21
22
- def construct_symbolic_mpo (table , factor , algo = "Hopcroft-Karp" ):
22
+ def construct_symbolic_mpo (table , primary_ops , factor , algo = "Hopcroft-Karp" ):
23
23
r"""
24
24
A General Compact (Symbolic) MPO Construction Routine
25
25
@@ -102,17 +102,18 @@ def construct_symbolic_mpo(table, factor, algo="Hopcroft-Karp"):
102
102
The local mpo is the transformation matrix between 0'',1'' to 0'''
103
103
"""
104
104
105
- qn_size = len (table [0 ][0 ].qn )
105
+ qn_size = len (primary_ops [0 ].qn )
106
+
106
107
# Simplest case. Cut to the chase
107
- if len ( table ) == 1 :
108
+ if table . shape [ 0 ] == 1 :
108
109
# The first layer: number of sites. The middle array: in and out virtual bond
109
110
# the 4th layer: operator sums
110
111
mpo : List [np .ndarray [List [Op ]]] = []
111
112
mpoqn = [np .zeros ((1 , qn_size ), dtype = int )]
112
- primary_ops = list (set (table [0 ]))
113
113
op2idx = dict (zip (primary_ops , range (len (primary_ops ))))
114
114
out_ops_list : List [List [OpTuple ]] = [[OpTuple ([0 ], qn = 0 , factor = 1 )]]
115
- for op in table [0 ]:
115
+ for idx in table [0 ]:
116
+ op = primary_ops [idx ]
116
117
mo = np .full ((1 , 1 ), None )
117
118
mo [0 ][0 ] = [op ]
118
119
mpo .append (mo )
@@ -130,14 +131,10 @@ def construct_symbolic_mpo(table, factor, algo="Hopcroft-Karp"):
130
131
return mpo , mpoqn , qntot , qnidx , out_ops_list , primary_ops
131
132
132
133
logger .debug (f"symbolic mpo algorithm: { algo } " )
133
- logger .debug (f"Input operator terms: { len (table )} " )
134
-
135
- table , factor , primary_ops = _transform_table (table , factor )
136
134
137
135
# add the first and last column for convenience
138
136
ta = np .zeros ((table .shape [0 ], 1 ), dtype = np .uint16 )
139
137
table = np .concatenate ((ta , table , ta ), axis = 1 )
140
- logger .debug (f"After combination of the same terms: { table .shape [0 ]} " )
141
138
142
139
# 0 represents the identity symbol. Identity might not present
143
140
# in `primary_ops` but the algorithm still works.
@@ -364,22 +361,38 @@ def _terms_to_table(model: Model, terms: List[Op], const: float):
364
361
365
362
table = []
366
363
factor_list = []
367
-
364
+
365
+ primary_ops_eachsite = []
366
+ primary_ops = []
367
+
368
+ index = 0
369
+
368
370
dummy_table_entry = []
369
371
for b in model .basis :
370
372
if b .multi_dof :
371
373
dof = b .dof [0 ]
372
374
else :
373
375
dof = b .dof
374
376
op = Op .identity (dof , qn_size = model .qn_size )
375
- dummy_table_entry .append (op )
377
+
378
+ primary_ops_eachsite .append ({op :index })
379
+ primary_ops .append (op )
380
+ dummy_table_entry .append (index )
381
+ index += 1
382
+
376
383
for op in terms :
377
384
elem_ops , factor = op .split_elementary (model .dof_to_siteidx )
378
385
table_entry = dummy_table_entry .copy ()
386
+
379
387
for elem_op in elem_ops :
380
388
# it is ensured in `elem_op` every symbol is on the same site
381
389
site_idx = model .dof_to_siteidx [elem_op .dofs [0 ]]
382
- table_entry [site_idx ] = elem_op
390
+ if elem_op not in primary_ops_eachsite [site_idx ].keys ():
391
+ primary_ops_eachsite [site_idx ][elem_op ] = index
392
+ primary_ops .append (elem_op )
393
+ index += 1
394
+ table_entry [site_idx ] = primary_ops_eachsite [site_idx ][elem_op ]
395
+
383
396
table .append (table_entry )
384
397
factor_list .append (factor )
385
398
@@ -389,10 +402,19 @@ def _terms_to_table(model: Model, terms: List[Op], const: float):
389
402
factor_list .append (const )
390
403
table .append (table_entry )
391
404
392
- factor_list = np .array (factor_list )
405
+ factor = np .array (factor_list )
393
406
logger .debug (f"# of operator terms: { len (table )} " )
407
+
408
+ # use np.uint32, np.uint16 to save memory
409
+ max_uint16 = np .iinfo (np .uint16 ).max
410
+ assert len (primary_ops ) < max_uint16
411
+
412
+ table = np .array (table , dtype = np .uint16 )
413
+ logger .debug (f"Input operator terms: { table .shape [0 ]} " )
414
+ table , factor = _deduplicate_table (table , factor )
415
+ logger .debug (f"After combination of the same terms: { table .shape [0 ]} " )
394
416
395
- return table , factor_list
417
+ return table , primary_ops , factor
396
418
397
419
398
420
def _deduplicate_table (table , factor ):
@@ -416,48 +438,6 @@ def _deduplicate_table(table, factor):
416
438
return new_table , factor
417
439
418
440
419
- def _transform_table (table , factor ):
420
- """Transforms the table to integer table and combine duplicate terms."""
421
-
422
- # use np.uint32, np.uint16 to save memory
423
- max_uint16 = np .iinfo (np .uint16 ).max
424
-
425
- # translate the symbolic operator table to an easy to manipulate numpy array
426
- table = np .array (table )
427
- # unique operators with DoF names taken into consideration
428
- # The inclusion of DoF names is necessary for multi-dof basis.
429
- unique_op = OrderedDict .fromkeys (table .ravel ())
430
- # Convert Set(table.ravel()) to List will change the Op order in list, OrderedDict made reproducible
431
- unique_op = list (unique_op .keys ())
432
-
433
- # check the index of different operators could be represented with np.uint16
434
- assert len (unique_op ) < max_uint16
435
-
436
- # Construct mapping from easy-to-manipulate integer to actual Op
437
- primary_ops = list (unique_op )
438
-
439
- op2idx = dict (zip (unique_op , range (len (unique_op ))))
440
- new_table = np .vectorize (op2idx .get )(table ).astype (np .uint16 )
441
-
442
- del unique_op
443
-
444
- if __debug__ :
445
- qn_size = len (table [0 ][0 ].qn )
446
- qn_table = np .array ([[x .qn for x in ta ] for ta in table ])
447
- factor_table = np .array ([[x .factor for x in ta ] for ta in table ])
448
- for idx in range (len (primary_ops )):
449
- coord = np .nonzero (new_table == idx )
450
- # check that op with the same symbol has the same factor and qn
451
- for j in range (qn_size ):
452
- assert np .unique (qn_table [:, :, j ][coord ]).size == 1
453
- assert np .all (factor_table [coord ] == factor_table [coord ][0 ])
454
-
455
- del factor_table , qn_table
456
-
457
- table , factor = _deduplicate_table (new_table , factor )
458
-
459
- return table , factor , primary_ops
460
-
461
441
462
442
# translate the numbers into symbolic Matrix Operator
463
443
def compose_symbolic_mo (in_ops , out_ops , primary_ops ):
0 commit comments