Exemplo n.º 1
0
    def from_dict(density_dict: dict) -> 'CircuitStateFn':
        """ Construct the CircuitStateFn from a dict mapping strings to probability densities.

        Args:
            density_dict: The dict representing the desired state.

        Returns:
            The CircuitStateFn created from the dict.
        """
        # If the dict is sparse (elements <= qubits), don't go
        # building a statevector to pass to Qiskit's
        # initializer, just create a sum.
        if len(density_dict) <= len(list(density_dict.keys())[0]):
            statefn_circuits = []
            for bstr, prob in density_dict.items():
                qc = QuantumCircuit(len(bstr))
                # NOTE: Reversing endianness!!
                for (index, bit) in enumerate(reversed(bstr)):
                    if bit == '1':
                        qc.x(index)
                sf_circuit = CircuitStateFn(qc, coeff=prob)
                statefn_circuits += [sf_circuit]
            if len(statefn_circuits) == 1:
                return statefn_circuits[0]
            else:
                return cast(
                    CircuitStateFn,
                    SummedOp(cast(List[OperatorBase], statefn_circuits)))
        else:
            sf_dict = StateFn(density_dict)
            return CircuitStateFn.from_vector(sf_dict.to_matrix())
Exemplo n.º 2
0
    def eval(
        self,
        front: Optional[
            Union[str, Dict[str, complex], np.ndarray, OperatorBase, Statevector]
        ] = None,
    ) -> Union[OperatorBase, complex]:
        if front is None:
            sparse_vector_state_fn = self.to_spmatrix_op().eval()
            return sparse_vector_state_fn

        if not self.is_measurement and isinstance(front, OperatorBase):
            raise ValueError(
                'Cannot compute overlap with StateFn or Operator if not Measurement. Try taking '
                'sf.adjoint() first to convert to measurement.')

        if isinstance(front, ListOp) and front.distributive:
            return front.combo_fn([self.eval(front.coeff * front_elem)
                                   for front_elem in front.oplist])

        # For now, always do this. If it's not performant, we can be more granular.
        if not isinstance(front, OperatorBase):
            front = StateFn(front)

        # pylint: disable=cyclic-import
        from ..operator_globals import EVAL_SIG_DIGITS

        # If the primitive is a lookup of bitstrings,
        # we define all missing strings to have a function value of
        # zero.
        if isinstance(front, DictStateFn):
            return np.round(
                cast(float, sum([v * front.primitive.get(b, 0) for (b, v) in
                                 self.primitive.items()]) * self.coeff * front.coeff),
                decimals=EVAL_SIG_DIGITS)

        # All remaining possibilities only apply when self.is_measurement is True

        if isinstance(front, VectorStateFn):
            # TODO does it need to be this way for measurement?
            # return sum([v * front.primitive.data[int(b, 2)] *
            # np.conj(front.primitive.data[int(b, 2)])
            return np.round(
                cast(float, sum([v * front.primitive.data[int(b, 2)] for (b, v) in
                                 self.primitive.items()]) * self.coeff),
                decimals=EVAL_SIG_DIGITS)

        from .circuit_state_fn import CircuitStateFn
        if isinstance(front, CircuitStateFn):
            # Don't reimplement logic from CircuitStateFn
            self_adjoint = cast(DictStateFn, self.adjoint())
            return np.conj(front.adjoint().eval(self_adjoint.primitive)) * self.coeff

        from .operator_state_fn import OperatorStateFn
        if isinstance(front, OperatorStateFn):
            return cast(Union[OperatorBase, float, complex], front.adjoint().eval(self.adjoint()))

        # All other OperatorBases go here
        self_adjoint = cast(DictStateFn, self.adjoint())
        adjointed_eval = cast(OperatorBase, front.adjoint().eval(self_adjoint.primitive))
        return adjointed_eval.adjoint() * self.coeff
    def eval(
        self,
        front: Optional[Union[str, dict, np.ndarray, OperatorBase,
                              Statevector]] = None
    ) -> Union[OperatorBase, complex]:
        if front is None:
            matrix = cast(MatrixOp,
                          self.primitive.to_matrix_op()).primitive.data
            # pylint: disable=cyclic-import
            from .vector_state_fn import VectorStateFn

            return VectorStateFn(matrix[0, :])

        if not self.is_measurement and isinstance(front, OperatorBase):
            raise ValueError(
                "Cannot compute overlap with StateFn or Operator if not Measurement. Try taking "
                "sf.adjoint() first to convert to measurement.")

        if not isinstance(front, OperatorBase):
            front = StateFn(front)

        if isinstance(self.primitive, ListOp) and self.primitive.distributive:
            evals = [
                OperatorStateFn(op,
                                is_measurement=self.is_measurement).eval(front)
                for op in self.primitive.oplist
            ]
            result = self.primitive.combo_fn(evals)
            if isinstance(result, list):
                multiplied = self.primitive.coeff * self.coeff * np.array(
                    result)
                return multiplied.tolist()
            return result * self.coeff * self.primitive.coeff

        # pylint: disable=cyclic-import
        from .vector_state_fn import VectorStateFn

        if isinstance(self.primitive, PauliSumOp) and isinstance(
                front, VectorStateFn):
            return (
                front.primitive.expectation_value(self.primitive.primitive) *
                self.coeff * front.coeff)

        # Need an ListOp-specific carve-out here to make sure measurement over a ListOp doesn't
        # produce two-dimensional ListOp from composing from both sides of primitive.
        # Can't use isinstance because this would include subclasses.
        # pylint: disable=unidiomatic-typecheck
        if isinstance(front, ListOp) and type(front) == ListOp:
            return front.combo_fn([
                self.eval(front.coeff * front_elem)
                for front_elem in front.oplist
            ])

        # If we evaluate against a circuit, evaluate it to a vector so we
        # make sure to only do the expensive circuit simulation once
        if isinstance(front, CircuitStateFn):
            front = front.eval()

        return front.adjoint().eval(
            cast(OperatorBase, self.primitive.eval(front))) * self.coeff
