Beispiel #1
0
    def construct_circuit(self, mode, register=None):
        """ Construct the eigenvalues estimation using the PhaseEstimationCircuit
        Args:
            mode (str): consctruction mode, 'matrix' not supported
            register (QuantumRegister): the register to use for the quantum state
        Returns:
            the QuantumCircuit object for the constructed circuit
        """

        if mode == 'matrix':
            raise ValueError(
                'QPE is only possible as a circuit not as a matrix.')

        pe = PhaseEstimationCircuit(operator=self._operator,
                                    state_in=None,
                                    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,
                                    evo_time=self._evo_time)

        a = QuantumRegister(self._num_ancillae)
        q = register

        qc = pe.construct_circuit(state_register=q, ancillary_register=a)

        # handle negative eigenvalues
        if self._negative_evals:
            self._handle_negative_evals(qc, a)

        self._circuit = qc
        self._output_register = a
        self._input_register = q
        return self._circuit
Beispiel #2
0
    def construct_circuit(self, measurement: bool = False) -> QuantumCircuit:
        """Construct the Amplitude Estimation quantum circuit.

        Args:
            measurement: Boolean flag to indicate if measurements should be included in the circuit.

        Returns:
            The QuantumCircuit object for the constructed circuit.
        """
        if self.state_preparation is None:  # circuit factories
            # ignore deprecation warnings from getter, user has already been warned when
            # the a_factory has been passed
            warnings.filterwarnings('ignore', category=DeprecationWarning)
            q_factory = self.q_factory
            warnings.filterwarnings('always', category=DeprecationWarning)

            iqft = QFT(self._m, do_swaps=False, inverse=True).reverse_bits() \
                if self._iqft is None else self._iqft
            pec = PhaseEstimationCircuit(
                iqft=iqft,
                num_ancillae=self._m,
                state_in_circuit_factory=self._a_factory,
                unitary_circuit_factory=q_factory)
            self._circuit = pec.construct_circuit(measurement=measurement)
        else:
            if self._pec is not None:
                pec = self._pec
            else:
                from qiskit.circuit.library import PhaseEstimation
                pec = PhaseEstimation(self._m,
                                      self.grover_operator,
                                      iqft=self._iqft)

            # mypy thinks self.circuit is None even after explicitly being set to QuantumCircuit
            self._circuit = QuantumCircuit(*pec.qregs)
            self._circuit.compose(
                self.state_preparation,  # type: ignore
                list(
                    range(self._m,
                          self._m + self.state_preparation.num_qubits)),
                inplace=True)
            self._circuit.compose(pec, inplace=True)  # type: ignore

            if measurement:
                cr = ClassicalRegister(self._m)
                self._circuit.add_register(cr)  # type: ignore
                self._circuit.measure(list(range(self._m)),
                                      list(range(self._m)))  # type: ignore

        return self._circuit
Beispiel #3
0
    def construct_circuit(self):
        """
        Construct the Amplitude Estimation quantum circuit.

        Returns:
            the QuantumCircuit object for the constructed circuit
        """
        pec = PhaseEstimationCircuit(
            iqft=self._iqft, num_ancillae=self._m,
            state_in_circuit_factory=self.a_factory,
            unitary_circuit_factory=self.q_factory
        )

        self._circuit = pec.construct_circuit()
        return self._circuit
Beispiel #4
0
    def construct_circuit(self, measurement: bool = False) -> QuantumCircuit:
        """Construct the Amplitude Estimation quantum circuit.

        Args:
            measurement: Boolean flag to indicate if measurements should be included in the circuit.

        Returns:
            The QuantumCircuit object for the constructed circuit.
        """
        pec = PhaseEstimationCircuit(iqft=self._iqft,
                                     num_ancillae=self._m,
                                     state_in_circuit_factory=self.a_factory,
                                     unitary_circuit_factory=self.q_factory)

        self._circuit = pec.construct_circuit(measurement=measurement)
        return self._circuit
