# -*- coding: utf-8 -*-
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017.
#
# 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.
"""Two-pulse single-qubit gate."""
import numpy
from qiskit.circuit.controlledgate import ControlledGate
from qiskit.circuit.gate import Gate
from qiskit.circuit.quantumregister import QuantumRegister
[docs]class U3Gate(Gate):
r"""Generic single-qubit rotation gate with 3 Euler angles.
Implemented using two X90 pulses on IBM Quantum systems:
.. math::
U3(\theta, \phi, \lambda) =
RZ(\phi - \pi/2) RX(\pi/2) RZ(\pi - \theta) RX(\pi/2) RZ(\lambda - \pi/2)
**Circuit symbol:**
.. parsed-literal::
┌───────────┐
q_0: ┤ U3(ϴ,φ,λ) ├
└───────────┘
**Matrix Representation:**
.. math::
\newcommand{\th}{\frac{\theta}{2}}
U3(\theta, \phi, \lambda) =
\begin{pmatrix}
\cos(\th) & -e^{i\lambda}\sin(\th) \\
e^{i\phi}\sin(\th) & e^{i(\phi+\lambda)}\cos(\th)
\end{pmatrix}
**Examples:**
.. math::
U3(\theta, -\frac{\pi}{2}, \frac{pi}{2}) = RX(\theta)
.. math::
U3(\theta, 0, 0) = RY(\theta)
"""
def __init__(self, theta, phi, lam, label=None):
"""Create new U3 gate."""
super().__init__('u3', 1, [theta, phi, lam], label=label)
[docs] def inverse(self):
r"""Return inverted U3 gate.
:math:`U3(\theta,\phi,\lambda)^{\dagger} =U3(-\theta,-\phi,-\lambda)`)
"""
return U3Gate(-self.params[0], -self.params[2], -self.params[1])
[docs] def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
"""Return a (mutli-)controlled-U3 gate.
Args:
num_ctrl_qubits (int): number of control qubits.
label (str or None): An optional label for the gate [Default: None]
ctrl_state (int or str or None): control state expressed as integer,
string (e.g. '110'), or None. If None, use all 1s.
Returns:
ControlledGate: controlled version of this gate.
"""
if num_ctrl_qubits == 1:
gate = CU3Gate(*self.params, label=label, ctrl_state=ctrl_state)
gate.base_gate.label = self.label
return gate
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)
[docs] def to_matrix(self):
"""Return a Numpy.array for the U3 gate."""
theta, phi, lam = self.params
theta, phi, lam = float(theta), float(phi), float(lam)
return numpy.array([
[
numpy.cos(theta / 2),
-numpy.exp(1j * lam) * numpy.sin(theta / 2)
],
[
numpy.exp(1j * phi) * numpy.sin(theta / 2),
numpy.exp(1j * (phi + lam)) * numpy.cos(theta / 2)
]
], dtype=complex)
class CU3Meta(type):
"""A metaclass to ensure that Cu3Gate and CU3Gate are of the same type.
Can be removed when Cu3Gate gets removed.
"""
@classmethod
def __instancecheck__(mcs, inst):
return type(inst) in {CU3Gate, Cu3Gate} # pylint: disable=unidiomatic-typecheck
[docs]class CU3Gate(ControlledGate, metaclass=CU3Meta):
r"""Controlled-U3 gate (3-parameter two-qubit gate).
This is a controlled version of the U3 gate (generic single qubit rotation).
It is restricted to 3 parameters, and so cannot cover generic two-qubit
controlled gates).
**Circuit symbol:**
.. parsed-literal::
q_0: ──────■──────
┌─────┴─────┐
q_1: ┤ U3(ϴ,φ,λ) ├
└───────────┘
**Matrix representation:**
.. math::
\newcommand{\th}{\frac{\theta}{2}}
CU3(\theta, \phi, \lambda)\ q_0, q_1 =
I \otimes |0\rangle\langle 0| +
U3(\theta,\phi,\lambda) \otimes |1\rangle\langle 1| =
\begin{pmatrix}
1 & 0 & 0 & 0 \\
0 & \cos(\th) & 0 & e^{-i\lambda}\sin(\th) \\
0 & 0 & 1 & 0 \\
0 & e^{i\phi}\sin(\th) & 0 & e^{i(\phi+\lambda)\cos(\th)}
\end{pmatrix}
.. note::
In Qiskit's convention, higher qubit indices are more significant
(little endian convention). In many textbooks, controlled gates are
presented with the assumption of more significant qubits as control,
which in our case would be q_1. Thus a textbook matrix for this
gate will be:
.. parsed-literal::
┌───────────┐
q_0: ┤ U3(ϴ,φ,λ) ├
└─────┬─────┘
q_1: ──────■──────
.. math::
CU3(\theta, \phi, \lambda)\ q_1, q_0 =
|0\rangle\langle 0| \otimes I +
|1\rangle\langle 1| \otimes U3(\theta,\phi,\lambda) =
\begin{pmatrix}
1 & 0 & 0 & 0 \\
0 & 1 & 0 & 0 \\
0 & 0 & \cos(\th) & e^{-i\lambda}\sin(\th) \\
0 & 0 & e^{i\phi}\sin(\th) & e^{i(\phi+\lambda)\cos(\th)}
\end{pmatrix}
"""
def __init__(self, theta, phi, lam, label=None, ctrl_state=None):
"""Create new CU3 gate."""
super().__init__('cu3', 2, [theta, phi, lam], num_ctrl_qubits=1,
label=label, ctrl_state=ctrl_state)
self.base_gate = U3Gate(theta, phi, lam)
def _define(self):
"""
gate cu3(theta,phi,lambda) c, t
{ u1((lambda+phi)/2) c;
u1((lambda-phi)/2) t;
cx c,t;
u3(-theta/2,0,-(phi+lambda)/2) t;
cx c,t;
u3(theta/2,phi,0) t;
}
"""
from .u1 import U1Gate
from .x import CXGate # pylint: disable=cyclic-import
definition = []
q = QuantumRegister(2, 'q')
rule = [
(U1Gate((self.params[2] + self.params[1]) / 2), [q[0]], []),
(U1Gate((self.params[2] - self.params[1]) / 2), [q[1]], []),
(CXGate(), [q[0], q[1]], []),
(U3Gate(-self.params[0] / 2, 0, -(self.params[1] + self.params[2]) / 2), [q[1]], []),
(CXGate(), [q[0], q[1]], []),
(U3Gate(self.params[0] / 2, self.params[1], 0), [q[1]], [])
]
for inst in rule:
definition.append(inst)
self.definition = definition
[docs] def inverse(self):
r"""Return inverted CU3 gate.
:math:`CU3(\theta,\phi,\lambda)^{\dagger} =CU3(-\theta,-\phi,-\lambda)`)
"""
return CU3Gate(-self.params[0], -self.params[2], -self.params[1])
class Cu3Gate(CU3Gate, metaclass=CU3Meta):
"""The deprecated CU3Gate class."""
def __init__(self, theta, phi, lam):
import warnings
warnings.warn('The class Cu3Gate is deprecated as of 0.14.0, and '
'will be removed no earlier than 3 months after that release date. '
'You should use the class CU3Gate instead.',
DeprecationWarning, stacklevel=2)
super().__init__(theta, phi, lam)
def _generate_gray_code(num_bits):
"""Generate the gray code for ``num_bits`` bits."""
if num_bits <= 0:
raise ValueError('Cannot generate the gray code for less than 1 bit.')
result = [0]
for i in range(num_bits):
result += [x + 2**i for x in reversed(result)]
return [format(x, '0%sb' % num_bits) for x in result]
def _gray_code_chain(q, num_ctrl_qubits, gate):
"""Apply the gate to the the last qubit in the register ``q``, controlled on all
preceding qubits. This function uses the gray code to propagate down to the last qubit.
Ported and adapted from Aqua (github.com/Qiskit/qiskit-aqua),
commit 769ca8d, file qiskit/aqua/circuits/gates/multi_control_u1_gate.py.
"""
from .x import CXGate
rule = []
q_controls, q_target = q[:num_ctrl_qubits], q[num_ctrl_qubits]
gray_code = _generate_gray_code(num_ctrl_qubits)
last_pattern = None
for pattern in gray_code:
if '1' not in pattern:
continue
if last_pattern is None:
last_pattern = pattern
# find left most set bit
lm_pos = list(pattern).index('1')
# find changed bit
comp = [i != j for i, j in zip(pattern, last_pattern)]
if True in comp:
pos = comp.index(True)
else:
pos = None
if pos is not None:
if pos != lm_pos:
rule.append(
(CXGate(), [q_controls[pos], q_controls[lm_pos]], [])
)
else:
indices = [i for i, x in enumerate(pattern) if x == '1']
for idx in indices[1:]:
rule.append(
(CXGate(), [q_controls[idx], q_controls[lm_pos]], [])
)
# check parity
if pattern.count('1') % 2 == 0:
# inverse
rule.append(
(gate.inverse(), [q_controls[lm_pos], q_target], [])
)
else:
rule.append(
(gate, [q_controls[lm_pos], q_target], [])
)
last_pattern = pattern
return rule