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))
Exemple #2
0
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)
Exemple #4
0
    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))
Exemple #6
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)]