@@ -361,10 +361,13 @@ def Rp(paulistring: typing.Union[PauliString, str], angle, control: typing.Union
361
361
return ExpPauli (paulistring = paulistring , angle = angle , control = control , * args , ** kwargs )
362
362
363
363
364
+ def GenRot (* args , ** kwargs ):
365
+ return GeneralizedRotation (* args , ** kwargs )
366
+
364
367
def GeneralizedRotation (angle : typing .Union [typing .List [typing .Hashable ], typing .List [numbers .Real ]],
365
368
generator : QubitHamiltonian ,
366
369
control : typing .Union [list , int ] = None ,
367
- eigenvalues_magnitude : float = 0.5 ,
370
+ eigenvalues_magnitude : float = 0.5 , p0 = None ,
368
371
steps : int = 1 , assume_real = False ) -> QCircuit :
369
372
"""
370
373
@@ -393,6 +396,8 @@ def GeneralizedRotation(angle: typing.Union[typing.List[typing.Hashable], typing
393
396
list of control qubits
394
397
eigenvalues_magnitude
395
398
magnitude of eigenvalues, in most papers referred to as "r" (default 0.5)
399
+ p0
400
+ possible nullspace projector (if the rotation is happens in Q = 1-P0). See arxiv:2011.05938
396
401
steps
397
402
possible Trotterization steps (default 1)
398
403
@@ -403,7 +408,7 @@ def GeneralizedRotation(angle: typing.Union[typing.List[typing.Hashable], typing
403
408
404
409
return QCircuit .wrap_gate (
405
410
impl .GeneralizedRotationImpl (angle = assign_variable (angle ), generator = generator , control = control ,
406
- eigenvalues_magnitude = eigenvalues_magnitude , steps = steps , assume_real = assume_real ))
411
+ eigenvalues_magnitude = eigenvalues_magnitude , steps = steps , assume_real = assume_real , p0 = p0 ))
407
412
408
413
409
414
@@ -473,7 +478,7 @@ def Trotterized(generator: QubitHamiltonian = None,
473
478
return QCircuit .wrap_gate (impl .TrotterizedGateImpl (generator = generator , angle = angle , steps = steps , control = control , randomize = randomize , ** kwargs ))
474
479
475
480
476
- def SWAP (first : int , second : int , control : typing .Union [int , list ] = None , power : float = None , * args ,
481
+ def SWAP (first : int , second : int , angle : float = None , control : typing .Union [int , list ] = None , power : float = None , * args ,
477
482
** kwargs ) -> QCircuit :
478
483
"""
479
484
Notes
@@ -486,10 +491,12 @@ def SWAP(first: int, second: int, control: typing.Union[int, list] = None, power
486
491
target qubit
487
492
second: int
488
493
target qubit
494
+ angle: numeric type or hashable type
495
+ exponent in the for e^{-i a/2 G}
489
496
control
490
497
int or list of ints
491
498
power
492
- numeric type (fixed exponent) or hashable type (parametrized exponent)
499
+ numeric type (fixed exponent) or hashable type (parametrized exponent in the form (SWAP)^n
493
500
494
501
Returns
495
502
-------
@@ -498,12 +505,82 @@ def SWAP(first: int, second: int, control: typing.Union[int, list] = None, power
498
505
"""
499
506
500
507
target = [first , second ]
508
+ if angle is not None :
509
+ assert power is None
510
+ angle = assign_variable (angle )
511
+ elif power is not None :
512
+ angle = assign_variable (power )* np .pi
501
513
generator = 0.5 * (paulis .X (target ) + paulis .Y (target ) + paulis .Z (target ) - paulis .I (target ))
502
- if power is None or power in [1 , 1.0 ]:
514
+ if angle is None or power in [1 , 1.0 ]:
503
515
return QGate (name = "SWAP" , target = target , control = control , generator = generator )
504
516
else :
505
- return GeneralizedRotation (angle = power * np . pi , control = control , generator = generator ,
517
+ return GeneralizedRotation (angle = angle , control = control , generator = generator ,
506
518
eigenvalues_magnitude = 0.25 )
519
+
520
+
521
+ def iSWAP (first : int , second : int , control : typing .Union [int , list ] = None , power : float = 1.0 , * args ,
522
+ ** kwargs ) -> QCircuit :
523
+ """
524
+ Notes
525
+ ----------
526
+ iSWAP gate
527
+ .. math::
528
+ iSWAP = e^{i\\ frac{\\ pi}{4} (X \\ otimes X + Y \\ otimes Y )}
529
+
530
+ Parameters
531
+ ----------
532
+ first: int
533
+ target qubit
534
+ second: int
535
+ target qubit
536
+ control
537
+ int or list of ints
538
+ power
539
+ numeric type (fixed exponent) or hashable type (parametrized exponent)
540
+
541
+ Returns
542
+ -------
543
+ QCircuit
544
+
545
+ """
546
+
547
+ generator = paulis .from_string (f"X({ first } )X({ second } ) + Y({ first } )Y({ second } )" )
548
+
549
+ p0 = paulis .Projector ("|00>" ) + paulis .Projector ("|11>" )
550
+ p0 = p0 .map_qubits ({0 :first , 1 :second })
551
+
552
+ gate = QubitExcitationImpl (angle = power * (- np .pi / 2 ), target = generator .qubits , generator = generator , p0 = p0 , control = control , compile_options = "vanilla" , * args , ** kwargs )
553
+
554
+ return QCircuit .wrap_gate (gate )
555
+
556
+
557
+ def Givens (first : int , second : int , control : typing .Union [int , list ] = None , angle : float = None , * args ,
558
+ ** kwargs ) -> QCircuit :
559
+ """
560
+ Notes
561
+ ----------
562
+ Givens gate G
563
+ .. math::
564
+ G = e^{-i\\ theta \\ frac{(Y \\ otimes X - X \\ otimes Y )}{2}}
565
+
566
+ Parameters
567
+ ----------
568
+ first: int
569
+ target qubit
570
+ second: int
571
+ target qubit
572
+ control
573
+ int or list of ints
574
+ angle
575
+ numeric type (fixed exponent) or hashable type (parametrized exponent), theta in the above formula
576
+
577
+ Returns
578
+ -------
579
+ QCircuit
580
+
581
+ """
582
+
583
+ return QubitExcitation (target = [second ,first ], angle = 2 * angle , control = control , * args , ** kwargs ) # twice the angle since theta is not divided by two in the matrix exponential
507
584
508
585
509
586
"""
@@ -965,11 +1042,7 @@ def QGate(name, target: typing.Union[list, int], control: typing.Union[list, int
965
1042
returns a QCircuit of primitive tq gates
966
1043
"""
967
1044
968
- class QubitExcitationImpl (impl .DifferentiableGateImpl ):
969
-
970
- @property
971
- def steps (self ):
972
- return 1
1045
+ class QubitExcitationImpl (impl .GeneralizedRotationImpl ):
973
1046
974
1047
def __init__ (self , angle , target , generator = None , p0 = None , assume_real = True , control = None , compile_options = None ):
975
1048
angle = assign_variable (angle )
@@ -990,15 +1063,9 @@ def __init__(self, angle, target, generator=None, p0=None, assume_real=True, con
990
1063
else :
991
1064
assert generator is not None
992
1065
assert p0 is not None
993
-
994
- super ().__init__ (name = "QubitExcitation" , parameter = angle , target = target , control = control )
995
- self .generator = generator
996
- if control is not None :
997
- # augment p0 for control qubits
998
- # Qp = 1/2(1+Z) = |0><0|
999
- p0 = p0 * paulis .Qp (control )
1000
- self .p0 = p0
1001
- self .assume_real = assume_real
1066
+
1067
+ super ().__init__ (name = "QubitExcitation" , angle = angle , generator = generator , target = target , p0 = p0 , control = control , assume_real = assume_real , steps = 1 )
1068
+
1002
1069
if compile_options is None :
1003
1070
self .compile_options = "optimize"
1004
1071
elif hasattr (compile_options , "lower" ):
@@ -1007,18 +1074,9 @@ def __init__(self, angle, target, generator=None, p0=None, assume_real=True, con
1007
1074
self .compile_options = compile_options
1008
1075
1009
1076
def map_qubits (self , qubit_map : dict ):
1010
- mapped_generator = self .generator .map_qubits (qubit_map = qubit_map )
1011
- mapped_p0 = self .p0 .map_qubits (qubit_map = qubit_map )
1012
- mapped_control = self .control
1013
- if mapped_control is not None :
1014
- mapped_control = tuple ([qubit_map [i ] for i in self .control ])
1015
- result = copy .deepcopy (self )
1016
- result .generator = mapped_generator
1017
- result .p0 = mapped_p0
1018
- result ._target = tuple ([qubit_map [x ] for x in self .target ])
1019
- result ._control = mapped_control
1020
- result .finalize ()
1021
- return result
1077
+ mapped = super ().map_qubits (qubit_map )
1078
+ mapped .p0 = self .p0 .map_qubits (qubit_map = qubit_map )
1079
+ return mapped
1022
1080
1023
1081
def compile (self , exponential_pauli = False , * args , ** kwargs ):
1024
1082
# optimized compiling for single and double qubit excitaitons following arxiv:2005.14475
@@ -1040,35 +1098,6 @@ def compile(self, exponential_pauli=False, *args, **kwargs):
1040
1098
else :
1041
1099
return Trotterized (angle = self .parameter , generator = self .generator , steps = 1 )
1042
1100
1043
- def shifted_gates (self ):
1044
- if not self .assume_real :
1045
- # following https://arxiv.org/abs/2104.05695
1046
- s = 0.5 * np .pi
1047
- shifts = [s , - s , 3 * s , - 3 * s ]
1048
- coeff1 = 0.25 * (np .sqrt (2 ) + 1 )/ np .sqrt (2 )
1049
- coeff2 = 0.25 * (np .sqrt (2 ) - 1 )/ np .sqrt (2 )
1050
- coefficients = [coeff1 , - coeff1 , - coeff2 , coeff2 ]
1051
- circuits = []
1052
- for i , shift in enumerate (shifts ):
1053
- shifted_gate = copy .deepcopy (self )
1054
- shifted_gate .parameter += shift
1055
- circuits .append ((coefficients [i ], shifted_gate ))
1056
- return circuits
1057
-
1058
- r = 0.25
1059
- s = 0.5 * np .pi
1060
-
1061
- Up1 = copy .deepcopy (self )
1062
- Up1 ._parameter = self .parameter + s
1063
- Up1 = QCircuit .wrap_gate (Up1 )
1064
- Up2 = GeneralizedRotation (angle = s , generator = self .p0 , eigenvalues_magnitude = r ) # controls are in p0
1065
- Um1 = copy .deepcopy (self )
1066
- Um1 ._parameter = self .parameter - s
1067
- Um1 = QCircuit .wrap_gate (Um1 )
1068
- Um2 = GeneralizedRotation (angle = - s , generator = self .p0 , eigenvalues_magnitude = r ) # controls are in p0
1069
-
1070
- return [(2.0 * r , Up1 + Up2 ), (- 2.0 * r , Um1 + Um2 )]
1071
-
1072
1101
def _convert_Paulistring (paulistring : typing .Union [PauliString , str , dict ]) -> PauliString :
1073
1102
'''
1074
1103
Function that given a paulistring as PauliString structure or
0 commit comments