1313"""Module to compute Trotter errors in the plane-wave dual basis."""
1414from __future__ import absolute_import
1515from future .utils import iteritems , itervalues
16- from math import sqrt
1716
1817import numpy
1918
@@ -261,7 +260,7 @@ def trivially_double_commutes_dual_basis(term_a, term_b, term_c):
261260
262261
263262def dual_basis_error_operator (terms , indices = None , is_hopping_operator = None ,
264- jellium_only = False ):
263+ jellium_only = False , verbose = False ):
265264 """Determine the difference between the exact generator of unitary
266265 evolution and the approximate generator given by the second-order
267266 Trotter-Suzuki expansion.
@@ -275,6 +274,7 @@ def dual_basis_error_operator(terms, indices=None, is_hopping_operator=None,
275274 rather than the full dual basis Hamiltonian (i.e. whether
276275 c_i = c for all number operators i^ i, or whether they
277276 depend on i as is possible in the general case).
277+ verbose: Whether to print percentage progress.
278278
279279 Returns:
280280 The difference between the true and effective generators of time
@@ -284,9 +284,18 @@ def dual_basis_error_operator(terms, indices=None, is_hopping_operator=None,
284284 Size Required for Accurate Quantum Simulation of Quantum Chemistry".
285285 """
286286 more_info = bool (indices )
287+ n_terms = len (terms )
288+
289+ if verbose :
290+ import time
291+ start = time .time ()
287292
288293 error_operator = FermionOperator .zero ()
289- for beta in range (len (terms )):
294+ for beta in range (n_terms ):
295+ if verbose and beta % (n_terms // 30 ) == 0 :
296+ print ('%4.3f percent done in' % (
297+ (float (beta ) / n_terms ) ** 3 * 100 ), time .time () - start )
298+
290299 for alpha in range (beta + 1 ):
291300 for alpha_prime in range (beta ):
292301 # If we have pre-computed info on indices, use it to determine
@@ -326,7 +335,7 @@ def dual_basis_error_operator(terms, indices=None, is_hopping_operator=None,
326335
327336
328337def dual_basis_error_bound (terms , indices = None , is_hopping_operator = None ,
329- jellium_only = False ):
338+ jellium_only = False , verbose = False ):
330339 """Numerically upper bound the error in the ground state energy
331340 for the second-order Trotter-Suzuki expansion.
332341
@@ -339,6 +348,7 @@ def dual_basis_error_bound(terms, indices=None, is_hopping_operator=None,
339348 rather than the full dual basis Hamiltonian (i.e. whether
340349 c_i = c for all number operators i^ i, or whether they
341350 depend on i as is possible in the general case).
351+ verbose: Whether to print percentage progress.
342352
343353 Returns:
344354 A float upper bound on norm of error in the ground state energy.
@@ -350,19 +360,22 @@ def dual_basis_error_bound(terms, indices=None, is_hopping_operator=None,
350360 """
351361 # Return the 1-norm of the error operator (upper bound on error).
352362 return numpy .sum (numpy .absolute (list (dual_basis_error_operator (
353- terms , indices , is_hopping_operator , jellium_only ).terms .values ())))
363+ terms , indices , is_hopping_operator ,
364+ jellium_only , verbose ).terms .values ())))
354365
355366
356- def ordered_dual_basis_terms_grouped_by_type_with_info (dual_basis_hamiltonian ):
367+ def simulation_ordered_grouped_dual_basis_terms_with_info (
368+ dual_basis_hamiltonian , input_ordering = None ):
357369 """Give terms from the dual basis Hamiltonian in simulated order.
358370
359- Roughly uses the simulation ordering, grouping terms into hopping
371+ Uses the simulation ordering, grouping terms into hopping
360372 (i^ j + j^ i) and number (i^j^ i j + c_i i^ i + c_j j^ j) operators.
361373 Pre-computes term information (indices each operator acts on, as
362374 well as whether each operator is a hopping operator.
363375
364376 Args:
365377 dual_basis_hamiltonian (FermionOperator): The Hamiltonian.
378+ input_ordering (list): The initial Jordan-Wigner canonical order.
366379
367380 Returns:
368381 A 3-tuple of terms from the plane-wave dual basis Hamiltonian in
@@ -371,99 +384,91 @@ def ordered_dual_basis_terms_grouped_by_type_with_info(dual_basis_hamiltonian):
371384 """
372385 zero = FermionOperator .zero ()
373386 hamiltonian = dual_basis_hamiltonian
374-
375387 n_qubits = count_qubits (hamiltonian )
376388
377389 ordered_terms = []
378390 ordered_indices = []
379391 ordered_is_hopping_operator = []
380392
381- # Number of times the two-number term i^ j^ i j appears for given i
382- # (stored in the ith position of the list).
383- two_number_operator_appearances = [0 ] * n_qubits
384-
385- for i in range (n_qubits ):
386- for j in range (n_qubits ):
387- two_number_action = ((i , 1 ), (j , 1 ), (i , 0 ), (j , 0 ))
388- if hamiltonian .terms .get (two_number_action ):
389- # Increment the number of times we've found two-number
390- # operators containing i and j.
391- two_number_operator_appearances [i ] += 1
392- two_number_operator_appearances [j ] += 1
393-
394- # Iterate over the different possible first qubit indices.
395- for i in range (n_qubits ):
396- # Iterate over possible offsets. Each stage of the algorithm
397- # simulates a different offset.
398- for j in range (1 , n_qubits ):
399- # The index_left is the low index, index_right is high.
400- index_left = min (i , (i + j ) % n_qubits )
401- index_right = max (i , (i + j ) % n_qubits )
402-
403- # Operators of the hopping term l^ r + r^ l.
404- hopping_action1 = ((index_left , 1 ), (index_right , 0 ))
405- hopping_action2 = ((index_right , 1 ), (index_left , 0 ))
406-
407- # Operators of the two-number term r^ l^ r l (after normal-
408- # ordering).
409- two_number_action = ((index_right , 1 ), (index_left , 1 ),
410- (index_right , 0 ), (index_left , 0 ))
411-
412- # Single-number terms l^ l and r^ r.
413- left_number_action = ((index_left , 1 ), (index_left , 0 ))
414- right_number_action = ((index_right , 1 ), (index_right , 0 ))
415-
416- # Calculate the hopping operator in the Hamiltonian.
417- hopping_operator = (
418- FermionOperator (hopping_action1 ,
419- hamiltonian .terms .get (hopping_action1 , 0.0 )) +
420- FermionOperator (hopping_action2 ,
421- hamiltonian .terms .get (hopping_action2 , 0.0 )))
422- hopping_operator .compress ()
423- # Divide by two to avoid double-counting the operator.
424- hopping_operator /= 2.0
425-
426- # Calculate the two-number operator in the Hamiltonian.
427- number_operator = FermionOperator (two_number_action ,
428- hamiltonian .terms .get (
429- two_number_action , 0.0 ))
430- # If it's zero, we have nothing to simulate at this stage;
431- # otherwise, group l^ l and r^ r with it.
432- if not number_operator .isclose (zero ):
433- number_operator += (
434- FermionOperator (
435- left_number_action ,
436- hamiltonian .terms .get (left_number_action , 0.0 ) /
437- two_number_operator_appearances [index_left ]) +
438- FermionOperator (
439- right_number_action ,
440- hamiltonian .terms .get (right_number_action , 0.0 ) /
441- two_number_operator_appearances [index_right ]))
442- number_operator .compress ()
443- # Divide by two to avoid double-counting.
444- number_operator /= 2.0
393+ # If no input mode ordering is specified, default to range(n_qubits).
394+ if not input_ordering :
395+ input_ordering = list (range (n_qubits ))
396+
397+ # Half a second-order Trotter step reverses the input ordering: this tells
398+ # us how much we need to include in the ordered list of terms.
399+ final_ordering = list (reversed (input_ordering ))
400+
401+ # Follow odd-even transposition sort. In alternating steps, swap each even
402+ # qubits with the odd qubit to its right, and in the next step swap each
403+ # the odd qubits with the even qubit to its right. Do this until the input
404+ # ordering has been reversed.
405+ odd = 0
406+ while input_ordering != final_ordering :
407+ for i in range (odd , n_qubits - 1 , 2 ):
408+ # Always keep the max on the left to avoid having to normal order.
409+ left = max (input_ordering [i ], input_ordering [i + 1 ])
410+ right = min (input_ordering [i ], input_ordering [i + 1 ])
411+
412+ # Calculate the hopping operators in the Hamiltonian.
413+ left_hopping_operator = FermionOperator (
414+ ((left , 1 ), (right , 0 )), hamiltonian .terms .get (
415+ ((left , 1 ), (right , 0 )), 0.0 ))
416+ right_hopping_operator = FermionOperator (
417+ ((right , 1 ), (left , 0 )), hamiltonian .terms .get (
418+ ((right , 1 ), (left , 0 )), 0.0 ))
419+
420+ # Calculate the two-number operator l^ r^ l r in the Hamiltonian.
421+ two_number_operator = FermionOperator (
422+ ((left , 1 ), (right , 1 ), (left , 0 ), (right , 0 )),
423+ hamiltonian .terms .get (
424+ ((left , 1 ), (right , 1 ), (left , 0 ), (right , 0 )), 0.0 ))
425+
426+ # Calculate the left number operator, left^ left.
427+ left_number_operator = FermionOperator (
428+ ((left , 1 ), (left , 0 )), hamiltonian .terms .get (
429+ ((left , 1 ), (left , 0 )), 0.0 ))
430+
431+ # Calculate the right number operator, right^ right.
432+ right_number_operator = FermionOperator (
433+ ((right , 1 ), (right , 0 )), hamiltonian .terms .get (
434+ ((right , 1 ), (right , 0 )), 0.0 ))
435+
436+ # Divide single-number terms by n_qubits-1 to avoid over-counting.
437+ # Each qubit is swapped n_qubits-1 times total.
438+ left_number_operator /= (n_qubits - 1 )
439+ right_number_operator /= (n_qubits - 1 )
445440
446441 # If the overall hopping operator isn't close to zero, append it.
447442 # Include the indices it acts on and that it's a hopping operator.
448- if not hopping_operator .isclose (zero ):
449- ordered_terms .append (hopping_operator )
450- ordered_indices .append (set ((index_left , index_right )))
443+ if not (left_hopping_operator +
444+ right_hopping_operator ).isclose (zero ):
445+ ordered_terms .append (left_hopping_operator +
446+ right_hopping_operator )
447+ ordered_indices .append (set ((left , right )))
451448 ordered_is_hopping_operator .append (True )
452449
453450 # If the overall number operator isn't close to zero, append it.
454451 # Include the indices it acts on and that it's a number operator.
455- if not number_operator .isclose (zero ):
456- ordered_terms .append (number_operator )
457- ordered_indices .append (set ((index_left , index_right )))
452+ if not (two_number_operator + left_number_operator +
453+ right_number_operator ).isclose (zero ):
454+ ordered_terms .append (two_number_operator +
455+ left_number_operator +
456+ right_number_operator )
457+ ordered_indices .append (set ((left , right )))
458458 ordered_is_hopping_operator .append (False )
459459
460+ # Track the current Jordan-Wigner canonical ordering.
461+ input_ordering [i ], input_ordering [i + 1 ] = (input_ordering [i + 1 ],
462+ input_ordering [i ])
463+
464+ # Alternate even and odd steps of the reversal procedure.
465+ odd = 1 - odd
466+
460467 return (ordered_terms , ordered_indices , ordered_is_hopping_operator )
461468
462469
463470def ordered_dual_basis_terms_no_info (dual_basis_hamiltonian ):
464- """Give terms from the dual basis Hamiltonian in simulated order.
465-
466- Orders the terms by dictionary output.
471+ """Give terms from the dual basis Hamiltonian in dictionary output order.
467472
468473 Args:
469474 dual_basis_hamiltonian (FermionOperator): The Hamiltonian.
0 commit comments