Source code for qiskit_nature.second_q.problems.eigenstate_result

# This code is part of a Qiskit project.
#
# (C) Copyright IBM 2020, 2023.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Eigenstate results module."""

from __future__ import annotations

from typing import Any, Sequence

import numpy as np

from qiskit_algorithms import AlgorithmResult, EigensolverResult, MinimumEigensolverResult
from qiskit_algorithms.list_or_dict import ListOrDict
from qiskit.circuit import QuantumCircuit
from qiskit.quantum_info import Statevector


def _statevector_to_circuit(state: Statevector) -> QuantumCircuit:
    circ = QuantumCircuit(state.num_qubits)
    circ.initialize(state, circ.qubits)
    return circ


[docs]class EigenstateResult(AlgorithmResult): """The eigenstate result interface. The following attributes can be read and updated once the ``EigenstateResult`` object has been constructed. Attributes: eigenvalues (np.ndarray | None): the computed eigenvalues. eigenstates (list[tuple[QuantumCircuit, Sequence[float] | None]] | None): the eigenstates belonging to each of the computed eigenvalues. aux_operators_evaluated (list[ListOrDict[complex]] | None): the evaluated aux operators. raw_result (AlgorithmResult | None): the raw result, wrapped by this ``EigenstateResult``. formatting_precision (int): the number of decimal places to use when formatting the result for printing. """ def __init__(self) -> None: super().__init__() self.eigenvalues: np.ndarray | None = None self.eigenstates: list[tuple[QuantumCircuit, Sequence[float] | None]] | None = None self.aux_operators_evaluated: list[ListOrDict[complex]] | None = None self.raw_result: AlgorithmResult | None = None self.formatting_precision: int = 12 @property def groundenergy(self) -> float | None: """Returns the lowest eigenvalue.""" energies = self.eigenvalues if isinstance(energies, np.ndarray) and energies.size: return energies[0].real return None @property def groundstate(self) -> tuple[QuantumCircuit, Sequence[float] | None] | None: """Returns the lowest eigenstate.""" states = self.eigenstates if states: return states[0] return None
[docs] @classmethod def from_result( cls, raw_result: EigenstateResult | EigensolverResult | MinimumEigensolverResult ) -> EigenstateResult: """Constructs an `EigenstateResult` from another result type. Args: raw_result: the raw result from which to build the new one. Raises: TypeError: when an unsupported result type is provided as input. Returns: The constructed `EigenstateResult`. """ # NOTE: the following inspection of class names is a work-around to handle backwards # compatibility for the deprecated qiskit.algorithms module without delving into complex # type unions. This logic can be removed once the qiskit.algorithms module no longer wants # to be supported. cls_names = {cls.__name__ for cls in raw_result.__class__.mro()} if isinstance(raw_result, EigenstateResult) or "EigenstateResult" in cls_names: return raw_result if isinstance(raw_result, EigensolverResult) or "EigensolverResult" in cls_names: return EigenstateResult.from_eigensolver_result(raw_result) if ( isinstance(raw_result, MinimumEigensolverResult) or "MinimumEigensolverResult" in cls_names ): return EigenstateResult.from_minimum_eigensolver_result(raw_result) raise TypeError( f"Cannot construct an EigenstateResult from a result of type, {type(raw_result)}." )
[docs] @classmethod def from_eigensolver_result(cls, raw_result: EigensolverResult) -> EigenstateResult: """Constructs an `EigenstateResult` from an :class:`~qiskit_algorithms.EigensolverResult`. Args: raw_result: the raw result from which to build the `EigenstateResult`. Returns: The constructed `EigenstateResult`. """ result = EigenstateResult() result.raw_result = raw_result result.eigenvalues = np.asarray(raw_result.eigenvalues) if hasattr(raw_result, "eigenstates"): result.eigenstates = [ (_statevector_to_circuit(Statevector(state)), None) for state in raw_result.eigenstates ] elif hasattr(raw_result, "optimal_circuits") and hasattr(raw_result, "optimal_points"): result.eigenstates = list(zip(raw_result.optimal_circuits, raw_result.optimal_points)) if raw_result.aux_operators_evaluated is not None: result.aux_operators_evaluated = [ cls._unwrap_aux_op_values(aux_op_eval) for aux_op_eval in raw_result.aux_operators_evaluated ] return result
[docs] @classmethod def from_minimum_eigensolver_result( cls, raw_result: MinimumEigensolverResult ) -> EigenstateResult: """Constructs an `EigenstateResult` from an :class:`~qiskit_algorithms.MinimumEigensolverResult`. Args: raw_result: the raw result from which to build the `EigenstateResult`. Returns: The constructed `EigenstateResult`. """ result = EigenstateResult() result.raw_result = raw_result result.eigenvalues = np.asarray([raw_result.eigenvalue]) if hasattr(raw_result, "eigenstate"): result.eigenstates = [ (_statevector_to_circuit(Statevector(raw_result.eigenstate)), None) ] elif hasattr(raw_result, "optimal_circuit") and hasattr(raw_result, "optimal_point"): result.eigenstates = [(raw_result.optimal_circuit, raw_result.optimal_point)] if raw_result.aux_operators_evaluated is not None: result.aux_operators_evaluated = [ cls._unwrap_aux_op_values(raw_result.aux_operators_evaluated) ] return result
@staticmethod def _unwrap_aux_op_values( aux_operators_evaluated: ListOrDict[tuple[complex, dict[str, Any]]] ) -> ListOrDict[complex]: aux_op_values: ListOrDict[complex] if isinstance(aux_operators_evaluated, list): aux_op_values = [val[0] for val in aux_operators_evaluated] else: aux_op_values = {key: val[0] for key, val in aux_operators_evaluated.items()} return aux_op_values