Ejemplo n.º 1
0
    def pad_paulis_to_equal_length(
            self, pauli_op1: PauliOp,
            pauli_op2: PauliOp) -> Tuple[PauliOp, PauliOp]:
        r"""
        If ``pauli_op1`` and ``pauli_op2`` do not act over the same number of qubits, pad
        identities to the end of the shorter of the two so they are of equal length. Padding is
        applied to the end of the Paulis. Note that the Terra represents Paulis in big-endian
        order, so this will appear as padding to the beginning of the Pauli x and z bit arrays.

        Args:
            pauli_op1: A pauli_op to possibly pad.
            pauli_op2: A pauli_op to possibly pad.

        Returns:
            A tuple containing the padded PauliOps.

        """
        num_qubits = max(pauli_op1.num_qubits, pauli_op2.num_qubits)
        pauli_1, pauli_2 = pauli_op1.primitive, pauli_op2.primitive

        # Padding to the end of the Pauli, but remember that Paulis are in reverse endianness.
        if not len(pauli_1.z) == num_qubits:
            missing_qubits = num_qubits - len(pauli_1.z)
            pauli_1 = Pauli((([False] * missing_qubits) + pauli_1.z.tolist(),
                             ([False] * missing_qubits) + pauli_1.x.tolist()))
        if not len(pauli_2.z) == num_qubits:
            missing_qubits = num_qubits - len(pauli_2.z)
            pauli_2 = Pauli((([False] * missing_qubits) + pauli_2.z.tolist(),
                             ([False] * missing_qubits) + pauli_2.x.tolist()))

        return PauliOp(pauli_1,
                       coeff=pauli_op1.coeff), PauliOp(pauli_2,
                                                       coeff=pauli_op2.coeff)
Ejemplo n.º 2
0
 def cliffords(self) -> List[PauliSumOp]:
     """
     Get clifford operators, build based on symmetries and single-qubit X.
     Returns:
         a list of unitaries used to diagonalize the Hamiltonian.
     """
     cliffords = [
         (PauliOp(pauli_symm) + PauliOp(sq_pauli)) / np.sqrt(2)
         for pauli_symm, sq_pauli in zip(self._symmetries, self._sq_paulis)
     ]
     return cliffords
Ejemplo n.º 3
0
    def to_pauli_op(self, massive: bool = False) -> Union[PauliOp, SummedOp]:
        def to_native(x):
            return x.item() if isinstance(x, np.generic) else x

        if len(self.primitive) == 1:
            return PauliOp(
                Pauli(
                    (self.primitive.paulis.z[0], self.primitive.paulis.x[0])),
                to_native(np.real_if_close(self.primitive.coeffs[0])) *
                self.coeff,
            )
        coeffs = np.real_if_close(self.primitive.coeffs)
        return SummedOp(
            [
                PauliOp(pauli, to_native(coeff))
                for pauli, coeff in zip(self.primitive.paulis, coeffs)
            ],
            coeff=self.coeff,
        )
Ejemplo n.º 4
0
    def to_pauli_op(self, massive: bool = False) -> Union[PauliOp, SummedOp]:
        def to_native(x):
            return x.item() if isinstance(x, np.generic) else x

        if len(self.primitive) == 1:
            return PauliOp(
                Pauli((self.primitive.table.Z[0], self.primitive.table.X[0])),
                to_native(np.real_if_close(self.primitive.coeffs[0])) *
                self.coeff,
            )
        tables = self.primitive.table
        coeffs = np.real_if_close(self.primitive.coeffs)
        return SummedOp(
            [
                PauliOp(
                    Pauli((t.Z[0], t.X[0])),
                    to_native(c),
                ) for t, c in zip(tables, coeffs)
            ],
            coeff=self.coeff,
        )
Ejemplo n.º 5
0
    def destination(self, dest: Union[Pauli, PauliOp]) -> None:
        r"""
        The destination ``PauliOp``, or ``None`` if using the default destination, the diagonal
        basis.
        """
        if isinstance(dest, Pauli):
            dest = PauliOp(dest)

        if not isinstance(dest, PauliOp):
            raise TypeError(
                "PauliBasisChange can only convert into Pauli bases, "
                "not {}.".format(type(dest)))
        self._destination = dest
Ejemplo n.º 6
0
    def get_diagonal_pauli_op(self, pauli_op: PauliOp) -> PauliOp:
        """ Get the diagonal ``PualiOp`` to which ``pauli_op`` could be rotated with only
        single-qubit operations.

        Args:
            pauli_op: The ``PauliOp`` whose diagonal to compute.

        Returns:
            The diagonal ``PauliOp``.
        """
        return PauliOp(Pauli((np.logical_or(pauli_op.primitive.z,
                                            pauli_op.primitive.x),
                              [False] * pauli_op.num_qubits)),
                       coeff=pauli_op.coeff)
Ejemplo n.º 7
0
    def consistent_tapering(self, operator: PauliSumOp) -> OperatorBase:
        """
        Tapering the `operator` with the same manner of how this tapered operator
        is created. i.e., using the same Cliffords and tapering values.

        Args:
            operator: the to-be-tapered operator

        Returns:
            The tapered operator

        Raises:
            OpflowError: The given operator does not commute with the symmetry
        """
        for symmetry in self._symmetries:
            commutator_op = cast(PauliSumOp, commutator(operator, PauliOp(symmetry)))
            if not commutator_op.is_zero():
                raise OpflowError(
                    "The given operator does not commute with " "the symmetry, can not taper it."
                )

        return self.taper(operator)
Ejemplo n.º 8
0
# Immutable convenience objects


def make_immutable(obj):
    """Delete the __setattr__ property to make the object mostly immutable."""

    # TODO figure out how to get correct error message
    # def throw_immutability_exception(self, *args):
    #     raise OpflowError('Operator convenience globals are immutable.')

    obj.__setattr__ = None
    return obj


