Source code for qiskit.aqua.algorithms.minimum_eigen_solvers.qpe

# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 2018, 2020.
#
# 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.

"""The Quantum Phase Estimation Algorithm."""

import logging
from typing import Optional, List, Dict, Union
import warnings

import numpy as np
from qiskit import QuantumCircuit
from qiskit.quantum_info import Pauli

from qiskit.providers import BaseBackend
from qiskit.aqua import QuantumInstance
from qiskit.aqua.operators import op_converter, OperatorBase
from qiskit.aqua.utils import get_subsystem_density_matrix
from qiskit.aqua.algorithms import QuantumAlgorithm
from qiskit.aqua.circuits import PhaseEstimationCircuit
from qiskit.aqua.operators import WeightedPauliOperator
from qiskit.aqua.operators import LegacyBaseOperator
from qiskit.aqua.components.initial_states import InitialState
from qiskit.aqua.components.iqfts import IQFT
from qiskit.aqua.utils.validation import validate_min, validate_in_set
from .minimum_eigen_solver import MinimumEigensolver, MinimumEigensolverResult

logger = logging.getLogger(__name__)


# pylint: disable=invalid-name


[docs]class QPE(QuantumAlgorithm, MinimumEigensolver): """The Quantum Phase Estimation algorithm. QPE (also sometimes abbreviated as PEA, for Phase Estimation Algorithm), has two quantum registers, **control** and **target**, where the control consists of several qubits initially put in uniform superposition, and the target a set of qubits prepared in an eigenstate (often a guess of the eigenstate) of the unitary operator of a quantum system. QPE then evolves the target under the control using dynamics on the unitary operator. The information of the corresponding eigenvalue is then 'kicked-back' into the phases of the control register, which can then be deconvoluted by an Inverse Quantum Fourier Transform (IQFT), and measured for read-out in binary decimal format. QPE also requires a reasonably good estimate of the eigen wave function to start the process. For example, when estimating molecular ground energies in chemistry, the Hartree-Fock method could be used to provide such trial eigen wave functions. """ def __init__(self, operator: Optional[Union[OperatorBase, LegacyBaseOperator]] = None, state_in: Optional[InitialState] = None, iqft: Optional[Union[QuantumCircuit, IQFT]] = None, num_time_slices: int = 1, num_ancillae: int = 1, expansion_mode: str = 'trotter', expansion_order: int = 1, shallow_circuit_concat: bool = False, quantum_instance: Optional[Union[QuantumInstance, BaseBackend]] = None) -> None: """ Args: operator: The Hamiltonian Operator state_in: An optional InitialState component representing an initial quantum state. ``None`` may be supplied. iqft: A Inverse Quantum Fourier Transform component num_time_slices: The number of time slices, has a minimum value of 1. num_ancillae: The number of ancillary qubits to use for the measurement, has a min. value of 1. expansion_mode: The expansion mode ('trotter'|'suzuki') expansion_order: The suzuki expansion order, has a min. value of 1. shallow_circuit_concat: Set True to use shallow (cheap) mode for circuit concatenation of evolution slices. By default this is False. See :meth:`qiskit.aqua.operators.common.evolution_instruction` for more information. quantum_instance: Quantum Instance or Backend """ validate_min('num_time_slices', num_time_slices, 1) validate_min('num_ancillae', num_ancillae, 1) validate_in_set('expansion_mode', expansion_mode, {'trotter', 'suzuki'}) validate_min('expansion_order', expansion_order, 1) super().__init__(quantum_instance) self._state_in = state_in if isinstance(iqft, IQFT): warnings.warn('The qiskit.aqua.components.iqfts.IQFT module is deprecated as of 0.7.0 ' 'and will be removed no earlier than 3 months after the release. ' 'You should pass a QuantumCircuit instead, see ' 'qiskit.circuit.library.QFT and the .inverse() method.', DeprecationWarning, stacklevel=2) self._iqft = iqft self._num_time_slices = num_time_slices self._num_ancillae = num_ancillae self._expansion_mode = expansion_mode self._expansion_order = expansion_order self._shallow_circuit_concat = shallow_circuit_concat self._binary_fractions = [1 / 2 ** p for p in range(1, self._num_ancillae + 1)] self._in_operator = operator self._operator = None self._ret = {} self._pauli_list = None self._phase_estimation_circuit = None self._setup(operator) def _setup(self, operator: Optional[Union[OperatorBase, LegacyBaseOperator]]) -> None: self._operator = None self._ret = {} self._pauli_list = None self._phase_estimation_circuit = None if operator: # Convert to Legacy Operator if Operator flow passed in if isinstance(operator, OperatorBase): operator = operator.to_legacy_op() self._operator = op_converter.to_weighted_pauli_operator(operator.copy()) self._ret['translation'] = sum([abs(p[0]) for p in self._operator.reorder_paulis()]) self._ret['stretch'] = 0.5 / self._ret['translation'] # translate the operator self._operator.simplify() translation_op = WeightedPauliOperator([ [ self._ret['translation'], Pauli( np.zeros(self._operator.num_qubits), np.zeros(self._operator.num_qubits) ) ] ]) translation_op.simplify() self._operator += translation_op self._pauli_list = self._operator.reorder_paulis() # stretch the operator for p in self._pauli_list: p[0] = p[0] * self._ret['stretch'] self._phase_estimation_circuit = PhaseEstimationCircuit( operator=self._operator, state_in=self._state_in, iqft=self._iqft, num_time_slices=self._num_time_slices, num_ancillae=self._num_ancillae, expansion_mode=self._expansion_mode, expansion_order=self._expansion_order, shallow_circuit_concat=self._shallow_circuit_concat, pauli_list=self._pauli_list ) @property def operator(self) -> Optional[LegacyBaseOperator]: """ Returns operator """ return self._in_operator @operator.setter def operator(self, operator: Union[OperatorBase, LegacyBaseOperator]) -> None: """ set operator """ self._in_operator = operator self._setup(operator) @property def aux_operators(self) -> Optional[List[Union[OperatorBase, LegacyBaseOperator]]]: """ Returns aux operators """ raise TypeError('aux_operators not supported.') @aux_operators.setter def aux_operators(self, aux_operators: Optional[List[Union[OperatorBase, LegacyBaseOperator]]] ) -> None: """ Set aux operators """ raise TypeError('aux_operators not supported.')
[docs] def construct_circuit(self, measurement: bool = False) -> QuantumCircuit: """ Construct circuit. Args: measurement: Boolean flag to indicate if measurement should be included in the circuit. Returns: QuantumCircuit: quantum circuit. """ if self._phase_estimation_circuit: return self._phase_estimation_circuit.construct_circuit(measurement=measurement) return None
[docs] def compute_minimum_eigenvalue( self, operator: Optional[Union[OperatorBase, LegacyBaseOperator]] = None, aux_operators: Optional[List[Union[OperatorBase, LegacyBaseOperator]]] = None ) -> MinimumEigensolverResult: super().compute_minimum_eigenvalue(operator, aux_operators) return self._run()
def _compute_energy(self): if self._quantum_instance.is_statevector: qc = self.construct_circuit(measurement=False) result = self._quantum_instance.execute(qc) complete_state_vec = result.get_statevector(qc) ancilla_density_mat = get_subsystem_density_matrix( complete_state_vec, range(self._num_ancillae, self._num_ancillae + self._operator.num_qubits) ) ancilla_density_mat_diag = np.diag(ancilla_density_mat) max_amplitude = \ max(ancilla_density_mat_diag.min(), ancilla_density_mat_diag.max(), key=abs) max_amplitude_idx = np.where(ancilla_density_mat_diag == max_amplitude)[0][0] top_measurement_label = np.binary_repr(max_amplitude_idx, self._num_ancillae)[::-1] else: qc = self.construct_circuit(measurement=True) result = self._quantum_instance.execute(qc) ancilla_counts = result.get_counts(qc) top_measurement_label = \ sorted([(ancilla_counts[k], k) for k in ancilla_counts])[::-1][0][-1][::-1] top_measurement_decimal = sum( [t[0] * t[1] for t in zip(self._binary_fractions, [int(n) for n in top_measurement_label])] ) self._ret['top_measurement_label'] = top_measurement_label self._ret['top_measurement_decimal'] = top_measurement_decimal self._ret['eigvals'] = \ [top_measurement_decimal / self._ret['stretch'] - self._ret['translation']] self._ret['energy'] = self._ret['eigvals'][0] def _run(self) -> 'QPEResult': self._compute_energy() result = QPEResult() if 'translation' in self._ret: result.translation = self._ret['translation'] if 'stretch' in self._ret: result.stretch = self._ret['stretch'] if 'top_measurement_label' in self._ret: result.top_measurement_label = self._ret['top_measurement_label'] if 'top_measurement_decimal' in self._ret: result.top_measurement_decimal = self._ret['top_measurement_decimal'] if 'eigvals' in self._ret: result.eigenvalue = self._ret['eigvals'][0] return result
class QPEResult(MinimumEigensolverResult): """ QPE Result.""" @property def translation(self) -> float: """ Returns translation """ return self.get('translation') @translation.setter def translation(self, value: float) -> None: """ Sets translation """ self.data['translation'] = value @property def stretch(self) -> float: """ Returns stretch """ return self.get('stretch') @stretch.setter def stretch(self, value: float) -> None: """ Sets stretch """ self.data['stretch'] = value @property def top_measurement_label(self) -> str: """ Returns top measurement label """ return self.get('top_measurement_label') @top_measurement_label.setter def top_measurement_label(self, value: str) -> None: """ Sets top measurement label """ self.data['top_measurement_label'] = value @property def top_measurement_decimal(self) -> float: """ Returns top measurement decimal """ return self.get('top_measurement_decimal') @top_measurement_decimal.setter def top_measurement_decimal(self, value: float) -> None: """ Sets top measurement decimal """ self.data['top_measurement_decimal'] = value @staticmethod def from_dict(a_dict: Dict) -> 'QPEResult': """ create new object from a dictionary """ return QPEResult(a_dict) def __getitem__(self, key: object) -> object: if key == 'aux_operator_eigenvalues': raise KeyError('aux_operator_eigenvalues not supported.') return super().__getitem__(key)