Source code for qiskit.aqua.operators.converters.abelian_grouper

# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 2020.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

""" AbelianGrouper Class """

import logging
import itertools
import networkx as nx

from qiskit.aqua import AquaError
from ..operator_base import OperatorBase
from ..list_ops.list_op import ListOp
from ..list_ops.summed_op import SummedOp
from ..state_fns.operator_state_fn import OperatorStateFn
from ..primitive_ops.pauli_op import PauliOp
from .converter_base import ConverterBase

logger = logging.getLogger(__name__)


[docs]class AbelianGrouper(ConverterBase): """ The AbelianGrouper converts SummedOps into a sum of Abelian sums. Meaning, it will traverse the Operator, and when it finds a SummedOp, it will evaluate which of the summed sub-Operators commute with one another. It will then convert each of the groups of commuting Operators into their own SummedOps, and return the sum-of-commuting-SummedOps. This is particularly useful for cases where mutually commuting groups can be handled similarly, as in the case of Pauli Expectations, where commuting Paulis have the same diagonalizing circuit rotation, or Pauli Evolutions, where commuting Paulis can be diagonalized together. """ def __init__(self, traverse: bool = True) -> None: """ Args: traverse: Whether to convert only the Operator passed to ``convert``, or traverse down that Operator. """ self._traverse = traverse
[docs] def convert(self, operator: OperatorBase) -> OperatorBase: """ Check if operator is a SummedOp, in which case covert it into a sum of mutually commuting sums, or if the Operator contains sub-Operators and ``traverse`` is True, attempt to convert any sub-Operators. Args: operator: The Operator to attempt to convert. Returns: The converted Operator. """ # pylint: disable=cyclic-import,import-outside-toplevel from ..evolutions.evolved_op import EvolvedOp if isinstance(operator, ListOp): if isinstance(operator, SummedOp) and all([isinstance(op, PauliOp) for op in operator.oplist]): # For now, we only support graphs over Paulis. return self.group_subops(operator) elif self._traverse: return operator.traverse(self.convert) else: return operator elif isinstance(operator, OperatorStateFn) and self._traverse: return OperatorStateFn(self.convert(operator.primitive), is_measurement=operator.is_measurement, coeff=operator.coeff) elif isinstance(operator, EvolvedOp) and self._traverse: return EvolvedOp(self.convert(operator.primitive), coeff=operator.coeff) else: return operator
[docs] def group_subops(self, list_op: ListOp) -> 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: AquaError: Any of list_op's sub-ops do not have a ``commutes`` method. """ if any([not hasattr(op, 'commutes') for op in list_op.oplist]): raise AquaError('Cannot determine Abelian groups if an Operator in list_op does not ' 'contain a `commutes` method'.format()) commutation_graph = nx.Graph() commutation_graph.add_nodes_from(list_op.oplist) commutation_graph.add_edges_from(filter(lambda ops: not ops[0].commutes(ops[1]), itertools.combinations(list_op.oplist, 2))) # Keys in coloring_dict are nodes, values are colors # pylint: disable=no-member coloring_dict = nx.coloring.greedy_color(commutation_graph, strategy='largest_first') groups = {} for op, color in coloring_dict.items(): groups.setdefault(color, []).append(op) group_ops = [list_op.__class__(group, abelian=True) for group in groups.values()] if len(group_ops) == 1: return group_ops[0] * list_op.coeff else: return list_op.__class__(group_ops, coeff=list_op.coeff)