示例#1
0
    def to_matrix(self, massive: bool = False) -> np.ndarray:
        OperatorBase._check_massive("to_matrix", True, self.num_qubits,
                                    massive)

        # Combination function must be able to handle classical values.
        # Note: this can end up, when we have list operators containing other list operators, as a
        #       ragged array and numpy 1.19 raises a deprecation warning unless this is explicitly
        #       done as object type now - was implicit before.
        mat = self.combo_fn(
            np.asarray([
                op.to_matrix(massive=massive) * self.coeff
                for op in self.oplist
            ],
                       dtype=object))
        # Note: As ComposedOp has a combo function of inner product we can end up here not with
        # a matrix (array) but a scalar. In which case we make a single element array of it.
        if isinstance(mat, Number):
            mat = [mat]
        return np.asarray(mat, dtype=complex)
示例#2
0
    def convert(self, operator: OperatorBase) -> OperatorBase:
        """ Convert the Operator to ``CircuitStateFns``, recursively if ``traverse`` is True.

        Args:
            operator: The Operator to convert

        Returns:
            The converted Operator.
        """

        if isinstance(operator, DictStateFn) and self._convert_dicts:
            return CircuitStateFn.from_dict(operator.primitive)
        if isinstance(operator, VectorStateFn) and self._convert_vectors:
            return CircuitStateFn.from_vector(operator.to_matrix(massive=True))
        elif isinstance(operator,
                        ListOp) and 'Dict' in operator.primitive_strings():
            return operator.traverse(self.convert)
        else:
            return operator
示例#3
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(ListOp, new_self)

        if front:
            return other.compose(new_self)
        # Avoid circular dependency
        # pylint: disable=cyclic-import
        from .composed_op import ComposedOp
        return ComposedOp([new_self, other])
示例#4
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)
        if front:
            return other.compose(new_self)
        if isinstance(other, ComposedOp):
            return ComposedOp([new_self] + other.oplist)

        return ComposedOp([new_self, other])
示例#5
0
    def equals(self, other: OperatorBase) -> bool:
        self_reduced, other_reduced = self.reduce(), other.reduce()

        if not isinstance(other_reduced, PauliSumOp):
            return False

        if isinstance(self_reduced.coeff, ParameterExpression) or isinstance(
                other_reduced.coeff, ParameterExpression):
            return (self_reduced.coeff == other_reduced.coeff
                    and self_reduced.primitive == other_reduced.primitive)
        return (len(self_reduced) == len(other_reduced)
                and self_reduced.primitive == other_reduced.primitive)
示例#6
0
    def convert(self, operator: OperatorBase) -> OperatorBase:
        """Accepts an Operator and returns a new Operator with the Pauli measurements replaced by
        diagonal Pauli post-rotation based measurements so they can be evaluated by sampling and
        averaging.

        Args:
            operator: The operator to convert.

        Returns:
            The converted operator.
        """
        if isinstance(operator, ListOp):
            return operator.traverse(self.convert).reduce()

        if isinstance(operator, OperatorStateFn) and operator.is_measurement:
            # Change to Pauli representation if necessary
            if (
                isinstance(operator.primitive, (ListOp, PrimitiveOp))
                and not isinstance(operator.primitive, PauliSumOp)
                and {"Pauli", "SparsePauliOp"} < operator.primitive_strings()
            ):
                logger.warning(
                    "Measured Observable 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 Observable with massive=True explicitly if they so choose.
                pauli_obsv = operator.primitive.to_pauli_op(massive=False)
                operator = StateFn(pauli_obsv, is_measurement=True, coeff=operator.coeff)

            if self._grouper and isinstance(operator.primitive, (ListOp, PauliSumOp)):
                grouped = self._grouper.convert(operator.primitive)
                operator = StateFn(grouped, is_measurement=True, coeff=operator.coeff)

            # Convert the measurement into diagonal basis (PauliBasisChange chooses
            # this basis by default).
            cob = PauliBasisChange(replacement_fn=PauliBasisChange.measurement_replacement_fn)
            return cob.convert(operator).reduce()

        return operator
示例#7
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)
        new_self.from_operator = self.from_operator

        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,
                from_operator=self.from_operator,
            )

        if isinstance(other, CircuitStateFn) and self.is_measurement:
            # pylint: disable=cyclic-import
            from ..operator_globals import Zero

            return self.compose(CircuitOp(other.primitive)).compose(
                (Zero ^ self.num_qubits) * other.coeff)

        return ComposedOp([new_self, other])
