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