Source code for qiskit.aqua.components.variational_forms.ry

# -*- 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.

"""Layers of Y rotations followed by entangling gates."""

import warnings
from typing import Optional, List
import numpy as np
from qiskit import QuantumRegister, QuantumCircuit
from qiskit.aqua.utils.validation import validate_min, validate_in_set
from qiskit.aqua.components.initial_states import InitialState
from .variational_form import VariationalForm


[docs]class RY(VariationalForm): r"""DEPRECATED. The RY Variational Form. The RY trial wave function is layers of :math:`y` rotations with entanglements. When none of qubits are unentangled to other qubits the number of parameters and the entanglement gates themselves have no additional parameters, the number of optimizer parameters this form creates and uses is given by :math:`q \times (d + 1)`, where :math:`q` is the total number of qubits and :math:`d` is the depth of the circuit. Nonetheless, in some cases, if an `entangler_map` does not include all qubits, that is, some qubits are not entangled by other qubits. The number of parameters is reduced by :math:`d \times q'` where :math:`q'` is the number of unentangled qubits. This is because adding more parameters to the unentangled qubits only introduce overhead without bringing any benefit; furthermore, theoretically, applying multiple RY gates in a row can be reduced to a single RY gate with the summed rotation angles. If the form uses entanglement gates with parameters (such as `'crx'`) the number of parameters increases by the number of entanglements. For instance with `'linear'` or `'sca'` entanglement the total number of parameters is :math:`2q \times (d + 1/2)`. For `'full'` entanglement an additional :math:`q \times (q - 1)/2 \times d` parameters, hence a total of :math:`d \times q \times (q + 1) / 2 + q`. It is possible to skip the final layer or :math:`y` rotations by setting the argument `skip_final_ry` to True. Then the number of parameters in above formulae decreases by :math:`q`. * 'full' entanglement is each qubit is entangled with all the others. * 'linear' entanglement is qubit :math:`i` entangled with qubit :math:`i + 1`, for all :math:`i \in \{0, 1, ... , q - 2\}`, where :math:`q` is the total number of qubits. * 'sca' (shifted-circular-alternating) entanglement it is a generalized and modified version of the proposed circuit 14 in `Sim et al. <https://arxiv.org/abs/1905.10876>`__. It consists of circular entanglement where the 'long' entanglement connecting the first with the last qubit is shifted by one each block. Furthermore the role of control and target qubits are swapped every block (therefore alternating). The `entanglement` parameter can be overridden by an `entangler_map` explicitly The entangler map is specified in the form of a list; where each element in the list is a list pair of a source qubit and a target qubit index. Indexes are integer values from :math:`0` to :math:`q-1`, where :math:`q` is the total number of qubits, as in the following example: entangler_map = [[0, 1], [0, 2], [1, 3]] """ def __init__(self, num_qubits: int, depth: int = 3, entangler_map: Optional[List[List[int]]] = None, entanglement: str = 'full', initial_state: Optional[InitialState] = None, entanglement_gate: str = 'cz', skip_unentangled_qubits: bool = False, skip_final_ry: bool = False) -> None: """ Args: num_qubits: Number of qubits, has a minimum value of 1. depth: Number of rotation layers, has a minimum value of 1. entangler_map: Describe the connectivity of qubits, each list pair describes [source, target], or None for as defined by `entanglement`. Note that the order is the list is the order of applying the two-qubit gate. entanglement: ('full' | 'linear' | 'sca'), overridden by 'entangler_map` if its provided. 'full' is all-to-all entanglement, 'linear' is nearest-neighbor and 'sca' is a shifted-circular-alternating entanglement. initial_state: An initial state object entanglement_gate: ('cz' | 'cx' | 'crx') skip_unentangled_qubits: Skip the qubits not in the entangler_map skip_final_ry: Skip the final layer of Y rotations """ warnings.warn('The qiskit.aqua.components.variational_forms.RY object is deprecated as of ' '0.7.0 and will be removed no sooner than 3 months after the release. You ' 'should use qiskit.circuit.library.RealAmplitudes (uses CX entangling) or ' 'qiskit.circuit.library.TwoLocal instead.', DeprecationWarning, stacklevel=2) validate_min('num_qubits', num_qubits, 1) validate_min('depth', depth, 1) validate_in_set('entanglement', entanglement, {'full', 'linear', 'sca'}) validate_in_set('entanglement_gate', entanglement_gate, {'cz', 'cx', 'crx'}) super().__init__() self._num_qubits = num_qubits self._depth = depth self._entanglement = entanglement if entangler_map is None: self._entangler_map = VariationalForm.get_entangler_map(entanglement, num_qubits) else: self._entangler_map = VariationalForm.validate_entangler_map(entangler_map, num_qubits) # determine the entangled qubits all_qubits = [] for src, targ in self._entangler_map: all_qubits.extend([src, targ]) self._entangled_qubits = sorted(list(set(all_qubits))) self._initial_state = initial_state self._entanglement_gate = entanglement_gate self._skip_unentangled_qubits = skip_unentangled_qubits self._skip_final_ry = skip_final_ry # for the first layer self._num_parameters = len(self._entangled_qubits) if self._skip_unentangled_qubits \ else self._num_qubits # for repeated block (minus one ry layer if we skip the last) self._num_parameters += len(self._entangled_qubits) * (depth - 1) if skip_final_ry \ else len(self._entangled_qubits) * depth # CRx gates have an additional parameter per entanglement if entanglement_gate == 'crx': self._num_parameters += len(self._entangler_map) * depth self._bounds = [(-np.pi, np.pi)] * self._num_parameters self._support_parameterized_circuit = True
[docs] def construct_circuit(self, parameters, q=None): """Construct the variational form, given its parameters. Args: parameters (Union(numpy.ndarray, list[Parameter], ParameterVector)): circuit parameters. q (QuantumRegister): Quantum Register for the circuit. Returns: QuantumCircuit: a quantum circuit with given `parameters` Raises: ValueError: the number of parameters is incorrect. """ if len(parameters) != self._num_parameters: raise ValueError('The number of parameters has to be {}'.format(self._num_parameters)) if q is None: q = QuantumRegister(self._num_qubits, name='q') if self._initial_state is not None: circuit = self._initial_state.construct_circuit('circuit', q) else: circuit = QuantumCircuit(q) param_idx = 0 for qubit in range(self._num_qubits): if not self._skip_unentangled_qubits or qubit in self._entangled_qubits: circuit.u3(parameters[param_idx], 0.0, 0.0, q[qubit]) # ry param_idx += 1 for block in range(self._depth): circuit.barrier(q) if self._entanglement == 'sca': self._entangler_map = VariationalForm.get_entangler_map( self._entanglement, self._num_qubits, offset=block) for src, targ in self._entangler_map: if self._entanglement_gate == 'cz': circuit.u2(0.0, np.pi, q[targ]) # h circuit.cx(q[src], q[targ]) circuit.u2(0.0, np.pi, q[targ]) # h elif self._entanglement_gate == 'crx': circuit.cu3(parameters[param_idx], -np.pi / 2, np.pi / 2, q[src], q[targ]) # crx param_idx += 1 else: circuit.cx(q[src], q[targ]) # Skip the final RY layer if it is specified and we reached the # last block if not self._skip_final_ry or block != self._depth - 1: circuit.barrier(q) for qubit in self._entangled_qubits: circuit.u3(parameters[param_idx], 0.0, 0.0, q[qubit]) # ry param_idx += 1 circuit.barrier(q) return circuit