Example #1
0
    def _find_taper_op(
        self,
        qubit_op: PauliSumOp,
        sector_locator: Optional[Callable[[Z2Symmetries],
                                          Optional[List[int]]]] = None,
    ) -> Tuple[PauliSumOp, Z2Symmetries]:
        # Return operator unchanged and empty symmetries if we do not taper
        tapered_qubit_op = qubit_op
        z2_symmetries = self._no_symmetries

        # If we were given a sector, or one might be located, we first need to find any symmetries
        if self.z2symmetry_reduction is not None:
            z2_symmetries = Z2Symmetries.find_Z2_symmetries(qubit_op)
            if z2_symmetries.is_empty():
                logger.debug("No Z2 symmetries found")
            else:
                # As we have symmetries, if we have a sector locator, if that provides one back
                # it will override any value defined on constructor
                if sector_locator is not None and self.z2symmetry_reduction == "auto":
                    z2symmetry_reduction = sector_locator(z2_symmetries)
                    if z2symmetry_reduction is not None:
                        self.z2symmetry_reduction = z2symmetry_reduction  # Overrides any value

                    # We may end up that neither were we given a sector nor that the locator
                    # returned one. Since though we may have found valid symmetries above we should
                    # simply just forget about them so as not to return something we are not using.
                    if self.z2symmetry_reduction is None:
                        z2_symmetries = self._no_symmetries

        # So now if we have a sector and have symmetries we found we can attempt to taper
        if (self.z2symmetry_reduction is not None
                and self.z2symmetry_reduction != "auto"
                and not z2_symmetries.is_empty()):
            # check sector definition fits to symmetries found
            if len(self._z2symmetry_reduction) != len(
                    z2_symmetries.symmetries):
                raise QiskitNatureError(
                    "z2symmetry_reduction tapering values list has "
                    "invalid length {} should be {}".format(
                        len(self._z2symmetry_reduction),
                        len(z2_symmetries.symmetries)))
            # Check all operators commute with main operator's symmetry
            logger.debug(
                "Sanity check that operator commutes with the symmetry")
            symmetry_ops = []
            for symmetry in z2_symmetries.symmetries:
                symmetry_ops.append(
                    PauliSumOp.from_list([(symmetry.to_label(), 1.0)]))
            commutes = QubitConverter._check_commutes(symmetry_ops, qubit_op)
            if not commutes:
                raise QiskitNatureError(
                    "Z2 symmetry failure. The operator must commute "
                    "with symmetries found from it!")

            z2_symmetries.tapering_values = self._z2symmetry_reduction
            tapered_qubit_op = z2_symmetries.taper(
                qubit_op) if commutes else None

        return tapered_qubit_op, z2_symmetries
Example #2
0
 def _no_symmetries(self) -> Z2Symmetries:
     return Z2Symmetries([], [], [], None)