Пример #1
0
 def setUp(self):
     super().setUp()
     z2_symmetries = Z2Symmetries(
         [Pauli("IIZI"), Pauli("ZIII")],
         [Pauli("IIXI"), Pauli("XIII")], [1, 3], [-1, 1])
     self.primitive = SparsePauliOp.from_list([
         ("II", (-1.052373245772859)),
         ("ZI", (-0.39793742484318007)),
         ("IZ", (0.39793742484318007)),
         ("ZZ", (-0.01128010425623538)),
         ("XX", (0.18093119978423142)),
     ])
     self.tapered_qubit_op = TaperedPauliSumOp(self.primitive,
                                               z2_symmetries)
Пример #2
0
    def test_coder_operators(self):
        """Test runtime encoder and decoder for operators."""
        x = Parameter("x")
        y = x + 1
        qc = QuantumCircuit(1)
        qc.h(0)
        coeffs = np.array([1, 2, 3, 4, 5, 6])
        table = PauliTable.from_labels(
            ["III", "IXI", "IYY", "YIZ", "XYZ", "III"])
        op = (2.0 * I ^ I)
        z2_symmetries = Z2Symmetries(
            [Pauli("IIZI"), Pauli("ZIII")],
            [Pauli("IIXI"), Pauli("XIII")], [1, 3], [-1, 1])
        isqrt2 = 1 / np.sqrt(2)
        sparse = scipy.sparse.csr_matrix([[0, isqrt2, 0, isqrt2]])

        subtests = (
            PauliSumOp(SparsePauliOp(Pauli("XYZX"), coeffs=[2]), coeff=3),
            PauliSumOp(SparsePauliOp(Pauli("XYZX"), coeffs=[1]), coeff=y),
            PauliSumOp(SparsePauliOp(Pauli("XYZX"), coeffs=[1 + 2j]),
                       coeff=3 - 2j),
            PauliSumOp.from_list([("II", -1.052373245772859),
                                  ("IZ", 0.39793742484318045)]),
            PauliSumOp(SparsePauliOp(table, coeffs), coeff=10),
            MatrixOp(primitive=np.array([[0, -1j], [1j, 0]]), coeff=x),
            PauliOp(primitive=Pauli("Y"), coeff=x),
            CircuitOp(qc, coeff=x),
            EvolvedOp(op, coeff=x),
            TaperedPauliSumOp(SparsePauliOp(Pauli("XYZX"), coeffs=[2]),
                              z2_symmetries),
            StateFn(qc, coeff=x),
            CircuitStateFn(qc, is_measurement=True),
            DictStateFn("1" * 3, is_measurement=True),
            VectorStateFn(np.ones(2**3, dtype=complex)),
            OperatorStateFn(CircuitOp(QuantumCircuit(1))),
            SparseVectorStateFn(sparse),
            Statevector([1, 0]),
            CVaRMeasurement(Z, 0.2),
            ComposedOp([(X ^ Y ^ Z), (Z ^ X ^ Y ^ Z).to_matrix_op()]),
            SummedOp([X ^ X * 2, Y ^ Y], 2),
            TensoredOp([(X ^ Y), (Z ^ I)]),
            (Z ^ Z) ^ (I ^ 2),
        )
        for op in subtests:
            with self.subTest(op=op):
                encoded = json.dumps(op, cls=RuntimeEncoder)
                self.assertIsInstance(encoded, str)
                decoded = json.loads(encoded, cls=RuntimeDecoder)
                self.assertEqual(op, decoded)