# 1-Qubit Paulis
X = make_immutable(PauliOp(Pauli("X")))
Y = make_immutable(PauliOp(Pauli("Y")))
Z = make_immutable(PauliOp(Pauli("Z")))
I = make_immutable(PauliOp(Pauli("I")))

# Clifford+T, and some other common non-parameterized gates
CX = make_immutable(CircuitOp(CXGate()))
S = make_immutable(CircuitOp(SGate()))
H = make_immutable(CircuitOp(HGate()))
T = make_immutable(CircuitOp(TGate()))
Swap = make_immutable(CircuitOp(SwapGate()))
CZ = make_immutable(CircuitOp(CZGate()))

# 1-Qubit Paulis
Zero = make_immutable(DictStateFn("0"))
One = make_immutable(DictStateFn("1"))
Ejemplo n.º 9
0
    def get_cob_circuit(
            self, origin: Union[Pauli,
                                PauliOp]) -> Tuple[PrimitiveOp, PauliOp]:
        r"""
        Construct an Operator which maps the +1 and -1 eigenvectors
        of the origin Pauli to the +1 and -1 eigenvectors of the destination Pauli. It does so by

        1) converting any \|i+⟩ or \|i+⟩ eigenvector bits in the origin to
           \|+⟩ and \|-⟩ with S†s, then

        2) converting any \|+⟩ or \|+⟩ eigenvector bits in the converted origin to
           \|0⟩ and \|1⟩ with Hs, then

        3) writing the parity of the significant (Z-measured, rather than I)
           bits in the origin to a single
           "origin anchor bit," using cnots, which will hold the parity of these bits,

        4) swapping the parity of the pauli anchor bit into a destination anchor bit using
           a swap gate (only if they are different, if there are any bits which are significant
           in both origin and dest, we set both anchors to one of these bits to avoid a swap).

        5) writing the parity of the destination anchor bit into the other significant bits
           of the destination,

        6) converting the \|0⟩ and \|1⟩ significant eigenvector bits to \|+⟩ and \|-⟩ eigenvector
           bits in the destination where the destination demands it
           (e.g. pauli.x == true for a bit), using Hs 8) converting the \|+⟩ and \|-⟩
           significant eigenvector bits to \|i+⟩ and \|i-⟩ eigenvector bits in the
           destination where the destination demands it
           (e.g. pauli.x == true and pauli.z == true for a bit), using Ss

        Args:
            origin: The ``Pauli`` or ``PauliOp`` to map.

        Returns:
            A tuple of a ``PrimitiveOp`` which equals the basis change mapping and a ``PauliOp``
            which equals the destination basis.

        Raises:
            TypeError: Attempting to convert from non-Pauli origin.
            ValueError: Attempting to change a non-identity Pauli to an identity Pauli, or vice
                versa.

        """

        # If pauli is an PrimitiveOp, extract the Pauli
        if isinstance(origin, Pauli):
            origin = PauliOp(origin)

        if not isinstance(origin, PauliOp):
            raise TypeError(
                f"PauliBasisChange can only convert Pauli-based OpPrimitives, not {type(origin)}"
            )

        # If no destination specified, assume nearest Pauli in {Z,I}^n basis,
        # the standard basis change for expectations.
        destination = self.destination or self.get_diagonal_pauli_op(origin)

        # Pad origin or destination if either are not as long as the other
        origin, destination = self.pad_paulis_to_equal_length(
            origin, destination)

        origin_sig_bits = np.logical_or(origin.primitive.x, origin.primitive.z)
        destination_sig_bits = np.logical_or(destination.primitive.x,
                                             destination.primitive.z)
        if not any(origin_sig_bits) or not any(destination_sig_bits):
            if not (any(origin_sig_bits) or any(destination_sig_bits)):
                # Both all Identity, just return Identities
                return I ^ origin.num_qubits, destination
            else:
                # One is Identity, one is not
                raise ValueError(
                    "Cannot change to or from a fully Identity Pauli.")

        # Steps 1 and 2
        cob_instruction = self.get_diagonalizing_clifford(origin)

        # Construct CNOT chain, assuming full connectivity... - Steps 3)-5)
        cob_instruction = self.construct_cnot_chain(
            origin, destination).compose(cob_instruction)

        # Step 6 and 7
        dest_diagonlizing_clifford = self.get_diagonalizing_clifford(
            destination).adjoint()
        cob_instruction = dest_diagonlizing_clifford.compose(cob_instruction)

        return cast(PrimitiveOp, cob_instruction), destination
Ejemplo n.º 10
0
# Immutable convenience objects


def make_immutable(obj):
    """ Delete the __setattr__ property to make the object mostly immutable. """

    # TODO figure out how to get correct error message
    # def throw_immutability_exception(self, *args):
    #     raise OpflowError('Operator convenience globals are immutable.')

    obj.__setattr__ = None
    return obj


# 1-Qubit Paulis
X = make_immutable(PauliOp(Pauli('X')))
Y = make_immutable(PauliOp(Pauli('Y')))
Z = make_immutable(PauliOp(Pauli('Z')))
I = make_immutable(PauliOp(Pauli('I')))

# Clifford+T, and some other common non-parameterized gates
CX = make_immutable(CircuitOp(CXGate()))
S = make_immutable(CircuitOp(SGate()))
H = make_immutable(CircuitOp(HGate()))
T = make_immutable(CircuitOp(TGate()))
Swap = make_immutable(CircuitOp(SwapGate()))
CZ = make_immutable(CircuitOp(CZGate()))

# 1-Qubit Paulis
Zero = make_immutable(DictStateFn('0'))
One = make_immutable(DictStateFn('1'))