# -*- 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 Swap+Z 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 SwapRZ(VariationalForm):
r"""DEPRECATED. The SwapRZ Variational Form.
This trial wave function is layers of swap plus :math:`z` rotations with entanglements.
It was designed principally to be a particle-preserving variational form for
:mod:`qiskit.chemistry`. Given an initial state as a set of 1's and 0's it will preserve
the number of 1's - where for chemistry a 1 will indicate a particle.
Note:
In chemistry, to define the particles for SwapRZ, use a
:class:`~qiskit.chemistry.components.initial_states.HartreeFock` initial state with
the `Jordan-Wigner` qubit mapping
For the case of none of qubits are unentangled to other qubits, the number of optimizer
parameters SwapRz creates and uses is given by
:math:`q + d \times \left(q + \sum_{k=0}^{q-1}|D(k)|\right)`, where :math:`|D(k)|` denotes the
*cardinality* of :math:`D(k)` or, more precisely, the *length* of :math:`D(k)`
(since :math:`D(k)` is not just a set, but a list).
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 Rz gates to the unentangled qubits only introduce overhead without
bringing any benefit; furthermore, theoretically, applying multiple Rz gates in a row can be
reduced to a single Rz gate with the summed rotation angles.
See :class:`RY` for more detail on `entangler_map` and `entanglement` which apply here too
but note SwapRZ only supports 'full' and 'linear' values.
"""
def __init__(self,
num_qubits: int,
depth: int = 3,
entangler_map: Optional[List[List[int]]] = None,
entanglement: str = 'full',
initial_state: Optional[InitialState] = None,
skip_unentangled_qubits: 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 describes
[source, target], or None for full entanglement.
Note that the order is the list is the order of applying the two-qubit gate.
entanglement: ('full' | 'linear') overridden by 'entangler_map` if its
provided. 'full' is all-to-all entanglement, 'linear' is nearest-neighbor.
initial_state: An initial state object
skip_unentangled_qubits: Skip the qubits not in the entangler_map
"""
warnings.warn('The qiskit.aqua.components.variational_forms.SwapRZ object is deprecated as '
'of 0.7.0 and will be removed no sooner than 3 months after the release. You '
'should use the qiskit.circuit.library.ExcitationPreserving object instead.',
DeprecationWarning, stacklevel=2)
validate_min('num_qubits', num_qubits, 1)
validate_min('depth', depth, 1)
validate_in_set('entanglement', entanglement, {'full', 'linear'})
super().__init__()
self._num_qubits = num_qubits
self._depth = depth
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._skip_unentangled_qubits = skip_unentangled_qubits
# for the first layer
self._num_parameters = len(self._entangled_qubits) if self._skip_unentangled_qubits \
else self._num_qubits
# for repeated block
self._num_parameters += (len(self._entangled_qubits) + 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.u1(parameters[param_idx], q[qubit]) # rz
param_idx += 1
for _ in range(self._depth):
circuit.barrier(q)
for src, targ in self._entangler_map:
# XX
circuit.u2(0, np.pi, q[src])
circuit.u2(0, np.pi, q[targ])
circuit.cx(q[src], q[targ])
circuit.u1(parameters[param_idx], q[targ])
circuit.cx(q[src], q[targ])
circuit.u2(0, np.pi, q[src])
circuit.u2(0, np.pi, q[targ])
# YY
circuit.u3(np.pi / 2, -np.pi / 2, np.pi / 2, q[src])
circuit.u3(np.pi / 2, -np.pi / 2, np.pi / 2, q[targ])
circuit.cx(q[src], q[targ])
circuit.u1(parameters[param_idx], q[targ])
circuit.cx(q[src], q[targ])
circuit.u3(-np.pi / 2, -np.pi / 2, np.pi / 2, q[src])
circuit.u3(-np.pi / 2, -np.pi / 2, np.pi / 2, q[targ])
param_idx += 1
circuit.barrier(q)
for qubit in self._entangled_qubits:
circuit.u1(parameters[param_idx], q[qubit]) # rz
param_idx += 1
circuit.barrier(q)
return circuit