示例#8
0
def _check_is_diagonal(operator: OperatorBase) -> bool:
    """Check whether ``operator`` is diagonal.

    Args:
        operator: The operator to check for diagonality.

    Returns:
        True, if the operator is diagonal, False otherwise.

    Raises:
        OpflowError: If the operator is not diagonal.
    """
    if isinstance(operator, PauliOp):
        # every X component must be False
        if not np.any(operator.primitive.x):
            return True
        return False

    if isinstance(operator, SummedOp):
        # cover the case of sums of diagonal paulis, but don't raise since there might be summands
        # canceling the non-diagonal parts

        # ignoring mypy since we know that all operators are PauliOps
        if all(
                isinstance(op, PauliOp) and not np.any(op.primitive.x)
                for op in operator.oplist):
            return True

    if isinstance(operator, ListOp):
        return all(operator.traverse(_check_is_diagonal))

    # cannot efficiently check if a operator is diagonal, converting to matrix
    matrix = operator.to_matrix()

    if np.all(matrix == np.diag(np.diagonal(matrix))):
        return True
    return False
示例#9
0
    def compose(self,
                other: OperatorBase,
                permutation: Optional[List[int]] = None,
                front: bool = False) -> OperatorBase:
        r"""
        Composition (Linear algebra-style: A@B(x) = A(B(x))) is not well defined for states
        in the binary function model, but is well defined for measurements.

        Args:
            other: The Operator to compose with self.
            permutation: ``List[int]`` which defines permutation on other operator.
            front: If front==True, return ``other.compose(self)``.

        Returns:
            An Operator equivalent to the function composition of self and other.

        Raises:
            ValueError: If self is not a measurement, it cannot be composed from the right.
        """
        # TODO maybe allow outers later to produce density operators or projectors, but not yet.
        if not self.is_measurement and not front:
            raise ValueError(
                "Composition with a Statefunction in the first operand is not defined."
            )

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

        if front:
            return other.compose(self)
        # TODO maybe include some reduction here in the subclasses - vector and Op, op and Op, etc.
        from ..primitive_ops.circuit_op import CircuitOp

        if self.primitive == {
                "0" * self.num_qubits: 1.0
        } and isinstance(other, CircuitOp):
            # Returning CircuitStateFn
            return StateFn(other.primitive,
                           is_measurement=self.is_measurement,
                           coeff=self.coeff * other.coeff)

        from ..list_ops.composed_op import ComposedOp

        if isinstance(other, ComposedOp):
            return ComposedOp([new_self] + other.oplist,
                              coeff=new_self.coeff * other.coeff)

        return ComposedOp([new_self, other])
示例#10
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(MatrixOp, new_self)

        if front:
            return other.compose(new_self)
        if isinstance(other, MatrixOp):
            return MatrixOp(
                new_self.primitive.compose(other.primitive, front=True),
                coeff=new_self.coeff * other.coeff,
            )

        return super(MatrixOp, new_self).compose(other)
示例#11
0
    def convert(self, operator: OperatorBase) -> OperatorBase:
        """Accept an Operator and return a new Operator with the Pauli measurements replaced by
        AerSnapshot-based expectation circuits.

        Args:
            operator: The operator to convert.

        Returns:
            The converted operator.
        """
        if isinstance(operator, OperatorStateFn) and operator.is_measurement:
            return self._replace_pauli_sums(operator.primitive) * operator.coeff
        elif isinstance(operator, ListOp):
            return operator.traverse(self.convert)
        else:
            return operator