Пример #3
0
 def test_taper_empty_operator(self):
     """Test tapering of empty operator"""
     z2_symmetries = Z2Symmetries(
         symmetries=[Pauli("IIZI"),
                     Pauli("IZIZ"),
                     Pauli("ZIII")],
         sq_paulis=[Pauli("IIXI"),
                    Pauli("IIIX"),
                    Pauli("XIII")],
         sq_list=[1, 0, 3],
         tapering_values=[1, -1, -1],
     )
     empty_op = PauliSumOp.from_list([("IIII", 0.0)])
     tapered_op = z2_symmetries.taper(empty_op)
     expected_op = PauliSumOp.from_list([("I", 0.0)])
     self.assertEqual(tapered_op, expected_op)
    def test_convert(self):
        """ convert test """

        qubit_op = PauliSumOp.from_list([
            ("IIII", -0.8105479805373266),
            ("IIIZ", 0.17218393261915552),
            ("IIZZ", -0.22575349222402472),
            ("IZZI", 0.1721839326191556),
            ("ZZII", -0.22575349222402466),
            ("IIZI", 0.1209126326177663),
            ("IZZZ", 0.16892753870087912),
            ("IXZX", -0.045232799946057854),
            ("ZXIX", 0.045232799946057854),
            ("IXIX", 0.045232799946057854),
            ("ZXZX", -0.045232799946057854),
            ("ZZIZ", 0.16614543256382414),
            ("IZIZ", 0.16614543256382414),
            ("ZZZZ", 0.17464343068300453),
            ("ZIZI", 0.1209126326177663),
        ])
        tapered_qubit_op = TwoQubitReduction(num_particles=2).convert(qubit_op)
        self.assertIsInstance(tapered_qubit_op, TaperedPauliSumOp)

        primitive = SparsePauliOp.from_list([
            ("II", -1.052373245772859),
            ("ZI", -0.39793742484318007),
            ("IZ", 0.39793742484318007),
            ("ZZ", -0.01128010425623538),
            ("XX", 0.18093119978423142),
        ])
        symmetries = [Pauli("IIZI"), Pauli("ZIII")]
        sq_paulis = [Pauli("IIXI"), Pauli("XIII")]
        sq_list = [1, 3]
        tapering_values = [-1, 1]
        z2_symmetries = Z2Symmetries(symmetries, sq_paulis, sq_list,
                                     tapering_values)
        expected_op = TaperedPauliSumOp(primitive, z2_symmetries)
        self.assertEqual(tapered_qubit_op, expected_op)
