|
13 | 13 |
|
14 | 14 | from math import isclose
|
15 | 15 | import numpy as np
|
16 |
| -from qiskit.circuit import QuantumCircuit, QuantumRegister |
| 16 | +from qiskit.circuit import QuantumCircuit, QuantumRegister, Gate |
17 | 17 | from qiskit.circuit.library.generalized_gates import UCRYGate
|
18 | 18 |
|
19 | 19 |
|
@@ -46,43 +46,86 @@ def __init__(
|
46 | 46 | """
|
47 | 47 | qr_state = QuantumRegister(num_state_qubits, "state")
|
48 | 48 | qr_flag = QuantumRegister(1, "flag")
|
49 |
| - circuit = QuantumCircuit(qr_state, qr_flag, name=name) |
| 49 | + super().__init__(qr_state, qr_flag, name=name) |
| 50 | + |
| 51 | + reciprocal = ExactReciprocalGate(num_state_qubits, scaling, neg_vals, label=name) |
| 52 | + self.append(reciprocal, self.qubits) |
| 53 | + |
| 54 | + |
| 55 | +class ExactReciprocalGate(Gate): |
| 56 | + r"""Implements an exact reciprocal function. |
| 57 | +
|
| 58 | + For a state :math:`|x\rangle` and a scaling factor :math:`s`, this gate implements the operation |
| 59 | +
|
| 60 | + .. math:: |
| 61 | +
|
| 62 | + |x\rangle |0\rangle \mapsto |
| 63 | + \cos\left(\arcsin\left(s\frac{2^n}{x}\right)\right)|x\rangle|0\rangle + |
| 64 | + \left(s\frac{2^n}{x}\right)|x\rangle|1\rangle. |
| 65 | +
|
| 66 | + States representing :math:`x = 0` or :math:`s 2^n / x \geq 1` are left unchanged, since |
| 67 | + this function would not be defined. |
| 68 | + """ |
| 69 | + |
| 70 | + def __init__( |
| 71 | + self, num_state_qubits: int, scaling: float, neg_vals: bool = False, label: str = "1/x" |
| 72 | + ) -> None: |
| 73 | + r""" |
| 74 | + Args: |
| 75 | + num_state_qubits: The number of qubits representing the value to invert. |
| 76 | + scaling: Scaling factor :math:`s` of the reciprocal function, i.e. to compute |
| 77 | + :math:`s / x`. |
| 78 | + neg_vals: Whether :math:`x` might represent negative values. In this case the first |
| 79 | + qubit is the sign, with :math:`|1\rangle` for negative and :math:`|0\rangle` for |
| 80 | + positive. For the negative case it is assumed that the remaining string represents |
| 81 | + :math:`1 - x`. This is because :math:`e^{-2 \pi i x} = e^{2 \pi i (1 - x)}` for |
| 82 | + :math:`x \in [0,1)`. |
| 83 | + label: The label of the object. |
| 84 | +
|
| 85 | + .. note:: |
| 86 | +
|
| 87 | + It is assumed that the binary string :math:`x` represents a number < 1. |
| 88 | + """ |
| 89 | + super().__init__("ExactReciprocal", num_state_qubits + 1, [], label=label) |
| 90 | + |
| 91 | + self.scaling = scaling |
| 92 | + self.neg_vals = neg_vals |
| 93 | + |
| 94 | + def _define(self): |
| 95 | + num_state_qubits = self.num_qubits - 1 |
| 96 | + qr_state = QuantumRegister(num_state_qubits, "state") |
| 97 | + qr_flag = QuantumRegister(1, "flag") |
| 98 | + circuit = QuantumCircuit(qr_state, qr_flag) |
50 | 99 |
|
51 | 100 | angles = [0.0]
|
52 |
| - nl = 2 ** (num_state_qubits - 1) if neg_vals else 2**num_state_qubits |
| 101 | + nl = 2 ** (num_state_qubits - 1) if self.neg_vals else 2**num_state_qubits |
53 | 102 |
|
54 | 103 | # Angles to rotate by scaling / x, where x = i / nl
|
55 | 104 | for i in range(1, nl):
|
56 |
| - if isclose(scaling * nl / i, 1, abs_tol=1e-5): |
| 105 | + if isclose(self.scaling * nl / i, 1, abs_tol=1e-5): |
57 | 106 | angles.append(np.pi)
|
58 |
| - elif scaling * nl / i < 1: |
59 |
| - angles.append(2 * np.arcsin(scaling * nl / i)) |
| 107 | + elif self.scaling * nl / i < 1: |
| 108 | + angles.append(2 * np.arcsin(self.scaling * nl / i)) |
60 | 109 | else:
|
61 | 110 | angles.append(0.0)
|
62 | 111 |
|
63 |
| - circuit.compose( |
64 |
| - UCRYGate(angles), [qr_flag[0]] + qr_state[: len(qr_state) - neg_vals], inplace=True |
65 |
| - ) |
| 112 | + circuit.append(UCRYGate(angles), [qr_flag[0]] + qr_state[: len(qr_state) - self.neg_vals]) |
66 | 113 |
|
67 |
| - if neg_vals: |
68 |
| - circuit.compose( |
| 114 | + if self.neg_vals: |
| 115 | + circuit.append( |
69 | 116 | UCRYGate([-theta for theta in angles]).control(),
|
70 | 117 | [qr_state[-1]] + [qr_flag[0]] + qr_state[:-1],
|
71 |
| - inplace=True, |
72 | 118 | )
|
73 | 119 | angles_neg = [0.0]
|
74 | 120 | for i in range(1, nl):
|
75 |
| - if isclose(scaling * (-1) / (1 - i / nl), -1, abs_tol=1e-5): |
| 121 | + if isclose(self.scaling * (-1) / (1 - i / nl), -1, abs_tol=1e-5): |
76 | 122 | angles_neg.append(-np.pi)
|
77 |
| - elif np.abs(scaling * (-1) / (1 - i / nl)) < 1: |
78 |
| - angles_neg.append(2 * np.arcsin(scaling * (-1) / (1 - i / nl))) |
| 123 | + elif np.abs(self.scaling * (-1) / (1 - i / nl)) < 1: |
| 124 | + angles_neg.append(2 * np.arcsin(self.scaling * (-1) / (1 - i / nl))) |
79 | 125 | else:
|
80 | 126 | angles_neg.append(0.0)
|
81 |
| - circuit.compose( |
82 |
| - UCRYGate(angles_neg).control(), |
83 |
| - [qr_state[-1]] + [qr_flag[0]] + qr_state[:-1], |
84 |
| - inplace=True, |
| 127 | + circuit.append( |
| 128 | + UCRYGate(angles_neg).control(), [qr_state[-1]] + [qr_flag[0]] + qr_state[:-1] |
85 | 129 | )
|
86 | 130 |
|
87 |
| - super().__init__(*circuit.qregs, name=name) |
88 |
| - self.compose(circuit.to_gate(), qubits=self.qubits, inplace=True) |
| 131 | + self.definition = circuit |
0 commit comments