Exemplo n.º 4
0
 def tensor(self, other: OperatorBase) -> OperatorBase:
     if isinstance(other, VectorStateFn):
         return StateFn(
             self.primitive.tensor(other.primitive),
             coeff=self.coeff * other.coeff,
             is_measurement=self.is_measurement,
         )
     return TensoredOp([self, other])
Exemplo n.º 5
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
Exemplo n.º 6
0
    def eval(
        self,
        front: Optional[
            Union[str, Dict[str, complex], np.ndarray, Statevector, OperatorBase]
        ] = None,
    ) -> Union[OperatorBase, complex]:
        if front is None:  # this object is already a VectorStateFn
            return self

        if not self.is_measurement and isinstance(front, OperatorBase):
            raise ValueError(
                "Cannot compute overlap with StateFn or Operator if not Measurement. Try taking "
                "sf.adjoint() first to convert to measurement."
            )

        if isinstance(front, ListOp) and front.distributive:
            return front.combo_fn(
                [self.eval(front.coeff * front_elem) for front_elem in front.oplist]
            )

        if not isinstance(front, OperatorBase):
            front = StateFn(front)

        # pylint: disable=cyclic-import
        from ..operator_globals import EVAL_SIG_DIGITS
        from .operator_state_fn import OperatorStateFn
        from .circuit_state_fn import CircuitStateFn
        from .dict_state_fn import DictStateFn

        if isinstance(front, DictStateFn):
            return np.round(
                sum(
                    [
                        v * self.primitive.data[int(b, 2)] * front.coeff
                        for (b, v) in front.primitive.items()
                    ]
                )
                * self.coeff,
                decimals=EVAL_SIG_DIGITS,
            )

        if isinstance(front, VectorStateFn):
            # Need to extract the element or np.array([1]) is returned.
            return np.round(
                np.dot(self.to_matrix(), front.to_matrix())[0], decimals=EVAL_SIG_DIGITS
            )

        if isinstance(front, CircuitStateFn):
            # Don't reimplement logic from CircuitStateFn
            return np.conj(front.adjoint().eval(self.adjoint().primitive)) * self.coeff

        if isinstance(front, OperatorStateFn):
            return front.adjoint().eval(self.primitive) * self.coeff

        return front.adjoint().eval(self.adjoint().primitive).adjoint() * self.coeff  # type: ignore
Exemplo n.º 7
0
 def tensor(self, other: OperatorBase) -> OperatorBase:
     # Both dicts
     if isinstance(other, DictStateFn):
         new_dict = {k1 + k2: v1 * v2 for ((k1, v1,), (k2, v2)) in
                     itertools.product(self.primitive.items(), other.primitive.items())}
         return StateFn(new_dict,
                        coeff=self.coeff * other.coeff,
                        is_measurement=self.is_measurement)
     # pylint: disable=cyclic-import
     from ..list_ops.tensored_op import TensoredOp
     return TensoredOp([self, other])
Exemplo n.º 8
0
    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)])
Exemplo n.º 9
0
    def measurement_replacement_fn(
        cob_instr_op: PrimitiveOp, dest_pauli_op: Union[PauliOp, PauliSumOp, ListOp]
    ) -> OperatorBase:
        r"""
        A built-in convenience replacement function which produces measurements
        isomorphic to an ``OperatorStateFn`` measurement holding the origin ``PauliOp``.

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

        Returns:
            The ``~StateFn @ CircuitOp`` composition equivalent to a measurement by the original
            ``PauliOp``.
        """
        return ComposedOp([StateFn(dest_pauli_op, is_measurement=True), cob_instr_op])