示例#12
0
    def equals(self, other: OperatorBase) -> bool:
        """Check if other is equal to self.

        Note:
            This is not a mathematical check for equality.
            If ``self`` and ``other`` implement the same operation but differ
            in the representation (e.g. different type of summands)
            ``equals`` will evaluate to ``False``.

        Args:
            other: The other operator to check for equality.

        Returns:
            True, if other and self are equal, otherwise False.

        Examples:
            >>> from qiskit.opflow import X, Z
            >>> 2 * X == X + X
            True
            >>> X + Z == Z + X
            True
        """
        self_reduced, other_reduced = self.reduce(), other.reduce()
        if not isinstance(other_reduced, type(self_reduced)):
            return False

        # check if reduced op is still a SummedOp
        if not isinstance(self_reduced, SummedOp):
            return self_reduced == other_reduced

        self_reduced = cast(SummedOp, self_reduced)
        other_reduced = cast(SummedOp, other_reduced)
        if len(self_reduced.oplist) != len(other_reduced.oplist):
            return False

        # absorb coeffs into the operators
        if self_reduced.coeff != 1:
            self_reduced = SummedOp(
                [op * self_reduced.coeff for op in self_reduced.oplist])
        if other_reduced.coeff != 1:
            other_reduced = SummedOp(
                [op * other_reduced.coeff for op in other_reduced.oplist])

        # compare independent of order
        return all(any(i == j for j in other_reduced) for i in self_reduced)
示例#13
0
    def equals(self, other: OperatorBase) -> bool:
        self_reduced, other_reduced = self.reduce(), other.reduce()

        if isinstance(other_reduced, PauliOp):
            other_reduced = PauliSumOp(
                SparsePauliOp(other_reduced.primitive,
                              coeffs=[other_reduced.coeff]))

        if not isinstance(other_reduced, PauliSumOp):
            return False

        if isinstance(self_reduced.coeff, ParameterExpression) or isinstance(
                other_reduced.coeff, ParameterExpression):
            return self_reduced.coeff == other_reduced.coeff and self_reduced.primitive.equiv(
                other_reduced.primitive)
        return len(self_reduced) == len(
            other_reduced) and self_reduced.primitive.equiv(
                other_reduced.primitive)
示例#14
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(PauliSumOp, new_self)

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

        # Both PauliSumOps
        if isinstance(other, PauliSumOp):
            return PauliSumOp(
                new_self.primitive.dot(other.primitive),
                coeff=new_self.coeff * other.coeff,
            )
        if isinstance(other, PauliOp):
            other_primitive = SparsePauliOp(other.primitive)
            return PauliSumOp(
                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)):
            pauli_op = cast(Union[PauliOp, SummedOp], new_self.to_pauli_op())
            return pauli_op.to_circuit_op().compose(other)

        return super(PauliSumOp, new_self).compose(other)