Пример #5
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
Пример #6
0
    def _do_transform(
        self,
        qmolecule: QMolecule,
        aux_operators: Optional[List[FermionicOperator]] = None
    ) -> Tuple[PauliSumOp, List[PauliSumOp]]:
        """
        Args:
            qmolecule: qmolecule
            aux_operators: Additional ``FermionicOperator``s to map to a qubit operator.

        Returns:
            (qubit operator, auxiliary operators)

        """
        logger.debug('Processing started...')
        # Save these values for later combination with the quantum computation result
        self._hf_energy = qmolecule.hf_energy
        self._nuclear_repulsion_energy = qmolecule.nuclear_repulsion_energy
        self._nuclear_dipole_moment = qmolecule.nuclear_dipole_moment
        self._reverse_dipole_sign = qmolecule.reverse_dipole_sign

        core_list = qmolecule.core_orbitals if self._freeze_core else []
        reduce_list = self._orbital_reduction

        if self._freeze_core:
            logger.info(
                "Freeze_core specified. Core orbitals to be frozen: %s",
                core_list)
        if reduce_list:
            logger.info("Configured orbital reduction list: %s", reduce_list)
            reduce_list = [
                x + qmolecule.num_orbitals if x < 0 else x for x in reduce_list
            ]

        freeze_list = []
        remove_list = []

        # Orbitals are specified by their index from 0 to n-1, where n is the number of orbitals the
        # molecule has. The combined list of the core orbitals, when freeze_core is true, with any
        # user supplied orbitals is what will be used. Negative numbers may be used to indicate the
        # upper virtual orbitals, so -1 is the highest, then -2 etc. and these will
        # be converted to the
        # positive 0-based index for computation.
        # In the combined list any orbitals that are occupied are added to a freeze list and an
        # energy is stored from these orbitals to be added later.
        # Unoccupied orbitals are just discarded.
        # Because freeze and eliminate is done in separate steps,
        # with freeze first, we have to re-base
        # the indexes for elimination according to how many orbitals were removed when freezing.
        #
        orb_list = list(set(core_list + reduce_list))
        num_alpha = qmolecule.num_alpha
        num_beta = qmolecule.num_beta
        new_num_alpha = num_alpha
        new_num_beta = num_beta
        if orb_list:
            orbitals_list = np.array(orb_list)
            orbitals_list = orbitals_list[
                (cast(np.ndarray, orbitals_list) >= 0)
                & (orbitals_list < qmolecule.num_orbitals)]

            freeze_list_alpha = [i for i in orbitals_list if i < num_alpha]
            freeze_list_beta = [i for i in orbitals_list if i < num_beta]
            freeze_list = np.append(
                freeze_list_alpha,
                [i + qmolecule.num_orbitals for i in freeze_list_beta])

            remove_list_alpha = [i for i in orbitals_list if i >= num_alpha]
            remove_list_beta = [i for i in orbitals_list if i >= num_beta]
            rla_adjust = -len(freeze_list_alpha)
            rlb_adjust = -len(freeze_list_alpha) - len(
                freeze_list_beta) + qmolecule.num_orbitals
            remove_list = np.append(
                [i + rla_adjust for i in remove_list_alpha],
                [i + rlb_adjust for i in remove_list_beta])

            logger.info("Combined orbital reduction list: %s", orbitals_list)
            logger.info(
                "  converting to spin orbital reduction list: %s",
                np.append(np.array(orbitals_list),
                          np.array(orbitals_list) + qmolecule.num_orbitals))
            logger.info("    => freezing spin orbitals: %s", freeze_list)
            logger.info(
                "    => removing spin orbitals: %s (indexes accounting for freeze %s)",
                np.append(remove_list_alpha,
                          np.array(remove_list_beta) + qmolecule.num_orbitals),
                remove_list)

            new_num_alpha -= len(freeze_list_alpha)
            new_num_beta -= len(freeze_list_beta)

        new_nel = [new_num_alpha, new_num_beta]

        # construct the fermionic operator
        fer_op = FermionicOperator(h1=qmolecule.one_body_integrals,
                                   h2=qmolecule.two_body_integrals)

        # try to reduce it according to the freeze and remove list
        fer_op, self._energy_shift, did_shift = \
            FermionicTransformation._try_reduce_fermionic_operator(fer_op, freeze_list, remove_list)
        # apply same transformation for the aux operators
        if aux_operators is not None:
            aux_operators = [
                FermionicTransformation._try_reduce_fermionic_operator(
                    op, freeze_list, remove_list)[0] for op in aux_operators
            ]

        if did_shift:
            logger.info("Frozen orbital energy shift: %s", self._energy_shift)
        # apply particle hole transformation, if specified
        if self._transformation == FermionicTransformationType.PARTICLE_HOLE.value:
            fer_op, ph_shift = fer_op.particle_hole_transformation(new_nel)
            self._ph_energy_shift = -ph_shift
            logger.info("Particle hole energy shift: %s",
                        self._ph_energy_shift)

            # apply the same transformation for the aux operators
            if aux_operators is not None:
                aux_operators = [
                    op.particle_hole_transformation(new_nel)[0]
                    for op in aux_operators
                ]

        logger.debug('Converting to qubit using %s mapping',
                     self._qubit_mapping)
        qubit_op = FermionicTransformation._map_fermionic_operator_to_qubit(
            fer_op, self._qubit_mapping, new_nel, self._two_qubit_reduction)
        qubit_op.name = 'Fermionic Operator'

        logger.debug('  num paulis: %d, num qubits: %d', len(qubit_op),
                     qubit_op.num_qubits)

        aux_ops = []  # list of the aux operators

        def _add_aux_op(aux_op: FermionicOperator, name: str) -> None:
            """
            Add auxiliary operators

            Args:
                aux_op: auxiliary operators
                name: name

            """
            aux_qop = FermionicTransformation._map_fermionic_operator_to_qubit(
                aux_op, self._qubit_mapping, new_nel,
                self._two_qubit_reduction)
            aux_qop.name = name

            aux_ops.append(aux_qop)
            logger.debug('  pauli: %s', aux_qop)

        # the first three operators are hardcoded to number of particles, angular momentum
        # and magnetization in this order
        logger.debug('Creating aux op for Number of Particles')
        _add_aux_op(fer_op.total_particle_number(), 'Number of Particles')
        logger.debug('Creating aux op for S^2')
        _add_aux_op(fer_op.total_angular_momentum(), 'S^2')
        logger.debug('Creating aux op for Magnetization')
        _add_aux_op(fer_op.total_magnetization(), 'Magnetization')

        # the next three are dipole moments, if supported by the qmolecule
        if qmolecule.has_dipole_integrals():
            self._has_dipole_moments = True

            def _dipole_op(dipole_integrals: np.ndarray, axis: str) \
                    -> Tuple[PauliSumOp, float, float]:
                """
                Dipole operators

                Args:
                    dipole_integrals: dipole integrals
                    axis: axis for dipole moment calculation

                Returns:
                    (qubit_op_, shift, ph_shift_)
                """
                logger.debug('Creating aux op for dipole %s', axis)
                fer_op_ = FermionicOperator(h1=dipole_integrals)
                fer_op_, shift, did_shift_ = self._try_reduce_fermionic_operator(
                    fer_op_, freeze_list, remove_list)
                if did_shift_:
                    logger.info("Frozen orbital %s dipole shift: %s", axis,
                                shift)
                ph_shift_ = 0.0
                if self._transformation == FermionicTransformationType.PARTICLE_HOLE.value:
                    fer_op_, ph_shift_ = fer_op_.particle_hole_transformation(
                        new_nel)
                    ph_shift_ = -ph_shift_
                    logger.info("Particle hole %s dipole shift: %s", axis,
                                ph_shift_)
                qubit_op_ = self._map_fermionic_operator_to_qubit(
                    fer_op_, self._qubit_mapping, new_nel,
                    self._two_qubit_reduction)
                qubit_op_.name = 'Dipole ' + axis
                logger.debug('  num paulis: %d', len(qubit_op_))
                return qubit_op_, shift, ph_shift_

            op_dipole_x, self._x_dipole_shift, self._ph_x_dipole_shift = \
                _dipole_op(qmolecule.x_dipole_integrals, 'x')
            op_dipole_y, self._y_dipole_shift, self._ph_y_dipole_shift = \
                _dipole_op(qmolecule.y_dipole_integrals, 'y')
            op_dipole_z, self._z_dipole_shift, self._ph_z_dipole_shift = \
                _dipole_op(qmolecule.z_dipole_integrals, 'z')

            aux_ops += [op_dipole_x, op_dipole_y, op_dipole_z]

        # add user specified auxiliary operators
        if aux_operators is not None:
            for aux_op in aux_operators:
                if hasattr(aux_op, 'name'):
                    name = aux_op.name  # type: ignore
                else:
                    name = ''
                _add_aux_op(aux_op, name)

        logger.info('Molecule num electrons: %s, remaining for processing: %s',
                    [num_alpha, num_beta], new_nel)
        nspinorbs = qmolecule.num_orbitals * 2
        new_nspinorbs = nspinorbs - len(freeze_list) - len(remove_list)
        logger.info(
            'Molecule num spin orbitals: %s, remaining for processing: %s',
            nspinorbs, new_nspinorbs)

        self._molecule_info['num_particles'] = (new_num_alpha, new_num_beta)
        self._molecule_info['num_orbitals'] = new_nspinorbs
        reduction = self._two_qubit_reduction if self._qubit_mapping == 'parity' else False
        self._molecule_info['two_qubit_reduction'] = reduction
        self._untapered_qubit_op = qubit_op

        z2symmetries = Z2Symmetries([], [], [], None)
        if self._z2symmetry_reduction is not None:
            logger.debug('Processing z2 symmetries')
            qubit_op, aux_ops, z2symmetries = self._process_z2symmetry_reduction(
                qubit_op, aux_ops)
        self._molecule_info['z2_symmetries'] = z2symmetries

        logger.debug('Processing complete ready to run algorithm')
        return qubit_op, aux_ops