Exemplo n.º 10
0
    def sample_circuits(
        self,
        circuit_sfns: Optional[List[CircuitStateFn]] = None,
        param_bindings: Optional[List[Dict[Parameter, float]]] = None,
    ) -> Dict[int, List[StateFn]]:
        r"""
        Samples the CircuitStateFns and returns a dict associating their ``id()`` values to their
        replacement DictStateFn or VectorStateFn. If param_bindings is provided,
        the CircuitStateFns are broken into their parameterizations, and a list of StateFns is
        returned in the dict for each circuit ``id()``. Note that param_bindings is provided here
        in a different format than in ``convert``, and lists of parameters within the dict is not
        supported, and only binding dicts which are valid to be passed into Terra can be included
        in this list.

        Args:
            circuit_sfns: The list of CircuitStateFns to sample.
            param_bindings: The parameterizations to bind to each CircuitStateFn.

        Returns:
            The dictionary mapping ids of the CircuitStateFns to their replacement StateFns.
        Raises:
            OpflowError: if extracted circuits are empty.
        """
        if not circuit_sfns and not self._transpiled_circ_cache:
            raise OpflowError("CircuitStateFn is empty and there is no cache.")

        if circuit_sfns:
            self._transpiled_circ_templates = None
            if self._statevector or circuit_sfns[0].from_operator:
                circuits = [op_c.to_circuit(meas=False) for op_c in circuit_sfns]
            else:
                circuits = [op_c.to_circuit(meas=True) for op_c in circuit_sfns]

            try:
                self._transpiled_circ_cache = self.quantum_instance.transpile(
                    circuits, pass_manager=self.quantum_instance.unbound_pass_manager
                )
            except QiskitError:
                logger.debug(
                    r"CircuitSampler failed to transpile circuits with unbound "
                    r"parameters. Attempting to transpile only when circuits are bound "
                    r"now, but this can hurt performance due to repeated transpilation."
                )
                self._transpile_before_bind = False
                self._transpiled_circ_cache = circuits
        else:
            circuit_sfns = list(self._circuit_ops_cache.values())

        if param_bindings is not None:
            if self._param_qobj:
                start_time = time()
                ready_circs = self._prepare_parameterized_run_config(param_bindings)
                end_time = time()
                logger.debug("Parameter conversion %.5f (ms)", (end_time - start_time) * 1000)
            else:
                start_time = time()
                ready_circs = [
                    circ.assign_parameters(_filter_params(circ, binding))
                    for circ in self._transpiled_circ_cache
                    for binding in param_bindings
                ]
                end_time = time()
                logger.debug("Parameter binding %.5f (ms)", (end_time - start_time) * 1000)
        else:
            ready_circs = self._transpiled_circ_cache

        # run transpiler passes on bound circuits
        if self._transpile_before_bind and self.quantum_instance.bound_pass_manager is not None:
            ready_circs = self.quantum_instance.transpile(
                ready_circs, pass_manager=self.quantum_instance.bound_pass_manager
            )

        results = self.quantum_instance.execute(
            ready_circs, had_transpiled=self._transpile_before_bind
        )

        if param_bindings is not None and self._param_qobj:
            self._clean_parameterized_run_config()

        # Wipe parameterizations, if any
        # self.quantum_instance._run_config.parameterizations = None

        sampled_statefn_dicts = {}
        for i, op_c in enumerate(circuit_sfns):
            # Taking square root because we're replacing a statevector
            # representation of probabilities.
            reps = len(param_bindings) if param_bindings is not None else 1
            c_statefns = []
            for j in range(reps):
                circ_index = (i * reps) + j
                circ_results = results.data(circ_index)

                if "expval_measurement" in circ_results:
                    avg = circ_results["expval_measurement"]
                    # Will be replaced with just avg when eval is called later
                    num_qubits = circuit_sfns[0].num_qubits
                    result_sfn = DictStateFn(
                        "0" * num_qubits,
                        coeff=avg * op_c.coeff,
                        is_measurement=op_c.is_measurement,
                        from_operator=op_c.from_operator,
                    )
                elif self._statevector:
                    result_sfn = StateFn(
                        op_c.coeff * results.get_statevector(circ_index),
                        is_measurement=op_c.is_measurement,
                    )
                else:
                    shots = self.quantum_instance._run_config.shots
                    result_sfn = DictStateFn(
                        {
                            b: (v / shots) ** 0.5 * op_c.coeff
                            for (b, v) in results.get_counts(circ_index).items()
                        },
                        is_measurement=op_c.is_measurement,
                        from_operator=op_c.from_operator,
                    )
                if self._attach_results:
                    result_sfn.execution_results = circ_results
                c_statefns.append(result_sfn)
            sampled_statefn_dicts[id(op_c)] = c_statefns
        return sampled_statefn_dicts
Exemplo n.º 11
0
    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