def test_transfer_operators(): assert (paulis.decompose_transfer_operator(ket=0, bra=0) == paulis.Qp(0)) assert (paulis.decompose_transfer_operator(ket=0, bra=1) == paulis.Sp(0)) assert (paulis.decompose_transfer_operator(ket=1, bra=0) == paulis.Sm(0)) assert (paulis.decompose_transfer_operator(ket=1, bra=1) == paulis.Qm(0)) assert (paulis.decompose_transfer_operator( ket=BitString.from_binary(binary="00"), bra=BitString.from_binary("00")) == paulis.Qp(0) * paulis.Qp(1)) assert (paulis.decompose_transfer_operator( ket=BitString.from_binary(binary="01"), bra=BitString.from_binary("01")) == paulis.Qp(0) * paulis.Qm(1)) assert (paulis.decompose_transfer_operator( ket=BitString.from_binary(binary="01"), bra=BitString.from_binary("10")) == paulis.Sp(0) * paulis.Sm(1)) assert (paulis.decompose_transfer_operator( ket=BitString.from_binary(binary="00"), bra=BitString.from_binary("11")) == paulis.Sp(0) * paulis.Sp(1)) assert (paulis.decompose_transfer_operator(ket=0, bra=0, qubits=[1]) == paulis.Qp(1)) assert (paulis.decompose_transfer_operator(ket=1, bra=0, qubits=[1]) == paulis.Sm(1)) assert (paulis.decompose_transfer_operator(ket=1, bra=1, qubits=[1]) == paulis.Qm(1))
def QubitExcitation(angle: typing.Union[numbers.Real, Variable, typing.Hashable], target: typing.List, control=None, assume_real: bool = False): """ A Qubit Excitation, as described under "qubit perspective" in https://doi.org/10.1039/D0SC06627C For the Fermionic operators under corresponding Qubit encodings: Use the chemistry interface Parameters ---------- angle: the angle of the excitation unitary target: even number of qubit indices interpreted as [0,1,2,3....] = [(0,1), (2,3), ...] i.e. as qubit excitations from 0 to 1, 2 to 3, etc control: possible control qubits assume_real: assume the wavefunction on which this acts is always real (cheaper gradients: see https://doi.org/10.1039/D0SC06627C) Returns ------- QubitExcitation gate wrapped into a tequila circuit """ try: assert len(target) % 2 == 0 except: raise Exception("QubitExcitation: Needs an even number of targets") generator = paulis.I() p0a = paulis.I() p0b = paulis.I() for i in range(len(target) // 2): generator *= paulis.Sp(target[2 * i]) * paulis.Sm(target[2 * i + 1]) p0a *= paulis.Qp(target[2 * i]) * paulis.Qm(target[2 * i + 1]) p0b *= paulis.Qm(target[2 * i]) * paulis.Qp(target[2 * i + 1]) generator = (1.0j * (generator - generator.dagger())).simplify() p0 = paulis.I() - p0a - p0b return QCircuit.wrap_gate( impl.QubitExcitationImpl(angle=angle, generator=generator, p0=p0, assume_real=assume_real))
def test_convenience(): i = numpy.random.randint(0, 10, 1)[0] assert paulis.X(i) + paulis.I(i) == paulis.X(i) + 1.0 assert paulis.Qp(i) == 0.5 * (1.0 + paulis.Z(i)) assert paulis.Qm(i) == 0.5 * (1.0 - paulis.Z(i)) assert paulis.Sp(i) == 0.5 * (paulis.X(i) + 1.j * paulis.Y(i)) assert paulis.Sm(i) == 0.5 * (paulis.X(i) - 1.j * paulis.Y(i)) i = numpy.random.randint(0, 10, 1)[0] assert paulis.Qp(i) == (0.5 + 0.5 * paulis.Z(i)) assert paulis.Qm(i) == (0.5 - 0.5 * paulis.Z(i)) assert paulis.Sp(i) == (0.5 * paulis.X(i) + 0.5j * paulis.Y(i)) assert paulis.Sm(i) == (0.5 * paulis.X(i) - 0.5j * paulis.Y(i)) assert -1.0 * paulis.Y(i) == -paulis.Y(i) test = paulis.Z(i) test *= -1.0 assert test == -paulis.Z(i) test = paulis.Z(i) test += 1.0 assert test == paulis.Z(i) + 1.0 test = paulis.X(i) test += paulis.Y(i + 1) assert test == paulis.X(i) + paulis.Y(i + 1) test = paulis.X(i) test -= paulis.Y(i) test += 3.0 test = -test assert test == -1.0 * (paulis.X(i) - paulis.Y(i) + 3.0) test = paulis.X([0, 1, 2, 3]) assert test == QubitHamiltonian.from_string("X(0)X(1)X(2)X(3)", False) test = paulis.Y([0, 1, 2, 3]) assert test == QubitHamiltonian.from_string("Y(0)Y(1)Y(2)Y(3)", False) test = paulis.Z([0, 1, 2, 3]) assert test == QubitHamiltonian.from_string("Z(0)Z(1)Z(2)Z(3)", False)
def __init__(self, angle, target=None, generator=None, p0=None, assume_real=True, control=None, compile_options=None): angle = assign_variable(angle) if generator is None: assert target is not None assert p0 is None generator = paulis.I() p0a = paulis.I() p0b = paulis.I() for i in range(len(target) // 2): generator *= paulis.Sp(target[2 * i]) * paulis.Sm( target[2 * i + 1]) p0a *= paulis.Qp(target[2 * i]) * paulis.Qm(target[2 * i + 1]) p0b *= paulis.Qm(target[2 * i]) * paulis.Qp(target[2 * i + 1]) generator = (1.0j * (generator - generator.dagger())).simplify() p0 = paulis.I() - p0a - p0b else: assert generator is not None assert p0 is not None super().__init__(name="QubitExcitation", parameter=angle, target=self.extract_targets(generator), control=control) self.generator = generator if control is not None: # augment p0 for control qubits # Qp = 1/2(1+Z) = |0><0| p0 = p0 * paulis.Qp(control) self.p0 = p0 self.assume_real = assume_real if compile_options is None: self.compile_options = "optimize" else: self.compile_options = compile_options
def test_special_operators(): # sigma+ sigma- as well as Q+ and Q- assert (paulis.Sp(0) * paulis.Sp(0) == QubitHamiltonian.zero()) assert (paulis.Sm(0) * paulis.Sm(0) == QubitHamiltonian.zero()) assert (paulis.Qp(0) * paulis.Qp(0) == paulis.Qp(0)) assert (paulis.Qm(0) * paulis.Qm(0) == paulis.Qm(0)) assert (paulis.Qp(0) * paulis.Qm(0) == QubitHamiltonian.zero()) assert (paulis.Qm(0) * paulis.Qp(0) == QubitHamiltonian.zero()) assert (paulis.Sp(0) * paulis.Sm(0) == paulis.Qp(0)) assert (paulis.Sm(0) * paulis.Sp(0) == paulis.Qm(0)) assert (paulis.Sp(0) + paulis.Sm(0) == paulis.X(0)) assert (paulis.Qp(0) + paulis.Qm(0) == paulis.I(0))
def shifted_gates(self, r=None): """ Default shift rule, override this for special strategies Returns ------- List of Tuples: [(weight, shifted_gate), (weight, shifted_gate)] The gradient compiler will assemble this the following way <H>_U with U = AU(a)B --> \sum weight <H>_V , with V=A shifted_gate shifted_gate can also be a whole circuit (either as QCircuit object or a list of gates) """ if r is None: r = self.eigenvalues_magnitude s = pi / (4 * r) shift_a = self.parameter + s shift_b = self.parameter - s right = copy.deepcopy(self) right.parameter = shift_a left = copy.deepcopy(self) left.parameter = shift_b if self.is_controlled(): # following https://doi.org/10.1039/D0SC06627C p0 = paulis.Qp(self.control) # Qp = |0><0| right2 = GeneralizedRotationImpl(angle=s, generator=p0, eigenvalues_magnitude=r / 2) # controls are in p0 left2 = GeneralizedRotationImpl(angle=-s, generator=p0, eigenvalues_magnitude=r / 2) # controls are in p0 if not self.assume_real: # 4-point shift rule of arxiv:2104.05695 would saves gates here return [(r / 2, [right, right2]), (-r / 2, [left, left2]), (r / 2, [right, left2]), (-r / 2, [left, right2])] else: return [(r, [right, right2]), (-r, [left, left2])] else: return [(r, right), (-r, left)]