Ejemplo n.º 1
0
    def tensor(self,
               other: OperatorBase) -> Union["CircuitStateFn", TensoredOp]:
        r"""
        Return tensor product between self and other, overloaded by ``^``.
        Note: You must be conscious of Qiskit's big-endian bit printing convention.
        Meaning, Plus.tensor(Zero)
        produces a \|+⟩ on qubit 0 and a \|0⟩ on qubit 1, or \|+⟩⨂\|0⟩, but would produce
        a QuantumCircuit like:

            \|0⟩--
            \|+⟩--

        Because Terra prints circuits and results with qubit 0 at the end of the string or circuit.

        Args:
            other: The ``OperatorBase`` to tensor product with self.

        Returns:
            An ``OperatorBase`` equivalent to the tensor product of self and other.
        """
        if isinstance(other, CircuitStateFn
                      ) and other.is_measurement == self.is_measurement:
            # Avoid reimplementing tensor, just use CircuitOp's
            c_op_self = CircuitOp(self.primitive, self.coeff)
            c_op_other = CircuitOp(other.primitive, other.coeff)
            c_op = c_op_self.tensor(c_op_other)
            if isinstance(c_op, CircuitOp):
                return CircuitStateFn(
                    primitive=c_op.primitive,  # pylint: disable=no-member
                    coeff=c_op.coeff,
                    is_measurement=self.is_measurement)
        return TensoredOp([self, other])
Ejemplo n.º 2
0
    def compose(self,
                other: OperatorBase,
                permutation: Optional[List[int]] = None,
                front: bool = False) -> OperatorBase:
        if not self.is_measurement and not front:
            raise ValueError(
                'Composition with a Statefunctions in the first operand is not defined.'
            )
        new_self, other = self._expand_shorter_operator_and_permute(
            other, permutation)

        if front:
            return other.compose(new_self)

        if isinstance(other, (PauliOp, CircuitOp, MatrixOp)):
            op_circuit_self = CircuitOp(self.primitive)

            # Avoid reimplementing compose logic
            composed_op_circs = cast(
                CircuitOp, op_circuit_self.compose(other.to_circuit_op()))

            # Returning CircuitStateFn
            return CircuitStateFn(composed_op_circs.primitive,
                                  is_measurement=self.is_measurement,
                                  coeff=self.coeff * other.coeff)

        if isinstance(other, CircuitStateFn) and self.is_measurement:
            # pylint: disable=cyclic-import
            from ..operator_globals import Zero
            return self.compose(CircuitOp(
                other.primitive, other.coeff)).compose(Zero ^ self.num_qubits)

        return ComposedOp([new_self, other])
Ejemplo n.º 3
0
    def permute(self, permutation: Optional[List[int]] = None) -> OperatorBase:
        """Creates a new MatrixOp that acts on the permuted qubits.

        Args:
            permutation: A list defining where each qubit should be permuted. The qubit at index
                j should be permuted to position permutation[j].

        Returns:
            A new MatrixOp representing the permuted operator.

        Raises:
            OpflowError: if indices do not define a new index for each qubit.
        """
        new_self = self
        new_matrix_size = max(permutation) + 1

        if self.num_qubits != len(permutation):
            raise OpflowError(
                "New index must be defined for each qubit of the operator.")
        if self.num_qubits < new_matrix_size:
            # pad the operator with identities
            new_self = self._expand_dim(new_matrix_size - self.num_qubits)
        qc = QuantumCircuit(new_matrix_size)

        # extend the indices to match the size of the new matrix
        permutation = (list(
            filter(lambda x: x not in permutation, range(new_matrix_size))) +
                       permutation)

        # decompose permutation into sequence of transpositions
        transpositions = arithmetic.transpositions(permutation)
        for trans in transpositions:
            qc.swap(trans[0], trans[1])
        matrix = CircuitOp(qc).to_matrix()
        return MatrixOp(matrix.transpose()) @ new_self @ MatrixOp(matrix)
Ejemplo n.º 4
0
    def _recursive_convert(self, operator: OperatorBase) -> OperatorBase:
        if isinstance(operator, EvolvedOp):
            if isinstance(operator.primitive, (PauliOp, PauliSumOp)):
                pauli = operator.primitive.primitive
                time = operator.coeff * operator.primitive.coeff
                evo = PauliEvolutionGate(
                    pauli,
                    time=time,
                    synthesis=self._get_evolution_synthesis())
                return CircuitOp(evo.definition)
                # operator = EvolvedOp(operator.primitive.to_pauli_op(), coeff=operator.coeff)
            if not {"Pauli"} == operator.primitive_strings():
                logger.warning(
                    "Evolved Hamiltonian is not composed of only Paulis, converting to "
                    "Pauli representation, which can be expensive.")
                # Setting massive=False because this conversion is implicit. User can perform this
                # action on the Hamiltonian with massive=True explicitly if they so choose.
                # TODO explore performance to see whether we should avoid doing this repeatedly
                pauli_ham = operator.primitive.to_pauli_op(massive=False)
                operator = EvolvedOp(pauli_ham, coeff=operator.coeff)

            if isinstance(operator.primitive, SummedOp):
                # TODO uncomment when we implement Abelian grouped evolution.
                # if operator.primitive.abelian:
                #     return self.evolution_for_abelian_paulisum(operator.primitive)
                # else:
                # Collect terms that are not the identity.
                oplist = [
                    x for x in operator.primitive
                    if not isinstance(x, PauliOp) or sum(x.primitive.x +
                                                         x.primitive.z) != 0
                ]
                # Collect the coefficients of any identity terms,
                # which become global phases when exponentiated.
                identity_phases = [
                    x.coeff for x in operator.primitive
                    if isinstance(x, PauliOp) and sum(x.primitive.x +
                                                      x.primitive.z) == 0
                ]
                # Construct sum without the identity operators.
                new_primitive = SummedOp(oplist,
                                         coeff=operator.primitive.coeff)
                trotterized = self.trotter.convert(new_primitive)
                circuit_no_identities = self._recursive_convert(trotterized)
                # Set the global phase of the QuantumCircuit to account for removed identity terms.
                global_phase = -sum(identity_phases) * operator.primitive.coeff
                circuit_no_identities.primitive.global_phase = global_phase
                return circuit_no_identities
            # Covers ListOp, ComposedOp, TensoredOp
            elif isinstance(operator.primitive, ListOp):
                converted_ops = [
                    self._recursive_convert(op)
                    for op in operator.primitive.oplist
                ]
                return operator.primitive.__class__(converted_ops,
                                                    coeff=operator.coeff)
        elif isinstance(operator, ListOp):
            return operator.traverse(self.convert).reduce()

        return operator
Ejemplo n.º 5
0
 def exp_i(self) -> OperatorBase:
     """Return a ``CircuitOp`` equivalent to e^-iH for this operator H"""
     return CircuitOp(HamiltonianGate(self.primitive, time=self.coeff))
Ejemplo n.º 6
0
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"))
Plus = make_immutable(H.compose(Zero))
Minus = make_immutable(H.compose(X).compose(Zero))