def _validate_num_orbitals(self, nelec_inactive: int, molecule_data: QMolecule): """Validates the number of orbitals. Args: nelec_inactive: the computed number of inactive electrons. molecule_data: the `QMolecule` to be transformed. Raises: QiskitNatureError: if more orbitals were requested than are available in total or if the number of selected orbitals mismatches the specified number of active orbitals. """ if self._active_orbitals is None: norbs_inactive = nelec_inactive // 2 if norbs_inactive + self._num_molecular_orbitals > molecule_data.num_molecular_orbitals: raise QiskitNatureError( "More orbitals requested than available.") else: if self._num_molecular_orbitals != len(self._active_orbitals): raise QiskitNatureError( "The number of selected active orbital indices does not " "match the specified number of active orbitals.") if max(self._active_orbitals ) >= molecule_data.num_molecular_orbitals: raise QiskitNatureError( "More orbitals requested than available.") if sum(self._mo_occ_total[ self._active_orbitals]) != self._num_electrons: raise QiskitNatureError( "The number of electrons in the selected active orbitals " "does not match the specified number of active electrons.")
def _validate_num_orbitals(self, nelec_inactive: int, particle_number: ParticleNumber) -> None: """Validates the number of orbitals. Args: nelec_inactive: the computed number of inactive electrons. particle_number: the `ParticleNumber` containing system size information. Raises: QiskitNatureError: if more orbitals were requested than are available in total or if the number of selected orbitals mismatches the specified number of active orbitals. """ if self._active_orbitals is None: norbs_inactive = nelec_inactive // 2 if (norbs_inactive + self._num_molecular_orbitals > particle_number._num_spin_orbitals // 2): raise QiskitNatureError( "More orbitals requested than available.") else: if self._num_molecular_orbitals != len(self._active_orbitals): raise QiskitNatureError( "The number of selected active orbital indices does not " "match the specified number of active orbitals.") if max(self._active_orbitals ) >= particle_number._num_spin_orbitals // 2: raise QiskitNatureError( "More orbitals requested than available.") expected_num_electrons = (self._num_electrons if isinstance( self._num_electrons, int) else sum(self._num_electrons)) if sum(self._mo_occ_total[ self._active_orbitals]) != expected_num_electrons: raise QiskitNatureError( "The number of electrons in the selected active orbitals " "does not match the specified number of active electrons.")
def run(self) -> GroupedSecondQuantizedProperty: """ Returns: GroupedSecondQuantizedProperty re-constructed from the HDF5 file. Raises: LookupError: file not found. QiskitNatureError: Legacy HDF5 format. QiskitNatureError: if the HDF5 file did not contain a GroupedSecondQuantizedProperty. """ hdf5_file = self._get_path() with h5py.File(hdf5_file, "r") as file: if "origin_driver" in file.keys(): raise QiskitNatureError( "Your HDF5 file contains a not supported legacy QMolecule object!" ) driver_result = load_from_hdf5(str(hdf5_file)) if not isinstance(driver_result, GroupedSecondQuantizedProperty): raise QiskitNatureError( f"Expected a GroupedSecondQuantizedProperty but found a {type(driver_result)} " "object instead.") return driver_result
def _run_psi4(input_file, output_file): # Run psi4. process = None try: with subprocess.Popen( [_optionals.PSI4, input_file, output_file], stdout=subprocess.PIPE, universal_newlines=True, ) as process: stdout, _ = process.communicate() process.wait() except Exception as ex: if process is not None: process.kill() raise QiskitNatureError( f"{_optionals.PSI4} run has failed") from ex if process.returncode != 0: errmsg = "" if stdout is not None: lines = stdout.splitlines() for line in lines: logger.error(line) errmsg += line + "\n" raise QiskitNatureError( f"{_optionals.PSI4} process return code {process.returncode}\n{errmsg}" )
def _parse_molecule(val, units, charge, multiplicity): val = _check_molecule_format(val) parts = [x.strip() for x in val.split(";")] if parts is None or len(parts) < 1: # pylint: disable=len-as-condition raise QiskitNatureError("Molecule format error: " + val) geom = [] for n, _ in enumerate(parts): part = parts[n] geom.append(_parse_atom(part)) if len(geom) < 1: # pylint: disable=len-as-condition raise QiskitNatureError("Molecule format error: " + val) try: # pylint: disable=import-error,import-outside-toplevel from pyquante2 import ( molecule, ) return molecule(geom, units=units, charge=charge, multiplicity=multiplicity) except Exception as exc: raise QiskitNatureError("Failed to create molecule") from exc
def _check_molecule_format(val): """If it seems to be zmatrix rather than xyz format we convert before returning""" atoms = [x.strip() for x in val.split(";")] if atoms is None or len(atoms) < 1: # pylint: disable=len-as-condition raise QiskitNatureError("Molecule format error: " + val) # An xyz format has 4 parts in each atom, if not then do zmatrix convert # Allows dummy atoms, using symbol 'X' in zmatrix format for coord computation to xyz parts = [x.strip() for x in atoms[0].split(" ")] if len(parts) != 4: try: zmat = [] for atom in atoms: parts = [x.strip() for x in atom.split(" ")] z = [parts[0]] for i in range(1, len(parts), 2): z.append(int(parts[i])) z.append(float(parts[i + 1])) zmat.append(z) xyz = z2xyz(zmat) new_val = "" for atm in xyz: if atm[0].upper() == "X": continue if new_val: new_val += "; " new_val += "{} {} {} {}".format(atm[0], atm[1], atm[2], atm[3]) return new_val except Exception as exc: raise QiskitNatureError("Failed to convert atom string: " + val) from exc return val
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
def run_g16(cfg: str) -> str: """ Runs Gaussian 16. We capture stdout and if error log the last 10 lines that should include the error description from Gaussian. Args: cfg: configuration Returns: Text log output Raises: QiskitNatureError: Failed run or log not captured. """ process = None try: with Popen(_optionals.GAUSSIAN_16, stdin=PIPE, stdout=PIPE, universal_newlines=True) as process: stdout, _ = process.communicate(cfg) process.wait() except Exception as ex: if process is not None: process.kill() raise QiskitNatureError( f"{_optionals.GAUSSIAN_16_DESC} run has failed") from ex if process.returncode != 0: errmsg = "" if stdout is not None: lines = stdout.splitlines() start = 0 if len(lines) > 10: start = len(lines) - 10 for i in range(start, len(lines)): logger.error(lines[i]) errmsg += lines[i] + "\n" raise QiskitNatureError( f"{_optionals.GAUSSIAN_16_DESC} process return code {process.returncode}\n{errmsg}" ) all_text = "" if stdout is not None: lines = stdout.splitlines() for line in lines: all_text += line + "\n" logger.debug("Gaussian output:\n%s", all_text) return all_text
def _validate_num_electrons(self, nelec_inactive: int): """Validates the number of electrons. Args: nelec_inactive: the computed number of inactive electrons. Raises: QiskitNatureError: if the number of inactive electrons is either negative or odd. """ if nelec_inactive < 0: raise QiskitNatureError("More electrons requested than available.") if nelec_inactive % 2 != 0: raise QiskitNatureError("The number of inactive electrons must be even.")
def _get_excitation_generators(self) -> List[Callable]: logger.debug('Gathering excitation generators...') generators: List[Callable] = [] extra_kwargs = { 'alpha_spin': self._alpha_spin, 'beta_spin': self._beta_spin, 'max_spin_excitation': self._max_spin_excitation } if isinstance(self.excitations, str): for exc in self.excitations: generators.append( partial(generate_fermionic_excitations, num_excitations=self.EXCITATION_TYPE[exc], **extra_kwargs)) elif isinstance(self.excitations, int): generators.append( partial(generate_fermionic_excitations, num_excitations=self.excitations, **extra_kwargs)) elif isinstance(self.excitations, list): for exc in self.excitations: # type: ignore generators.append( partial(generate_fermionic_excitations, num_excitations=exc, **extra_kwargs)) elif callable(self.excitations): generators = [self.excitations] else: raise QiskitNatureError( "Invalid excitation configuration: {}".format( self.excitations)) return generators
def _parse_atom(val): if val is None or len(val) < 1: # pylint: disable=len-as-condition raise QiskitNatureError("Molecule atom format error: empty") parts = re.split(r"\s+", val) if len(parts) != 4: raise QiskitNatureError("Molecule atom format error: " + val) parts[0] = parts[0].lower().capitalize() if not parts[0].isdigit(): if parts[0] in QMolecule.symbols: parts[0] = QMolecule.symbols.index(parts[0]) else: raise QiskitNatureError("Molecule atom symbol error: " + parts[0]) return int(float(parts[0])), float(parts[1]), float(parts[2]), float(parts[3])
def _single_mul(cls, label1: str, label2: str) -> Tuple[str, complex]: if len(label1) != len(label2): raise QiskitNatureError("Operators act on Fermion Registers of different length") new_label = [] sign = 1 # count the number of `+` and `-` in the first label ahead of time count = label1.count("+") + label1.count("-") for pair in zip(label1, label2): # update the count as we progress char1, char2 = pair if char1 in "+-": count -= 1 new_char = cls._MAPPING[pair] if new_char == "0": # if the new symbol is a zero-op, return early return "I" * len(label1), 0 new_label.append(new_char) # NOTE: we can ignore the type because the only scenario where an `int` occurs is caught # by the `if`-statement above. # If char2 is one of `+` or `-` we pick up a phase when commuting it to the position # of char1. However, we only care about this if the number of permutations has odd # parity. if count % 2 and char2 in "+-": sign *= -1 return "".join(new_label), sign
def to_second_q_op(self) -> VibrationalOp: """Creates the operator representing the Hamiltonian defined by these vibrational integrals. Returns: The :class:`~qiskit_nature.second_q.operators.VibrationalOp` given by these vibrational integrals. Raises: QiskitNatureError: if no basis has been set yet. """ try: matrix = self.to_basis() except QiskitNatureError as exc: raise QiskitNatureError() from exc num_modals_per_mode = self.basis._num_modals_per_mode num_modes = len(num_modals_per_mode) nonzero = np.nonzero(matrix) if not np.any(np.asarray(nonzero)): return VibrationalOp.zero(num_modes, num_modals_per_mode) labels = [] for coeff, indices in zip(matrix[nonzero], zip(*nonzero)): # the indices need to be grouped into triplets of the form: (mode, modal_1, modal_2) grouped_indices = [ tuple(int(j) for j in indices[i : i + 3]) for i in range(0, len(indices), 3) ] # the index groups need to processed in sorted order to produce a valid label coeff_label = self._create_label_for_coeff(sorted(grouped_indices)) labels.append((coeff_label, coeff)) return VibrationalOp(labels, num_modes, num_modals_per_mode)
def transform_basis(self, transform: ElectronicBasisTransform) -> OneBodyElectronicIntegrals: # pylint: disable=line-too-long """Transforms the integrals according to the given transform object. If the integrals are already in the correct basis, ``self`` is returned. Args: transform: the transformation object with the integral coefficients. Returns: The transformed :class:`~qiskit_nature.properties.second_quantization.electronic.integrals.ElectronicIntegrals`. Raises: QiskitNatureError: if the integrals do not match :class:`~qiskit_nature.properties.second_quantization.electronic.bases.ElectronicBasisTransform.initial_basis`. """ if self._basis == transform.final_basis: return self if self._basis != transform.initial_basis: raise QiskitNatureError( f"The integrals' basis, {self._basis}, does not match the initial basis of the " f"transform, {transform.initial_basis}." ) matrix_a = np.dot(np.dot(transform.coeff_alpha.T, self._matrices[0]), transform.coeff_alpha) matrix_b = None if self._matrices[1] is not None or not transform.is_alpha_equal_beta(): matrix_b = np.dot( np.dot(transform.coeff_beta.T, self.get_matrix(1)), transform.coeff_beta ) return OneBodyElectronicIntegrals(transform.final_basis, (matrix_a, matrix_b))
def _determine_active_space( self, grouped_property: GroupedElectronicProperty ) -> Tuple[List[int], List[int]]: """Determines the active and inactive orbital indices. Args: grouped_property: the `ElectronicStructureDriverResult` to be transformed. Returns: The list of active and inactive orbital indices. Raises: QiskitNatureError: if a GroupedElectronicProperty is provided which is not also an ElectronicElectronicStructureDriverResult. """ if not isinstance(grouped_property, ElectronicStructureDriverResult): raise QiskitNatureError( "The FreezeCoreTransformer requires an `ElectronicStructureDriverResult`, not a " f"property of type {type(grouped_property)}." ) molecule = grouped_property.molecule particle_number = grouped_property.get_property("ParticleNumber") inactive_orbs_idxs = list(range(self.count_core_orbitals(molecule.atoms))) if self._remove_orbitals is not None: inactive_orbs_idxs.extend(self._remove_orbitals) active_orbs_idxs = [ o for o, _ in enumerate(particle_number.occupation_alpha) if o not in inactive_orbs_idxs ] self._active_orbitals = active_orbs_idxs self._num_molecular_orbitals = len(active_orbs_idxs) return (active_orbs_idxs, inactive_orbs_idxs)
def run(self) -> GaussianLogResult: # type: ignore """Runs the driver to produce a result given the supplied job control file. Returns: A log file result. Raises: QiskitNatureError: Missing output log """ # The job control file, needs to end with a blank line to be valid for # Gaussian to process it. We simply add the blank line here if not. cfg = self._jcf while not cfg.endswith("\n\n"): cfg += "\n" logger.debug( "User supplied job control file raw: '%s'", cfg.replace("\r", "\\r").replace("\n", "\\n"), ) logger.debug("User supplied job control file\n%s", cfg) all_text = run_g16(cfg) if not all_text: raise QiskitNatureError("Failed to capture log from stdout") return GaussianLogResult(all_text)
def run_pyquante(self): """Runs the PyQuante calculation. This method is part of the public interface to allow the user to easily overwrite it in a subclass to further tailor the behavior to some specific use case. Raises: QiskitNatureError: If an invalid HF method type was supplied. """ # pylint: disable=import-error from pyquante2 import rhf, uhf, rohf, basisset self._bfs = basisset(self._mol, self.basis.value) if self.method == MethodType.RHF: self._calc = rhf(self._mol, self._bfs) elif self.method == MethodType.ROHF: self._calc = rohf(self._mol, self._bfs) elif self.method == MethodType.UHF: self._calc = uhf(self._mol, self._bfs) else: raise QiskitNatureError(f"Invalid method type: {self.method}") self._calc.converge(tol=self.tol, maxiters=self.maxiters) logger.debug("PyQuante2 processing information:\n%s", self._calc)
def compute_integrals(atoms, units, charge, multiplicity, basis, hf_method="rhf", tol=1e-8, maxiters=100): """Compute integrals""" # Get config from input parameters # Molecule is in this format xyz as below or in Z-matrix e.g "H; O 1 1.08; H 2 1.08 1 107.5": # atoms=H .0 .0 .0; H .0 .0 0.2 # units=Angstrom # charge=0 # multiplicity=1 # where we support symbol for atom as well as number units = _check_units(units) mol = _parse_molecule(atoms, units, charge, multiplicity) hf_method = hf_method.lower() try: q_mol = _calculate_integrals(mol, basis, hf_method, tol, maxiters) except Exception as exc: raise QiskitNatureError( "Failed electronic structure computation") from exc return q_mol
def _get_excitation_generators(self) -> List[Callable]: logger.debug("Gathering excitation generators...") generators: List[Callable] = [] if isinstance(self.excitations, str): for exc in self.excitations: generators.append( partial( generate_vibration_excitations, num_excitations=self.EXCITATION_TYPE[exc], )) elif isinstance(self.excitations, int): generators.append( partial( generate_vibration_excitations, num_excitations=self.excitations, )) elif isinstance(self.excitations, list): for exc in self.excitations: # type: ignore generators.append( partial( generate_vibration_excitations, num_excitations=exc, )) elif callable(self.excitations): generators = [self.excitations] else: raise QiskitNatureError( f"Invalid excitation configuration: {self.excitations}") return generators
def _build_single_hopping_operator( excitation: Tuple[Tuple[int, ...], Tuple[int, ...]], num_spin_orbitals: int, qubit_converter: QubitConverter, ) -> Tuple[PauliSumOp, List[bool]]: label = ["I"] * num_spin_orbitals for occ in excitation[0]: label[occ] = "+" for unocc in excitation[1]: label[unocc] = "-" fer_op = FermionicOp(("".join(label), 4.0**len(excitation[0]))) qubit_op: PauliSumOp = qubit_converter.convert_match(fer_op) z2_symmetries = qubit_converter.z2symmetries commutativities = [] if not z2_symmetries.is_empty(): for symmetry in z2_symmetries.symmetries: symmetry_op = PauliSumOp.from_list([(symmetry.to_label(), 1.0)]) commuting = qubit_op.primitive.table.commutes_with_all( symmetry_op.primitive.table) anticommuting = qubit_op.primitive.table.anticommutes_with_all( symmetry_op.primitive.table) if commuting != anticommuting: # only one of them is True if commuting: commutativities.append(True) elif anticommuting: commutativities.append(False) else: raise QiskitNatureError( "Symmetry {} is nor commute neither anti-commute " "to exciting operator.".format(symmetry.to_label())) return qubit_op, commutativities
def _check_units(units): if units.lower() in ["angstrom", "ang", "a"]: units = "Angstrom" elif units.lower() in ["bohr", "b"]: units = "Bohr" else: raise QiskitNatureError("Molecule units format error: " + units) return units
def _validate_num_particles(self, num_particles): try: assert num_particles[0] == num_particles[1] except AssertionError as exc: raise QiskitNatureError( 'The PUCCD Ansatz only works for singlet-spin systems. However, you specified ' 'differing numbers of alpha and beta electrons:', str(num_particles)) from exc
def check_valid() -> None: """Checks if Gaussian is installed and available""" if _G16PROG is None: raise QiskitNatureError( "Could not locate {} executable '{}'. Please check that it is installed correctly.".format( _GAUSSIAN_16_DESC, _GAUSSIAN_16 ) )
def num_particles(self) -> Tuple[int, int]: if self._grouped_property_transformed is None: raise QiskitNatureError( "`num_particles` is only available _after_ `second_q_ops()` has been called! " "Note, that if you run this manually, the method will run again during solving." ) return self._grouped_property_transformed.get_property( "ParticleNumber").num_particles
def _check_units(units): if units.lower() in ["angstrom", "ang", "a"]: units = 'Angstrom' elif units.lower() in ["bohr", "b"]: units = 'Bohr' else: raise QiskitNatureError('Molecule units format error: ' + units) return units
def get_qubit_operators( self, problem: BaseProblem, aux_operators: Optional[ListOrDictType[Union[SecondQuantizedOp, PauliSumOp]]] = None, ) -> Tuple[PauliSumOp, Optional[ListOrDictType[PauliSumOp]]]: """Gets the operator and auxiliary operators, and transforms the provided auxiliary operators""" # Note that ``aux_ops`` contains not only the transformed ``aux_operators`` passed by the # user but also additional ones from the transformation second_q_ops = problem.second_q_ops() aux_second_q_ops: ListOrDictType[SecondQuantizedOp] if isinstance(second_q_ops, list): main_second_q_op = second_q_ops[0] aux_second_q_ops = second_q_ops[1:] elif isinstance(second_q_ops, dict): name = problem.main_property_name main_second_q_op = second_q_ops.pop(name, None) if main_second_q_op is None: raise ValueError( f"The main `SecondQuantizedOp` associated with the {name} property cannot be " "`None`." ) aux_second_q_ops = second_q_ops main_operator = self._qubit_converter.convert( main_second_q_op, num_particles=problem.num_particles, sector_locator=problem.symmetry_sector_locator, ) aux_ops = self._qubit_converter.convert_match(aux_second_q_ops) if aux_operators is not None: wrapped_aux_operators: ListOrDict[Union[SecondQuantizedOp, PauliSumOp]] = ListOrDict( aux_operators ) for name_aux, aux_op in iter(wrapped_aux_operators): if isinstance(aux_op, SecondQuantizedOp): converted_aux_op = self._qubit_converter.convert_match(aux_op, True) else: converted_aux_op = aux_op if isinstance(aux_ops, list): aux_ops.append(converted_aux_op) elif isinstance(aux_ops, dict): if name_aux in aux_ops.keys(): raise QiskitNatureError( f"The key '{name_aux}' is already taken by an internally constructed " "auxiliary operator! Please use a different name for your custom " "operator." ) aux_ops[name_aux] = converted_aux_op if isinstance(self._solver, EigensolverFactory): # this must be called after transformation.transform self._solver = self._solver.get_solver(problem) # if the eigensolver does not support auxiliary operators, reset them if not self._solver.supports_aux_operators(): aux_ops = None return main_operator, aux_ops
def _parse_atom(val: str) -> Tuple[int, float, float, float]: if val is None or len(val) < 1: raise QiskitNatureError("Molecule atom format error: empty") parts = re.split(r"\s+", val) if len(parts) != 4: raise QiskitNatureError("Molecule atom format error: " + val) parts[0] = parts[0].lower().capitalize() if not parts[0].isdigit(): if parts[0] in PERIODIC_TABLE: parts[0] = PERIODIC_TABLE.index(parts[0]) else: raise QiskitNatureError("Molecule atom symbol error: " + parts[0]) return int(float(parts[0])), float(parts[1]), float(parts[2]), float( parts[3])
def __init__( self, atoms: Union[str, List[str]] = "H 0.0 0.0 0.0; H 0.0 0.0 0.735", units: UnitsType = UnitsType.ANGSTROM, charge: int = 0, multiplicity: int = 1, basis: BasisType = BasisType.BSTO3G, method: MethodType = MethodType.RHF, tol: float = 1e-8, maxiters: int = 100, ) -> None: """ Args: atoms: Atoms list or string separated by semicolons or line breaks. Each element in the list is an atom followed by position e.g. `H 0.0 0.0 0.5`. The preceding example shows the `XYZ` format for position but `Z-Matrix` format is supported too here. units: Angstrom or Bohr. charge: Charge on the molecule. multiplicity: Spin multiplicity (2S+1) basis: Basis set; sto3g, 6-31g or 6-31g** method: Hartree-Fock Method type. tol: Convergence tolerance see pyquante2.scf hamiltonians and iterators maxiters: Convergence max iterations see pyquante2.scf hamiltonians and iterators, has a min. value of 1. Raises: QiskitNatureError: Invalid Input """ super().__init__() # pylint: disable=import-error from pyquante2 import molecule as pyquante_molecule from pyquante2 import rhf, uhf, rohf, basisset validate_min("maxiters", maxiters, 1) PyQuanteDriver.check_method_supported(method) if not isinstance(atoms, str) and not isinstance(atoms, list): raise QiskitNatureError( f"Invalid atom input for PYQUANTE Driver '{atoms}'") if isinstance(atoms, list): atoms = ";".join(atoms) elif isinstance(atoms, str): atoms = atoms.replace("\n", ";") self._atoms = atoms self._units = units self._charge = charge self._multiplicity = multiplicity self._basis = basis self._method = method self._tol = tol self._maxiters = maxiters self._mol: pyquante_molecule = None self._bfs: basisset = None self._calc: Union[rhf, rohf, uhf] = None self._nmo: int = None
def num_spin_orbitals(self) -> int: """Returns the number of spin orbitals.""" if self._grouped_property_transformed is None: raise QiskitNatureError( "`num_spin_orbitals` is only available _after_ `second_q_ops()` has been called! " "Note, that if you run this manually, the method will run again during solving." ) return self._grouped_property_transformed.get_property( "ParticleNumber").num_spin_orbitals
def _do_transform( self, watson: WatsonHamiltonian, aux_operators: Optional[List[Union[BosonicOperator, PauliSumOp]]] = None ) -> Tuple[PauliSumOp, List[PauliSumOp]]: self._num_modes = watson.num_modes # type: ignore if self._transformation_type == 'harmonic': if isinstance(self._basis_size, int): self._basis_size = [self._basis_size] * self._num_modes self._h_mat = HarmonicBasis( watson, # type: ignore self._basis_size, self._truncation_order).convert() else: raise QiskitNatureError('Unknown Transformation type') bos_op = BosonicOperator(self._h_mat, self._basis_size) qubit_op = bos_op.mapping(qubit_mapping=self._qubit_mapping) self._untapered_qubit_op = qubit_op qubit_op.name = 'Bosonic Operator' aux_ops = [] def _add_aux_op(aux_op: BosonicOperator, name: str) -> None: """ Add auxiliary operators Args: aux_op: auxiliary operators name: name """ if not isinstance(aux_op, PauliSumOp): aux_qop = BosonicTransformation._map_bosonic_operator_to_qubit( aux_op, self._qubit_mapping) aux_qop.name = name else: aux_qop = aux_op aux_ops.append(aux_qop) logger.debug(' pauli: %s', str(aux_qop)) logger.debug('Creating aux op for number of occupied modals per mode') for mode in range(self._num_modes): _add_aux_op(bos_op.number_occupied_modals_per_mode(mode), 'Number of occupied modals in mode {}'.format(mode)) # add user specified auxiliary operators if aux_operators is not None: for aux_op in aux_operators: _add_aux_op(aux_op, aux_op.name) # type: ignore return qubit_op, aux_ops