Source code for qolumbina.programs.pauli_rotations.uniform_controlled_rotations
# The original version of the following code is sourced from Qiskit circuit library:
# https://github.com/SRI-International/QC-App-Oriented-Benchmarks/blob/master/hhl/qiskit/uniform_controlled_rotation.py
#
# Original repository link:
# https://github.com/SRI-International/QC-App-Oriented-Benchmarks?tab=Apache-2.0-1-ov-file
#
# This program is adapted for use as a benchmark in controlled software testing experiments.
# Modifications made to the original code include (for Apache License 2.0):
# - Stucture reorganization:
# - Modify the code in the `QuantumCircuit` class
# - Add type hints for better code readability
# - Remove measurements from subcircuits, as they can be added in the main circuit if needed
# - Exposed input parameters
# - Optimize the main circuit construction to avoid redundant subcircuit creation
# - Input validation:
# - Ensure the length of `thetas` is a power of two
#
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 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.
"""
Uniformly controlled rotation from arXiv:0407010
"""
import numpy as np
from sympy.combinatorics.graycode import GrayCode
from qiskit import QuantumCircuit, QuantumRegister
# ---------- benchmark registration ----------
from ..benchmark_registry import register_benchmark
from pathlib import Path
[docs]
@register_benchmark(
Path(__file__).stem,
family=Path(__file__).resolve().parent.name,
description="Uniformly controlled rotations: Applies rotations on an ancilla qubit controlled by multiple qubits.",
class_name="UniformlyControlledRotations",
source={
"repo": "https://github.com/SRI-International/QC-App-Oriented-Benchmarks?tab=Apache-2.0-1-ov-file",
"file": "hhl/qiskit/uniform_controlled_rotation.py",
"sdk": "Qiskit",
"available_doc": False
},
testability_refactoring=[
"Structure reorganization",
"Input validation"
]
)
def create_uniformly_controlled_rotations(thetas: list[float], if_barrier: bool = False):
return UniformlyControlledRotations(theta_list=thetas, if_barrier=if_barrier)
[docs]
class UniformlyControlledRotations(QuantumCircuit):
r"""
Uniformly controlled rotation circuit.
Applies a rotation on the ancilla qubit controlled by the state of n qubits.
"""
def __init__(self, theta_list: list[float], if_barrier: bool = False, name: str | None = None):
r"""
Args:
theta_list: List of rotation angles for each control state, i.e.,
:math:`[\theta_0, \theta_1, \ldots, \theta_{2^n-1}]`.
The length of the list must be the power of two,
i.e., :math:`2^n` for :math:`n` control qubits.
if_barrier: Whether to add barriers. Default is ``False``.
name: Optional name of the circuit.
Raises:
ValueError: If the length of ``theta_list`` is not a power of two.
Note:
The circuit applies RY rotations on the ancilla qubit.
"""
self._theta_list= theta_list
self._if_barrier = if_barrier
n = int(np.log2(len(theta_list)))
if len(theta_list) != 2 ** n:
raise ValueError("Length of thetas must be 2^n"
f" but got len(thetas)={len(theta_list)} for n={n}")
super().__init__(n + 1, name=name or "UniformlyControlledRotations")
self._build()
def _dot_product(self, str1: str, str2: str) -> int:
""" dot product between 2 binary string """
prod = 0
for j in range(len(str1)):
if str1[j] == '1' and str2[j] == '1':
prod = (prod + 1)%2
return prod
[docs]
def conversion_matrix(self, N: int) -> np.ndarray:
r"""
conversion matrix from alpha to theta
"""
M = np.zeros((N,N))
n = int(np.log2(N))
gc_list = list(GrayCode(n).generate_gray()) # list of gray code strings
for i in range(N):
g_i = gc_list[i]
for j in range(N):
b_j = np.binary_repr(j, width=n)[::-1]
M[i,j] = (-1)**self._dot_product(g_i, b_j)/(2**n)
return M
[docs]
def alpha2theta(self, alpha: list[float]) -> np.ndarray:
r"""
Args:
alpha : list of angles that get applied controlled on :math:`0,\cdots,2^n-1`
theta : list of angles occuring in circuit construction
"""
N = len(alpha)
M = self.conversion_matrix(N)
theta = M @ np.array(alpha)
return theta
[docs]
def uni_con_rot_recursive_step(
self,
qc: QuantumCircuit,
qubits: QuantumRegister,
anc: QuantumRegister,
theta: list[float]
) -> QuantumCircuit:
r"""
Args:
qc : qiskit QuantumCircuit object
qubits : qiskit QuantumRegister object
anc : ancilla qubit register on which rotation acts
theta : list of angles specifying rotations for :math:`0,\cdots,2^{n-1}`
"""
if type(qubits) == list:
n = len(qubits)
else:
n = qubits.size
# lowest level of recursion
if n == 1:
qc.ry(theta[0], anc[0])
qc.cx(qubits[0], anc[0])
qc.ry(theta[1], anc[0])
elif n > 1:
qc = self.uni_con_rot_recursive_step(qc, qubits[1:], anc, theta[0:int(len(theta)/2)])
qc.cx(qubits[0], anc[0])
qc = self.uni_con_rot_recursive_step(qc, qubits[1:], anc, theta[int(len(theta)/2):])
return qc
[docs]
def uniformly_controlled_rot(self, n: int, theta: list[float]) -> QuantumCircuit:
qubits = QuantumRegister(n)
anc_reg = QuantumRegister(1)
qc = QuantumCircuit(qubits, anc_reg, name = 'INV_ROT')
qc = self.uni_con_rot_recursive_step(qc, qubits, anc_reg, theta)
qc.cx(qubits[0], anc_reg[0])
return qc
def _build(self) -> None:
theta_list = self._theta_list
theta_internal = self.alpha2theta(theta_list) # convert to theta used in circuit construction
n = int(np.log2(len(self._theta_list)))
N = len(theta_list)
for x in range(N): # state prep
#x_bin = np.binary_repr(x, width=n)
qubits = QuantumRegister(n)
anc = QuantumRegister(1)
qc = QuantumCircuit(qubits, anc)
# state prep
x_bin = np.binary_repr(x, width=n)
for q in range(n):
if x_bin[n - 1 - q] == '1':
qc.x(q)
if self._if_barrier:
qc.barrier()
qc = self.uniformly_controlled_rot(n, list(theta_internal))
if self._if_barrier:
qc.barrier()
# Combine all into self
self.compose(qc, inplace=True)