示例#15
0
    def convert(self, operator: OperatorBase) -> OperatorBase:
        """
        Converts the Operator to tapered one by Z2 symmetries.

        Args:
            operator: the operator
        Returns:
            A new operator whose qubit number is reduced by 2.
        """
        if not isinstance(operator, PauliSumOp):
            return operator

        operator = cast(PauliSumOp, operator)

        if operator.is_zero():
            logger.info("Operator is empty, can not do two qubit reduction. "
                        "Return the empty operator back.")
            return PauliSumOp.from_list([("I" * (operator.num_qubits - 2), 0)])

        num_qubits = operator.num_qubits
        last_idx = num_qubits - 1
        mid_idx = num_qubits // 2 - 1
        sq_list = [mid_idx, last_idx]

        # build symmetries, sq_paulis:
        symmetries, sq_paulis = [], []
        for idx in sq_list:
            pauli_str = ["I"] * num_qubits

            pauli_str[idx] = "Z"
            z_sym = Pauli("".join(pauli_str)[::-1])
            symmetries.append(z_sym)

            pauli_str[idx] = "X"
            sq_pauli = Pauli("".join(pauli_str)[::-1])
            sq_paulis.append(sq_pauli)

        z2_symmetries = Z2Symmetries(symmetries, sq_paulis, sq_list,
                                     self._tapering_values)
        return z2_symmetries.taper(operator)
    def convert(self, operator: OperatorBase) -> OperatorBase:
        """Accept an Operator and return a new Operator with the Pauli measurements replaced by
        AerSnapshot-based expectation circuits.

        Args:
            operator: The operator to convert. If it contains non-hermitian terms, the
                operator is decomposed into hermitian and anti-hermitian parts.

        Returns:
            The converted operator.
        """

        if isinstance(operator, OperatorStateFn) and operator.is_measurement:
            if isinstance(operator.primitive, ListOp):
                is_herm = all(
                    (op.is_hermitian() for op in operator.primitive.oplist))
            else:
                is_herm = operator.primitive.is_hermitian()

            if not is_herm:
                pauli_sum_re = (self._replace_pauli_sums(
                    1 / 2 * (operator.primitive +
                             operator.primitive.adjoint()).reduce()) *
                                operator.coeff)
                pauli_sum_im = (self._replace_pauli_sums(
                    1 / 2j * (operator.primitive -
                              operator.primitive.adjoint()).reduce()) *
                                operator.coeff)
                pauli_sum = (pauli_sum_re + 1j * pauli_sum_im).reduce()
            else:
                pauli_sum = self._replace_pauli_sums(
                    operator.primitive) * operator.coeff
            return pauli_sum
        elif isinstance(operator, ListOp):
            return operator.traverse(self.convert)
        else:
            return operator
示例#17
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 .circuit_op import CircuitOp
        from ..state_fns.circuit_state_fn import CircuitStateFn

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

        return super(PauliOp, new_self).compose(other)
示例#18
0
 def to_density_matrix(self, massive: bool = False) -> np.ndarray:
     OperatorBase._check_massive('to_density_matrix', True, self.num_qubits, massive)
     states = int(2 ** self.num_qubits)
     return self.to_matrix(massive=massive) * np.eye(states) * self.coeff
示例#19
0
 def to_matrix(self, massive: bool = False) -> np.ndarray:
     OperatorBase._check_massive("to_matrix", True, self.num_qubits, massive)
     return self.primitive.to_matrix() * self.coeff
示例#20
0
 def to_matrix(self, massive: bool = False) -> np.ndarray:
     OperatorBase._check_massive("to_matrix", True, self.num_qubits,
                                 massive)
     unitary = qiskit.quantum_info.Operator(self.to_circuit()).data
     return unitary * self.coeff
 def to_matrix(self, massive: bool = False) -> np.ndarray:
     OperatorBase._check_massive("to_matrix", False, self.num_qubits,
                                 massive)
     vec = self.primitive.data * self.coeff
     return vec if not self.is_measurement else vec.reshape(1, -1)
