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)