Exemple #1
0
    def compose(
        self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False
    ) -> OperatorBase:

        new_self, other = self._expand_shorter_operator_and_permute(other, permutation)
        new_self = cast(PauliOp, new_self)

        if front:
            return other.compose(new_self)
        # If self is identity, just return other.
        if not any(new_self.primitive.x + new_self.primitive.z):
            return other * new_self.coeff

        # Both Paulis
        if isinstance(other, PauliOp):
            product = new_self.primitive.dot(other.primitive)
            return PrimitiveOp(product, coeff=new_self.coeff * other.coeff)

        # pylint: disable=cyclic-import
        from .pauli_sum_op import PauliSumOp

        if isinstance(other, PauliSumOp):
            return PauliSumOp(
                SparsePauliOp(new_self.primitive).dot(other.primitive),
                coeff=new_self.coeff * other.coeff,
            )

        # pylint: disable=cyclic-import
        from ..state_fns.circuit_state_fn import CircuitStateFn
        from .circuit_op import CircuitOp

        if isinstance(other, (CircuitOp, CircuitStateFn)):
            return new_self.to_circuit_op().compose(other)

        return super(PauliOp, new_self).compose(other)
    def statefn_replacement_fn(
            cob_instr_op: PrimitiveOp,
            dest_pauli_op: Union[PauliOp, PauliSumOp, ListOp]) -> OperatorBase:
        r"""
        A built-in convenience replacement function which produces state functions
        isomorphic to an ``OperatorStateFn`` state function holding the origin ``PauliOp``.

        Args:
            cob_instr_op: The basis-change ``CircuitOp``.
            dest_pauli_op: The destination Pauli type operator.

        Returns:
            The ``~CircuitOp @ StateFn`` composition equivalent to a state function defined by the
            original ``PauliOp``.
        """
        return ComposedOp([cob_instr_op.adjoint(), StateFn(dest_pauli_op)])
Exemple #3
0
    def operator_replacement_fn(
        cob_instr_op: PrimitiveOp, dest_pauli_op: Union[PauliOp, PauliSumOp, ListOp]
    ) -> OperatorBase:
        r"""
        A built-in convenience replacement function which produces Operators
        isomorphic to the origin ``PauliOp``.

        Args:
            cob_instr_op: The basis-change ``CircuitOp``.
            dest_pauli_op: The destination ``PauliOp``.

        Returns:
            The ``~CircuitOp @ PauliOp @ CircuitOp`` composition isomorphic to the
            original ``PauliOp``.
        """
        return ComposedOp([cob_instr_op.adjoint(), dest_pauli_op, cob_instr_op])
    def construct_cnot_chain(self, diag_pauli_op1: PauliOp,
                             diag_pauli_op2: PauliOp) -> PrimitiveOp:
        r"""
        Construct a ``CircuitOp`` (or ``PauliOp`` if equal to the identity) which takes the
        eigenvectors of ``diag_pauli_op1`` to the eigenvectors of ``diag_pauli_op2``,
        assuming both are diagonal (or performing this operation on their diagonalized Paulis
        implicitly if not). This works by the insight that the eigenvalue of a diagonal Pauli's
        eigenvector is equal to or -1 if the parity is 1 and 1 if the parity is 0, or
        1 - (2 * parity). Therefore, using CNOTs, we can write the parity of diag_pauli_op1's
        significant bits onto some qubit, and then write out that parity onto diag_pauli_op2's
        significant bits.

        Args:
            diag_pauli_op1: The origin ``PauliOp``.
            diag_pauli_op2: The destination ``PauliOp``.

        Return:
            The ``PrimitiveOp`` performs the mapping.
        """
        # TODO be smarter about connectivity and actual distance between pauli and destination
        # TODO be smarter in general

        pauli_1 = (diag_pauli_op1.primitive if isinstance(
            diag_pauli_op1, PauliOp) else diag_pauli_op1)
        pauli_2 = (diag_pauli_op2.primitive if isinstance(
            diag_pauli_op2, PauliOp) else diag_pauli_op2)
        origin_sig_bits = np.logical_or(pauli_1.z, pauli_1.x)
        destination_sig_bits = np.logical_or(pauli_2.z, pauli_2.x)
        num_qubits = max(len(pauli_1.z), len(pauli_2.z))

        sig_equal_sig_bits = np.logical_and(origin_sig_bits,
                                            destination_sig_bits)
        non_equal_sig_bits = np.logical_not(
            origin_sig_bits == destination_sig_bits)
        # Equivalent to np.logical_xor(origin_sig_bits, destination_sig_bits)

        if not any(non_equal_sig_bits):
            return I ^ num_qubits

        # I am deeply sorry for this code, but I don't know another way to do it.
        sig_in_origin_only_indices = np.extract(
            np.logical_and(non_equal_sig_bits, origin_sig_bits),
            np.arange(num_qubits))
        sig_in_dest_only_indices = np.extract(
            np.logical_and(non_equal_sig_bits, destination_sig_bits),
            np.arange(num_qubits))

        if len(sig_in_origin_only_indices) > 0 and len(
                sig_in_dest_only_indices) > 0:
            origin_anchor_bit = min(sig_in_origin_only_indices)
            dest_anchor_bit = min(sig_in_dest_only_indices)
        else:
            # Set to lowest equal bit
            origin_anchor_bit = min(
                np.extract(sig_equal_sig_bits, np.arange(num_qubits)))
            dest_anchor_bit = origin_anchor_bit

        cnots = QuantumCircuit(num_qubits)
        # Step 3) Take the indices of bits which are sig_bits in
        # pauli but but not in dest, and cnot them to the pauli anchor.
        for i in sig_in_origin_only_indices:
            if not i == origin_anchor_bit:
                cnots.cx(i, origin_anchor_bit)

        # Step 4)
        if not origin_anchor_bit == dest_anchor_bit:
            cnots.swap(origin_anchor_bit, dest_anchor_bit)

        # Need to do this or a Terra bug sometimes flips cnots. No time to investigate.
        cnots.i(0)

        # Step 6)
        for i in sig_in_dest_only_indices:
            if not i == dest_anchor_bit:
                cnots.cx(i, dest_anchor_bit)

        return PrimitiveOp(cnots)