Beispiel #5
0
class QPE(QuantumAlgorithm):
    """The Quantum Phase Estimation algorithm."""

    PROP_NUM_TIME_SLICES = 'num_time_slices'
    PROP_EXPANSION_MODE = 'expansion_mode'
    PROP_EXPANSION_ORDER = 'expansion_order'
    PROP_NUM_ANCILLAE = 'num_ancillae'

    CONFIGURATION = {
        'name':
        'QPE',
        'description':
        'Quantum Phase Estimation for Quantum Systems',
        'input_schema': {
            '$schema': 'http://json-schema.org/draft-07/schema#',
            'id': 'qpe_schema',
            'type': 'object',
            'properties': {
                PROP_NUM_TIME_SLICES: {
                    'type': 'integer',
                    'default': 1,
                    'minimum': 1
                },
                PROP_EXPANSION_MODE: {
                    'type': 'string',
                    'default': 'trotter',
                    'enum': ['suzuki', 'trotter']
                },
                PROP_EXPANSION_ORDER: {
                    'type': 'integer',
                    'default': 1,
                    'minimum': 1
                },
                PROP_NUM_ANCILLAE: {
                    'type': 'integer',
                    'default': 1,
                    'minimum': 1
                }
            },
            'additionalProperties': False
        },
        'problems': ['energy'],
        'depends': [
            {
                'pluggable_type': 'initial_state',
                'default': {
                    'name': 'ZERO'
                },
            },
            {
                'pluggable_type': 'iqft',
                'default': {
                    'name': 'STANDARD',
                },
            },
        ],
    }

    def __init__(self,
                 operator,
                 state_in,
                 iqft,
                 num_time_slices=1,
                 num_ancillae=1,
                 expansion_mode='trotter',
                 expansion_order=1,
                 shallow_circuit_concat=False):
        """
        Constructor.

        Args:
            operator (BaseOperator): the hamiltonian Operator object
            state_in (InitialState): the InitialState pluggable component
                representing the initial quantum state
            iqft (IQFT): the Inverse Quantum Fourier Transform pluggable component
            num_time_slices (int): the number of time slices
            num_ancillae (int): the number of ancillary qubits to use for the measurement
            expansion_mode (str): the expansion mode (trotter|suzuki)
            expansion_order (int): the suzuki expansion order
            shallow_circuit_concat (bool): indicate whether to use shallow
                (cheap) mode for circuit concatenation
        """
        self.validate(locals())
        super().__init__()
        self._operator = op_converter.to_weighted_pauli_operator(operator)
        self._num_ancillae = num_ancillae
        self._ret = {}

        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=state_in,
            iqft=iqft,
            num_time_slices=num_time_slices,
            num_ancillae=num_ancillae,
            expansion_mode=expansion_mode,
            expansion_order=expansion_order,
            shallow_circuit_concat=shallow_circuit_concat,
            pauli_list=self._pauli_list)
        self._binary_fractions = [1 / 2**p for p in range(1, num_ancillae + 1)]

    @classmethod
    def init_params(cls, params, algo_input):
        """
        Initialize via parameters dictionary and algorithm input instance.

        Args:
            params (dict): parameters dictionary
            algo_input (EnergyInput): instance
        Returns:
            QPE: instance of this class
        Raises:
            AquaError: EnergyInput instance is required.
        """
        if algo_input is None:
            raise AquaError("EnergyInput instance is required.")

        operator = algo_input.qubit_op

        qpe_params = params.get(Pluggable.SECTION_KEY_ALGORITHM)
        num_time_slices = qpe_params.get(QPE.PROP_NUM_TIME_SLICES)
        expansion_mode = qpe_params.get(QPE.PROP_EXPANSION_MODE)
        expansion_order = qpe_params.get(QPE.PROP_EXPANSION_ORDER)
        num_ancillae = qpe_params.get(QPE.PROP_NUM_ANCILLAE)

        # Set up initial state, we need to add computed num qubits to params
        init_state_params = params.get(Pluggable.SECTION_KEY_INITIAL_STATE)
        init_state_params['num_qubits'] = operator.num_qubits
        init_state = get_pluggable_class(
            PluggableType.INITIAL_STATE,
            init_state_params['name']).init_params(params)

        # Set up iqft, we need to add num qubits to params which is our num_ancillae bits here
        iqft_params = params.get(Pluggable.SECTION_KEY_IQFT)
        iqft_params['num_qubits'] = num_ancillae
        iqft = get_pluggable_class(PluggableType.IQFT,
                                   iqft_params['name']).init_params(params)

        return cls(operator,
                   init_state,
                   iqft,
                   num_time_slices,
                   num_ancillae,
                   expansion_mode=expansion_mode,
                   expansion_order=expansion_order)

    def construct_circuit(self, measurement=False):
        """
        Construct circuit.

        Args:
            measurement (bool): Boolean flag to indicate if measurement
                should be included in the circuit.

        Returns:
            QuantumCircuit: quantum circuit.
        """
        qc = self._phase_estimation_circuit.construct_circuit(
            measurement=measurement)
        return qc

    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):
        self._compute_energy()
        return self._ret
Beispiel #6
0
class QPE(QuantumAlgorithm):
    """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: BaseOperator, state_in: Optional[InitialState],
            iqft: IQFT, num_time_slices: int = 1,
            num_ancillae: int = 1, expansion_mode: str = 'trotter',
            expansion_order: int = 1, shallow_circuit_concat: bool = False) -> 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.
        """
        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__()
        self._operator = op_converter.to_weighted_pauli_operator(operator.copy())
        self._num_ancillae = num_ancillae
        self._ret = {}

        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=state_in, iqft=iqft,
            num_time_slices=num_time_slices, num_ancillae=num_ancillae,
            expansion_mode=expansion_mode, expansion_order=expansion_order,
            shallow_circuit_concat=shallow_circuit_concat, pauli_list=self._pauli_list
        )
        self._binary_fractions = [1 / 2 ** p for p in range(1, num_ancillae + 1)]

    def construct_circuit(self, measurement=False):
        """
        Construct circuit.

        Args:
            measurement (bool): Boolean flag to indicate if measurement
                should be included in the circuit.

        Returns:
            QuantumCircuit: quantum circuit.
        """
        qc = self._phase_estimation_circuit.construct_circuit(measurement=measurement)
        return qc

    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):
        self._compute_energy()
        return self._ret
