예제 #1
0
    def add(self, other: OperatorBase) -> Union["EvolvedOp", SummedOp]:
        if not self.num_qubits == other.num_qubits:
            raise ValueError(
                "Sum over operators with different numbers of qubits, {} and {}, is not well "
                "defined".format(self.num_qubits, other.num_qubits))

        if isinstance(other, EvolvedOp) and self.primitive == other.primitive:
            return EvolvedOp(self.primitive, coeff=self.coeff + other.coeff)

        if isinstance(other, SummedOp):
            op_list = [cast(OperatorBase, self)] + other.oplist
            return SummedOp(op_list)

        return SummedOp([self, other])
예제 #2
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())
예제 #3
0
    def add(self, other: OperatorBase) -> OperatorBase:
        if not self.num_qubits == other.num_qubits:
            raise ValueError(
                "Sum over operators with different numbers of qubits, {} and {}, is not well "
                "defined".format(self.num_qubits, other.num_qubits)
            )

        if isinstance(other, PauliOp) and self.primitive == other.primitive:
            return PauliOp(self.primitive, coeff=self.coeff + other.coeff)

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

        if (
            isinstance(other, PauliOp)
            and isinstance(self.coeff, (int, float, complex))
            and isinstance(other.coeff, (int, float, complex))
        ):
            return PauliSumOp(
                SparsePauliOp(self.primitive, coeffs=[self.coeff])
                + SparsePauliOp(other.primitive, coeffs=[other.coeff])
            )

        if isinstance(other, PauliSumOp) and isinstance(self.coeff, (int, float, complex)):
            return PauliSumOp(SparsePauliOp(self.primitive, coeffs=[self.coeff])) + other

        return SummedOp([self, other])
예제 #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
    def add(self, other: OperatorBase) -> Union["OperatorStateFn", SummedOp]:
        if not self.num_qubits == other.num_qubits:
            raise ValueError(
                "Sum over statefns with different numbers of qubits, {} and {}, is not well "
                "defined".format(self.num_qubits, other.num_qubits))

        # Right now doesn't make sense to add a StateFn to a Measurement
        if isinstance(other, OperatorStateFn
                      ) and self.is_measurement == other.is_measurement:
            if isinstance(other.primitive,
                          OperatorBase) and self.primitive == other.primitive:
                return OperatorStateFn(
                    self.primitive,
                    coeff=self.coeff + other.coeff,
                    is_measurement=self.is_measurement,
                )
            # Covers Statevector and custom.
            elif isinstance(other, OperatorStateFn):
                # Also assumes scalar multiplication is available
                return OperatorStateFn(
                    (self.coeff * self.primitive).add(other.primitive *
                                                      other.coeff),
                    is_measurement=self._is_measurement,
                )

        return SummedOp([self, other])
예제 #6
0
    def add(self, other: OperatorBase) -> OperatorBase:
        if not self.num_qubits == other.num_qubits:
            raise ValueError('Sum over operators with different numbers of qubits, '
                             '{} and {}, is not well '
                             'defined'.format(self.num_qubits, other.num_qubits))

        if isinstance(other, CircuitStateFn) and self.primitive == other.primitive:
            return CircuitStateFn(self.primitive, coeff=self.coeff + other.coeff)

        # Covers all else.
        return SummedOp([self, other])
예제 #7
0
    def add(self, other: OperatorBase) -> OperatorBase:
        if not self.num_qubits == other.num_qubits:
            raise ValueError(
                'Sum over statefns with different numbers of qubits, {} and {}, is not well '
                'defined'.format(self.num_qubits, other.num_qubits))

        # Right now doesn't make sense to add a StateFn to a Measurement
        if isinstance(other, VectorStateFn) and self.is_measurement == other.is_measurement:
            # Covers Statevector and custom.
            return VectorStateFn((self.coeff * self.primitive) + (other.primitive * other.coeff),
                                 is_measurement=self._is_measurement)
        return SummedOp([self, other])