示例#22
0
    def build(operator: OperatorBase,
              backend: Optional[Union[Backend, BaseBackend, QuantumInstance]] = None,
              include_custom: bool = True) -> ExpectationBase:
        """
        A factory method for convenient automatic selection of an Expectation based on the
        Operator to be converted and backend used to sample the expectation value.

        Args:
            operator: The Operator whose expectation value will be taken.
            backend: The backend which will be used to sample the expectation value.
            include_custom: Whether the factory will include the (Aer) specific custom
                expectations if their behavior against the backend might not be as expected.
                For instance when using Aer qasm_simulator with paulis the Aer snapshot can
                be used but the outcome lacks shot noise and hence does not intuitively behave
                overall as people might expect when choosing a qasm_simulator. It is however
                fast as long as the more state vector like behavior is acceptable.

        Returns:
            The expectation algorithm which best fits the Operator and backend.

        Raises:
            ValueError: If operator is not of a composition for which we know the best Expectation
                method.
        """
        backend_to_check = backend.backend if isinstance(backend, QuantumInstance) else backend

        # pylint: disable=cyclic-import
        primitives = operator.primitive_strings()
        if primitives in ({'Pauli'}, {'SparsePauliOp'}):

            if backend_to_check is None:
                # If user has Aer but didn't specify a backend, use the Aer fast expectation
                if has_aer():
                    from qiskit import Aer
                    backend_to_check = Aer.get_backend('qasm_simulator')
                # If user doesn't have Aer, use statevector_simulator
                # for < 16 qubits, and qasm with warning for more.
                else:
                    if operator.num_qubits <= 16:
                        backend_to_check = BasicAer.get_backend('statevector_simulator')
                    else:
                        logger.warning(
                            '%d qubits is a very large expectation value. '
                            'Consider installing Aer to use '
                            'Aer\'s fast expectation, which will perform better here. We\'ll use '
                            'the BasicAer qasm backend for this expectation to avoid having to '
                            'construct the %dx%d operator matrix.',
                            operator.num_qubits,
                            2 ** operator.num_qubits,
                            2 ** operator.num_qubits)
                        backend_to_check = BasicAer.get_backend('qasm_simulator')

            # If the user specified Aer qasm backend and is using a
            # Pauli operator, use the Aer fast expectation if we are including such
            # custom behaviors.
            if is_aer_qasm(backend_to_check) and include_custom:
                return AerPauliExpectation()

            # If the user specified a statevector backend (either Aer or BasicAer),
            # use a converter to produce a
            # Matrix operator and compute using matmul
            elif is_statevector_backend(backend_to_check):
                if operator.num_qubits >= 16:
                    logger.warning(
                        'Note: Using a statevector_simulator with %d qubits can be very expensive. '
                        'Consider using the Aer qasm_simulator instead to take advantage of Aer\'s '
                        'built-in fast Pauli Expectation', operator.num_qubits)
                return MatrixExpectation()

            # All other backends, including IBMQ, BasicAer QASM, go here.
            else:
                return PauliExpectation()

        elif primitives == {'Matrix'}:
            return MatrixExpectation()

        else:
            raise ValueError('Expectations of Mixed Operators not yet supported.')
    def convert(self, operator: OperatorBase) -> OperatorBase:
        r"""
        Given a ``PauliOp``, or an Operator containing ``PauliOps`` if ``_traverse`` is True,
        converts each Pauli into the basis specified by self._destination and a
        basis-change-circuit, calls ``replacement_fn`` with these two Operators, and replaces
        the ``PauliOps`` with the output of ``replacement_fn``. For example, for the built-in
        ``operator_replacement_fn`` below, each PauliOp p will be replaced by the composition
        of the basis-change Clifford ``CircuitOp`` c with the destination PauliOp d and c†,
        such that p = c·d·c†, up to global phase.

        Args:
            operator: The Operator to convert.

        Returns:
            The converted Operator.

        """
        if (isinstance(operator, OperatorStateFn)
                and isinstance(operator.primitive, PauliSumOp)
                and operator.primitive.grouping_type == "TPB"):
            primitive = operator.primitive.primitive.copy()
            origin_x = reduce(np.logical_or, primitive.table.X)
            origin_z = reduce(np.logical_or, primitive.table.Z)
            origin_pauli = Pauli((origin_z, origin_x))
            cob_instr_op, _ = self.get_cob_circuit(origin_pauli)
            primitive.table.Z = np.logical_or(primitive.table.X,
                                              primitive.table.Z)
            primitive.table.X = False
            dest_pauli_sum_op = PauliSumOp(primitive,
                                           coeff=operator.coeff,
                                           grouping_type="TPB")
            return self._replacement_fn(cob_instr_op, dest_pauli_sum_op)

        if (isinstance(operator, OperatorStateFn)
                and isinstance(operator.primitive, SummedOp) and all(
                    isinstance(op, PauliSumOp) and op.grouping_type == "TPB"
                    for op in operator.primitive.oplist)):
            sf_list: List[OperatorBase] = [
                StateFn(op, is_measurement=operator.is_measurement)
                for op in operator.primitive.oplist
            ]
            listop_of_statefns = SummedOp(oplist=sf_list, coeff=operator.coeff)
            return listop_of_statefns.traverse(self.convert)

        if isinstance(operator, OperatorStateFn) and isinstance(
                operator.primitive, PauliSumOp):
            operator = OperatorStateFn(
                operator.primitive.to_pauli_op(),
                coeff=operator.coeff,
                is_measurement=operator.is_measurement,
            )

        if isinstance(operator, PauliSumOp):
            operator = operator.to_pauli_op()

        if isinstance(operator, (Pauli, PauliOp)):
            cob_instr_op, dest_pauli_op = self.get_cob_circuit(operator)
            return self._replacement_fn(cob_instr_op, dest_pauli_op)
        if isinstance(operator,
                      StateFn) and "Pauli" in operator.primitive_strings():
            # If the StateFn/Meas only contains a Pauli, use it directly.
            if isinstance(operator.primitive, PauliOp):
                cob_instr_op, dest_pauli_op = self.get_cob_circuit(
                    operator.primitive)
                return self._replacement_fn(cob_instr_op,
                                            dest_pauli_op * operator.coeff)
            # TODO make a canonical "distribute" or graph swap as method in ListOp?
            elif operator.primitive.distributive:
                if operator.primitive.abelian:
                    origin_pauli = self.get_tpb_pauli(operator.primitive)
                    cob_instr_op, _ = self.get_cob_circuit(origin_pauli)
                    diag_ops: List[OperatorBase] = [
                        self.get_diagonal_pauli_op(op)
                        for op in operator.primitive.oplist
                    ]
                    dest_pauli_op = operator.primitive.__class__(
                        diag_ops, coeff=operator.coeff, abelian=True)
                    return self._replacement_fn(cob_instr_op, dest_pauli_op)
                else:
                    sf_list = [
                        StateFn(op, is_measurement=operator.is_measurement)
                        for op in operator.primitive.oplist
                    ]
                    listop_of_statefns = operator.primitive.__class__(
                        oplist=sf_list, coeff=operator.coeff)
                    return listop_of_statefns.traverse(self.convert)

        elif (isinstance(operator, ListOp) and self._traverse
              and "Pauli" in operator.primitive_strings()):
            # If ListOp is abelian we can find a single post-rotation circuit
            # for the whole set. For now,
            # assume operator can only be abelian if all elements are
            # Paulis (enforced in AbelianGrouper).
            if operator.abelian:
                origin_pauli = self.get_tpb_pauli(operator)
                cob_instr_op, _ = self.get_cob_circuit(origin_pauli)
                oplist = cast(List[PauliOp], operator.oplist)
                diag_ops = [self.get_diagonal_pauli_op(op) for op in oplist]
                dest_list_op = operator.__class__(diag_ops,
                                                  coeff=operator.coeff,
                                                  abelian=True)
                return self._replacement_fn(cob_instr_op, dest_list_op)
            else:
                return operator.traverse(self.convert)

        return operator