Beispiel #7
0
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.')

    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

    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
Beispiel #8
0
class QPE(QuantumAlgorithm):
    """The Quantum Phase Estimation algorithm."""
    def __init__(self,
                 operator: BaseOperator,
                 state_in: InitialState,
                 iqft: IQFT,
                 num_time_slices: int = 1,
                 num_ancillae: int = 1,
                 expansion_mode: str = 'trotter',
                 expansion_order: int = 1,
                 shallow_circuit_concat: bool = False) -> None:
        """

        Args:
            operator: the hamiltonian Operator object
            state_in: the InitialState component
                representing the initial quantum state
            iqft: the Inverse Quantum Fourier Transform component
            num_time_slices: the number of time slices, has a min. 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: indicate whether to use shallow
                (cheap) mode for circuit concatenation
        """
        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__()
        self._operator = op_converter.to_weighted_pauli_operator(
            operator.copy())
        self._num_ancillae = num_ancillae
        self._ret = {}

        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=state_in,
            iqft=iqft,
            num_time_slices=num_time_slices,
            num_ancillae=num_ancillae,
            expansion_mode=expansion_mode,
            expansion_order=expansion_order,
            shallow_circuit_concat=shallow_circuit_concat,
            pauli_list=self._pauli_list)
        self._binary_fractions = [1 / 2**p for p in range(1, num_ancillae + 1)]

    def construct_circuit(self, measurement=False):
        """
        Construct circuit.

        Args:
            measurement (bool): Boolean flag to indicate if measurement
                should be included in the circuit.

        Returns:
            QuantumCircuit: quantum circuit.
        """
        qc = self._phase_estimation_circuit.construct_circuit(
            measurement=measurement)
        return qc

    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):
        self._compute_energy()
        return self._ret
Beispiel #9
0
class QPE(QuantumAlgorithm):
    """The Quantum Phase Estimation algorithm."""

    PROP_NUM_TIME_SLICES = 'num_time_slices'
    PROP_EXPANSION_MODE = 'expansion_mode'
    PROP_EXPANSION_ORDER = 'expansion_order'
    PROP_NUM_ANCILLAE = 'num_ancillae'

    _INPUT_SCHEMA = {
        '$schema': 'http://json-schema.org/draft-07/schema#',
        'id': 'qpe_schema',
        'type': 'object',
        'properties': {
            PROP_NUM_TIME_SLICES: {
                'type': 'integer',
                'default': 1,
                'minimum': 1
            },
            PROP_EXPANSION_MODE: {
                'type': 'string',
                'default': 'trotter',
                'enum': ['suzuki', 'trotter']
            },
            PROP_EXPANSION_ORDER: {
                'type': 'integer',
                'default': 1,
                'minimum': 1
            },
            PROP_NUM_ANCILLAE: {
                'type': 'integer',
                'default': 1,
                'minimum': 1
            }
        },
        'additionalProperties': False
    }

    def __init__(self,
                 operator,
                 state_in,
                 iqft,
                 num_time_slices=1,
                 num_ancillae=1,
                 expansion_mode='trotter',
                 expansion_order=1,
                 shallow_circuit_concat=False):
        """

        Args:
            operator (BaseOperator): the hamiltonian Operator object
            state_in (InitialState): the InitialState component
                representing the initial quantum state
            iqft (IQFT): the Inverse Quantum Fourier Transform component
            num_time_slices (int): the number of time slices
            num_ancillae (int): the number of ancillary qubits to use for the measurement
            expansion_mode (str): the expansion mode (trotter|suzuki)
            expansion_order (int): the suzuki expansion order
            shallow_circuit_concat (bool): indicate whether to use shallow
                (cheap) mode for circuit concatenation
        """
        validate(locals(), self._INPUT_SCHEMA)
        super().__init__()
        self._operator = op_converter.to_weighted_pauli_operator(
            operator.copy())
        self._num_ancillae = num_ancillae
        self._ret = {}

        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=state_in,
            iqft=iqft,
            num_time_slices=num_time_slices,
            num_ancillae=num_ancillae,
            expansion_mode=expansion_mode,
            expansion_order=expansion_order,
            shallow_circuit_concat=shallow_circuit_concat,
            pauli_list=self._pauli_list)
        self._binary_fractions = [1 / 2**p for p in range(1, num_ancillae + 1)]

    def construct_circuit(self, measurement=False):
        """
        Construct circuit.

        Args:
            measurement (bool): Boolean flag to indicate if measurement
                should be included in the circuit.

        Returns:
            QuantumCircuit: quantum circuit.
        """
        qc = self._phase_estimation_circuit.construct_circuit(
            measurement=measurement)
        return qc

    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):
        self._compute_energy()
        return self._ret