def run( self, qmolecule: QMolecule ) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: """ run method""" 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] fer_op = FermionicOperator(h1=qmolecule.one_body_integrals, h2=qmolecule.two_body_integrals) fer_op, self._energy_shift, did_shift = \ Hamiltonian._try_reduce_fermionic_operator(fer_op, freeze_list, remove_list) if did_shift: logger.info("Frozen orbital energy shift: %s", self._energy_shift) if self._transformation == TransformationType.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) logger.debug('Converting to qubit using %s mapping', self._qubit_mapping) qubit_op = Hamiltonian._map_fermionic_operator_to_qubit( fer_op, self._qubit_mapping, new_nel, self._two_qubit_reduction) qubit_op.name = 'Electronic Hamiltonian' logger.debug(' num paulis: %s, num qubits: %s', len(qubit_op.paulis), qubit_op.num_qubits) aux_ops = [] def _add_aux_op(aux_op, name): aux_qop = Hamiltonian._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(' num paulis: %s', aux_qop.paulis) 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') if qmolecule.has_dipole_integrals(): def _dipole_op(dipole_integrals, axis): 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 == TransformationType.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: %s', len(qubit_op_.paulis)) 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.append(op_dipole_x) aux_ops.append(op_dipole_y) aux_ops.append(op_dipole_z) 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._add_molecule_info(self.INFO_NUM_PARTICLES, (new_num_alpha, new_num_beta)) self._add_molecule_info(self.INFO_NUM_ORBITALS, new_nspinorbs) self._add_molecule_info( self.INFO_TWO_QUBIT_REDUCTION, self._two_qubit_reduction if self._qubit_mapping == 'parity' else False) 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._add_molecule_info(self.INFO_Z2SYMMETRIES, z2symmetries) logger.debug('Processing complete ready to run algorithm') return qubit_op, aux_ops
def _do_transform( self, qmolecule: QMolecule, aux_operators: Optional[List[FermionicOperator]] = None ) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: """ 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: %s, num qubits: %s', len(qubit_op.paulis), 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(' num paulis: %s', aux_qop.paulis) # 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[WeightedPauliOperator, 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: %s', len(qubit_op_.paulis)) 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 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