def test_find_Z2_symmetries(self):
        """test for find_Z2_symmetries"""

        qubit_op = PauliSumOp.from_list([
            ("II", -1.0537076071291125),
            ("IZ", 0.393983679438514),
            ("ZI", -0.39398367943851387),
            ("ZZ", -0.01123658523318205),
            ("XX", 0.1812888082114961),
        ])
        z2_symmetries = Z2Symmetries.find_Z2_symmetries(qubit_op)
        self.assertEqual(z2_symmetries.symmetries, [Pauli("ZZ")])
        self.assertEqual(z2_symmetries.sq_paulis, [Pauli("IX")])
        self.assertEqual(z2_symmetries.sq_list, [0])
        self.assertEqual(z2_symmetries.tapering_values, None)

        tapered_op = z2_symmetries.taper(qubit_op)[1]
        self.assertEqual(tapered_op.z2_symmetries.symmetries, [Pauli("ZZ")])
        self.assertEqual(tapered_op.z2_symmetries.sq_paulis, [Pauli("IX")])
        self.assertEqual(tapered_op.z2_symmetries.sq_list, [0])
        self.assertEqual(tapered_op.z2_symmetries.tapering_values, [-1])

        z2_symmetries.tapering_values = [-1]
        primitive = SparsePauliOp.from_list([
            ("I", -1.0424710218959303),
            ("Z", -0.7879673588770277),
            ("X", -0.18128880821149604),
        ])
        expected_op = TaperedPauliSumOp(primitive, z2_symmetries)
        self.assertEqual(tapered_op, expected_op)
    def test_truncate_tapered_op(self):
        """Test setting cutoff tolerances for the tapered operator works."""
        qubit_op = PauliSumOp.from_list([
            ("II", -1.0537076071291125),
            ("IZ", 0.393983679438514),
            ("ZI", -0.39398367943851387),
            ("ZZ", -0.01123658523318205),
            ("XX", 0.1812888082114961),
        ])
        z2_symmetries = Z2Symmetries.find_Z2_symmetries(qubit_op)
        z2_symmetries.tol = 0.2  # removes the X part of the tapered op which is < 0.2

        tapered_op = z2_symmetries.taper(qubit_op)[1]
        primitive = SparsePauliOp.from_list([
            ("I", -1.0424710218959303),
            ("Z", -0.7879673588770277),
        ])
        expected_op = TaperedPauliSumOp(primitive, z2_symmetries)
        self.assertEqual(tapered_op, expected_op)
예제 #3
0
    def _process_z2symmetry_reduction(self, qubit_op: PauliSumOp,
                                      aux_ops: List[PauliSumOp]) -> Tuple:
        """
        Implement z2 symmetries in the qubit operator

        Args:
            qubit_op : qubit operator
            aux_ops: auxiliary operators

        Returns:
            (z2_qubit_op, z2_aux_ops, z2_symmetries)

        Raises:
            QiskitNatureError: Invalid input
        """
        z2_symmetries = Z2Symmetries.find_Z2_symmetries(qubit_op)
        if z2_symmetries.is_empty():
            logger.debug('No Z2 symmetries found')
            z2_qubit_op = qubit_op
            z2_aux_ops = aux_ops
            z2_symmetries = Z2Symmetries([], [], [], None)
        else:
            logger.debug(
                '%s Z2 symmetries found: %s', len(z2_symmetries.symmetries),
                ','.join(
                    [symm.to_label() for symm in z2_symmetries.symmetries]))

            # Check auxiliary operators commute with main operator's symmetry
            logger.debug('Checking operators commute with symmetry:')
            symmetry_ops = []
            for symmetry in z2_symmetries.symmetries:
                symmetry_ops.append(
                    PauliSumOp.from_list([(symmetry.to_label(), 1.0)]))
            commutes = FermionicTransformation._check_commutes(
                symmetry_ops, qubit_op)
            if not commutes:
                raise QiskitNatureError(
                    'Z2 symmetry failure main operator must commute '
                    'with symmetries found from it')
            for i, aux_op in enumerate(aux_ops):
                commutes = FermionicTransformation._check_commutes(
                    symmetry_ops, aux_op)
                if not commutes:
                    aux_ops[
                        i] = None  # Discard since no meaningful measurement can be done

            if self._z2symmetry_reduction == 'auto':
                from ..circuit.library.initial_states.hartree_fock import hartree_fock_bitstring
                hf_bitstr = hartree_fock_bitstring(
                    num_orbitals=self._molecule_info['num_orbitals'],
                    qubit_mapping=self._qubit_mapping,
                    two_qubit_reduction=self._two_qubit_reduction,
                    num_particles=self._molecule_info['num_particles'])
                z2_symmetries = FermionicTransformation._pick_sector(
                    z2_symmetries, hf_bitstr)
            else:
                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)))
                valid = np.all(np.isin(self._z2symmetry_reduction, [-1, 1]))
                if not valid:
                    raise QiskitNatureError(
                        'z2symmetry_reduction tapering values list must '
                        'contain -1\'s and/or 1\'s only was {}'.format(
                            self._z2symmetry_reduction, ))
                z2_symmetries.tapering_values = self._z2symmetry_reduction

            logger.debug('Apply symmetry with tapering values %s',
                         z2_symmetries.tapering_values)
            chop_to = 0.00000001  # Use same threshold as qubit mapping to chop tapered operator
            z2_qubit_op = z2_symmetries.taper(qubit_op).chop(chop_to)
            z2_aux_ops = []
            for aux_op in aux_ops:
                if aux_op is None:
                    z2_aux_ops += [None]
                else:
                    z2_aux_ops += [z2_symmetries.taper(aux_op).chop(chop_to)]

        return z2_qubit_op, z2_aux_ops, z2_symmetries