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)
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)
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)
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
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
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
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