Example #1
0
    def __init__(self, group_paulis: bool = True) -> None:
        """
        Args:
            group_paulis: Whether to group the Pauli measurements into commuting sums, which all
                have the same diagonalizing circuit.

        """
        self._grouper = AbelianGrouper() if group_paulis else None
Example #2
0
class PauliExpectation(ExpectationBase):
    r"""
    An Expectation converter for Pauli-basis observables by changing Pauli measurements to a
    diagonal ({Z, I}^n) basis and appending circuit post-rotations to the measured state function.
    Optionally groups the Paulis with the same post-rotations (those that commute with one
    another, or form Abelian groups) into single measurements to reduce circuit execution
    overhead.

    """
    def __init__(self, group_paulis: bool = True) -> None:
        """
        Args:
            group_paulis: Whether to group the Pauli measurements into commuting sums, which all
                have the same diagonalizing circuit.

        """
        self._grouper = AbelianGrouper() if group_paulis else None

    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

    def compute_variance(
            self, exp_op: OperatorBase) -> Union[list, float, np.ndarray]:
        def sum_variance(operator):
            if isinstance(operator, ComposedOp):
                sfdict = operator.oplist[1]
                measurement = operator.oplist[0]
                average = measurement.eval(sfdict)
                variance = sum((v * (measurement.eval(b) - average))**2
                               for (b, v) in sfdict.primitive.items())
                return operator.coeff * variance

            elif isinstance(operator, ListOp):
                return operator.combo_fn(
                    [sum_variance(op) for op in operator.oplist])

            return 0.0

        return sum_variance(exp_op)