def interpret( self, raw_result: Union[EigenstateResult, EigensolverResult, MinimumEigensolverResult], ) -> LatticeModelResult: """Interprets a raw result in the context of this transformation. Args: raw_result: a raw result to be interpreted Returns: A lattice model result. """ eigenstate_result = None if isinstance(raw_result, EigenstateResult): eigenstate_result = raw_result elif isinstance(raw_result, EigensolverResult): eigenstate_result = EigenstateResult() eigenstate_result.raw_result = raw_result eigenstate_result.eigenenergies = raw_result.eigenvalues eigenstate_result.eigenstates = raw_result.eigenstates eigenstate_result.aux_operator_eigenvalues = raw_result.aux_operator_eigenvalues elif isinstance(raw_result, MinimumEigensolverResult): eigenstate_result = EigenstateResult() eigenstate_result.raw_result = raw_result eigenstate_result.eigenenergies = np.asarray( [raw_result.eigenvalue]) eigenstate_result.eigenstates = [raw_result.eigenstate] eigenstate_result.aux_operator_eigenvalues = [ raw_result.aux_operator_eigenvalues ] result = LatticeModelResult() result.combine(eigenstate_result) result.computed_lattice_energies = eigenstate_result.eigenenergies return result
def interpret( self, raw_result: Union[EigenstateResult, EigensolverResult, MinimumEigensolverResult], ) -> VibrationalStructureResult: """Interprets an EigenstateResult in the context of this transformation. Args: raw_result: an eigenstate result object. Returns: An vibrational structure result. """ eigenstate_result = None if isinstance(raw_result, EigenstateResult): eigenstate_result = raw_result elif isinstance(raw_result, EigensolverResult): eigenstate_result = EigenstateResult() eigenstate_result.raw_result = raw_result eigenstate_result.eigenenergies = raw_result.eigenvalues eigenstate_result.eigenstates = raw_result.eigenstates eigenstate_result.aux_operator_eigenvalues = raw_result.aux_operator_eigenvalues elif isinstance(raw_result, MinimumEigensolverResult): eigenstate_result = EigenstateResult() eigenstate_result.raw_result = raw_result eigenstate_result.eigenenergies = np.asarray([raw_result.eigenvalue]) eigenstate_result.eigenstates = [raw_result.eigenstate] eigenstate_result.aux_operator_eigenvalues = [raw_result.aux_operator_eigenvalues] result = VibrationalStructureResult() result.combine(eigenstate_result) self._grouped_property_transformed.interpret(result) result.computed_vibrational_energies = eigenstate_result.eigenenergies return result
def interpret( self, raw_result: Union[EigenstateResult, EigensolverResult, MinimumEigensolverResult] ) -> VibronicStructureResult: """Interprets an EigenstateResult in the context of this transformation. Args: raw_result: an eigenstate result object. Returns: An vibronic structure result. """ eigenstate_result = None if isinstance(raw_result, EigenstateResult): eigenstate_result = raw_result elif isinstance(raw_result, EigensolverResult): eigenstate_result = EigenstateResult() eigenstate_result.raw_result = raw_result eigenstate_result.eigenenergies = raw_result.eigenvalues eigenstate_result.eigenstates = raw_result.eigenstates eigenstate_result.aux_operator_eigenvalues = raw_result.aux_operator_eigenvalues elif isinstance(raw_result, MinimumEigensolverResult): eigenstate_result = EigenstateResult() eigenstate_result.raw_result = raw_result eigenstate_result.eigenenergies = np.asarray( [raw_result.eigenvalue]) eigenstate_result.eigenstates = [raw_result.eigenstate] eigenstate_result.aux_operator_eigenvalues = raw_result.aux_operator_eigenvalues result = VibronicStructureResult() result.combine(eigenstate_result) result.computed_vibronic_energies = eigenstate_result.eigenenergies if result.aux_operator_eigenvalues is not None: if not isinstance(result.aux_operator_eigenvalues, list): aux_operator_eigenvalues = [result.aux_operator_eigenvalues] else: aux_operator_eigenvalues = result.aux_operator_eigenvalues # type: ignore result.num_occupied_modals_per_mode = [] for aux_op_eigenvalues in aux_operator_eigenvalues: occ_modals = [] for mode in range(self._num_modes): if aux_op_eigenvalues[mode] is not None: occ_modals.append( aux_op_eigenvalues[mode][0].real) # type: ignore else: occ_modals.append(None) result.num_occupied_modals_per_mode.append( occ_modals) # type: ignore return result
def solve( self, problem: BaseProblem, aux_operators: Optional[List[Union[SecondQuantizedOp, PauliSumOp]]] = None, ) -> EigenstateResult: """Compute Ground and Excited States properties. Args: problem: a class encoding a problem to be solved. aux_operators: Additional auxiliary operators to evaluate. Raises: NotImplementedError: If an operator in ``aux_operators`` is not of type ``FermionicOperator``. Returns: An interpreted :class:`~.EigenstateResult`. For more information see also :meth:`~.BaseProblem.interpret`. """ # get the operator and auxiliary operators, and transform the provided auxiliary operators # note that ``aux_operators`` 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() main_operator = self._qubit_converter.convert( second_q_ops[0], num_particles=problem.num_particles, sector_locator=problem.symmetry_sector_locator, ) aux_ops = self._qubit_converter.convert_match(second_q_ops[1:]) if aux_operators is not None: for aux_op in aux_operators: if isinstance(aux_op, SecondQuantizedOp): aux_ops.append( self._qubit_converter.convert_match(aux_op, True)) else: aux_ops.append(aux_op) if isinstance(self._solver, EigensolverFactory): # this must be called after transformation.transform solver = self._solver.get_solver(problem) else: solver = self._solver # if the eigensolver does not support auxiliary operators, reset them if not solver.supports_aux_operators(): aux_ops = None raw_es_result = solver.compute_eigenvalues(main_operator, aux_ops) eigenstate_result = EigenstateResult() eigenstate_result.raw_result = raw_es_result eigenstate_result.eigenenergies = raw_es_result.eigenvalues eigenstate_result.eigenstates = raw_es_result.eigenstates eigenstate_result.aux_operator_eigenvalues = raw_es_result.aux_operator_eigenvalues result = problem.interpret(eigenstate_result) return result
def _interpret_raw_result(raw_result): eigenstate_result = None if isinstance(raw_result, EigenstateResult): eigenstate_result = raw_result elif isinstance(raw_result, EigensolverResult): eigenstate_result = EigenstateResult() eigenstate_result.raw_result = raw_result eigenstate_result.eigenenergies = raw_result.eigenvalues eigenstate_result.eigenstates = raw_result.eigenstates eigenstate_result.aux_operator_eigenvalues = raw_result.aux_operator_eigenvalues elif isinstance(raw_result, MinimumEigensolverResult): eigenstate_result = EigenstateResult() eigenstate_result.raw_result = raw_result eigenstate_result.eigenenergies = np.asarray([raw_result.eigenvalue]) eigenstate_result.eigenstates = [raw_result.eigenstate] eigenstate_result.aux_operator_eigenvalues = raw_result.aux_operator_eigenvalues return eigenstate_result
def solve( self, driver: BaseDriver, aux_operators: Optional[List[Any]] = None ) -> Union[ElectronicStructureResult, VibronicStructureResult]: """Compute Ground and Excited States properties. Args: driver: a chemistry driver object which defines the chemical problem that is to be solved by this calculation. aux_operators: Additional auxiliary operators to evaluate. Must be of type ``FermionicOperator`` if the qubit transformation is fermionic and of type ``BosonicOperator`` it is bosonic. Raises: NotImplementedError: If an operator in ``aux_operators`` is not of type ``FermionicOperator``. Returns: An eigenstate result. Depending on the transformation this can be an electronic structure or bosonic result. """ if aux_operators is not None: if any(not isinstance(op, (PauliSumOp, FermionicOperator)) for op in aux_operators): raise NotImplementedError( 'Currently only fermionic problems are supported.') # get the operator and auxiliary operators, and transform the provided auxiliary operators # note that ``aux_operators`` contains not only the transformed ``aux_operators`` passed # by the user but also additional ones from the transformation operator, aux_operators = self.transformation.transform( driver, aux_operators) if isinstance(self._solver, EigensolverFactory): # this must be called after transformation.transform solver = self._solver.get_solver(self.transformation) else: solver = self._solver # if the eigensolver does not support auxiliary operators, reset them if not solver.supports_aux_operators(): aux_operators = None raw_es_result = solver.compute_eigenvalues(operator, aux_operators) eigenstate_result = EigenstateResult() eigenstate_result.raw_result = raw_es_result eigenstate_result.eigenenergies = raw_es_result.eigenvalues eigenstate_result.eigenstates = raw_es_result.eigenstates eigenstate_result.aux_operator_eigenvalues = raw_es_result.aux_operator_eigenvalues result = self.transformation.interpret(eigenstate_result) return result
def test_interpret(self): """Tests that the result is interpreted""" num_nodes = 4 boundary_condition = BoundaryCondition.OPEN line_lattice = LineLattice(num_nodes=num_nodes, boundary_condition=boundary_condition) fhm = FermiHubbardModel(lattice=line_lattice, onsite_interaction=5.0) eigenenergies = np.array([-1]) eigenstates = [np.array([1, 0])] aux_operator_eigenvalues = [(1, 2)] # For EigenstateResult lmp = LatticeModelProblem(fhm) eigenstate_result = EigenstateResult() eigenstate_result.eigenenergies = eigenenergies eigenstate_result.eigenstates = eigenstates eigenstate_result.aux_operator_eigenvalues = aux_operator_eigenvalues lmr = lmp.interpret(eigenstate_result) self.assertEqual(lmr.eigenenergies, eigenstate_result.eigenenergies) self.assertEqual(lmr.eigenstates, eigenstate_result.eigenstates) self.assertEqual(lmr.aux_operator_eigenvalues, eigenstate_result.aux_operator_eigenvalues) # For EigenSOlverResult lmp = LatticeModelProblem(fhm) eigensolver_result = EigensolverResult() eigensolver_result.eigenvalues = eigenenergies eigensolver_result.eigenstates = eigenstates eigensolver_result.aux_operator_eigenvalues = [ aux_operator_eigenvalues ] lmr = lmp.interpret(eigensolver_result) self.assertEqual(lmr.eigenenergies, eigensolver_result.eigenvalues) self.assertEqual(lmr.eigenstates, eigensolver_result.eigenstates) self.assertEqual(lmr.aux_operator_eigenvalues, eigensolver_result.aux_operator_eigenvalues) # For MinimumEigensolverResult lmp = LatticeModelProblem(fhm) mes_result = MinimumEigensolverResult() mes_result.eigenvalue = -1 mes_result.eigenstate = np.array([1, 0]) mes_result.aux_operator_eigenvalues = aux_operator_eigenvalues lmr = lmp.interpret(mes_result) self.assertEqual(lmr.eigenenergies, np.asarray([mes_result.eigenvalue])) self.assertEqual(lmr.eigenstates, [mes_result.eigenstate]) self.assertEqual(lmr.aux_operator_eigenvalues, [mes_result.aux_operator_eigenvalues])
def solve( self, problem: BaseProblem, aux_operators: Optional[ListOrDictType[Union[SecondQuantizedOp, PauliSumOp]]] = None, ) -> EigenstateResult: """Compute Ground and Excited States properties. Args: problem: a class encoding a problem to be solved. aux_operators: Additional auxiliary operators to evaluate. Raises: ValueError: if the grouped property object returned by the driver does not contain a main property as requested by the problem being solved (`problem.main_property_name`) QiskitNatureError: if the user-provided `aux_operators` contain a name which clashes with an internally constructed auxiliary operator. Note: the names used for the internal auxiliary operators correspond to the `Property.name` attributes which generated the respective operators. Returns: An interpreted :class:`~.EigenstateResult`. For more information see also :meth:`~.BaseProblem.interpret`. """ # get the operator and auxiliary operators, and transform the provided auxiliary operators # note that ``aux_operators`` contains not only the transformed ``aux_operators`` passed # by the user but also additional ones from the transformation main_operator, aux_ops = self.get_qubit_operators(problem, aux_operators) raw_es_result = self._solver.compute_eigenvalues(main_operator, aux_ops) # type: ignore eigenstate_result = EigenstateResult() eigenstate_result.raw_result = raw_es_result eigenstate_result.eigenenergies = raw_es_result.eigenvalues eigenstate_result.eigenstates = raw_es_result.eigenstates eigenstate_result.aux_operator_eigenvalues = raw_es_result.aux_operator_eigenvalues result = problem.interpret(eigenstate_result) return result
def interpret( self, raw_result: Union[EigenstateResult, EigensolverResult, MinimumEigensolverResult] ) -> ElectronicStructureResult: """Interprets an EigenstateResult in the context of this transformation. Args: raw_result: an eigenstate result object. Returns: An electronic structure result. """ eigenstate_result = None if isinstance(raw_result, EigenstateResult): eigenstate_result = raw_result elif isinstance(raw_result, EigensolverResult): eigenstate_result = EigenstateResult() eigenstate_result.raw_result = raw_result eigenstate_result.eigenenergies = raw_result.eigenvalues eigenstate_result.eigenstates = raw_result.eigenstates eigenstate_result.aux_operator_eigenvalues = raw_result.aux_operator_eigenvalues elif isinstance(raw_result, MinimumEigensolverResult): eigenstate_result = EigenstateResult() eigenstate_result.raw_result = raw_result eigenstate_result.eigenenergies = np.asarray( [raw_result.eigenvalue]) eigenstate_result.eigenstates = [raw_result.eigenstate] eigenstate_result.aux_operator_eigenvalues = [ raw_result.aux_operator_eigenvalues ] result = ElectronicStructureResult() result.combine(eigenstate_result) result.computed_energies = np.asarray( [e.real for e in eigenstate_result.eigenenergies]) result.hartree_fock_energy = self._hf_energy result.nuclear_repulsion_energy = self._nuclear_repulsion_energy if self._nuclear_dipole_moment is not None: result.nuclear_dipole_moment = tuple( x for x in self._nuclear_dipole_moment) result.ph_extracted_energy = self._ph_energy_shift result.frozen_extracted_energy = self._energy_shift if result.aux_operator_eigenvalues is not None: # the first three values are hardcoded to number of particles, angular momentum # and magnetization in this order result.num_particles = [] result.total_angular_momentum = [] result.magnetization = [] result.computed_dipole_moment = [] result.ph_extracted_dipole_moment = [] result.frozen_extracted_dipole_moment = [] if not isinstance(result.aux_operator_eigenvalues, list): aux_operator_eigenvalues = [result.aux_operator_eigenvalues] else: aux_operator_eigenvalues = result.aux_operator_eigenvalues # type: ignore for aux_op_eigenvalues in aux_operator_eigenvalues: if aux_op_eigenvalues is None: continue if aux_op_eigenvalues[0] is not None: result.num_particles.append( aux_op_eigenvalues[0][0].real) # type: ignore if aux_op_eigenvalues[1] is not None: result.total_angular_momentum.append( aux_op_eigenvalues[1][0].real) # type: ignore if aux_op_eigenvalues[2] is not None: result.magnetization.append( aux_op_eigenvalues[2][0].real) # type: ignore # the next three are hardcoded to Dipole moments, if they are set if len(aux_op_eigenvalues) >= 6 and self._has_dipole_moments: # check if the names match # extract dipole moment in each axis dipole_moment = [] for moment in aux_op_eigenvalues[3:6]: if moment is not None: dipole_moment += [moment[0].real] # type: ignore else: dipole_moment += [None] result.reverse_dipole_sign = self._reverse_dipole_sign result.computed_dipole_moment.append( cast(DipoleTuple, tuple(dipole_moment))) result.ph_extracted_dipole_moment.append( (self._ph_x_dipole_shift, self._ph_y_dipole_shift, self._ph_z_dipole_shift)) result.frozen_extracted_dipole_moment.append( (self._x_dipole_shift, self._y_dipole_shift, self._z_dipole_shift)) return result
def solve( self, problem: BaseProblem, aux_operators: Optional[List[SecondQuantizedOp]] = None ) -> EigenstateResult: """Run the excited-states calculation. Construct and solves the EOM pseudo-eigenvalue problem to obtain the excitation energies and the excitation operators expansion coefficients. Args: problem: a class encoding a problem to be solved. aux_operators: Additional auxiliary operators to evaluate. Returns: An interpreted :class:`~.EigenstateResult`. For more information see also :meth:`~.BaseProblem.interpret`. """ if aux_operators is not None: logger.warning( "With qEOM the auxiliary operators can currently only be " "evaluated on the ground state.") # 1. Run ground state calculation groundstate_result = self._gsc.solve(problem) # 2. Prepare the excitation operators self._untapered_qubit_op_main = self._gsc._qubit_converter.map( problem.second_q_ops()[0]) matrix_operators_dict, size = self._prepare_matrix_operators(problem) # 3. Evaluate eom operators measurement_results = self._gsc.evaluate_operators( groundstate_result.eigenstates[0], matrix_operators_dict) measurement_results = cast(Dict[str, List[float]], measurement_results) # 4. Post-process ground_state_result to construct eom matrices m_mat, v_mat, q_mat, w_mat, m_mat_std, v_mat_std, q_mat_std, w_mat_std = \ self._build_eom_matrices(measurement_results, size) # 5. solve pseudo-eigenvalue problem energy_gaps, expansion_coefs = self._compute_excitation_energies( m_mat, v_mat, q_mat, w_mat) qeom_result = QEOMResult() qeom_result.ground_state_raw_result = groundstate_result.raw_result qeom_result.expansion_coefficients = expansion_coefs qeom_result.excitation_energies = energy_gaps qeom_result.m_matrix = m_mat qeom_result.v_matrix = v_mat qeom_result.q_matrix = q_mat qeom_result.w_matrix = w_mat qeom_result.m_matrix_std = m_mat_std qeom_result.v_matrix_std = v_mat_std qeom_result.q_matrix_std = q_mat_std qeom_result.w_matrix_std = w_mat_std eigenstate_result = EigenstateResult() eigenstate_result.eigenstates = groundstate_result.eigenstates eigenstate_result.aux_operator_eigenvalues = groundstate_result.aux_operator_eigenvalues eigenstate_result.raw_result = qeom_result eigenstate_result.eigenenergies = np.append( groundstate_result.eigenenergies, np.asarray([ groundstate_result.eigenenergies[0] + gap for gap in energy_gaps ])) result = problem.interpret(eigenstate_result) return result
def solve( self, driver: BaseDriver, aux_operators: Optional[Union[List[FermionicOperator], List[BosonicOperator]]] = None ) -> Union[ElectronicStructureResult, VibronicStructureResult]: """Run the excited-states calculation. Construct and solves the EOM pseudo-eigenvalue problem to obtain the excitation energies and the excitation operators expansion coefficients. Args: driver: a chemistry driver object which defines the chemical problem that is to be solved by this calculation. aux_operators: Additional auxiliary operators to evaluate. Must be of type ``FermionicOperator`` if the qubit transformation is fermionic and of type ``BosonicOperator`` it is bosonic. Returns: The excited states result. In case of a fermionic problem a ``ElectronicStructureResult`` is returned and in the bosonic case a ``VibronicStructureResult``. """ if aux_operators is not None: logger.warning( "With qEOM the auxiliary operators can currently only be " "evaluated on the ground state.") # 1. Run ground state calculation groundstate_result = self._gsc.solve(driver, aux_operators) # 2. Prepare the excitation operators matrix_operators_dict, size = self._prepare_matrix_operators() # 3. Evaluate eom operators measurement_results = self._gsc.evaluate_operators( groundstate_result.raw_result['eigenstate'], matrix_operators_dict) measurement_results = cast(Dict[str, List[float]], measurement_results) # 4. Post-process ground_state_result to construct eom matrices m_mat, v_mat, q_mat, w_mat, m_mat_std, v_mat_std, q_mat_std, w_mat_std = \ self._build_eom_matrices(measurement_results, size) # 5. solve pseudo-eigenvalue problem energy_gaps, expansion_coefs = self._compute_excitation_energies( m_mat, v_mat, q_mat, w_mat) qeom_result = QEOMResult() qeom_result.ground_state_raw_result = groundstate_result.raw_result qeom_result.expansion_coefficients = expansion_coefs qeom_result.excitation_energies = energy_gaps qeom_result.m_matrix = m_mat qeom_result.v_matrix = v_mat qeom_result.q_matrix = q_mat qeom_result.w_matrix = w_mat qeom_result.m_matrix_std = m_mat_std qeom_result.v_matrix_std = v_mat_std qeom_result.q_matrix_std = q_mat_std qeom_result.w_matrix_std = w_mat_std eigenstate_result = EigenstateResult() eigenstate_result.eigenstates = groundstate_result.eigenstates eigenstate_result.aux_operator_eigenvalues = groundstate_result.aux_operator_eigenvalues eigenstate_result.raw_result = qeom_result eigenstate_result.eigenenergies = np.append( groundstate_result.eigenenergies, np.asarray([ groundstate_result.eigenenergies[0] + gap for gap in energy_gaps ])) result = self._gsc.transformation.interpret(eigenstate_result) return result
def solve( self, problem: BaseProblem, aux_operators: Optional[ListOrDictType[Union[SecondQuantizedOp, PauliSumOp]]] = None, ) -> EigenstateResult: """Compute Ground and Excited States properties. Args: problem: a class encoding a problem to be solved. aux_operators: Additional auxiliary operators to evaluate. Raises: ValueError: if the grouped property object returned by the driver does not contain a main property as requested by the problem being solved (`problem.main_property_name`) QiskitNatureError: if the user-provided `aux_operators` contain a name which clashes with an internally constructed auxiliary operator. Note: the names used for the internal auxiliary operators correspond to the `Property.name` attributes which generated the respective operators. Returns: An interpreted :class:`~.EigenstateResult`. For more information see also :meth:`~.BaseProblem.interpret`. """ # get the operator and auxiliary operators, and transform the provided auxiliary operators # note that ``aux_operators`` 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 solver = self._solver.get_solver(problem) else: solver = self._solver # if the eigensolver does not support auxiliary operators, reset them if not solver.supports_aux_operators(): aux_ops = None raw_es_result = solver.compute_eigenvalues(main_operator, aux_ops) eigenstate_result = EigenstateResult() eigenstate_result.raw_result = raw_es_result eigenstate_result.eigenenergies = raw_es_result.eigenvalues eigenstate_result.eigenstates = raw_es_result.eigenstates eigenstate_result.aux_operator_eigenvalues = raw_es_result.aux_operator_eigenvalues result = problem.interpret(eigenstate_result) return result