From 301930cbbf0ffb44e3d2a0c724ef981b06bd2ba2 Mon Sep 17 00:00:00 2001 From: Justin Kalloor Date: Mon, 23 Sep 2024 15:32:03 -0700 Subject: [PATCH 01/16] Adding Multiplexed Gates --- bqskit/ir/gates/parameterized/__init__.py | 4 + bqskit/ir/gates/parameterized/mcry.py | 148 ++++++++++++++++++++++ bqskit/ir/gates/parameterized/mcrz.py | 130 +++++++++++++++++++ 3 files changed, 282 insertions(+) create mode 100644 bqskit/ir/gates/parameterized/mcry.py create mode 100644 bqskit/ir/gates/parameterized/mcrz.py diff --git a/bqskit/ir/gates/parameterized/__init__.py b/bqskit/ir/gates/parameterized/__init__.py index 546520b60..a592b66ef 100644 --- a/bqskit/ir/gates/parameterized/__init__.py +++ b/bqskit/ir/gates/parameterized/__init__.py @@ -11,6 +11,8 @@ from bqskit.ir.gates.parameterized.crz import CRZGate from bqskit.ir.gates.parameterized.cu import CUGate from bqskit.ir.gates.parameterized.fsim import FSIMGate +from bqskit.ir.gates.parameterized.mcry import MCRYGate +from bqskit.ir.gates.parameterized.mcrz import MCRZGate from bqskit.ir.gates.parameterized.pauli import PauliGate from bqskit.ir.gates.parameterized.pauliz import PauliZGate from bqskit.ir.gates.parameterized.phasedxz import PhasedXZGate @@ -41,6 +43,8 @@ 'CRZGate', 'CUGate', 'FSIMGate', + 'MCRYGate', + 'MCRZGate', 'PauliGate', 'PauliZGate', 'PhasedXZGate', diff --git a/bqskit/ir/gates/parameterized/mcry.py b/bqskit/ir/gates/parameterized/mcry.py new file mode 100644 index 000000000..d38e35bdb --- /dev/null +++ b/bqskit/ir/gates/parameterized/mcry.py @@ -0,0 +1,148 @@ +"""This module implements the MCRYGate.""" +from __future__ import annotations + +import numpy as np +import numpy.typing as npt + +from bqskit.ir.gates.qubitgate import QubitGate +from bqskit.qis.unitary.differentiable import DifferentiableUnitary +from bqskit.qis.unitary.optimizable import LocallyOptimizableUnitary +from bqskit.qis.unitary.unitary import RealVector +from bqskit.qis.unitary.unitarymatrix import UnitaryMatrix +from bqskit.utils.cachedclass import CachedClass +import logging + +def get_indices(index: int, target_qudit, num_qudits): + """ + Get indices for the matrix based on the target qubit. + """ + shift_qubit = num_qudits - target_qudit - 1 + shift = 2 ** shift_qubit + # Split into two parts around target qubit + # 100 | 111 + left = index // shift + right = index % shift + + # Now, shift left by one spot to + # make room for the target qubit + left *= (shift * 2) + # Now add 0 * new_ind and 1 * new_ind to get indices + return left + right, left + shift + right + +class MCRYGate( + QubitGate, + DifferentiableUnitary, + CachedClass, + LocallyOptimizableUnitary +): + """ + A gate representing a multiplexed Y rotation. A multiplexed Y rotation + uses n - 1 qubits as select qubits and applies a Y rotation to the target. + If the target qubit is the last qubit, then the unitary is block diagonal. + Each block is a 2x2 RY matrix with parameter theta. + + Since there are n - 1 select qubits, there are 2^(n-1) parameters (thetas). + + We allow the target qubit to be specified to any qubit, and the other qubits + maintain their order. Qubit 0 is the most significant qubit. + + See this paper: https://arxiv.org/pdf/quant-ph/0406176 + """ + + _qasm_name = 'mcry' + + def __init__(self, num_qudits: int, controlled_qubit: int) -> None: + self._num_qudits = num_qudits + # 1 param for each configuration of the selec qubits + self._num_params = 2 ** (num_qudits - 1) + self.controlled_qubit = controlled_qubit + super().__init__() + + def get_unitary(self, params: RealVector = []) -> UnitaryMatrix: + """Return the unitary for this gate, see :class:`Unitary` for more.""" + self.check_parameters(params) + + matrix = np.zeros((2 ** self.num_qudits, 2 ** self.num_qudits), dtype=np.complex128) + for i, param in enumerate(params): + cos = np.cos(param / 2) + sin = np.sin(param / 2) + + # Now, get indices based on target qubit. + # i corresponds to the configuration of the + # select qubits (e.g 5 = 101). Now, the + # target qubit is 0,1 for both the row and col + # indices. So, if i = 5 and the target_qubit is 2 + # Then the rows/cols are 1001 and 1101 + x1, x2 = get_indices(i, self.controlled_qubit, self.num_qudits) + + matrix[x1, x1] = cos + matrix[x2, x2] = cos + matrix[x2, x1] = sin + matrix[x1, x2] = -1 * sin + + return UnitaryMatrix(matrix) + + def get_grad(self, params: RealVector = []) -> npt.NDArray[np.complex128]: + """ + Return the gradient for this gate. + See :class:`DifferentiableUnitary` for more info. + """ + self.check_parameters(params) + + matrix = np.zeros((2 ** self.num_qudits, 2 ** self.num_qudits), dtype=np.complex128) + for i, param in enumerate(params): + dcos = -np.sin(param / 2) / 2 + dsin = -1j * np.cos(param / 2) / 2 + + # Again, get indices based on target qubit. + x1, x2 = get_indices(i, self.controlled_qubit, self.num_qudits) + + matrix[x1, x1] = dcos + matrix[x2, x2] = dcos + matrix[x2, x1] = dsin + matrix[x1, x2] = -1 * dsin + + return UnitaryMatrix(matrix) + + + def optimize(self, env_matrix: npt.NDArray[np.complex128]) -> list[float]: + """ + Return the optimal parameters with respect to an environment matrix. + See :class:`LocallyOptimizableUnitary` for more info. + """ + self.check_env_matrix(env_matrix) + thetas = [0] * self.num_params + + for i in range(self.num_params): + x1, x2 = get_indices(i, self.controlled_qubit, self.num_qudits) + a = np.real(env_matrix[x1, x1] + env_matrix[x2, x2]) + b = np.real(env_matrix[x2, x1] - env_matrix[x1, x2]) + theta = 2 * np.arccos(a / np.sqrt(a ** 2 + b ** 2)) + theta *= -1 if b > 0 else 1 + thetas[i] = theta + + return thetas + + @staticmethod + def get_decomposition(params: RealVector = []) -> tuple[RealVector, RealVector]: + ''' + Get the corresponding parameters for one level of decomposition + of a multiplexed gate. This is used in the decomposition of both + the MCRY and MCRZ gates. See :class:`MGDPass` for more info. + ''' + new_num_params = len(params) // 2 + left_params = np.zeros(new_num_params) + right_params = np.zeros(new_num_params) + for i in range(len(left_params)): + left_param = (params[i] + params[i + new_num_params]) / 2 + right_param = (params[i] - params[i + new_num_params]) / 2 + left_params[i] = left_param + right_params[i] = right_param + + return left_params, right_params + + @property + def name(self) -> str: + """The name of this gate, with the number of qudits appended.""" + base_name = getattr(self, '_name', self.__class__.__name__) + return f"{base_name}_{self.num_qudits}" \ No newline at end of file diff --git a/bqskit/ir/gates/parameterized/mcrz.py b/bqskit/ir/gates/parameterized/mcrz.py new file mode 100644 index 000000000..912814f5c --- /dev/null +++ b/bqskit/ir/gates/parameterized/mcrz.py @@ -0,0 +1,130 @@ +"""This module implements the MCRZGate.""" +from __future__ import annotations + +import numpy as np +import numpy.typing as npt + +from bqskit.ir.gates.qubitgate import QubitGate +from bqskit.qis.unitary.differentiable import DifferentiableUnitary +from bqskit.qis.unitary.optimizable import LocallyOptimizableUnitary +from bqskit.qis.unitary.unitary import RealVector +from bqskit.qis.unitary.unitarymatrix import UnitaryMatrix +from bqskit.utils.cachedclass import CachedClass +from bqskit.ir.gates.parameterized.mcry import get_indices +from typing import Any + +class MCRZGate( + QubitGate, + DifferentiableUnitary, + CachedClass, + LocallyOptimizableUnitary +): + """ + A gate representing a multiplexed Z rotation. A multiplexed Z rotation + uses n - 1 qubits as select qubits and applies a Z rotation to the target. + If the target qubit is the last qubit, then the unitary is block diagonal. + Each block is a 2x2 RZ matrix with parameter theta. + + Since there are n - 1 select qubits, there are 2^(n-1) parameters (thetas). + + We allow the target qubit to be specified to any qubit, and the other qubits + maintain their order. Qubit 0 is the most significant qubit. + + + Why is 0 the MSB? Typically, in the QSD diagram, we see the block drawn + with qubit 0 at the top and qubit n-1 at the bottom. Then, the decomposition + slowly moves from the bottom to the top. + + See this paper: https://arxiv.org/pdf/quant-ph/0406176 + """ + + _qasm_name = 'mcrz' + + def __init__(self, num_qudits: int, target_qubit: int = -1) -> None: + ''' + Create a new MCRZGate with `num_qudits` qubits and + `target_qubit` as the target qubit. We then have 2^(n-1) parameters + for this gate. + + For Example: + `num_qudits` = 3, `target_qubit` = 1 + + Then, the select qubits are 0 and 2 with 0 as the MSB. + + If the input vector is |0x0> then the selection is 00, and + RZ(theta_0) is applied to the target qubit. + + If the input vector is |1x0> then the selection is 01, and + RZ(theta_1) is applied to the target qubit. + ''' + self._num_qudits = num_qudits + # 1 param for each configuration of the selec qubits + self._num_params = 2 ** (num_qudits - 1) + # By default, the controlled qubit is the last qubit + if target_qubit == -1: + target_qubit = num_qudits - 1 + self.target_qubit = target_qubit + super().__init__() + + def get_unitary(self, params: RealVector = []) -> UnitaryMatrix: + """Return the unitary for this gate, see :class:`Unitary` for more.""" + self.check_parameters(params) + matrix = np.zeros((2 ** self.num_qudits, 2 ** self.num_qudits), dtype=np.complex128) + for i, param in enumerate(params): + pos = np.exp(1j * param / 2) + neg = np.exp(-1j * param / 2) + + # Get correct indices based on target qubit + # See :class:`mcry` for more info + x1, x2 = get_indices(i, self.target_qubit, self.num_qudits) + + matrix[x1, x1] = neg + matrix[x2, x2] = pos + + return UnitaryMatrix(matrix) + + def get_grad(self, params: RealVector = []) -> npt.NDArray[np.complex128]: + """ + Return the gradient for this gate. + + See :class:`DifferentiableUnitary` for more info. + """ + self.check_parameters(params) + matrix = np.zeros((2 ** self.num_qudits, 2 ** self.num_qudits), dtype=np.complex128) + for i, param in enumerate(params): + dpos = 1j / 2 * np.exp(1j * param / 2) + dneg = -1j / 2 * np.exp(-1j * param / 2) + + # Again, get indices based on target qubit. + x1, x2 = get_indices(i, self.target_qubit, self.num_qudits) + + matrix[x1, x1] = dpos + matrix[x2, x2] = dneg + + return UnitaryMatrix(matrix) + + + def optimize(self, env_matrix: npt.NDArray[np.complex128]) -> list[float]: + """ + Return the optimal parameters with respect to an environment matrix. + See :class:`LocallyOptimizableUnitary` for more info. + """ + self.check_env_matrix(env_matrix) + thetas = [0] * self.num_params + + for i in range(self.num_params): + x1, x2 = get_indices(i, self.target_qubit, self.num_qudits) + # Optimize each RZ independently from indices + # Taken from QFACTOR repo + a = np.angle(env_matrix[x1, x1]) + b = np.angle(env_matrix[x2, x2]) + # print(thetas) + thetas[i] = a - b + + return thetas + + @property + def name(self) -> str: + """The name of this gate, with the number of qudits appended.""" + base_name = getattr(self, '_name', self.__class__.__name__) + return f"{base_name}_{self.num_qudits}" \ No newline at end of file From 73103c13b938150a8ca5d44823efc4d3bbcc55e3 Mon Sep 17 00:00:00 2001 From: Justin Kalloor Date: Mon, 23 Sep 2024 16:11:39 -0700 Subject: [PATCH 02/16] Add tests for MCR gates, fixing comment on MCRY gate --- bqskit/ir/gates/parameterized/mcry.py | 5 ++- tests/ir/gates/parameterized/test_mcry.py | 52 ++++++++++++++++++++++ tests/ir/gates/parameterized/test_mcrz.py | 53 +++++++++++++++++++++++ 3 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 tests/ir/gates/parameterized/test_mcry.py create mode 100644 tests/ir/gates/parameterized/test_mcrz.py diff --git a/bqskit/ir/gates/parameterized/mcry.py b/bqskit/ir/gates/parameterized/mcry.py index d38e35bdb..f150fb703 100644 --- a/bqskit/ir/gates/parameterized/mcry.py +++ b/bqskit/ir/gates/parameterized/mcry.py @@ -51,10 +51,13 @@ class MCRYGate( _qasm_name = 'mcry' - def __init__(self, num_qudits: int, controlled_qubit: int) -> None: + def __init__(self, num_qudits: int, controlled_qubit: int = -1) -> None: self._num_qudits = num_qudits # 1 param for each configuration of the selec qubits self._num_params = 2 ** (num_qudits - 1) + # By default, the controlled qubit is the last qubit + if target_qubit == -1: + target_qubit = num_qudits - 1 self.controlled_qubit = controlled_qubit super().__init__() diff --git a/tests/ir/gates/parameterized/test_mcry.py b/tests/ir/gates/parameterized/test_mcry.py new file mode 100644 index 000000000..e3e6aa1cc --- /dev/null +++ b/tests/ir/gates/parameterized/test_mcry.py @@ -0,0 +1,52 @@ +"""This module tests the U1Gate class.""" +from __future__ import annotations + +import numpy as np +from scipy.linalg import block_diag +from hypothesis import given +from hypothesis.strategies import floats, integers, lists + +from bqskit.ir.gates.parameterized import MCRYGate, RYGate +from bqskit.ir.gates.constant import PermutationGate + +@given(lists(elements=floats(allow_nan=False, allow_infinity=False, width=32), min_size=2, max_size=16)) +def test_get_unitary(thetas: list[float]) -> None: + ''' + Test the get_unitary method of the MCRYGate class. + Use the default target qubit. + ''' + # Assert that len(thetas) is a power of 2 + assert (len(thetas) & (len(thetas) - 1)) == 0 + + # There are 2 ** (n - 1) parameters + num_qudits = np.log2(len(thetas)) + 1 + mcry = MCRYGate(num_qudits=num_qudits) + block_unitaries = [RYGate().get_unitary([theta]) for theta in thetas] + blocked_unitary = block_diag(*block_unitaries) + dist = mcry.get_unitary(thetas).get_distance_from(blocked_unitary) + assert dist < 1e-7 + +@given(integers(min_value=0, max_value=4)) +def test_get_unitary_target_select(target_qubit: int) -> None: + ''' + Test the get_unitary method of the MCRYGate class when + the target qubit is set. + ''' + # Create an MCRY gate with 6 qubits and random parameters + mcry = MCRYGate(num_qudits=6, target_qubit=target_qubit) + thetas = list(np.random.rand(2 ** 5) * 2 * np.pi) + + # Create the block diagonal matrix + block_unitaries = [RYGate().get_unitary([theta]) for theta in thetas] + blocked_unitary = block_diag(*block_unitaries) + + # Apply a permutation to the block diagonal matrix + # Swap the target qubit with the last qubit + perm = np.arange(6) + perm[-1], perm[target_qubit] = perm[target_qubit], perm[-1] + perm_gate = PermutationGate(6, perm) + + full_utry = perm_gate.get_unitary() @ blocked_unitary + + dist = mcry.get_unitary(thetas).get_distance_from(full_utry) + assert dist < 1e-7 \ No newline at end of file diff --git a/tests/ir/gates/parameterized/test_mcrz.py b/tests/ir/gates/parameterized/test_mcrz.py new file mode 100644 index 000000000..bedc20822 --- /dev/null +++ b/tests/ir/gates/parameterized/test_mcrz.py @@ -0,0 +1,53 @@ +"""This module tests the U1Gate class.""" +from __future__ import annotations + +import numpy as np +from scipy.linalg import block_diag + +from hypothesis import given +from hypothesis.strategies import floats, integers, lists + +from bqskit.ir.gates.parameterized import MCRZGate, RZGate +from bqskit.ir.gates.constant import PermutationGate + +@given(lists(elements=floats(allow_nan=False, allow_infinity=False, width=32), min_size=2, max_size=16)) +def test_get_unitary(thetas: list[float]) -> None: + ''' + Test the get_unitary method of the MCRZGate class. + Use the default target qubit. + ''' + # Assert that len(thetas) is a power of 2 + assert (len(thetas) & (len(thetas) - 1)) == 0 + + # There are 2 ** (n - 1) parameters + num_qudits = np.log2(len(thetas)) + 1 + mcry = MCRZGate(num_qudits=num_qudits) + block_unitaries = [RZGate().get_unitary([theta]) for theta in thetas] + blocked_unitary = block_diag(*block_unitaries) + dist = mcry.get_unitary(thetas).get_distance_from(blocked_unitary) + assert dist < 1e-7 + +@given(integers(min_value=0, max_value=4)) +def test_get_unitary_target_select(target_qubit: int) -> None: + ''' + Test the get_unitary method of the MCRZGate class when + the target qubit is set. + ''' + # Create an MCRY gate with 6 qubits and random parameters + mcry = MCRZGate(num_qudits=6, target_qubit=target_qubit) + thetas = list(np.random.rand(2 ** 5) * 2 * np.pi) + + # Create the block diagonal matrix + block_unitaries = [RZGate().get_unitary([theta]) for theta in thetas] + blocked_unitary = block_diag(*block_unitaries) + + # Apply a permutation to the block diagonal matrix + # Swap the target qubit with the last qubit + perm = np.arange(6) + perm[-1], perm[target_qubit] = perm[target_qubit], perm[-1] + perm_gate = PermutationGate(6, perm) + + full_utry = perm_gate.get_unitary() @ blocked_unitary + + dist = mcry.get_unitary(thetas).get_distance_from(full_utry) + assert dist < 1e-7 \ No newline at end of file From ceb33a6387ab21bf93a78962409e6b19e4428a26 Mon Sep 17 00:00:00 2001 From: Justin Kalloor Date: Mon, 23 Sep 2024 16:53:26 -0700 Subject: [PATCH 03/16] Fixing gates and tests --- bqskit/ir/gates/parameterized/mcry.py | 4 ++-- tests/ir/gates/parameterized/test_mcry.py | 27 ++++++++++++--------- tests/ir/gates/parameterized/test_mcrz.py | 29 +++++++++++++---------- 3 files changed, 35 insertions(+), 25 deletions(-) diff --git a/bqskit/ir/gates/parameterized/mcry.py b/bqskit/ir/gates/parameterized/mcry.py index f150fb703..f603da2c7 100644 --- a/bqskit/ir/gates/parameterized/mcry.py +++ b/bqskit/ir/gates/parameterized/mcry.py @@ -51,14 +51,14 @@ class MCRYGate( _qasm_name = 'mcry' - def __init__(self, num_qudits: int, controlled_qubit: int = -1) -> None: + def __init__(self, num_qudits: int, target_qubit: int = -1) -> None: self._num_qudits = num_qudits # 1 param for each configuration of the selec qubits self._num_params = 2 ** (num_qudits - 1) # By default, the controlled qubit is the last qubit if target_qubit == -1: target_qubit = num_qudits - 1 - self.controlled_qubit = controlled_qubit + self.controlled_qubit = target_qubit super().__init__() def get_unitary(self, params: RealVector = []) -> UnitaryMatrix: diff --git a/tests/ir/gates/parameterized/test_mcry.py b/tests/ir/gates/parameterized/test_mcry.py index e3e6aa1cc..7f5f5f997 100644 --- a/tests/ir/gates/parameterized/test_mcry.py +++ b/tests/ir/gates/parameterized/test_mcry.py @@ -15,11 +15,10 @@ def test_get_unitary(thetas: list[float]) -> None: Test the get_unitary method of the MCRYGate class. Use the default target qubit. ''' - # Assert that len(thetas) is a power of 2 - assert (len(thetas) & (len(thetas) - 1)) == 0 - + # Ensure that len(thetas) is a power of 2 # There are 2 ** (n - 1) parameters - num_qudits = np.log2(len(thetas)) + 1 + num_qudits = int(np.log2(len(thetas))) + 1 + thetas = thetas[:2 ** (num_qudits - 1)] mcry = MCRYGate(num_qudits=num_qudits) block_unitaries = [RYGate().get_unitary([theta]) for theta in thetas] blocked_unitary = block_diag(*block_unitaries) @@ -33,20 +32,26 @@ def test_get_unitary_target_select(target_qubit: int) -> None: the target qubit is set. ''' # Create an MCRY gate with 6 qubits and random parameters - mcry = MCRYGate(num_qudits=6, target_qubit=target_qubit) - thetas = list(np.random.rand(2 ** 5) * 2 * np.pi) + num_qudits = 6 + mcry = MCRYGate(num_qudits=num_qudits, target_qubit=target_qubit) + thetas = list(np.random.rand(2 ** (num_qudits - 1)) * 2 * np.pi) # Create the block diagonal matrix block_unitaries = [RYGate().get_unitary([theta]) for theta in thetas] blocked_unitary = block_diag(*block_unitaries) - # Apply a permutation to the block diagonal matrix + # Apply a permutation transformation + # to the block diagonal matrix # Swap the target qubit with the last qubit - perm = np.arange(6) - perm[-1], perm[target_qubit] = perm[target_qubit], perm[-1] - perm_gate = PermutationGate(6, perm) + # perm = np.arange(num_qudits) + perm = list(range(num_qudits)) + for i in range(target_qubit, num_qudits): + perm[i] = i + 1 + perm[-1] = target_qubit + + perm_gate = PermutationGate(num_qudits, perm) - full_utry = perm_gate.get_unitary() @ blocked_unitary + full_utry = perm_gate.get_unitary().conj().T @ blocked_unitary @ perm_gate.get_unitary() dist = mcry.get_unitary(thetas).get_distance_from(full_utry) assert dist < 1e-7 \ No newline at end of file diff --git a/tests/ir/gates/parameterized/test_mcrz.py b/tests/ir/gates/parameterized/test_mcrz.py index bedc20822..3290a7658 100644 --- a/tests/ir/gates/parameterized/test_mcrz.py +++ b/tests/ir/gates/parameterized/test_mcrz.py @@ -16,11 +16,10 @@ def test_get_unitary(thetas: list[float]) -> None: Test the get_unitary method of the MCRZGate class. Use the default target qubit. ''' - # Assert that len(thetas) is a power of 2 - assert (len(thetas) & (len(thetas) - 1)) == 0 - + # Ensure that len(thetas) is a power of 2 # There are 2 ** (n - 1) parameters - num_qudits = np.log2(len(thetas)) + 1 + num_qudits = int(np.log2(len(thetas))) + 1 + thetas = thetas[:2 ** (num_qudits - 1)] mcry = MCRZGate(num_qudits=num_qudits) block_unitaries = [RZGate().get_unitary([theta]) for theta in thetas] blocked_unitary = block_diag(*block_unitaries) @@ -33,21 +32,27 @@ def test_get_unitary_target_select(target_qubit: int) -> None: Test the get_unitary method of the MCRZGate class when the target qubit is set. ''' - # Create an MCRY gate with 6 qubits and random parameters - mcry = MCRZGate(num_qudits=6, target_qubit=target_qubit) - thetas = list(np.random.rand(2 ** 5) * 2 * np.pi) + # Create an MCRZ gate with 6 qubits and random parameters + num_qudits = 6 + mcry = MCRZGate(num_qudits=num_qudits, target_qubit=target_qubit) + thetas = list(np.random.rand(2 ** (num_qudits - 1)) * 2 * np.pi) # Create the block diagonal matrix block_unitaries = [RZGate().get_unitary([theta]) for theta in thetas] blocked_unitary = block_diag(*block_unitaries) - # Apply a permutation to the block diagonal matrix + # Apply a permutation transformation + # to the block diagonal matrix # Swap the target qubit with the last qubit - perm = np.arange(6) - perm[-1], perm[target_qubit] = perm[target_qubit], perm[-1] - perm_gate = PermutationGate(6, perm) + # perm = np.arange(num_qudits) + perm = list(range(num_qudits)) + for i in range(target_qubit, num_qudits): + perm[i] = i + 1 + perm[-1] = target_qubit + + perm_gate = PermutationGate(num_qudits, perm) - full_utry = perm_gate.get_unitary() @ blocked_unitary + full_utry = perm_gate.get_unitary().conj().T @ blocked_unitary @ perm_gate.get_unitary() dist = mcry.get_unitary(thetas).get_distance_from(full_utry) assert dist < 1e-7 \ No newline at end of file From 62c0b62147ddc0b1a46e4f771fa844f233750f6c Mon Sep 17 00:00:00 2001 From: Justin Kalloor Date: Mon, 23 Sep 2024 17:13:31 -0700 Subject: [PATCH 04/16] Fixing gradient calculation and running tox --- bqskit/ir/gates/parameterized/mcry.py | 78 ++++++++++++++-------- bqskit/ir/gates/parameterized/mcrz.py | 58 ++++++++++------ testing_mcr_gates.py | 80 +++++++++++++++++++++++ tests/ir/gates/parameterized/test_mcry.py | 41 ++++++++---- tests/ir/gates/parameterized/test_mcrz.py | 42 +++++++----- 5 files changed, 224 insertions(+), 75 deletions(-) create mode 100644 testing_mcr_gates.py diff --git a/bqskit/ir/gates/parameterized/mcry.py b/bqskit/ir/gates/parameterized/mcry.py index f603da2c7..38bf47653 100644 --- a/bqskit/ir/gates/parameterized/mcry.py +++ b/bqskit/ir/gates/parameterized/mcry.py @@ -10,12 +10,14 @@ from bqskit.qis.unitary.unitary import RealVector from bqskit.qis.unitary.unitarymatrix import UnitaryMatrix from bqskit.utils.cachedclass import CachedClass -import logging -def get_indices(index: int, target_qudit, num_qudits): - """ - Get indices for the matrix based on the target qubit. - """ + +def get_indices( + index: int, + target_qudit: int, + num_qudits: int, +) -> tuple[int, int]: + """Get indices for the matrix based on the target qubit.""" shift_qubit = num_qudits - target_qudit - 1 shift = 2 ** shift_qubit # Split into two parts around target qubit @@ -23,60 +25,70 @@ def get_indices(index: int, target_qudit, num_qudits): left = index // shift right = index % shift - # Now, shift left by one spot to + # Now, shift left by one spot to # make room for the target qubit left *= (shift * 2) # Now add 0 * new_ind and 1 * new_ind to get indices return left + right, left + shift + right + class MCRYGate( QubitGate, DifferentiableUnitary, CachedClass, - LocallyOptimizableUnitary + LocallyOptimizableUnitary, ): """ A gate representing a multiplexed Y rotation. A multiplexed Y rotation uses n - 1 qubits as select qubits and applies a Y rotation to the target. If the target qubit is the last qubit, then the unitary is block diagonal. - Each block is a 2x2 RY matrix with parameter theta. + Each block is a 2x2 RY matrix with parameter theta. Since there are n - 1 select qubits, there are 2^(n-1) parameters (thetas). We allow the target qubit to be specified to any qubit, and the other qubits - maintain their order. Qubit 0 is the most significant qubit. + maintain their order. Qubit 0 is the most significant qubit. See this paper: https://arxiv.org/pdf/quant-ph/0406176 """ _qasm_name = 'mcry' - def __init__(self, num_qudits: int, target_qubit: int = -1) -> None: + def __init__( + self, + num_qudits: int, + target_qubit: int = -1, + ) -> None: self._num_qudits = num_qudits # 1 param for each configuration of the selec qubits self._num_params = 2 ** (num_qudits - 1) # By default, the controlled qubit is the last qubit if target_qubit == -1: target_qubit = num_qudits - 1 - self.controlled_qubit = target_qubit + self.target_qubit = target_qubit super().__init__() def get_unitary(self, params: RealVector = []) -> UnitaryMatrix: """Return the unitary for this gate, see :class:`Unitary` for more.""" self.check_parameters(params) - matrix = np.zeros((2 ** self.num_qudits, 2 ** self.num_qudits), dtype=np.complex128) + matrix = np.zeros( + ( + 2 ** self.num_qudits, + 2 ** self.num_qudits, + ), dtype=np.complex128, + ) for i, param in enumerate(params): cos = np.cos(param / 2) sin = np.sin(param / 2) # Now, get indices based on target qubit. - # i corresponds to the configuration of the - # select qubits (e.g 5 = 101). Now, the + # i corresponds to the configuration of the + # select qubits (e.g 5 = 101). Now, the # target qubit is 0,1 for both the row and col # indices. So, if i = 5 and the target_qubit is 2 # Then the rows/cols are 1001 and 1101 - x1, x2 = get_indices(i, self.controlled_qubit, self.num_qudits) + x1, x2 = get_indices(i, self.target_qubit, self.num_qudits) matrix[x1, x1] = cos matrix[x2, x2] = cos @@ -88,36 +100,45 @@ def get_unitary(self, params: RealVector = []) -> UnitaryMatrix: def get_grad(self, params: RealVector = []) -> npt.NDArray[np.complex128]: """ Return the gradient for this gate. + See :class:`DifferentiableUnitary` for more info. """ self.check_parameters(params) - matrix = np.zeros((2 ** self.num_qudits, 2 ** self.num_qudits), dtype=np.complex128) + orig_utry = self.get_unitary(params).numpy + grad = [] + + # For each parameter, calculate the derivative + # with respect to that parameter for i, param in enumerate(params): dcos = -np.sin(param / 2) / 2 dsin = -1j * np.cos(param / 2) / 2 # Again, get indices based on target qubit. - x1, x2 = get_indices(i, self.controlled_qubit, self.num_qudits) + x1, x2 = get_indices(i, self.target_qubit, self.num_qudits) + + matrix = orig_utry.copy() matrix[x1, x1] = dcos matrix[x2, x2] = dcos matrix[x2, x1] = dsin matrix[x1, x2] = -1 * dsin - return UnitaryMatrix(matrix) + grad.append(matrix) + return np.array(grad) def optimize(self, env_matrix: npt.NDArray[np.complex128]) -> list[float]: """ Return the optimal parameters with respect to an environment matrix. + See :class:`LocallyOptimizableUnitary` for more info. """ self.check_env_matrix(env_matrix) - thetas = [0] * self.num_params + thetas: list[float] = [0] * self.num_params for i in range(self.num_params): - x1, x2 = get_indices(i, self.controlled_qubit, self.num_qudits) + x1, x2 = get_indices(i, self.target_qubit, self.num_qudits) a = np.real(env_matrix[x1, x1] + env_matrix[x2, x2]) b = np.real(env_matrix[x2, x1] - env_matrix[x1, x2]) theta = 2 * np.arccos(a / np.sqrt(a ** 2 + b ** 2)) @@ -127,12 +148,17 @@ def optimize(self, env_matrix: npt.NDArray[np.complex128]) -> list[float]: return thetas @staticmethod - def get_decomposition(params: RealVector = []) -> tuple[RealVector, RealVector]: - ''' - Get the corresponding parameters for one level of decomposition - of a multiplexed gate. This is used in the decomposition of both + def get_decomposition(params: RealVector = []) -> tuple[ + RealVector, + RealVector, + ]: + """ + Get the corresponding parameters for one level of decomposition of a + multiplexed gate. + + This is used in the decomposition of both the MCRY and MCRZ gates. See :class:`MGDPass` for more info. - ''' + """ new_num_params = len(params) // 2 left_params = np.zeros(new_num_params) right_params = np.zeros(new_num_params) @@ -148,4 +174,4 @@ def get_decomposition(params: RealVector = []) -> tuple[RealVector, RealVector]: def name(self) -> str: """The name of this gate, with the number of qudits appended.""" base_name = getattr(self, '_name', self.__class__.__name__) - return f"{base_name}_{self.num_qudits}" \ No newline at end of file + return f'{base_name}_{self.num_qudits}' diff --git a/bqskit/ir/gates/parameterized/mcrz.py b/bqskit/ir/gates/parameterized/mcrz.py index 912814f5c..02439d012 100644 --- a/bqskit/ir/gates/parameterized/mcrz.py +++ b/bqskit/ir/gates/parameterized/mcrz.py @@ -4,31 +4,31 @@ import numpy as np import numpy.typing as npt +from bqskit.ir.gates.parameterized.mcry import get_indices from bqskit.ir.gates.qubitgate import QubitGate from bqskit.qis.unitary.differentiable import DifferentiableUnitary from bqskit.qis.unitary.optimizable import LocallyOptimizableUnitary from bqskit.qis.unitary.unitary import RealVector from bqskit.qis.unitary.unitarymatrix import UnitaryMatrix from bqskit.utils.cachedclass import CachedClass -from bqskit.ir.gates.parameterized.mcry import get_indices -from typing import Any + class MCRZGate( QubitGate, DifferentiableUnitary, CachedClass, - LocallyOptimizableUnitary + LocallyOptimizableUnitary, ): """ A gate representing a multiplexed Z rotation. A multiplexed Z rotation uses n - 1 qubits as select qubits and applies a Z rotation to the target. If the target qubit is the last qubit, then the unitary is block diagonal. - Each block is a 2x2 RZ matrix with parameter theta. + Each block is a 2x2 RZ matrix with parameter theta. Since there are n - 1 select qubits, there are 2^(n-1) parameters (thetas). We allow the target qubit to be specified to any qubit, and the other qubits - maintain their order. Qubit 0 is the most significant qubit. + maintain their order. Qubit 0 is the most significant qubit. Why is 0 the MSB? Typically, in the QSD diagram, we see the block drawn @@ -40,11 +40,14 @@ class MCRZGate( _qasm_name = 'mcrz' - def __init__(self, num_qudits: int, target_qubit: int = -1) -> None: - ''' - Create a new MCRZGate with `num_qudits` qubits and - `target_qubit` as the target qubit. We then have 2^(n-1) parameters - for this gate. + def __init__( + self, + num_qudits: int, + target_qubit: int = -1, + ) -> None: + """ + Create a new MCRZGate with `num_qudits` qubits and `target_qubit` as the + target qubit. We then have 2^(n-1) parameters for this gate. For Example: `num_qudits` = 3, `target_qubit` = 1 @@ -56,7 +59,7 @@ def __init__(self, num_qudits: int, target_qubit: int = -1) -> None: If the input vector is |1x0> then the selection is 01, and RZ(theta_1) is applied to the target qubit. - ''' + """ self._num_qudits = num_qudits # 1 param for each configuration of the selec qubits self._num_params = 2 ** (num_qudits - 1) @@ -69,7 +72,12 @@ def __init__(self, num_qudits: int, target_qubit: int = -1) -> None: def get_unitary(self, params: RealVector = []) -> UnitaryMatrix: """Return the unitary for this gate, see :class:`Unitary` for more.""" self.check_parameters(params) - matrix = np.zeros((2 ** self.num_qudits, 2 ** self.num_qudits), dtype=np.complex128) + matrix = np.zeros( + ( + 2 ** self.num_qudits, + 2 ** self.num_qudits, + ), dtype=np.complex128, + ) for i, param in enumerate(params): pos = np.exp(1j * param / 2) neg = np.exp(-1j * param / 2) @@ -90,27 +98,37 @@ def get_grad(self, params: RealVector = []) -> npt.NDArray[np.complex128]: See :class:`DifferentiableUnitary` for more info. """ self.check_parameters(params) - matrix = np.zeros((2 ** self.num_qudits, 2 ** self.num_qudits), dtype=np.complex128) + orig_utry = self.get_unitary(params).numpy + grad = [] + + # For each parameter, calculate the derivative + # with respect to that parameter for i, param in enumerate(params): - dpos = 1j / 2 * np.exp(1j * param / 2) - dneg = -1j / 2 * np.exp(-1j * param / 2) + dcos = -np.sin(param / 2) / 2 + dsin = -1j * np.cos(param / 2) / 2 # Again, get indices based on target qubit. x1, x2 = get_indices(i, self.target_qubit, self.num_qudits) - matrix[x1, x1] = dpos - matrix[x2, x2] = dneg + matrix = orig_utry.copy() - return UnitaryMatrix(matrix) + matrix[x1, x1] = dcos + matrix[x2, x2] = dcos + matrix[x2, x1] = dsin + matrix[x1, x2] = -1 * dsin + grad.append(matrix) + + return np.array(grad) def optimize(self, env_matrix: npt.NDArray[np.complex128]) -> list[float]: """ Return the optimal parameters with respect to an environment matrix. + See :class:`LocallyOptimizableUnitary` for more info. """ self.check_env_matrix(env_matrix) - thetas = [0] * self.num_params + thetas: list[float] = [0] * self.num_params for i in range(self.num_params): x1, x2 = get_indices(i, self.target_qubit, self.num_qudits) @@ -127,4 +145,4 @@ def optimize(self, env_matrix: npt.NDArray[np.complex128]) -> list[float]: def name(self) -> str: """The name of this gate, with the number of qudits appended.""" base_name = getattr(self, '_name', self.__class__.__name__) - return f"{base_name}_{self.num_qudits}" \ No newline at end of file + return f'{base_name}_{self.num_qudits}' diff --git a/testing_mcr_gates.py b/testing_mcr_gates.py new file mode 100644 index 000000000..7da791b0d --- /dev/null +++ b/testing_mcr_gates.py @@ -0,0 +1,80 @@ +import numpy as np +from scipy.linalg import block_diag + +from bqskit.ir.gates.parameterized import MCRYGate, RYGate, MCRZGate, RZGate +from bqskit.ir.gates.constant import PermutationGate + +def test_get_unitary_mcry(thetas: list[float]) -> None: + ''' + Test the get_unitary method of the MCRYGate class. + Use the default target qubit. + ''' + # Ensure that len(thetas) is a power of 2 + # There are 2 ** (n - 1) parameters + num_qudits = int(np.log2(len(thetas))) + 1 + thetas = thetas[:2 ** (num_qudits - 1)] + + mcry = MCRYGate(num_qudits=num_qudits) + block_unitaries = [RYGate().get_unitary([theta]) for theta in thetas] + blocked_unitary = block_diag(*block_unitaries) + dist = mcry.get_unitary(thetas).get_distance_from(blocked_unitary) + assert dist < 1e-7 + +def test_get_unitary_mcrz(thetas: list[float]) -> None: + ''' + Test the get_unitary method of the MCRYGate class. + Use the default target qubit. + ''' + # Ensure that len(thetas) is a power of 2 + # There are 2 ** (n - 1) parameters + num_qudits = int(np.log2(len(thetas))) + 1 + thetas = thetas[:2 ** (num_qudits - 1)] + + mcry = MCRZGate(num_qudits=num_qudits) + block_unitaries = [RZGate().get_unitary([theta]) for theta in thetas] + blocked_unitary = block_diag(*block_unitaries) + dist = mcry.get_unitary(thetas).get_distance_from(blocked_unitary) + assert dist < 1e-7 + +def test_get_unitary_target_select_mcry(target_qubit: int) -> None: + ''' + Test the get_unitary method of the MCRYGate class when + the target qubit is set. + ''' + # Create an MCRY gate with 6 qubits and random parameters + num_qudits = 6 + mcry = MCRYGate(num_qudits=num_qudits, target_qubit=target_qubit) + thetas = list(np.random.rand(2 ** (num_qudits - 1)) * 2 * np.pi) + + # Create the block diagonal matrix + block_unitaries = [RYGate().get_unitary([theta]) for theta in thetas] + blocked_unitary = block_diag(*block_unitaries) + + # Apply a permutation transformation + # to the block diagonal matrix + # Swap the target qubit with the last qubit + # perm = np.arange(num_qudits) + perm = list(range(num_qudits)) + for i in range(target_qubit, num_qudits): + perm[i] = i + 1 + perm[-1] = target_qubit + + perm_gate = PermutationGate(num_qudits, perm) + + full_utry = perm_gate.get_unitary().conj().T @ blocked_unitary @ perm_gate.get_unitary() + + dist = mcry.get_unitary(thetas).get_distance_from(full_utry) + assert dist < 1e-7 + + + + +for num_params in [2,4,8, 20]: + params = np.random.rand(num_params) * 2 * np.pi + test_get_unitary_mcry(params) + test_get_unitary_mcrz(params) + +np.printoptions(precision=3, threshold=np.inf, linewidth=np.inf) + +for target_qubit in [0,1,2,3,4,5]: + test_get_unitary_target_select_mcry(target_qubit) \ No newline at end of file diff --git a/tests/ir/gates/parameterized/test_mcry.py b/tests/ir/gates/parameterized/test_mcry.py index 7f5f5f997..bf7887b29 100644 --- a/tests/ir/gates/parameterized/test_mcry.py +++ b/tests/ir/gates/parameterized/test_mcry.py @@ -2,19 +2,32 @@ from __future__ import annotations import numpy as np -from scipy.linalg import block_diag from hypothesis import given -from hypothesis.strategies import floats, integers, lists +from hypothesis.strategies import floats +from hypothesis.strategies import integers +from hypothesis.strategies import lists +from scipy.linalg import block_diag -from bqskit.ir.gates.parameterized import MCRYGate, RYGate from bqskit.ir.gates.constant import PermutationGate +from bqskit.ir.gates.parameterized import MCRYGate +from bqskit.ir.gates.parameterized import RYGate + -@given(lists(elements=floats(allow_nan=False, allow_infinity=False, width=32), min_size=2, max_size=16)) +@given( + lists( + elements=floats( + allow_nan=False, + allow_infinity=False, + width=32, + ), min_size=2, max_size=16, + ), +) def test_get_unitary(thetas: list[float]) -> None: - ''' + """ Test the get_unitary method of the MCRYGate class. + Use the default target qubit. - ''' + """ # Ensure that len(thetas) is a power of 2 # There are 2 ** (n - 1) parameters num_qudits = int(np.log2(len(thetas))) + 1 @@ -25,12 +38,11 @@ def test_get_unitary(thetas: list[float]) -> None: dist = mcry.get_unitary(thetas).get_distance_from(blocked_unitary) assert dist < 1e-7 + @given(integers(min_value=0, max_value=4)) def test_get_unitary_target_select(target_qubit: int) -> None: - ''' - Test the get_unitary method of the MCRYGate class when - the target qubit is set. - ''' + """Test the get_unitary method of the MCRYGate class when the target qubit + is set.""" # Create an MCRY gate with 6 qubits and random parameters num_qudits = 6 mcry = MCRYGate(num_qudits=num_qudits, target_qubit=target_qubit) @@ -40,7 +52,7 @@ def test_get_unitary_target_select(target_qubit: int) -> None: block_unitaries = [RYGate().get_unitary([theta]) for theta in thetas] blocked_unitary = block_diag(*block_unitaries) - # Apply a permutation transformation + # Apply a permutation transformation # to the block diagonal matrix # Swap the target qubit with the last qubit # perm = np.arange(num_qudits) @@ -49,9 +61,10 @@ def test_get_unitary_target_select(target_qubit: int) -> None: perm[i] = i + 1 perm[-1] = target_qubit - perm_gate = PermutationGate(num_qudits, perm) + perm_gate = PermutationGate(num_qudits, perm) - full_utry = perm_gate.get_unitary().conj().T @ blocked_unitary @ perm_gate.get_unitary() + full_utry = perm_gate.get_unitary().conj( + ).T @ blocked_unitary @ perm_gate.get_unitary() dist = mcry.get_unitary(thetas).get_distance_from(full_utry) - assert dist < 1e-7 \ No newline at end of file + assert dist < 1e-7 diff --git a/tests/ir/gates/parameterized/test_mcrz.py b/tests/ir/gates/parameterized/test_mcrz.py index 3290a7658..392ae7a23 100644 --- a/tests/ir/gates/parameterized/test_mcrz.py +++ b/tests/ir/gates/parameterized/test_mcrz.py @@ -2,20 +2,32 @@ from __future__ import annotations import numpy as np -from scipy.linalg import block_diag - from hypothesis import given -from hypothesis.strategies import floats, integers, lists +from hypothesis.strategies import floats +from hypothesis.strategies import integers +from hypothesis.strategies import lists +from scipy.linalg import block_diag -from bqskit.ir.gates.parameterized import MCRZGate, RZGate from bqskit.ir.gates.constant import PermutationGate +from bqskit.ir.gates.parameterized import MCRZGate +from bqskit.ir.gates.parameterized import RZGate + -@given(lists(elements=floats(allow_nan=False, allow_infinity=False, width=32), min_size=2, max_size=16)) +@given( + lists( + elements=floats( + allow_nan=False, + allow_infinity=False, + width=32, + ), min_size=2, max_size=16, + ), +) def test_get_unitary(thetas: list[float]) -> None: - ''' + """ Test the get_unitary method of the MCRZGate class. + Use the default target qubit. - ''' + """ # Ensure that len(thetas) is a power of 2 # There are 2 ** (n - 1) parameters num_qudits = int(np.log2(len(thetas))) + 1 @@ -26,12 +38,11 @@ def test_get_unitary(thetas: list[float]) -> None: dist = mcry.get_unitary(thetas).get_distance_from(blocked_unitary) assert dist < 1e-7 + @given(integers(min_value=0, max_value=4)) def test_get_unitary_target_select(target_qubit: int) -> None: - ''' - Test the get_unitary method of the MCRZGate class when - the target qubit is set. - ''' + """Test the get_unitary method of the MCRZGate class when the target qubit + is set.""" # Create an MCRZ gate with 6 qubits and random parameters num_qudits = 6 mcry = MCRZGate(num_qudits=num_qudits, target_qubit=target_qubit) @@ -41,7 +52,7 @@ def test_get_unitary_target_select(target_qubit: int) -> None: block_unitaries = [RZGate().get_unitary([theta]) for theta in thetas] blocked_unitary = block_diag(*block_unitaries) - # Apply a permutation transformation + # Apply a permutation transformation # to the block diagonal matrix # Swap the target qubit with the last qubit # perm = np.arange(num_qudits) @@ -50,9 +61,10 @@ def test_get_unitary_target_select(target_qubit: int) -> None: perm[i] = i + 1 perm[-1] = target_qubit - perm_gate = PermutationGate(num_qudits, perm) + perm_gate = PermutationGate(num_qudits, perm) - full_utry = perm_gate.get_unitary().conj().T @ blocked_unitary @ perm_gate.get_unitary() + full_utry = perm_gate.get_unitary().conj( + ).T @ blocked_unitary @ perm_gate.get_unitary() dist = mcry.get_unitary(thetas).get_distance_from(full_utry) - assert dist < 1e-7 \ No newline at end of file + assert dist < 1e-7 From 2a53462cea24ce24931caff43f90a4e2b0fe1fc0 Mon Sep 17 00:00:00 2001 From: Justin Kalloor Date: Mon, 23 Sep 2024 17:16:30 -0700 Subject: [PATCH 05/16] Removing extra test file --- testing_mcr_gates.py | 80 -------------------------------------------- 1 file changed, 80 deletions(-) delete mode 100644 testing_mcr_gates.py diff --git a/testing_mcr_gates.py b/testing_mcr_gates.py deleted file mode 100644 index 7da791b0d..000000000 --- a/testing_mcr_gates.py +++ /dev/null @@ -1,80 +0,0 @@ -import numpy as np -from scipy.linalg import block_diag - -from bqskit.ir.gates.parameterized import MCRYGate, RYGate, MCRZGate, RZGate -from bqskit.ir.gates.constant import PermutationGate - -def test_get_unitary_mcry(thetas: list[float]) -> None: - ''' - Test the get_unitary method of the MCRYGate class. - Use the default target qubit. - ''' - # Ensure that len(thetas) is a power of 2 - # There are 2 ** (n - 1) parameters - num_qudits = int(np.log2(len(thetas))) + 1 - thetas = thetas[:2 ** (num_qudits - 1)] - - mcry = MCRYGate(num_qudits=num_qudits) - block_unitaries = [RYGate().get_unitary([theta]) for theta in thetas] - blocked_unitary = block_diag(*block_unitaries) - dist = mcry.get_unitary(thetas).get_distance_from(blocked_unitary) - assert dist < 1e-7 - -def test_get_unitary_mcrz(thetas: list[float]) -> None: - ''' - Test the get_unitary method of the MCRYGate class. - Use the default target qubit. - ''' - # Ensure that len(thetas) is a power of 2 - # There are 2 ** (n - 1) parameters - num_qudits = int(np.log2(len(thetas))) + 1 - thetas = thetas[:2 ** (num_qudits - 1)] - - mcry = MCRZGate(num_qudits=num_qudits) - block_unitaries = [RZGate().get_unitary([theta]) for theta in thetas] - blocked_unitary = block_diag(*block_unitaries) - dist = mcry.get_unitary(thetas).get_distance_from(blocked_unitary) - assert dist < 1e-7 - -def test_get_unitary_target_select_mcry(target_qubit: int) -> None: - ''' - Test the get_unitary method of the MCRYGate class when - the target qubit is set. - ''' - # Create an MCRY gate with 6 qubits and random parameters - num_qudits = 6 - mcry = MCRYGate(num_qudits=num_qudits, target_qubit=target_qubit) - thetas = list(np.random.rand(2 ** (num_qudits - 1)) * 2 * np.pi) - - # Create the block diagonal matrix - block_unitaries = [RYGate().get_unitary([theta]) for theta in thetas] - blocked_unitary = block_diag(*block_unitaries) - - # Apply a permutation transformation - # to the block diagonal matrix - # Swap the target qubit with the last qubit - # perm = np.arange(num_qudits) - perm = list(range(num_qudits)) - for i in range(target_qubit, num_qudits): - perm[i] = i + 1 - perm[-1] = target_qubit - - perm_gate = PermutationGate(num_qudits, perm) - - full_utry = perm_gate.get_unitary().conj().T @ blocked_unitary @ perm_gate.get_unitary() - - dist = mcry.get_unitary(thetas).get_distance_from(full_utry) - assert dist < 1e-7 - - - - -for num_params in [2,4,8, 20]: - params = np.random.rand(num_params) * 2 * np.pi - test_get_unitary_mcry(params) - test_get_unitary_mcrz(params) - -np.printoptions(precision=3, threshold=np.inf, linewidth=np.inf) - -for target_qubit in [0,1,2,3,4,5]: - test_get_unitary_target_select_mcry(target_qubit) \ No newline at end of file From ea7c8ce1e99849af850e67f5d547f712135e2e70 Mon Sep 17 00:00:00 2001 From: Justin Kalloor Date: Tue, 24 Sep 2024 11:51:54 -0700 Subject: [PATCH 06/16] Fixing docstring error --- bqskit/ir/gates/parameterized/mcry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bqskit/ir/gates/parameterized/mcry.py b/bqskit/ir/gates/parameterized/mcry.py index 38bf47653..b359ab5a7 100644 --- a/bqskit/ir/gates/parameterized/mcry.py +++ b/bqskit/ir/gates/parameterized/mcry.py @@ -157,7 +157,7 @@ def get_decomposition(params: RealVector = []) -> tuple[ multiplexed gate. This is used in the decomposition of both - the MCRY and MCRZ gates. See :class:`MGDPass` for more info. + the MCRY and MCRZ gates. """ new_num_params = len(params) // 2 left_params = np.zeros(new_num_params) From 5281ff6f41a4874b4f91be6fa017b1ea541ce8db Mon Sep 17 00:00:00 2001 From: Justin Kalloor Date: Tue, 24 Sep 2024 11:55:29 -0700 Subject: [PATCH 07/16] tox --- bqskit/ir/gates/parameterized/mcry.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bqskit/ir/gates/parameterized/mcry.py b/bqskit/ir/gates/parameterized/mcry.py index b359ab5a7..4246e3968 100644 --- a/bqskit/ir/gates/parameterized/mcry.py +++ b/bqskit/ir/gates/parameterized/mcry.py @@ -156,8 +156,7 @@ def get_decomposition(params: RealVector = []) -> tuple[ Get the corresponding parameters for one level of decomposition of a multiplexed gate. - This is used in the decomposition of both - the MCRY and MCRZ gates. + This is used in the decomposition of both the MCRY and MCRZ gates. """ new_num_params = len(params) // 2 left_params = np.zeros(new_num_params) From 4ff99ffead035b0fc7f22223dd85ad95d2ad716d Mon Sep 17 00:00:00 2001 From: Justin Kalloor Date: Fri, 27 Sep 2024 14:13:19 -0700 Subject: [PATCH 08/16] Renaming from MCRX to MPRX --- bqskit/ir/gates/parameterized/__init__.py | 8 +++---- .../gates/parameterized/{mcry.py => mpry.py} | 8 +++---- .../gates/parameterized/{mcrz.py => mprz.py} | 10 ++++----- .../{test_mcry.py => test_mpry.py} | 22 +++++++++---------- .../{test_mcrz.py => test_mprz.py} | 22 +++++++++---------- 5 files changed, 35 insertions(+), 35 deletions(-) rename bqskit/ir/gates/parameterized/{mcry.py => mpry.py} (97%) rename bqskit/ir/gates/parameterized/{mcrz.py => mprz.py} (95%) rename tests/ir/gates/parameterized/{test_mcry.py => test_mpry.py} (73%) rename tests/ir/gates/parameterized/{test_mcrz.py => test_mprz.py} (73%) diff --git a/bqskit/ir/gates/parameterized/__init__.py b/bqskit/ir/gates/parameterized/__init__.py index a592b66ef..34b51aee5 100644 --- a/bqskit/ir/gates/parameterized/__init__.py +++ b/bqskit/ir/gates/parameterized/__init__.py @@ -11,8 +11,8 @@ from bqskit.ir.gates.parameterized.crz import CRZGate from bqskit.ir.gates.parameterized.cu import CUGate from bqskit.ir.gates.parameterized.fsim import FSIMGate -from bqskit.ir.gates.parameterized.mcry import MCRYGate -from bqskit.ir.gates.parameterized.mcrz import MCRZGate +from bqskit.ir.gates.parameterized.mpry import MPRYGate +from bqskit.ir.gates.parameterized.mprz import MPRZGate from bqskit.ir.gates.parameterized.pauli import PauliGate from bqskit.ir.gates.parameterized.pauliz import PauliZGate from bqskit.ir.gates.parameterized.phasedxz import PhasedXZGate @@ -43,8 +43,8 @@ 'CRZGate', 'CUGate', 'FSIMGate', - 'MCRYGate', - 'MCRZGate', + 'MPRYGate', + 'MPRZGate', 'PauliGate', 'PauliZGate', 'PhasedXZGate', diff --git a/bqskit/ir/gates/parameterized/mcry.py b/bqskit/ir/gates/parameterized/mpry.py similarity index 97% rename from bqskit/ir/gates/parameterized/mcry.py rename to bqskit/ir/gates/parameterized/mpry.py index 4246e3968..373a90fa7 100644 --- a/bqskit/ir/gates/parameterized/mcry.py +++ b/bqskit/ir/gates/parameterized/mpry.py @@ -1,4 +1,4 @@ -"""This module implements the MCRYGate.""" +"""This module implements the MPRYGate.""" from __future__ import annotations import numpy as np @@ -32,7 +32,7 @@ def get_indices( return left + right, left + shift + right -class MCRYGate( +class MPRYGate( QubitGate, DifferentiableUnitary, CachedClass, @@ -52,7 +52,7 @@ class MCRYGate( See this paper: https://arxiv.org/pdf/quant-ph/0406176 """ - _qasm_name = 'mcry' + _qasm_name = 'mpry' def __init__( self, @@ -156,7 +156,7 @@ def get_decomposition(params: RealVector = []) -> tuple[ Get the corresponding parameters for one level of decomposition of a multiplexed gate. - This is used in the decomposition of both the MCRY and MCRZ gates. + This is used in the decomposition of both the MPRY and MPRZ gates. """ new_num_params = len(params) // 2 left_params = np.zeros(new_num_params) diff --git a/bqskit/ir/gates/parameterized/mcrz.py b/bqskit/ir/gates/parameterized/mprz.py similarity index 95% rename from bqskit/ir/gates/parameterized/mcrz.py rename to bqskit/ir/gates/parameterized/mprz.py index 02439d012..2dc86ecd1 100644 --- a/bqskit/ir/gates/parameterized/mcrz.py +++ b/bqskit/ir/gates/parameterized/mprz.py @@ -1,10 +1,10 @@ -"""This module implements the MCRZGate.""" +"""This module implements the MPRZGate.""" from __future__ import annotations import numpy as np import numpy.typing as npt -from bqskit.ir.gates.parameterized.mcry import get_indices +from bqskit.ir.gates.parameterized.mpry import get_indices from bqskit.ir.gates.qubitgate import QubitGate from bqskit.qis.unitary.differentiable import DifferentiableUnitary from bqskit.qis.unitary.optimizable import LocallyOptimizableUnitary @@ -13,7 +13,7 @@ from bqskit.utils.cachedclass import CachedClass -class MCRZGate( +class MPRZGate( QubitGate, DifferentiableUnitary, CachedClass, @@ -38,7 +38,7 @@ class MCRZGate( See this paper: https://arxiv.org/pdf/quant-ph/0406176 """ - _qasm_name = 'mcrz' + _qasm_name = 'mprz' def __init__( self, @@ -46,7 +46,7 @@ def __init__( target_qubit: int = -1, ) -> None: """ - Create a new MCRZGate with `num_qudits` qubits and `target_qubit` as the + Create a new MPRZGate with `num_qudits` qubits and `target_qubit` as the target qubit. We then have 2^(n-1) parameters for this gate. For Example: diff --git a/tests/ir/gates/parameterized/test_mcry.py b/tests/ir/gates/parameterized/test_mpry.py similarity index 73% rename from tests/ir/gates/parameterized/test_mcry.py rename to tests/ir/gates/parameterized/test_mpry.py index bf7887b29..20d0d4ce0 100644 --- a/tests/ir/gates/parameterized/test_mcry.py +++ b/tests/ir/gates/parameterized/test_mpry.py @@ -1,4 +1,4 @@ -"""This module tests the U1Gate class.""" +"""This module tests the MPRYGate class.""" from __future__ import annotations import numpy as np @@ -9,7 +9,7 @@ from scipy.linalg import block_diag from bqskit.ir.gates.constant import PermutationGate -from bqskit.ir.gates.parameterized import MCRYGate +from bqskit.ir.gates.parameterized import MPRYGate from bqskit.ir.gates.parameterized import RYGate @@ -24,7 +24,7 @@ ) def test_get_unitary(thetas: list[float]) -> None: """ - Test the get_unitary method of the MCRYGate class. + Test the get_unitary method of the MPRYGate class. Use the default target qubit. """ @@ -32,20 +32,20 @@ def test_get_unitary(thetas: list[float]) -> None: # There are 2 ** (n - 1) parameters num_qudits = int(np.log2(len(thetas))) + 1 thetas = thetas[:2 ** (num_qudits - 1)] - mcry = MCRYGate(num_qudits=num_qudits) + MPRy = MPRYGate(num_qudits=num_qudits) block_unitaries = [RYGate().get_unitary([theta]) for theta in thetas] blocked_unitary = block_diag(*block_unitaries) - dist = mcry.get_unitary(thetas).get_distance_from(blocked_unitary) + dist = MPRy.get_unitary(thetas).get_distance_from(blocked_unitary) assert dist < 1e-7 @given(integers(min_value=0, max_value=4)) def test_get_unitary_target_select(target_qubit: int) -> None: - """Test the get_unitary method of the MCRYGate class when the target qubit + """Test the get_unitary method of the MPRYGate class when the target qubit is set.""" - # Create an MCRY gate with 6 qubits and random parameters + # Create an MPRY gate with 6 qubits and random parameters num_qudits = 6 - mcry = MCRYGate(num_qudits=num_qudits, target_qubit=target_qubit) + MPRy = MPRYGate(num_qudits=num_qudits, target_qubit=target_qubit) thetas = list(np.random.rand(2 ** (num_qudits - 1)) * 2 * np.pi) # Create the block diagonal matrix @@ -63,8 +63,8 @@ def test_get_unitary_target_select(target_qubit: int) -> None: perm_gate = PermutationGate(num_qudits, perm) - full_utry = perm_gate.get_unitary().conj( - ).T @ blocked_unitary @ perm_gate.get_unitary() + full_utry = (perm_gate.get_unitary().conj().T + @ blocked_unitary @ perm_gate.get_unitary()) - dist = mcry.get_unitary(thetas).get_distance_from(full_utry) + dist = MPRy.get_unitary(thetas).get_distance_from(full_utry) assert dist < 1e-7 diff --git a/tests/ir/gates/parameterized/test_mcrz.py b/tests/ir/gates/parameterized/test_mprz.py similarity index 73% rename from tests/ir/gates/parameterized/test_mcrz.py rename to tests/ir/gates/parameterized/test_mprz.py index 392ae7a23..5c7cc9f9a 100644 --- a/tests/ir/gates/parameterized/test_mcrz.py +++ b/tests/ir/gates/parameterized/test_mprz.py @@ -1,4 +1,4 @@ -"""This module tests the U1Gate class.""" +"""This module tests the MPRZGate class.""" from __future__ import annotations import numpy as np @@ -9,7 +9,7 @@ from scipy.linalg import block_diag from bqskit.ir.gates.constant import PermutationGate -from bqskit.ir.gates.parameterized import MCRZGate +from bqskit.ir.gates.parameterized import MPRZGate from bqskit.ir.gates.parameterized import RZGate @@ -24,7 +24,7 @@ ) def test_get_unitary(thetas: list[float]) -> None: """ - Test the get_unitary method of the MCRZGate class. + Test the get_unitary method of the MPRZGate class. Use the default target qubit. """ @@ -32,20 +32,20 @@ def test_get_unitary(thetas: list[float]) -> None: # There are 2 ** (n - 1) parameters num_qudits = int(np.log2(len(thetas))) + 1 thetas = thetas[:2 ** (num_qudits - 1)] - mcry = MCRZGate(num_qudits=num_qudits) + MPRy = MPRZGate(num_qudits=num_qudits) block_unitaries = [RZGate().get_unitary([theta]) for theta in thetas] blocked_unitary = block_diag(*block_unitaries) - dist = mcry.get_unitary(thetas).get_distance_from(blocked_unitary) + dist = MPRy.get_unitary(thetas).get_distance_from(blocked_unitary) assert dist < 1e-7 @given(integers(min_value=0, max_value=4)) def test_get_unitary_target_select(target_qubit: int) -> None: - """Test the get_unitary method of the MCRZGate class when the target qubit + """Test the get_unitary method of the MPRZGate class when the target qubit is set.""" - # Create an MCRZ gate with 6 qubits and random parameters + # Create an MPRZ gate with 6 qubits and random parameters num_qudits = 6 - mcry = MCRZGate(num_qudits=num_qudits, target_qubit=target_qubit) + MPRy = MPRZGate(num_qudits=num_qudits, target_qubit=target_qubit) thetas = list(np.random.rand(2 ** (num_qudits - 1)) * 2 * np.pi) # Create the block diagonal matrix @@ -63,8 +63,8 @@ def test_get_unitary_target_select(target_qubit: int) -> None: perm_gate = PermutationGate(num_qudits, perm) - full_utry = perm_gate.get_unitary().conj( - ).T @ blocked_unitary @ perm_gate.get_unitary() + full_utry = (perm_gate.get_unitary().conj().T + @ blocked_unitary @ perm_gate.get_unitary()) - dist = mcry.get_unitary(thetas).get_distance_from(full_utry) + dist = MPRy.get_unitary(thetas).get_distance_from(full_utry) assert dist < 1e-7 From 44cc84f8dce1247743f93fa31789b71702118f2a Mon Sep 17 00:00:00 2001 From: Justin Kalloor Date: Fri, 27 Sep 2024 14:13:39 -0700 Subject: [PATCH 09/16] tox --- tests/ir/gates/parameterized/test_mpry.py | 6 ++++-- tests/ir/gates/parameterized/test_mprz.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/ir/gates/parameterized/test_mpry.py b/tests/ir/gates/parameterized/test_mpry.py index 20d0d4ce0..f16e6827b 100644 --- a/tests/ir/gates/parameterized/test_mpry.py +++ b/tests/ir/gates/parameterized/test_mpry.py @@ -63,8 +63,10 @@ def test_get_unitary_target_select(target_qubit: int) -> None: perm_gate = PermutationGate(num_qudits, perm) - full_utry = (perm_gate.get_unitary().conj().T - @ blocked_unitary @ perm_gate.get_unitary()) + full_utry = ( + perm_gate.get_unitary().conj().T + @ blocked_unitary @ perm_gate.get_unitary() + ) dist = MPRy.get_unitary(thetas).get_distance_from(full_utry) assert dist < 1e-7 diff --git a/tests/ir/gates/parameterized/test_mprz.py b/tests/ir/gates/parameterized/test_mprz.py index 5c7cc9f9a..b19c5d925 100644 --- a/tests/ir/gates/parameterized/test_mprz.py +++ b/tests/ir/gates/parameterized/test_mprz.py @@ -63,8 +63,10 @@ def test_get_unitary_target_select(target_qubit: int) -> None: perm_gate = PermutationGate(num_qudits, perm) - full_utry = (perm_gate.get_unitary().conj().T - @ blocked_unitary @ perm_gate.get_unitary()) + full_utry = ( + perm_gate.get_unitary().conj().T + @ blocked_unitary @ perm_gate.get_unitary() + ) dist = MPRy.get_unitary(thetas).get_distance_from(full_utry) assert dist < 1e-7 From 699f86076e080c0c44710b4421388f471cdd418c Mon Sep 17 00:00:00 2001 From: Justin Kalloor Date: Fri, 27 Sep 2024 15:57:38 -0700 Subject: [PATCH 10/16] Fixing gradient calcs --- bqskit/ir/gates/parameterized/mpry.py | 18 +++++++----------- bqskit/ir/gates/parameterized/mprz.py | 21 ++++++++------------- 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/bqskit/ir/gates/parameterized/mpry.py b/bqskit/ir/gates/parameterized/mpry.py index 373a90fa7..6fdc888c3 100644 --- a/bqskit/ir/gates/parameterized/mpry.py +++ b/bqskit/ir/gates/parameterized/mpry.py @@ -105,8 +105,8 @@ def get_grad(self, params: RealVector = []) -> npt.NDArray[np.complex128]: """ self.check_parameters(params) - orig_utry = self.get_unitary(params).numpy - grad = [] + grad = np.zeros((len(params), 2 ** self.num_qudits, + 2 ** self.num_qudits), dtype=np.complex128) # For each parameter, calculate the derivative # with respect to that parameter @@ -117,16 +117,12 @@ def get_grad(self, params: RealVector = []) -> npt.NDArray[np.complex128]: # Again, get indices based on target qubit. x1, x2 = get_indices(i, self.target_qubit, self.num_qudits) - matrix = orig_utry.copy() + grad[i, x1, x1] = dcos + grad[i, x2, x2] = dcos + grad[i, x2, x1] = dsin + grad[i, x1, x2] = -1 * dsin - matrix[x1, x1] = dcos - matrix[x2, x2] = dcos - matrix[x2, x1] = dsin - matrix[x1, x2] = -1 * dsin - - grad.append(matrix) - - return np.array(grad) + return grad def optimize(self, env_matrix: npt.NDArray[np.complex128]) -> list[float]: """ diff --git a/bqskit/ir/gates/parameterized/mprz.py b/bqskit/ir/gates/parameterized/mprz.py index 2dc86ecd1..96e49ac85 100644 --- a/bqskit/ir/gates/parameterized/mprz.py +++ b/bqskit/ir/gates/parameterized/mprz.py @@ -98,28 +98,23 @@ def get_grad(self, params: RealVector = []) -> npt.NDArray[np.complex128]: See :class:`DifferentiableUnitary` for more info. """ self.check_parameters(params) - orig_utry = self.get_unitary(params).numpy - grad = [] + + grad = np.zeros((len(params), 2 ** self.num_qudits, + 2 ** self.num_qudits), dtype=np.complex128) # For each parameter, calculate the derivative # with respect to that parameter for i, param in enumerate(params): - dcos = -np.sin(param / 2) / 2 - dsin = -1j * np.cos(param / 2) / 2 + dpos = 1j * np.exp(1j * param / 2) / 2 + dneg = -1j * np.exp(-1j * param / 2) / 2 # Again, get indices based on target qubit. x1, x2 = get_indices(i, self.target_qubit, self.num_qudits) - matrix = orig_utry.copy() - - matrix[x1, x1] = dcos - matrix[x2, x2] = dcos - matrix[x2, x1] = dsin - matrix[x1, x2] = -1 * dsin - - grad.append(matrix) + grad[i, x1, x1] = dpos + grad[i, x2, x2] = dneg - return np.array(grad) + return grad def optimize(self, env_matrix: npt.NDArray[np.complex128]) -> list[float]: """ From 0d545919da4bdedf364a93a9f4f8fdb1337a1011 Mon Sep 17 00:00:00 2001 From: Justin Kalloor Date: Fri, 27 Sep 2024 15:58:12 -0700 Subject: [PATCH 11/16] tox --- bqskit/ir/gates/parameterized/mpry.py | 8 ++++++-- bqskit/ir/gates/parameterized/mprz.py | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/bqskit/ir/gates/parameterized/mpry.py b/bqskit/ir/gates/parameterized/mpry.py index 6fdc888c3..19f9e8648 100644 --- a/bqskit/ir/gates/parameterized/mpry.py +++ b/bqskit/ir/gates/parameterized/mpry.py @@ -105,8 +105,12 @@ def get_grad(self, params: RealVector = []) -> npt.NDArray[np.complex128]: """ self.check_parameters(params) - grad = np.zeros((len(params), 2 ** self.num_qudits, - 2 ** self.num_qudits), dtype=np.complex128) + grad = np.zeros( + ( + len(params), 2 ** self.num_qudits, + 2 ** self.num_qudits, + ), dtype=np.complex128, + ) # For each parameter, calculate the derivative # with respect to that parameter diff --git a/bqskit/ir/gates/parameterized/mprz.py b/bqskit/ir/gates/parameterized/mprz.py index 96e49ac85..f35e8747a 100644 --- a/bqskit/ir/gates/parameterized/mprz.py +++ b/bqskit/ir/gates/parameterized/mprz.py @@ -99,8 +99,12 @@ def get_grad(self, params: RealVector = []) -> npt.NDArray[np.complex128]: """ self.check_parameters(params) - grad = np.zeros((len(params), 2 ** self.num_qudits, - 2 ** self.num_qudits), dtype=np.complex128) + grad = np.zeros( + ( + len(params), 2 ** self.num_qudits, + 2 ** self.num_qudits, + ), dtype=np.complex128, + ) # For each parameter, calculate the derivative # with respect to that parameter From 60553459c569fcb1d3298a16331116b11ffa02ec Mon Sep 17 00:00:00 2001 From: Justin Kalloor Date: Tue, 8 Oct 2024 09:03:22 -0700 Subject: [PATCH 12/16] Adding Gate Docstring --- bqskit/ir/gates/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bqskit/ir/gates/__init__.py b/bqskit/ir/gates/__init__.py index 183a6426c..fb9affdb1 100644 --- a/bqskit/ir/gates/__init__.py +++ b/bqskit/ir/gates/__init__.py @@ -72,6 +72,8 @@ CRZGate CUGate FSIMGate + MPRYGate + MPRZGate PauliGate PauliZGate PhasedXZGate From c486811bbaf16d60efa7d9274c344abad1862d8e Mon Sep 17 00:00:00 2001 From: Justin Kalloor Date: Tue, 8 Oct 2024 09:06:12 -0700 Subject: [PATCH 13/16] Updating pre-commit hooks --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5fd25af39..9c64255f8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ ci: skip: [mypy] repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer From bf48f85b39e9219ae452123ad6fea03df4f06053 Mon Sep 17 00:00:00 2001 From: Justin Kalloor Date: Tue, 8 Oct 2024 11:29:36 -0700 Subject: [PATCH 14/16] Migrating/updating pre-commit hooks --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9c64255f8..abdc70115 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -66,7 +66,7 @@ repos: args: - --in-place - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.11.1 + rev: v1.11.2 hooks: - id: mypy exclude: tests/qis/test_pauli.py From 883c5077bc5947ad5f8e7cc7768d2014361bd9a3 Mon Sep 17 00:00:00 2001 From: Justin Kalloor Date: Tue, 8 Oct 2024 12:17:51 -0700 Subject: [PATCH 15/16] Adding temporary fix to switch docformatter repo until issue is fixed --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index abdc70115..43ceec2ac 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,8 +20,8 @@ repos: - id: fix-byte-order-marker - id: fix-encoding-pragma args: ['--remove'] -- repo: https://github.com/PyCQA/docformatter - rev: v1.7.5 +- repo: https://github.com/s-weigand/docformatter + rev: 1ec30b7 hooks: - id: docformatter args: From d673f1306914836d5ac1826bfe9a007bb83ce9d6 Mon Sep 17 00:00:00 2001 From: Justin Kalloor Date: Tue, 8 Oct 2024 15:58:12 -0700 Subject: [PATCH 16/16] Trying to reorder imports to pass the docs --- tests/ir/gates/parameterized/test_mpry.py | 6 +++--- tests/ir/gates/parameterized/test_mprz.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/ir/gates/parameterized/test_mpry.py b/tests/ir/gates/parameterized/test_mpry.py index f16e6827b..8d4b7aa58 100644 --- a/tests/ir/gates/parameterized/test_mpry.py +++ b/tests/ir/gates/parameterized/test_mpry.py @@ -2,11 +2,11 @@ from __future__ import annotations import numpy as np +import scipy.linalg as la from hypothesis import given from hypothesis.strategies import floats from hypothesis.strategies import integers from hypothesis.strategies import lists -from scipy.linalg import block_diag from bqskit.ir.gates.constant import PermutationGate from bqskit.ir.gates.parameterized import MPRYGate @@ -34,7 +34,7 @@ def test_get_unitary(thetas: list[float]) -> None: thetas = thetas[:2 ** (num_qudits - 1)] MPRy = MPRYGate(num_qudits=num_qudits) block_unitaries = [RYGate().get_unitary([theta]) for theta in thetas] - blocked_unitary = block_diag(*block_unitaries) + blocked_unitary = la.block_diag(*block_unitaries) dist = MPRy.get_unitary(thetas).get_distance_from(blocked_unitary) assert dist < 1e-7 @@ -50,7 +50,7 @@ def test_get_unitary_target_select(target_qubit: int) -> None: # Create the block diagonal matrix block_unitaries = [RYGate().get_unitary([theta]) for theta in thetas] - blocked_unitary = block_diag(*block_unitaries) + blocked_unitary = la.block_diag(*block_unitaries) # Apply a permutation transformation # to the block diagonal matrix diff --git a/tests/ir/gates/parameterized/test_mprz.py b/tests/ir/gates/parameterized/test_mprz.py index b19c5d925..0ba506e30 100644 --- a/tests/ir/gates/parameterized/test_mprz.py +++ b/tests/ir/gates/parameterized/test_mprz.py @@ -2,11 +2,11 @@ from __future__ import annotations import numpy as np +import scipy.linalg as la from hypothesis import given from hypothesis.strategies import floats from hypothesis.strategies import integers from hypothesis.strategies import lists -from scipy.linalg import block_diag from bqskit.ir.gates.constant import PermutationGate from bqskit.ir.gates.parameterized import MPRZGate @@ -34,7 +34,7 @@ def test_get_unitary(thetas: list[float]) -> None: thetas = thetas[:2 ** (num_qudits - 1)] MPRy = MPRZGate(num_qudits=num_qudits) block_unitaries = [RZGate().get_unitary([theta]) for theta in thetas] - blocked_unitary = block_diag(*block_unitaries) + blocked_unitary = la.block_diag(*block_unitaries) dist = MPRy.get_unitary(thetas).get_distance_from(blocked_unitary) assert dist < 1e-7 @@ -50,7 +50,7 @@ def test_get_unitary_target_select(target_qubit: int) -> None: # Create the block diagonal matrix block_unitaries = [RZGate().get_unitary([theta]) for theta in thetas] - blocked_unitary = block_diag(*block_unitaries) + blocked_unitary = la.block_diag(*block_unitaries) # Apply a permutation transformation # to the block diagonal matrix