Пример #7
0
    def _build_all_commutators(self, hopping_operators: dict,
                               type_of_commutativities: dict,
                               size: int) -> dict:
        """Building all commutators for Q, W, M, V matrices.

        Args:
            hopping_operators: all hopping operators based on excitations_list,
                key is the string of single/double excitation;
                value is corresponding operator.
            type_of_commutativities: if tapering is used, it records the commutativities of
                hopping operators with the
                Z2 symmetries found in the original operator.
            size: the number of excitations (size of the qEOM pseudo-eigenvalue problem)

        Returns:
            a dictionary that contains the operators for each matrix element
        """

        all_matrix_operators = {}

        mus, nus = np.triu_indices(size)

        def _build_one_sector(available_hopping_ops, untapered_op,
                              z2_symmetries):

            to_be_computed_list = []
            for idx, _ in enumerate(mus):
                m_u = mus[idx]
                n_u = nus[idx]
                left_op = available_hopping_ops.get('E_{}'.format(m_u))
                right_op_1 = available_hopping_ops.get('E_{}'.format(n_u))
                right_op_2 = available_hopping_ops.get('Edag_{}'.format(n_u))
                to_be_computed_list.append(
                    (m_u, n_u, left_op, right_op_1, right_op_2))

            if logger.isEnabledFor(logging.INFO):
                logger.info("Building all commutators:")
                TextProgressBar(sys.stderr)
            results = parallel_map(
                self._build_commutator_routine,
                to_be_computed_list,
                task_args=(untapered_op, z2_symmetries),
                num_processes=algorithm_globals.num_processes)
            for result in results:
                m_u, n_u, q_mat_op, w_mat_op, m_mat_op, v_mat_op = result

                if q_mat_op is not None:
                    all_matrix_operators['q_{}_{}'.format(m_u, n_u)] = q_mat_op
                if w_mat_op is not None:
                    all_matrix_operators['w_{}_{}'.format(m_u, n_u)] = w_mat_op
                if m_mat_op is not None:
                    all_matrix_operators['m_{}_{}'.format(m_u, n_u)] = m_mat_op
                if v_mat_op is not None:
                    all_matrix_operators['v_{}_{}'.format(m_u, n_u)] = v_mat_op

        try:
            # The next step only works in the case of the FermionicTransformation. Thus, it is done
            # in a try-except block. However, mypy doesn't detect this and thus we ignore it.
            z2_symmetries = self._gsc.qubit_converter.z2symmetries  # type: ignore
        except AttributeError:
            z2_symmetries = Z2Symmetries([], [], [])

        if not z2_symmetries.is_empty():
            combinations = itertools.product([1, -1],
                                             repeat=len(
                                                 z2_symmetries.symmetries))
            for targeted_tapering_values in combinations:
                logger.info(
                    "In sector: (%s)",
                    ','.join([str(x) for x in targeted_tapering_values]))
                # remove the excited operators which are not suitable for the sector

                available_hopping_ops = {}
                targeted_sector = (np.asarray(targeted_tapering_values) == 1)
                for key, value in type_of_commutativities.items():
                    value = np.asarray(value)
                    if np.all(value == targeted_sector):
                        available_hopping_ops[key] = hopping_operators[key]
                # untapered_qubit_op is a PauliSumOp and should not be exposed.
                _build_one_sector(available_hopping_ops,
                                  self._untapered_qubit_op_main, z2_symmetries)

        else:
            # untapered_qubit_op is a PauliSumOp and should not be exposed.
            _build_one_sector(hopping_operators, self._untapered_qubit_op_main,
                              z2_symmetries)

        return all_matrix_operators
