classiq 0.99.0__py3-none-any.whl → 0.102.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- classiq/__init__.py +3 -0
- classiq/_internals/api_wrapper.py +29 -4
- classiq/applications/chemistry/op_utils.py +31 -1
- classiq/applications/chemistry/problems.py +18 -6
- classiq/applications/chemistry/ucc.py +2 -2
- classiq/evaluators/parameter_types.py +1 -4
- classiq/evaluators/qmod_node_evaluators/utils.py +6 -3
- classiq/execution/__init__.py +11 -1
- classiq/execution/jobs.py +122 -5
- classiq/interface/_version.py +1 -1
- classiq/interface/exceptions.py +0 -42
- classiq/interface/executor/execution_request.py +1 -0
- classiq/interface/executor/quantum_code.py +0 -6
- classiq/interface/executor/user_budget.py +2 -6
- classiq/interface/generator/generation_request.py +40 -0
- classiq/interface/generator/quantum_program.py +8 -36
- classiq/interface/generator/transpiler_basis_gates.py +1 -3
- classiq/interface/generator/types/compilation_metadata.py +1 -1
- classiq/interface/helpers/model_normalizer.py +24 -0
- classiq/interface/helpers/text_utils.py +20 -5
- classiq/interface/model/bind_operation.py +3 -0
- classiq/interface/model/invert.py +7 -0
- classiq/interface/model/model.py +42 -3
- classiq/interface/model/quantum_function_call.py +17 -5
- classiq/model_expansions/arithmetic_compute_result_attrs.py +10 -1
- classiq/model_expansions/interpreters/base_interpreter.py +3 -2
- classiq/model_expansions/quantum_operations/call_emitter.py +0 -3
- classiq/model_expansions/visitors/uncomputation_signature_inference.py +15 -38
- classiq/open_library/functions/__init__.py +55 -27
- classiq/open_library/functions/bit_operations.py +30 -0
- classiq/open_library/functions/encodings.py +182 -0
- classiq/open_library/functions/modular_arithmetics.py +597 -0
- classiq/open_library/functions/qft_space_arithmetics.py +81 -0
- classiq/open_library/functions/state_preparation.py +13 -7
- classiq/open_library/functions/utility_functions.py +22 -3
- classiq/qmod/builtins/functions/exponentiation.py +2 -2
- classiq/qmod/builtins/operations.py +29 -4
- classiq/qmod/native/pretty_printer.py +15 -4
- classiq/qmod/pretty_print/pretty_printer.py +14 -2
- classiq/qmod/qmod_variable.py +1 -1
- classiq/qmod/quantum_callable.py +8 -2
- classiq/qmod/quantum_expandable.py +3 -1
- classiq/qmod/quantum_function.py +2 -1
- classiq/qmod/semantics/error_manager.py +11 -1
- classiq/qmod/utilities.py +7 -4
- classiq/synthesis_action/__init__.py +20 -0
- classiq/synthesis_action/actions.py +106 -0
- {classiq-0.99.0.dist-info → classiq-0.102.0.dist-info}/METADATA +1 -1
- {classiq-0.99.0.dist-info → classiq-0.102.0.dist-info}/RECORD +51 -47
- classiq/interface/executor/register_initialization.py +0 -36
- classiq/open_library/functions/modular_exponentiation.py +0 -272
- classiq/open_library/functions/qsvt_temp.py +0 -536
- {classiq-0.99.0.dist-info → classiq-0.102.0.dist-info}/WHEEL +0 -0
- {classiq-0.99.0.dist-info → classiq-0.102.0.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
|
|
3
|
+
from classiq.interface.exceptions import ClassiqDeprecationWarning
|
|
4
|
+
|
|
5
|
+
from classiq.open_library.functions.bit_operations import (
|
|
6
|
+
bitwise_negate,
|
|
7
|
+
cyclic_shift_left,
|
|
8
|
+
)
|
|
9
|
+
from classiq.open_library.functions.qft_functions import qft
|
|
10
|
+
from classiq.open_library.functions.qft_space_arithmetics import modular_add_qft_space
|
|
11
|
+
from classiq.open_library.functions.utility_functions import multiswap
|
|
12
|
+
from classiq.qmod.builtins.constants import SIGNED
|
|
13
|
+
from classiq.qmod.builtins.functions.allocation import free
|
|
14
|
+
from classiq.qmod.builtins.functions.standard_gates import X
|
|
15
|
+
from classiq.qmod.builtins.operations import (
|
|
16
|
+
allocate,
|
|
17
|
+
bind,
|
|
18
|
+
control,
|
|
19
|
+
if_,
|
|
20
|
+
inplace_add,
|
|
21
|
+
inplace_xor,
|
|
22
|
+
invert,
|
|
23
|
+
repeat,
|
|
24
|
+
within_apply,
|
|
25
|
+
)
|
|
26
|
+
from classiq.qmod.cparam import CInt
|
|
27
|
+
from classiq.qmod.qfunc import qperm
|
|
28
|
+
from classiq.qmod.qmod_variable import Const, Output, QArray, QBit, QNum
|
|
29
|
+
from classiq.qmod.symbolic import mod_inverse
|
|
30
|
+
|
|
31
|
+
# Modular Adding and Subtraction
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@qperm
|
|
35
|
+
def modular_add_inplace(modulus: CInt, x: Const[QNum], y: QNum) -> None:
|
|
36
|
+
"""
|
|
37
|
+
[Qmod Classiq-library function]
|
|
38
|
+
|
|
39
|
+
Performs the transformation |x>|y> -> |x>|(x + y mod modulus)>.
|
|
40
|
+
Note:
|
|
41
|
+
|x> and |y> should have values smaller than `modulus`.
|
|
42
|
+
The modulus should satisfy 1 < modulus < 2**n, where n is the size of |x> and |y>.
|
|
43
|
+
|
|
44
|
+
Implementation based on: https://arxiv.org/pdf/1706.06752 Chapter 3.2 Fig 3
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
modulus: Classical number modulus (CInt)
|
|
48
|
+
x: 1st quantum number input (unsigned).
|
|
49
|
+
y: 2nd quantum number input (unsigned). Will hold the result after the operation.
|
|
50
|
+
"""
|
|
51
|
+
# Use a carry qubit to detect a negative result after subtracting modulus (underflow)
|
|
52
|
+
carry = QBit()
|
|
53
|
+
allocate(carry)
|
|
54
|
+
temp: QNum = QNum("temp", y.size + 1, SIGNED, 0)
|
|
55
|
+
within_apply(
|
|
56
|
+
lambda: bind([y, carry], temp),
|
|
57
|
+
lambda: (
|
|
58
|
+
inplace_add(x, temp),
|
|
59
|
+
inplace_add(-modulus, temp),
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
# If carry is set (negative result), add modulus back to y
|
|
63
|
+
control(carry, lambda: inplace_add(modulus, y))
|
|
64
|
+
# Update carry qubit based on comparison (y >= x after operation)
|
|
65
|
+
carry ^= y >= x
|
|
66
|
+
free(carry)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@qperm
|
|
70
|
+
def modular_negate_inplace(modulus: CInt, x: QNum) -> None:
|
|
71
|
+
"""
|
|
72
|
+
[Qmod Classiq-library function]
|
|
73
|
+
|
|
74
|
+
Performs the transformation |x> -> |(-x mod modulus)>.
|
|
75
|
+
Note:
|
|
76
|
+
|x> should have values smaller than `modulus`.
|
|
77
|
+
The modulus should satisfy 1 < modulus < 2**n, where n is the size of |x>.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
modulus: Classical number modulus
|
|
81
|
+
x: Quantum number input (unsigned). Will hold the result after the operation.
|
|
82
|
+
"""
|
|
83
|
+
n = x.size
|
|
84
|
+
neg_modulus = 2**n - modulus - 1
|
|
85
|
+
is_all_zeros = QBit()
|
|
86
|
+
allocate(is_all_zeros)
|
|
87
|
+
is_all_zeros ^= x == 0
|
|
88
|
+
control(is_all_zeros, lambda: inplace_add(modulus, x))
|
|
89
|
+
inplace_add(neg_modulus, x)
|
|
90
|
+
# If x=0, then we have neg_modulus + modulus = all ones
|
|
91
|
+
is_all_zeros ^= x == (2**n - 1)
|
|
92
|
+
bitwise_negate(x)
|
|
93
|
+
free(is_all_zeros)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@qperm
|
|
97
|
+
def modular_subtract_inplace(modulus: CInt, x: Const[QNum], y: QNum) -> None:
|
|
98
|
+
"""
|
|
99
|
+
[Qmod Classiq-library function]
|
|
100
|
+
|
|
101
|
+
Performs the transformation |x>|y> -> |x>|(x - y mod modulus)>.
|
|
102
|
+
Note:
|
|
103
|
+
|x> and |y> should have values smaller than `modulus`.
|
|
104
|
+
The modulus should satisfy 1 < modulus < 2**n, where n is the size of |x> and |y>.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
modulus: Classical number modulus
|
|
108
|
+
x: 1st quantum number input (unsigned). Const.
|
|
109
|
+
y: 2nd quantum number input (unsigned). In-place target, will hold the result after the operation.
|
|
110
|
+
"""
|
|
111
|
+
modular_negate_inplace(modulus, y)
|
|
112
|
+
modular_add_inplace(modulus, x, y)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@qperm
|
|
116
|
+
def modular_double_inplace(modulus: CInt, x: QNum) -> None:
|
|
117
|
+
"""
|
|
118
|
+
[Qmod Classiq-library function]
|
|
119
|
+
|
|
120
|
+
Performs the transformation |x> -> |(2x mod modulus)>.
|
|
121
|
+
Note:
|
|
122
|
+
|x> should have a value smaller than `modulus`.
|
|
123
|
+
The modulus must be a constant odd integer.
|
|
124
|
+
The modulus should satisfy 1 < modulus < 2**n, where n is the size of |x>.
|
|
125
|
+
|
|
126
|
+
Implementation based on: https://arxiv.org/pdf/1706.06752 Chapter 3.2 Fig 4
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
modulus: Classical number modulus
|
|
130
|
+
x: Quantum number input (unsigned). Will hold the result after the operation.
|
|
131
|
+
"""
|
|
132
|
+
carry = QBit()
|
|
133
|
+
allocate(carry)
|
|
134
|
+
res_and_carry: QNum = QNum("res_and_carry", x.size + 1, SIGNED, 0)
|
|
135
|
+
within_apply(
|
|
136
|
+
lambda: bind([x, carry], res_and_carry),
|
|
137
|
+
lambda: (
|
|
138
|
+
cyclic_shift_left(res_and_carry), # holds 2*x
|
|
139
|
+
inplace_add(-modulus, res_and_carry),
|
|
140
|
+
),
|
|
141
|
+
)
|
|
142
|
+
control(carry, lambda: inplace_add(modulus, x))
|
|
143
|
+
# Post-fix carry
|
|
144
|
+
carry ^= (x % 2) == 0
|
|
145
|
+
free(carry)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@qperm
|
|
149
|
+
def modular_add_constant_inplace(modulus: CInt, a: CInt, x: QNum) -> None:
|
|
150
|
+
"""
|
|
151
|
+
[Qmod Classiq-library function]
|
|
152
|
+
|
|
153
|
+
Performs the transformation |x> -> |(x + a mod modulus)>.
|
|
154
|
+
Note:
|
|
155
|
+
|x> and `a` should have values smaller than `modulus`.
|
|
156
|
+
The modulus should satisfy 1 < modulus < 2**n, where n is the size of |x>.
|
|
157
|
+
|
|
158
|
+
Implementation is based on the logic in: https://arxiv.org/pdf/1706.06752 Chapter 3.2 Fig 3
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
modulus: Classical number modulus
|
|
162
|
+
a: constant unsigned number input for the addition.
|
|
163
|
+
x: Quantum number input (unsigned). Will hold the result after the operation.
|
|
164
|
+
"""
|
|
165
|
+
carry = QBit()
|
|
166
|
+
allocate(carry)
|
|
167
|
+
temp: QNum = QNum("temp", x.size + 1, SIGNED, 0)
|
|
168
|
+
within_apply(
|
|
169
|
+
lambda: bind([x, carry], temp),
|
|
170
|
+
lambda: (
|
|
171
|
+
inplace_add(a, temp),
|
|
172
|
+
inplace_add(-modulus, temp),
|
|
173
|
+
),
|
|
174
|
+
)
|
|
175
|
+
# If carry is set, we need to add modulus back
|
|
176
|
+
control(carry, lambda: inplace_add(modulus, x))
|
|
177
|
+
carry ^= x >= a
|
|
178
|
+
free(carry)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
# Modular Multiplication
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@qperm
|
|
185
|
+
def modular_multiply(
|
|
186
|
+
modulus: CInt,
|
|
187
|
+
x: Const[QArray[QBit]],
|
|
188
|
+
y: Const[QArray[QBit]],
|
|
189
|
+
z: QArray[QBit],
|
|
190
|
+
) -> None:
|
|
191
|
+
"""
|
|
192
|
+
[Qmod Classiq-library function]
|
|
193
|
+
Performs the transformation |x>|y>|0> -> |x>|y>|(x*y mod modulus)>
|
|
194
|
+
Note:
|
|
195
|
+
|x>, |y> should have the same size and have values smaller than `modulus`.
|
|
196
|
+
The modulus must be a constant odd integer.
|
|
197
|
+
The modulus should satisfy 1 < modulus < 2**n, where n is the size of |x> and |y>.
|
|
198
|
+
The output register z must be pre-allocated with the same size as x and y.
|
|
199
|
+
Implementation is based on the logic in: https://arxiv.org/pdf/1706.06752 Chapter 3.2 Fig 5
|
|
200
|
+
Args:
|
|
201
|
+
modulus: Classical number modulus
|
|
202
|
+
x: Quantum number input (unsigned), multiplicand.
|
|
203
|
+
y: Quantum number input (unsigned), multiplier.
|
|
204
|
+
z: Quantum number (unsigned), pre-allocated output variable that will hold the result.
|
|
205
|
+
"""
|
|
206
|
+
n = x.len
|
|
207
|
+
repeat(
|
|
208
|
+
n,
|
|
209
|
+
lambda idx: [
|
|
210
|
+
control(x[n - idx - 1], lambda: modular_add_inplace(modulus, y, z)),
|
|
211
|
+
if_(idx != (n - 1), lambda: modular_double_inplace(modulus, z)),
|
|
212
|
+
],
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@qperm
|
|
217
|
+
def modular_square(modulus: CInt, x: Const[QArray[QBit]], z: QArray[QBit]) -> None:
|
|
218
|
+
"""
|
|
219
|
+
[Qmod Classiq-library function]
|
|
220
|
+
Performs the transformation |x>|0> -> |x>|(x^2 mod modulus)>.
|
|
221
|
+
Note:
|
|
222
|
+
|x> should have the same size and have values smaller than `modulus`.
|
|
223
|
+
The modulus must be a constant odd integer.
|
|
224
|
+
The modulus should satisfy 1 < modulus < 2**n, where n is the size of |x>.
|
|
225
|
+
The output register z must be pre-allocated with the same size as x.
|
|
226
|
+
Implementation is based on: https://arxiv.org/pdf/1706.06752 Chapter 3.2 Fig 6
|
|
227
|
+
Args:
|
|
228
|
+
modulus: Classical number modulus
|
|
229
|
+
x: Quantum number input (unsigned), the input to square.
|
|
230
|
+
z: Quantum number (unsigned), pre-allocated output variable to hold the result.
|
|
231
|
+
"""
|
|
232
|
+
n = x.len
|
|
233
|
+
repeat(
|
|
234
|
+
n - 1,
|
|
235
|
+
lambda i: [
|
|
236
|
+
control(x[n - i - 1], lambda: modular_add_inplace(modulus, x, z)),
|
|
237
|
+
modular_double_inplace(modulus, z),
|
|
238
|
+
],
|
|
239
|
+
)
|
|
240
|
+
control(x[0], lambda: modular_add_inplace(modulus, x, z))
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
@qperm(disable_perm_check=True)
|
|
244
|
+
def modular_multiply_constant(modulus: CInt, x: Const[QNum], a: CInt, y: QNum) -> None:
|
|
245
|
+
"""
|
|
246
|
+
[Qmod Classiq-library function]
|
|
247
|
+
Performs the transformation |x>|y> -> |x>|(x * a mod modulus)>.
|
|
248
|
+
Note:
|
|
249
|
+
|x> and |y> should have values smaller than `modulus`.
|
|
250
|
+
The modulus must be a constant odd integer.
|
|
251
|
+
The modulus should satisfy 1 < modulus < 2**n, where n is the size of |x> and |y>.
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
modulus: Classical number modulus
|
|
255
|
+
x: Quantum number (unsigned), input variable.
|
|
256
|
+
a: Classical number constant
|
|
257
|
+
y: Quantum number (unsigned), output variable that will hold the result.
|
|
258
|
+
"""
|
|
259
|
+
x_arr: QArray[QBit] = QArray()
|
|
260
|
+
within_apply(
|
|
261
|
+
lambda: [bind(x, x_arr), qft(y)],
|
|
262
|
+
lambda: repeat(
|
|
263
|
+
count=x_arr.len,
|
|
264
|
+
iteration=lambda index: control(
|
|
265
|
+
x_arr[index],
|
|
266
|
+
lambda: modular_add_qft_space(modulus, (a * (2**index)) % modulus, y),
|
|
267
|
+
),
|
|
268
|
+
),
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
@qperm
|
|
273
|
+
def modular_multiply_constant_inplace(modulus: CInt, a: CInt, x: QNum) -> None:
|
|
274
|
+
"""
|
|
275
|
+
[Qmod Classiq-library function]
|
|
276
|
+
In-place modular multiplication of x by a classical constant modulo a symbolic modulus.
|
|
277
|
+
Performs |x> -> |(x * a mod modulus)>.
|
|
278
|
+
Note:
|
|
279
|
+
|x> should have values smaller than `modulus`.
|
|
280
|
+
The modulus should satisfy 1 < modulus < 2**n, where n is the size of |x>.
|
|
281
|
+
The constant `a` should have an inverse modulo `modulus`, i.e. gcd(a, modulus) = 1.
|
|
282
|
+
The constant `a` should satisfy 0 <= a < modulus.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
modulus: Classical number modulus
|
|
286
|
+
a: Classical number constant
|
|
287
|
+
x: Quantum number (unsigned), in-place input/output.
|
|
288
|
+
"""
|
|
289
|
+
y: QNum = QNum("y", x.size + 1)
|
|
290
|
+
allocate(y)
|
|
291
|
+
modular_multiply_constant(modulus, x, a, y)
|
|
292
|
+
multiswap(x, y)
|
|
293
|
+
invert(lambda: modular_multiply_constant(modulus, x, mod_inverse(a, modulus), y))
|
|
294
|
+
free(y)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
@qperm
|
|
298
|
+
def inplace_modular_multiply(n: CInt, a: CInt, x: QArray[QBit]) -> None:
|
|
299
|
+
"""
|
|
300
|
+
[Qmod Classiq-library function]
|
|
301
|
+
|
|
302
|
+
Performs multiplication of a quantum number `x` by a classical number `a` modulo classical number `n`
|
|
303
|
+
(Applies $x=xa \\mod n$).
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
n: The modulo number. Should be non-negative.
|
|
307
|
+
a: The classical factor. Should be non-negative.
|
|
308
|
+
x: The quantum factor.
|
|
309
|
+
|
|
310
|
+
Comment: It is assumed that `a` has an inverse modulo `n`
|
|
311
|
+
"""
|
|
312
|
+
warnings.warn(
|
|
313
|
+
"Function 'inplace_modular_multiply' is deprecated. Use 'modular_multiply_constant_inplace' instead.",
|
|
314
|
+
ClassiqDeprecationWarning,
|
|
315
|
+
stacklevel=1,
|
|
316
|
+
)
|
|
317
|
+
modular_multiply_constant_inplace(n, a, x)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
@qperm
|
|
321
|
+
def modular_exp(n: CInt, a: CInt, x: QArray[QBit], power: QArray[QBit]) -> None:
|
|
322
|
+
"""
|
|
323
|
+
[Qmod Classiq-library function]
|
|
324
|
+
Raises a classical integer `a` to the power of a quantum number `power` modulo classical integer `n`
|
|
325
|
+
times a quantum number `x`. Performs $x=(a^{power} \\mod n)*x$ in-place.
|
|
326
|
+
(and specifically if at the input $x=1$, at the output $x=a^{power} \\mod n$).
|
|
327
|
+
Args:
|
|
328
|
+
n: The modulus number. Should be non-negative.
|
|
329
|
+
a: The base of the exponentiation. Should be non-negative.
|
|
330
|
+
x: A quantum number that multiplies the modular exponentiation and holds the output. It should be at least the size of $\\lceil \\log(n) \rceil$.
|
|
331
|
+
power: The power of the exponentiation.
|
|
332
|
+
"""
|
|
333
|
+
warnings.warn(
|
|
334
|
+
"Function 'modular_exp' is deprecated. Use 'modular_exponentiate_inplace' instead.",
|
|
335
|
+
ClassiqDeprecationWarning,
|
|
336
|
+
stacklevel=1,
|
|
337
|
+
)
|
|
338
|
+
repeat(
|
|
339
|
+
count=power.len,
|
|
340
|
+
iteration=lambda index: control(
|
|
341
|
+
power[index],
|
|
342
|
+
lambda: modular_multiply_constant_inplace(n, (a ** (2**index)) % n, x),
|
|
343
|
+
),
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
@qperm
|
|
348
|
+
def modular_exponentiate(modulus: CInt, a: CInt, x: QNum, p: QNum) -> None:
|
|
349
|
+
"""
|
|
350
|
+
[Qmod Classiq-library function]
|
|
351
|
+
|
|
352
|
+
Raises a classical integer `a` to the power of a quantum number `p` modulo classical integer `modulus`
|
|
353
|
+
times a quantum number `x`. Performs $x=(a^{p} \\mod modulus)*x$ in-place.
|
|
354
|
+
(and specifically if at the input $x=1$, at the output $x=a^{p} \\mod modulus$).
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
modulus: The modulus number. Should be non-negative.
|
|
358
|
+
a: The base of the exponentiation. Should be non-negative.
|
|
359
|
+
x: A quantum number that multiplies the modular exponentiation and holds the output. It should be at least the size of $\\lceil \\log(modulus) \rceil$.
|
|
360
|
+
p: The power of the exponentiation.
|
|
361
|
+
"""
|
|
362
|
+
p_arr: QArray[QBit] = QArray(length=p.size)
|
|
363
|
+
within_apply(
|
|
364
|
+
lambda: bind(p, p_arr),
|
|
365
|
+
lambda: repeat(
|
|
366
|
+
count=p_arr.len,
|
|
367
|
+
iteration=lambda index: control(
|
|
368
|
+
p_arr[index],
|
|
369
|
+
lambda: modular_multiply_constant_inplace(
|
|
370
|
+
modulus, (a ** (2**index)) % modulus, x
|
|
371
|
+
),
|
|
372
|
+
),
|
|
373
|
+
),
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
# Helper Functions
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def get_bit(number: int, index: int) -> int:
|
|
381
|
+
"""
|
|
382
|
+
Returns the value (0 or 1) of the bit at the specified index in a non-negative integer number.
|
|
383
|
+
Index 0 is the least significant bit (LSB).
|
|
384
|
+
"""
|
|
385
|
+
return (number >> index) & 1
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
# Montgomery representation
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
@qperm
|
|
392
|
+
def modular_to_montgomery_inplace(modulus: CInt, x: QNum) -> None:
|
|
393
|
+
"""
|
|
394
|
+
[Qmod Classiq-library function]
|
|
395
|
+
Converts a quantum integer |x> into its Montgomery representation modulo modulus in place.
|
|
396
|
+
The Montgomery factor is R = 2**n, where n = x.size (the number of qubits in |x>).
|
|
397
|
+
This function performs the transformation |x> -> |(x * R mod modulus)>.
|
|
398
|
+
Note:
|
|
399
|
+
|x> should have values smaller than `modulus`.
|
|
400
|
+
The modulus should satisfy 1 < modulus < 2**n, where n is the size of |x>.
|
|
401
|
+
The modulus must be odd so that R = 2**n is invertible modulo modulus (gcd(R, modulus) = 1).
|
|
402
|
+
|
|
403
|
+
Args:
|
|
404
|
+
modulus: Classical number modulus
|
|
405
|
+
x: Quantum number, in-place operand to convert to Montgomery form.
|
|
406
|
+
"""
|
|
407
|
+
n = x.size
|
|
408
|
+
mont_factor = 2**n % modulus
|
|
409
|
+
modular_multiply_constant_inplace(modulus, mont_factor, x)
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
@qperm
|
|
413
|
+
def modular_montgomery_to_standard_inplace(modulus: CInt, x: QNum) -> None:
|
|
414
|
+
"""
|
|
415
|
+
[Qmod Classiq-library function]
|
|
416
|
+
Converts quantum integer |x> from Montgomery representation to standard form in place modulo modulus.
|
|
417
|
+
The Montgomery factor is R = 2**n, where n = x.size (the number of qubits in |x>).
|
|
418
|
+
This function performs the transformation |x> -> |(x * R^-1 mod modulus)>.
|
|
419
|
+
Note:
|
|
420
|
+
|x> should have values smaller than `modulus`.
|
|
421
|
+
The modulus should satisfy 1 < modulus < 2**n, where n is the size of |x>.
|
|
422
|
+
The modulus must be odd so that R = 2**n is invertible modulo modulus (gcd(R, modulus) = 1).
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
modulus: Classical number modulus
|
|
426
|
+
x: Quantum number, in-place operand to convert from Montgomery form.
|
|
427
|
+
"""
|
|
428
|
+
n = x.size
|
|
429
|
+
modular_multiply_constant_inplace(modulus, mod_inverse(2**n % modulus, modulus), x)
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
# Modular Inverse
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
@qperm
|
|
436
|
+
def modular_inverse_inplace(modulus: CInt, v: QNum, m: Output[QArray[QBit]]) -> None:
|
|
437
|
+
"""
|
|
438
|
+
[Qmod Classiq-library function]
|
|
439
|
+
Computes the modular inverse of a quantum number |v> modulo modulus in place, using the Kaliski algorithm.
|
|
440
|
+
Performs the transformation |v> -> |(v^-1 mod modulus)>.
|
|
441
|
+
|
|
442
|
+
Based on: https://arxiv.org/pdf/2302.06639 Chapter 5
|
|
443
|
+
|
|
444
|
+
Note:
|
|
445
|
+
|v> should have values smaller than `modulus`.
|
|
446
|
+
If |v> = 0, the output will be 0 (although 0 does not have an inverse modulo `modulus`).
|
|
447
|
+
The modulus should be prime OR at least gcd(v, modulus) = 1.
|
|
448
|
+
The modulus must be a constant odd integer.
|
|
449
|
+
The modulus should satisfy 1 < modulus < 2**n, where n is the size of |v>.
|
|
450
|
+
The ancilla qubits m are provided as Output, will be allocated to length 2*n.
|
|
451
|
+
|
|
452
|
+
Args:
|
|
453
|
+
modulus: Classical number modulus
|
|
454
|
+
v: Quantum number, in-place operand to compute the modular inverse.
|
|
455
|
+
m: Output quantum array (QArray[QBit]) allocated to length 2*n (n = v.size) and used as ancilla during the algorithm.
|
|
456
|
+
"""
|
|
457
|
+
n = v.size
|
|
458
|
+
allocate(2 * n, m)
|
|
459
|
+
# Convert v to Montgomery form
|
|
460
|
+
modular_to_montgomery_inplace(modulus, v)
|
|
461
|
+
u: QNum = QNum("u", n)
|
|
462
|
+
r: QNum = QNum("r", n + 1)
|
|
463
|
+
s: QNum = QNum("s", n + 1)
|
|
464
|
+
a: QBit = QBit()
|
|
465
|
+
b: QBit = QBit()
|
|
466
|
+
f: QBit = QBit()
|
|
467
|
+
allocate(u)
|
|
468
|
+
allocate(r)
|
|
469
|
+
allocate(s)
|
|
470
|
+
allocate(a)
|
|
471
|
+
allocate(b)
|
|
472
|
+
allocate(f)
|
|
473
|
+
# Initialize
|
|
474
|
+
u ^= modulus
|
|
475
|
+
r ^= 0
|
|
476
|
+
s ^= 1
|
|
477
|
+
f ^= 1
|
|
478
|
+
# Main loop (2*n iterations)
|
|
479
|
+
repeat(2 * n, lambda i: kaliski_iteration(modulus, i, v, m, u, r, s, a, b, f))
|
|
480
|
+
# Finalization steps
|
|
481
|
+
modular_rsub_inplace(2 * modulus, modulus, r)
|
|
482
|
+
multiswap(v, r)
|
|
483
|
+
m_num: QNum = QNum("m_num", 2 * n)
|
|
484
|
+
bind(m, m_num)
|
|
485
|
+
control(
|
|
486
|
+
m_num == 1,
|
|
487
|
+
stmt_block=lambda: [inplace_xor(1, s), inplace_xor(modulus, u)], # type: ignore[arg-type]
|
|
488
|
+
else_block=lambda: [inplace_xor(1, u), inplace_xor(modulus, s)], # type: ignore[arg-type]
|
|
489
|
+
)
|
|
490
|
+
bind(m_num, m)
|
|
491
|
+
modular_montgomery_to_standard_inplace(modulus, v)
|
|
492
|
+
# Free variables
|
|
493
|
+
free(u)
|
|
494
|
+
free(r)
|
|
495
|
+
free(s)
|
|
496
|
+
free(a)
|
|
497
|
+
free(b)
|
|
498
|
+
free(f)
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
@qperm
|
|
502
|
+
def kaliski_iteration(
|
|
503
|
+
modulus: CInt,
|
|
504
|
+
i: CInt,
|
|
505
|
+
v: QNum,
|
|
506
|
+
m: QArray[QBit],
|
|
507
|
+
u: QNum,
|
|
508
|
+
r: QNum,
|
|
509
|
+
s: QNum,
|
|
510
|
+
a: QBit,
|
|
511
|
+
b: QBit,
|
|
512
|
+
f: QBit,
|
|
513
|
+
) -> None:
|
|
514
|
+
"""
|
|
515
|
+
Single iteration of the Kaliski modular inverse algorithm main loop.
|
|
516
|
+
Based on: https://arxiv.org/pdf/2302.06639 Figure 15
|
|
517
|
+
|
|
518
|
+
Note:
|
|
519
|
+
Assumes the global inversion constraints (odd modulus, 1 < modulus < 2**n).
|
|
520
|
+
Called with 0 <= v < modulus; per-iteration ancilla bit is m[i].
|
|
521
|
+
|
|
522
|
+
Args:
|
|
523
|
+
modulus: Classical number modulus (CInt)
|
|
524
|
+
i: Loop iteration index.
|
|
525
|
+
v: The QNum to invert (quantum number, will be mutated).
|
|
526
|
+
m: Quantum array of ancilla qubits (QArray[QBit]).
|
|
527
|
+
u: QNum (quantum number, auxiliary for algorithm).
|
|
528
|
+
r: QNum (quantum number, auxiliary).
|
|
529
|
+
s: QNum (quantum number, auxiliary).
|
|
530
|
+
a: QBit (ancilla qubit)
|
|
531
|
+
b: QBit (ancilla qubit)
|
|
532
|
+
f: QBit (ancilla qubit)
|
|
533
|
+
"""
|
|
534
|
+
# Step 1: Update f, m[i]
|
|
535
|
+
control((v == 0) & f, lambda: X(m[i]))
|
|
536
|
+
f ^= m[i]
|
|
537
|
+
# Step 2: Update a, b, m[i]
|
|
538
|
+
control(f & ((u % 2) == 0), lambda: X(a))
|
|
539
|
+
control(f & ~a & ((v % 2) == 0), lambda: X(m[i]))
|
|
540
|
+
b ^= a
|
|
541
|
+
b ^= m[i]
|
|
542
|
+
# Step 3: Update a, m[i]
|
|
543
|
+
control(
|
|
544
|
+
(u > v) & f & ~b,
|
|
545
|
+
lambda: (
|
|
546
|
+
X(a),
|
|
547
|
+
X(m[i]),
|
|
548
|
+
),
|
|
549
|
+
)
|
|
550
|
+
# Step 4: Update u, v, r, s
|
|
551
|
+
control(
|
|
552
|
+
a,
|
|
553
|
+
lambda: (
|
|
554
|
+
multiswap(u, v),
|
|
555
|
+
multiswap(r, s),
|
|
556
|
+
),
|
|
557
|
+
)
|
|
558
|
+
# Step 5: Update u, v, r, s
|
|
559
|
+
control(f & ~b, lambda: (inplace_add(-u, v), inplace_add(r, s)))
|
|
560
|
+
# Step 6: Update a, b, v, r
|
|
561
|
+
b ^= m[i]
|
|
562
|
+
b ^= a
|
|
563
|
+
control(f, lambda: invert(lambda: cyclic_shift_left(v)))
|
|
564
|
+
modular_double_inplace(2 * modulus, r)
|
|
565
|
+
larger_than_modulus = QBit()
|
|
566
|
+
allocate(larger_than_modulus)
|
|
567
|
+
larger_than_modulus ^= r > modulus
|
|
568
|
+
control(larger_than_modulus, lambda: inplace_add(-modulus, r))
|
|
569
|
+
control(((r % 2) == 1), lambda: X(larger_than_modulus))
|
|
570
|
+
free(larger_than_modulus)
|
|
571
|
+
control(
|
|
572
|
+
a,
|
|
573
|
+
lambda: (
|
|
574
|
+
multiswap(u, v),
|
|
575
|
+
multiswap(r, s),
|
|
576
|
+
),
|
|
577
|
+
)
|
|
578
|
+
control(((s % 2) == 0), lambda: X(a))
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
@qperm
|
|
582
|
+
def modular_rsub_inplace(modulus: CInt, a: CInt, x: QNum) -> None:
|
|
583
|
+
"""
|
|
584
|
+
[Qmod Classiq-library function]
|
|
585
|
+
Performs the in-place modular right-subtraction |x> -> |(a - x mod modulus)>.
|
|
586
|
+
Note:
|
|
587
|
+
|x> should have values smaller than `modulus`.
|
|
588
|
+
The modulus should satisfy 1 < modulus < 2**n, where n is the size of |x>.
|
|
589
|
+
The classical constant `a` should be in the range 0 <= a < modulus.
|
|
590
|
+
|
|
591
|
+
Args:
|
|
592
|
+
modulus: Classical number modulus
|
|
593
|
+
a: Classical constant to subtract from
|
|
594
|
+
x: Quantum number, in-place operand to perform the modular right-subtraction.
|
|
595
|
+
"""
|
|
596
|
+
modular_negate_inplace(modulus, x)
|
|
597
|
+
modular_add_constant_inplace(modulus, a, x)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from classiq.open_library.functions.qft_functions import qft_no_swap
|
|
2
|
+
from classiq.qmod.builtins.classical_functions import qft_const_adder_phase
|
|
3
|
+
from classiq.qmod.builtins.functions.allocation import free
|
|
4
|
+
from classiq.qmod.builtins.functions.standard_gates import PHASE, X
|
|
5
|
+
from classiq.qmod.builtins.operations import (
|
|
6
|
+
allocate,
|
|
7
|
+
control,
|
|
8
|
+
invert,
|
|
9
|
+
repeat,
|
|
10
|
+
skip_control,
|
|
11
|
+
within_apply,
|
|
12
|
+
)
|
|
13
|
+
from classiq.qmod.cparam import CInt
|
|
14
|
+
from classiq.qmod.qfunc import qfunc, qperm
|
|
15
|
+
from classiq.qmod.qmod_variable import QArray, QBit
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@qfunc
|
|
19
|
+
def _check_msb(ref: CInt, x: QArray[QBit], aux: QBit) -> None:
|
|
20
|
+
within_apply(
|
|
21
|
+
lambda: invert(lambda: qft_no_swap(x)),
|
|
22
|
+
lambda: control(x[0] == ref, lambda: X(aux)),
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@qfunc
|
|
27
|
+
def qft_space_add_const(value: CInt, phi_b: QArray[QBit]) -> None:
|
|
28
|
+
"""
|
|
29
|
+
[Qmod Classiq-library function]
|
|
30
|
+
|
|
31
|
+
Adds a constant to a quantum number (in the Fourier space) using the Quantum Fourier Transform (QFT) Adder algorithm.
|
|
32
|
+
Assuming that the input `phi_b` has `n` qubits, the result will be $\\phi_b+=value \\mod 2^n$.
|
|
33
|
+
|
|
34
|
+
To perform the full algorithm, use:
|
|
35
|
+
within_apply(lambda: QFT(phi_b), qft_space_add_const(value, phi_b))
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
value: The constant to add to the quantum number.
|
|
39
|
+
phi_b: The quantum number (at the aft space) to which the constant is added.
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
repeat(
|
|
43
|
+
count=phi_b.len,
|
|
44
|
+
iteration=lambda index: PHASE(
|
|
45
|
+
theta=qft_const_adder_phase(
|
|
46
|
+
index, value, phi_b.len # type:ignore[arg-type]
|
|
47
|
+
),
|
|
48
|
+
target=phi_b[index],
|
|
49
|
+
),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@qperm(disable_perm_check=True)
|
|
54
|
+
def modular_add_qft_space(n: CInt, a: CInt, phi_b: QArray[QBit]) -> None:
|
|
55
|
+
"""
|
|
56
|
+
[Qmod Classiq-library function]
|
|
57
|
+
|
|
58
|
+
Adds a constant `a` to a quantum number `phi_b` modulo the constant `n`.
|
|
59
|
+
The quantum number `phi_b` is assumed to be in the QFT space.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
n: The modulo number.
|
|
63
|
+
a: The constant to add to the quantum number.
|
|
64
|
+
phi_b: The quantum number to which the constant is added.
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
aux = QBit()
|
|
68
|
+
|
|
69
|
+
allocate(aux)
|
|
70
|
+
qft_space_add_const(a, phi_b),
|
|
71
|
+
skip_control(
|
|
72
|
+
lambda: (
|
|
73
|
+
invert(lambda: qft_space_add_const(n, phi_b)),
|
|
74
|
+
_check_msb(1, phi_b, aux),
|
|
75
|
+
control(aux, lambda: qft_space_add_const(n, phi_b)),
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
invert(lambda: qft_space_add_const(a, phi_b))
|
|
79
|
+
skip_control(lambda: _check_msb(0, phi_b, aux))
|
|
80
|
+
qft_space_add_const(a, phi_b)
|
|
81
|
+
free(aux)
|