예제 #8
0
    def group_subops(cls, list_op: Union[ListOp, PauliSumOp]) -> ListOp:
        """Given a ListOp, attempt to group into Abelian ListOps of the same type.

        Args:
            list_op: The Operator to group into Abelian groups

        Returns:
            The grouped Operator.

        Raises:
            OpflowError: If any of list_op's sub-ops is not ``PauliOp``.
        """
        if isinstance(list_op, ListOp):
            for op in list_op.oplist:
                if not isinstance(op, PauliOp):
                    raise OpflowError(
                        "Cannot determine Abelian groups if any Operator in list_op is not "
                        f"`PauliOp`. E.g., {op} ({type(op)})")

        edges = cls._anti_commutation_graph(list_op)
        nodes = range(len(list_op))

        graph = rx.PyGraph()
        graph.add_nodes_from(nodes)
        graph.add_edges_from_no_data(edges)
        # Keys in coloring_dict are nodes, values are colors
        coloring_dict = rx.graph_greedy_color(graph)
        groups = defaultdict(list)
        for idx, color in coloring_dict.items():
            groups[color].append(idx)

        if isinstance(list_op, PauliSumOp):
            primitive = list_op.primitive
            return SummedOp(
                [
                    PauliSumOp(primitive[group], grouping_type="TPB")
                    for group in groups.values()
                ],
                coeff=list_op.coeff,
            )

        group_ops: List[ListOp] = [
            list_op.__class__([list_op[idx] for idx in group], abelian=True)
            for group in groups.values()
        ]
        if len(group_ops) == 1:
            return group_ops[0].mul(list_op.coeff)
        return list_op.__class__(group_ops, coeff=list_op.coeff)
예제 #9
0
    def add(self, other: OperatorBase) -> OperatorBase:
        if not self.num_qubits == other.num_qubits:
            raise ValueError(
                f"Sum of operators with different numbers of qubits, {self.num_qubits} and "
                f"{other.num_qubits}, is not well defined"
            )

        if isinstance(other, PauliSumOp):
            return PauliSumOp(self.coeff * self.primitive + other.coeff * other.primitive, coeff=1)

        if isinstance(other, PauliOp):
            return PauliSumOp(
                self.coeff * self.primitive + other.coeff * SparsePauliOp(other.primitive)
            )

        return SummedOp([self, other])
예제 #10
0
    def add(self, other: OperatorBase) -> Union["MatrixOp", SummedOp]:
        if not self.num_qubits == other.num_qubits:
            raise ValueError(
                "Sum over operators with different numbers of qubits, {} and {}, is not well "
                "defined".format(self.num_qubits, other.num_qubits))

        if isinstance(other, MatrixOp) and self.primitive == other.primitive:
            return MatrixOp(self.primitive, coeff=self.coeff + other.coeff)

        # Terra's Operator cannot handle ParameterExpressions
        if (isinstance(other, MatrixOp)
                and not isinstance(self.coeff, ParameterExpression)
                and not isinstance(other.coeff, ParameterExpression)):
            return MatrixOp((self.coeff * self.primitive) +
                            (other.coeff * other.primitive))

        # Covers Paulis, Circuits, and all else.
        return SummedOp([self, other])
예제 #11
0
    def to_pauli_op(self, massive: bool = False) -> Union[PauliOp, SummedOp]:
        def to_native(x):
            return x.item() if isinstance(x, np.generic) else x

        if len(self.primitive) == 1:
            return PauliOp(
                Pauli(
                    (self.primitive.paulis.z[0], self.primitive.paulis.x[0])),
                to_native(np.real_if_close(self.primitive.coeffs[0])) *
                self.coeff,
            )
        coeffs = np.real_if_close(self.primitive.coeffs)
        return SummedOp(
            [
                PauliOp(pauli, to_native(coeff))
                for pauli, coeff in zip(self.primitive.paulis, coeffs)
            ],
            coeff=self.coeff,
        )
예제 #12
0
    def to_pauli_op(self, massive: bool = False) -> Union[PauliOp, SummedOp]:
        def to_native(x):
            return x.item() if isinstance(x, np.generic) else x

        if len(self.primitive) == 1:
            return PauliOp(
                Pauli((self.primitive.table.Z[0], self.primitive.table.X[0])),
                to_native(np.real_if_close(self.primitive.coeffs[0])) *
                self.coeff,
            )
        tables = self.primitive.table
        coeffs = np.real_if_close(self.primitive.coeffs)
        return SummedOp(
            [
                PauliOp(
                    Pauli((t.Z[0], t.X[0])),
                    to_native(c),
                ) for t, c in zip(tables, coeffs)
            ],
            coeff=self.coeff,
        )
예제 #13
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