Пример #8
0
    def __init__(self,
                 num_orbitals: int,
                 num_particles: Union[Tuple[int, int], List[int], int],
                 reps: int = 1,
                 active_occupied: Optional[List[int]] = None,
                 active_unoccupied: Optional[List[int]] = None,
                 initial_state: Optional[QuantumCircuit] = None,
                 qubit_mapping: str = 'parity',
                 two_qubit_reduction: bool = True,
                 num_time_slices: int = 1,
                 shallow_circuit_concat: bool = True,
                 z2_symmetries: Optional[Z2Symmetries] = None,
                 method_singles: str = 'both',
                 method_doubles: str = 'ucc',
                 excitation_type: str = 'sd',
                 same_spin_doubles: bool = True,
                 skip_commute_test: bool = False) -> None:
        """Constructor.

        Args:
            num_orbitals: number of spin orbitals, has a min. value of 1.
            num_particles: number of particles, if it is a list,
                            the first number is alpha and the second number if beta.
            reps: number of repetitions of basic module, has a min. value of 1.
            active_occupied: list of occupied orbitals to consider as active space.
            active_unoccupied: list of unoccupied orbitals to consider as active space.
            initial_state: An initial state object.
            qubit_mapping: qubit mapping type.
            two_qubit_reduction: two qubit reduction is applied or not.
            num_time_slices: parameters for dynamics, has a min. value of 1.
            shallow_circuit_concat: indicate whether to use shallow (cheap) mode for
                                           circuit concatenation.
            z2_symmetries: represent the Z2 symmetries, including symmetries,
                            sq_paulis, sq_list, tapering_values, and cliffords.
            method_singles: specify the single excitation considered. 'alpha', 'beta',
                                'both' only alpha or beta spin-orbital single excitations or
                                both (all of them).
            method_doubles: specify the single excitation considered. 'ucc' (conventional
                                ucc), succ (singlet ucc), succ_full (singlet ucc full),
                                pucc (pair ucc).
            excitation_type: specify the excitation type 'sd', 's', 'd' respectively
                                for single and double, only single, only double excitations.
            same_spin_doubles: enable double excitations of the same spin.
            skip_commute_test: when tapering excitation operators we test and exclude any that do
                                not commute with symmetries. This test can be skipped to include
                                all tapered excitation operators whether they commute or not.


         Raises:
             ValueError: Num particles list is not 2 entries
        """
        validate_min('num_orbitals', num_orbitals, 1)
        if isinstance(num_particles, list) and len(num_particles) != 2:
            raise ValueError(
                'Num particles value {}. Number of values allowed is 2'.format(
                    num_particles))
        validate_min('reps', reps, 1)
        validate_in_set('qubit_mapping', qubit_mapping,
                        {'jordan_wigner', 'parity', 'bravyi_kitaev'})
        validate_min('num_time_slices', num_time_slices, 1)
        validate_in_set('method_singles', method_singles,
                        {'both', 'alpha', 'beta'})
        validate_in_set('method_doubles', method_doubles,
                        {'ucc', 'pucc', 'succ', 'succ_full'})
        validate_in_set('excitation_type', excitation_type, {'sd', 's', 'd'})
        super().__init__()

        self._z2_symmetries = Z2Symmetries([], [], [], []) \
            if z2_symmetries is None else z2_symmetries

        self._num_qubits = num_orbitals if not two_qubit_reduction else num_orbitals - 2
        self._num_qubits = self._num_qubits if self._z2_symmetries.is_empty() \
            else self._num_qubits - len(self._z2_symmetries.sq_list)
        self._reps = reps
        self._num_orbitals = num_orbitals
        if isinstance(num_particles, (tuple, list)):
            self._num_alpha = num_particles[0]
            self._num_beta = num_particles[1]
        else:
            logger.info(
                "We assume that the number of alphas and betas are the same.")
            self._num_alpha = num_particles // 2
            self._num_beta = num_particles // 2

        self._num_particles = [self._num_alpha, self._num_beta]

        if sum(self._num_particles) > self._num_orbitals:
            raise ValueError(
                '# of particles must be less than or equal to # of orbitals.')

        self._initial_state = initial_state
        self._qubit_mapping = qubit_mapping
        self._two_qubit_reduction = two_qubit_reduction
        self._num_time_slices = num_time_slices
        self._shallow_circuit_concat = shallow_circuit_concat

        # advanced parameters
        self._method_singles = method_singles
        self._method_doubles = method_doubles
        self._excitation_type = excitation_type
        self.same_spin_doubles = same_spin_doubles
        self._skip_commute_test = skip_commute_test

        self._single_excitations, self._double_excitations = \
            UCCSD.compute_excitation_lists([self._num_alpha, self._num_beta], self._num_orbitals,
                                           active_occupied, active_unoccupied,
                                           same_spin_doubles=self.same_spin_doubles,
                                           method_singles=self._method_singles,
                                           method_doubles=self._method_doubles,
                                           excitation_type=self._excitation_type,)

        self._hopping_ops, self._num_parameters = self._build_hopping_operators(
        )
        self._excitation_pool = None  # type: Optional[List[PauliSumOp]]
        self._bounds = [(-np.pi, np.pi) for _ in range(self._num_parameters)]

        self._logging_construct_circuit = True
        self._support_parameterized_circuit = True

        self.uccd_singlet = False
        if self._method_doubles == 'succ_full':
            self.uccd_singlet = True
            self._single_excitations, self._double_excitations = \
                UCCSD.compute_excitation_lists([self._num_alpha, self._num_beta],
                                               self._num_orbitals,
                                               active_occupied, active_unoccupied,
                                               same_spin_doubles=self.same_spin_doubles,
                                               method_singles=self._method_singles,
                                               method_doubles=self._method_doubles,
                                               excitation_type=self._excitation_type,
                                               )
        if self.uccd_singlet:
            self._hopping_ops, _ = self._build_hopping_operators()
        else:
            self._hopping_ops, self._num_parameters = self._build_hopping_operators(
            )
            self._bounds = [(-np.pi, np.pi)
                            for _ in range(self._num_parameters)]

        if self.uccd_singlet:
            self._double_excitations_grouped = \
                UCCSD.compute_excitation_lists_singlet(self._double_excitations, num_orbitals)
            self.num_groups = len(self._double_excitations_grouped)

            logging.debug('Grouped double excitations for singlet ucc')
            logging.debug(self._double_excitations_grouped)

            self._num_parameters = self.num_groups
            self._bounds = [(-np.pi, np.pi) for _ in range(self.num_groups)]

            # this will order the hopping operators
            self.labeled_double_excitations = []
            for i in range(len(self._double_excitations)):
                self.labeled_double_excitations.append(
                    (self._double_excitations[i], i))

            order_hopping_op = UCCSD.order_labels_for_hopping_ops(
                self._double_excitations, self._double_excitations_grouped)
            logging.debug('New order for hopping ops')
            logging.debug(order_hopping_op)

            self._hopping_ops_doubles_temp = []
            self._hopping_ops_doubles = self._hopping_ops[
                len(self._single_excitations):]
            for i in order_hopping_op:
                self._hopping_ops_doubles_temp.append(
                    self._hopping_ops_doubles[i])

            self._hopping_ops[len(self._single_excitations
                                  ):] = self._hopping_ops_doubles_temp

        self._logging_construct_circuit = True