示例#24
0
 def to_matrix(self, massive: bool = False) -> np.ndarray:
     OperatorBase._check_massive("to_matrix", True, self.num_qubits, massive)
     if isinstance(self.coeff, ParameterExpression):
         return (self.primitive.to_matrix(sparse=True)).toarray() * self.coeff
     return (self.primitive.to_matrix(sparse=True) * self.coeff).toarray()
示例#25
0
    def convert(
        self,
        operator: OperatorBase,
        params: Optional[Dict[Parameter, Union[float, List[float], List[List[float]]]]] = None,
    ) -> OperatorBase:
        r"""
        Converts the Operator to one in which the CircuitStateFns are replaced by
        DictStateFns or VectorStateFns. Extracts the CircuitStateFns out of the Operator,
        caches them, calls ``sample_circuits`` below to get their converted replacements,
        and replaces the CircuitStateFns in operator with the replacement StateFns.

        Args:
            operator: The Operator to convert
            params: A dictionary mapping parameters to either single binding values or lists of
                binding values.

        Returns:
            The converted Operator with CircuitStateFns replaced by DictStateFns or VectorStateFns.
        Raises:
            OpflowError: if extracted circuits are empty.
        """
        # check if the operator should be cached
        op_id = operator.instance_id
        # op_id = id(operator)
        if op_id not in self._cached_ops.keys():
            # delete cache if we only want to cache one operator
            if self._caching == "last":
                self.clear_cache()

            # convert to circuit and reduce
            operator_dicts_replaced = operator.to_circuit_op()
            self._reduced_op_cache = operator_dicts_replaced.reduce()

            # extract circuits
            self._circuit_ops_cache = {}
            self._extract_circuitstatefns(self._reduced_op_cache)
            if not self._circuit_ops_cache:
                raise OpflowError(
                    "Circuits are empty. "
                    "Check that the operator is an instance of CircuitStateFn or its ListOp."
                )
            self._transpiled_circ_cache = None
            self._transpile_before_bind = True
        else:
            # load the cached circuits
            self._reduced_op_cache = self._cached_ops[op_id].reduced_op_cache
            self._circuit_ops_cache = self._cached_ops[op_id].circuit_ops_cache
            self._transpiled_circ_cache = self._cached_ops[op_id].transpiled_circ_cache
            self._transpile_before_bind = self._cached_ops[op_id].transpile_before_bind
            self._transpiled_circ_templates = self._cached_ops[op_id].transpiled_circ_templates

        return_as_list = False
        if params is not None and len(params.keys()) > 0:
            p_0 = list(params.values())[0]
            if isinstance(p_0, (list, np.ndarray)):
                num_parameterizations = len(p_0)
                param_bindings = [
                    {param: value_list[i] for param, value_list in params.items()}  # type: ignore
                    for i in range(num_parameterizations)
                ]
                return_as_list = True
            else:
                num_parameterizations = 1
                param_bindings = [params]

        else:
            param_bindings = None
            num_parameterizations = 1

        # Don't pass circuits if we have in the cache, the sampling function knows to use the cache
        circs = list(self._circuit_ops_cache.values()) if not self._transpiled_circ_cache else None
        p_b = cast(List[Dict[Parameter, float]], param_bindings)
        sampled_statefn_dicts = self.sample_circuits(circuit_sfns=circs, param_bindings=p_b)

        def replace_circuits_with_dicts(operator, param_index=0):
            if isinstance(operator, CircuitStateFn):
                return sampled_statefn_dicts[id(operator)][param_index]
            elif isinstance(operator, ListOp):
                return operator.traverse(
                    partial(replace_circuits_with_dicts, param_index=param_index)
                )
            else:
                return operator

        # store the operator we constructed, if it isn't stored already
        if op_id not in self._cached_ops.keys():
            op_cache = OperatorCache()
            op_cache.reduced_op_cache = self._reduced_op_cache
            op_cache.circuit_ops_cache = self._circuit_ops_cache
            op_cache.transpiled_circ_cache = self._transpiled_circ_cache
            op_cache.transpile_before_bind = self._transpile_before_bind
            op_cache.transpiled_circ_templates = self._transpiled_circ_templates
            self._cached_ops[op_id] = op_cache

        if return_as_list:
            return ListOp(
                [
                    replace_circuits_with_dicts(self._reduced_op_cache, param_index=i)
                    for i in range(num_parameterizations)
                ]
            )
        else:
            return replace_circuits_with_dicts(self._reduced_op_cache, param_index=0)
示例#26
0
 def to_density_matrix(self, massive: bool = False) -> np.ndarray:
     OperatorBase._check_massive('to_density_matrix', True, self.num_qubits, massive)
     return self.primitive.to_operator().data * self.coeff