classiq 0.91.1__py3-none-any.whl → 0.93.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 +13 -0
- classiq/_internals/config.py +1 -1
- classiq/analyzer/show_interactive_hack.py +1 -1
- classiq/applications/__init__.py +2 -6
- classiq/applications/qsp/__init__.py +7 -0
- classiq/applications/qsp/qsp.py +366 -0
- classiq/evaluators/parameter_types.py +13 -7
- classiq/execution/jobs.py +18 -8
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +8 -8
- classiq/interface/exceptions.py +4 -0
- classiq/interface/executor/result.py +4 -0
- classiq/interface/generator/functions/builtins/internal_operators.py +1 -0
- classiq/interface/generator/generated_circuit_data.py +5 -17
- classiq/interface/generator/transpiler_basis_gates.py +1 -1
- classiq/interface/helpers/versioned_model.py +0 -2
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/bind_operation.py +0 -12
- classiq/interface/model/handle_binding.py +3 -0
- classiq/interface/model/skip_control.py +11 -0
- classiq/interface/model/statement_block.py +3 -0
- classiq/interface/server/routes.py +0 -3
- classiq/model_expansions/interpreters/generative_interpreter.py +18 -1
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +6 -0
- classiq/model_expansions/quantum_operations/bind.py +14 -0
- classiq/model_expansions/quantum_operations/skip_control_verifier.py +20 -0
- classiq/model_expansions/quantum_operations/variable_decleration.py +59 -27
- classiq/open_library/functions/__init__.py +2 -0
- classiq/open_library/functions/discrete_sine_cosine_transform.py +15 -10
- classiq/open_library/functions/qsvt.py +60 -6
- classiq/qmod/builtins/operations.py +24 -0
- classiq/qmod/classical_variable.py +4 -2
- classiq/qmod/native/pretty_printer.py +8 -0
- classiq/qmod/pretty_print/pretty_printer.py +5 -0
- classiq/qmod/quantum_expandable.py +31 -15
- classiq/qmod/symbolic_expr.py +12 -4
- classiq/synthesis.py +1 -1
- {classiq-0.91.1.dist-info → classiq-0.93.0.dist-info}/METADATA +39 -34
- {classiq-0.91.1.dist-info → classiq-0.93.0.dist-info}/RECORD +41 -37
- classiq-0.93.0.dist-info/WHEEL +4 -0
- classiq-0.93.0.dist-info/licenses/LICENSE.txt +27 -0
- classiq/interface/ide/ide_data.py +0 -102
- classiq-0.91.1.dist-info/WHEEL +0 -4
classiq/__init__.py
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
"""Classiq SDK."""
|
|
2
2
|
|
|
3
|
+
import sys
|
|
4
|
+
import warnings
|
|
5
|
+
|
|
6
|
+
from classiq.interface.exceptions import ClassiqDeprecationWarning
|
|
7
|
+
|
|
8
|
+
if sys.version_info[0:2] <= (3, 9):
|
|
9
|
+
warnings.warn(
|
|
10
|
+
"Python version 3.9 will no longer be supported starting on 2025-10-01 "
|
|
11
|
+
"at the earliest",
|
|
12
|
+
ClassiqDeprecationWarning,
|
|
13
|
+
stacklevel=2,
|
|
14
|
+
)
|
|
15
|
+
|
|
3
16
|
from classiq.interface._version import VERSION as _VERSION
|
|
4
17
|
from classiq.interface.generator.application_apis import * # noqa: F403
|
|
5
18
|
from classiq.interface.generator.arith.register_user_input import (
|
classiq/_internals/config.py
CHANGED
|
@@ -11,7 +11,7 @@ from pydantic import BaseModel
|
|
|
11
11
|
from classiq.interface.enum_utils import StrEnum
|
|
12
12
|
from classiq.interface.server.routes import DEFAULT_IDE_FE_APP
|
|
13
13
|
|
|
14
|
-
DEFAULT_HOST = "https://
|
|
14
|
+
DEFAULT_HOST = "https://platform.classiq.io"
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class SDKMode(StrEnum):
|
classiq/applications/__init__.py
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
from classiq.applications import chemistry, combinatorial_optimization, qsvm
|
|
1
|
+
from classiq.applications import chemistry, combinatorial_optimization, qsp, qsvm
|
|
2
2
|
|
|
3
|
-
__all__ = [
|
|
4
|
-
"chemistry",
|
|
5
|
-
"combinatorial_optimization",
|
|
6
|
-
"qsvm",
|
|
7
|
-
]
|
|
3
|
+
__all__ = ["chemistry", "combinatorial_optimization", "qsp", "qsvm"]
|
|
8
4
|
|
|
9
5
|
|
|
10
6
|
_NON_IMPORTED_PUBLIC_SUBMODULES = ["qnn"]
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import builtins
|
|
2
|
+
import importlib.util
|
|
3
|
+
from functools import wraps
|
|
4
|
+
from typing import Callable, Optional
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
from matplotlib import pyplot as plt
|
|
8
|
+
from numpy.polynomial import Chebyshev, Polynomial
|
|
9
|
+
from numpy.polynomial.chebyshev import cheb2poly, poly2cheb
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _require_qsp() -> None:
|
|
13
|
+
missing = [m for m in ("cvxpy", "pyqsp") if importlib.util.find_spec(m) is None]
|
|
14
|
+
if missing:
|
|
15
|
+
raise RuntimeError(
|
|
16
|
+
"This feature needs the 'qsp' extra."
|
|
17
|
+
"Install with: pip install classiq[qsp]"
|
|
18
|
+
f"(missing: {', '.join(missing)})"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def silence(func: Callable) -> Callable:
|
|
23
|
+
@wraps(func)
|
|
24
|
+
def wrapper(*args, **kwargs): # type:ignore[no-untyped-def]
|
|
25
|
+
try:
|
|
26
|
+
original_print = print
|
|
27
|
+
builtins.print = lambda *a, **k: None
|
|
28
|
+
|
|
29
|
+
result = func(*args, **kwargs)
|
|
30
|
+
finally:
|
|
31
|
+
builtins.print = original_print
|
|
32
|
+
return result
|
|
33
|
+
|
|
34
|
+
return wrapper
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@silence
|
|
38
|
+
def _pyqsp_get_phases(
|
|
39
|
+
poly_coeffs: np.ndarray, tol: float = 1e-12
|
|
40
|
+
) -> tuple[np.ndarray, float]:
|
|
41
|
+
from pyqsp.sym_qsp_opt import newton_solver # type:ignore[import]
|
|
42
|
+
|
|
43
|
+
parity = (len(poly_coeffs) + 1) % 2
|
|
44
|
+
reduced_coefs = poly_coeffs[parity::2]
|
|
45
|
+
_phases, err, _tot_iter, opt = newton_solver(reduced_coefs, parity, crit=tol)
|
|
46
|
+
return opt.full_phases, err
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def qsvt_phases(
|
|
50
|
+
poly_coeffs: np.ndarray, cheb_basis: bool = True, tol: float = 1e-12
|
|
51
|
+
) -> np.ndarray:
|
|
52
|
+
r"""
|
|
53
|
+
Get QSVT phases that will generate the given Chebyshev polynomial.
|
|
54
|
+
The phases are ready to be used in `qsvt` and `qsvt_lcu` functions in the classiq library. The convetion
|
|
55
|
+
is the reflection signal operator, and the measurement basis is the hadamard basis (see https://arxiv.org/abs/2105.02859
|
|
56
|
+
APPENDIX A.).
|
|
57
|
+
The current implementation is using the pyqsp package, based on techniques in https://arxiv.org/abs/2003.02831.
|
|
58
|
+
|
|
59
|
+
Notes:
|
|
60
|
+
1. The polynomial should have a definite parity, and bounded in magnitude by 1 in the interval [-1, 1].
|
|
61
|
+
2. The phase finding works in the Chebyshev basis. If the a monomial basis polynomial is provided,
|
|
62
|
+
it will be converted to the chebyshev basis (and introduce an additional overhead).
|
|
63
|
+
3. The user is advised to get the polynomial using the `qsp_approximate` function.
|
|
64
|
+
4. If the function fails, try to scale down the polynomial by a factor, it should ease the angle finding.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
poly_coeffs: Array of polynomial coefficients (Chebyshev\Monomial, depending on cheb_basis).
|
|
68
|
+
cheb_basis: Whether the poly coefficients are given in Chebyshev (True) or Monomial(False). Defaults to Chebyshev.
|
|
69
|
+
tol: Error tolerance for the phases.
|
|
70
|
+
Returns:
|
|
71
|
+
phases: array of the qsvt phases corresponding to the given polynomial.
|
|
72
|
+
"""
|
|
73
|
+
_require_qsp()
|
|
74
|
+
|
|
75
|
+
assert poly_coeffs is not None
|
|
76
|
+
assert len(poly_coeffs) > 1, "polynomial should have degree >= 1"
|
|
77
|
+
|
|
78
|
+
# verify parity
|
|
79
|
+
is_even = np.sum(np.abs(poly_coeffs[0::2])) > 1e-8
|
|
80
|
+
is_odd = np.sum(np.abs(poly_coeffs[1::2])) > 1e-8
|
|
81
|
+
assert is_even or is_odd, "Polynomial should have a definite parity"
|
|
82
|
+
|
|
83
|
+
poly_coeffs = np.array(np.trim_zeros(poly_coeffs, "b"))
|
|
84
|
+
|
|
85
|
+
if not cheb_basis:
|
|
86
|
+
poly_coeffs = poly2cheb(poly_coeffs)
|
|
87
|
+
|
|
88
|
+
# heuristic bound verification
|
|
89
|
+
grid = np.linspace(-1, 1, 1000)
|
|
90
|
+
assert (
|
|
91
|
+
np.max(np.abs(Chebyshev(poly_coeffs)(grid))) <= 1
|
|
92
|
+
), "polynomial should be bounded in magnitude by 1"
|
|
93
|
+
|
|
94
|
+
# get the phases using pyqsp
|
|
95
|
+
phases, err = _pyqsp_get_phases(poly_coeffs, tol)
|
|
96
|
+
if err > tol:
|
|
97
|
+
raise RuntimeError(
|
|
98
|
+
f"Phase finding did not meet target tolerance "
|
|
99
|
+
f"(target={tol:.3e}, achieved={err:.3e}). "
|
|
100
|
+
"Consider increasing the degree, relaxing tol, or changing solver settings."
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# verify conventions.
|
|
104
|
+
## change the R(x) to W(x), as the phases are in the W(x) conventions
|
|
105
|
+
## minus is due to exp(-i*phi*z) in qsvt in comparison to qsp
|
|
106
|
+
phases[1:-1] = phases[1:-1] - np.pi / 2
|
|
107
|
+
phases[0] = phases[0] - np.pi / 4
|
|
108
|
+
phases[-1] = phases[-1] + (2 * (len(phases) - 1) - 1) * np.pi / 4
|
|
109
|
+
|
|
110
|
+
## the symmetric method creates the polynomial on Im[P(x)] with Im[Q(x)]=0, so adjust the phases
|
|
111
|
+
## to extract that (equivalent to applying S on the auxiliary after the first H and before the last H)
|
|
112
|
+
phases[0] -= np.pi / 4
|
|
113
|
+
phases[-1] -= np.pi / 4
|
|
114
|
+
|
|
115
|
+
## multiply by 2 as RZ(theta) = exp(-i*theta/2)
|
|
116
|
+
phases = -2 * phases
|
|
117
|
+
return phases
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _plot_qsp_approx(
|
|
121
|
+
poly_cheb: np.ndarray,
|
|
122
|
+
f_target: Callable[[np.ndarray], np.ndarray],
|
|
123
|
+
interval: tuple[float, float] = (-1, 1),
|
|
124
|
+
) -> None:
|
|
125
|
+
grid_full = np.linspace(-1, 1, 3000)
|
|
126
|
+
grid_interval = np.linspace(interval[0], interval[1], 3000)
|
|
127
|
+
|
|
128
|
+
y_target = f_target(grid_interval)
|
|
129
|
+
y_approx = np.polynomial.Chebyshev(poly_cheb)(grid_full)
|
|
130
|
+
|
|
131
|
+
# Plot
|
|
132
|
+
plt.figure(figsize=(10, 5))
|
|
133
|
+
plt.plot(grid_interval, y_target, label="Target function", linewidth=4)
|
|
134
|
+
plt.plot(
|
|
135
|
+
grid_full,
|
|
136
|
+
y_approx,
|
|
137
|
+
"--",
|
|
138
|
+
label="Polynomial approximation",
|
|
139
|
+
linewidth=2,
|
|
140
|
+
c="r",
|
|
141
|
+
)
|
|
142
|
+
plt.title("Polynomial Approximation vs Target Function")
|
|
143
|
+
plt.xlabel("x")
|
|
144
|
+
plt.ylabel("f(x)")
|
|
145
|
+
# Draw vertical lines
|
|
146
|
+
plt.axvline(interval[0], color="gray", linestyle=":", linewidth=3)
|
|
147
|
+
plt.axvline(interval[1], color="gray", linestyle=":", linewidth=3)
|
|
148
|
+
|
|
149
|
+
plt.legend()
|
|
150
|
+
plt.grid(True)
|
|
151
|
+
plt.show()
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def qsp_approximate(
|
|
155
|
+
f_target: Callable[[np.ndarray], np.ndarray],
|
|
156
|
+
degree: int,
|
|
157
|
+
parity: Optional[int] = None,
|
|
158
|
+
interval: tuple[float, float] = (-1, 1),
|
|
159
|
+
bound: float = 0.99,
|
|
160
|
+
num_grid_points: Optional[int] = None,
|
|
161
|
+
plot: bool = False,
|
|
162
|
+
) -> tuple[np.ndarray, float]:
|
|
163
|
+
"""
|
|
164
|
+
Approximate the target function on the given (sub-)interval of [-1,1], using QSP-compatible chebyshev polynomials.
|
|
165
|
+
The approximating polynomial is enforced to |P(x)| <= bound on all of [-1,1].
|
|
166
|
+
|
|
167
|
+
Note: scaling f_target by a factor < 1 might help the convergence and also a later qsp phase factor finiding.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
f_target: Real function to approximate within the given interval. Should be bounded by [-1, 1] in the given interval.
|
|
171
|
+
degree: Approximating polynomial degree.
|
|
172
|
+
parity: None - full polynomial, 0 - restrict to even polynomial, 1 - odd polynomial.
|
|
173
|
+
interval: sub interval of [-1, 1] to approximate the function within.
|
|
174
|
+
bound: global polynomial bound on [-1,1] (defaults to 0.99).
|
|
175
|
+
num_grid_points: sets the number of grid points used for the polynomial approximation (defaults to `max(2 * degree, 1000)`).
|
|
176
|
+
plot: A flag for plotting the resulting approximation vs the target function.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
coeffs: Array of Chebyshev coefficients. In case of definite parity, still a full coefficients array is returned.
|
|
180
|
+
max_error: (Approximated) maximum error between the target function and the approximating polynomial within the interval.
|
|
181
|
+
"""
|
|
182
|
+
_require_qsp()
|
|
183
|
+
import cvxpy as cp # type:ignore[import]
|
|
184
|
+
|
|
185
|
+
if num_grid_points is None:
|
|
186
|
+
num_grid_points = max(2 * degree, 1000)
|
|
187
|
+
# Discretize [-1, 1] using the grid points (interpolants)
|
|
188
|
+
xj_full = np.cos(
|
|
189
|
+
np.pi * np.arange(num_grid_points) / (num_grid_points - 1)
|
|
190
|
+
) # Chebyshev nodes on [-1, 1]
|
|
191
|
+
|
|
192
|
+
# Select grid points for the objective in [w_min, w_max]
|
|
193
|
+
xj_obj = xj_full[(xj_full >= interval[0]) & (xj_full <= interval[1])]
|
|
194
|
+
|
|
195
|
+
yj_obj = f_target(xj_obj)
|
|
196
|
+
# heuristic verification
|
|
197
|
+
bound = min(1, bound)
|
|
198
|
+
assert (
|
|
199
|
+
np.max(np.abs(yj_obj)) <= bound
|
|
200
|
+
), f"f_target function values should be bounded in magnitude by bound={bound} within the interval:{interval}"
|
|
201
|
+
|
|
202
|
+
# Define the Chebyshev polynomials
|
|
203
|
+
con_mat = np.polynomial.chebyshev.chebvander(xj_full, degree)
|
|
204
|
+
obj_mat = np.polynomial.chebyshev.chebvander(xj_obj, degree)
|
|
205
|
+
|
|
206
|
+
# Choose which Chebyshev indices to use
|
|
207
|
+
if parity is None:
|
|
208
|
+
cols = np.arange(degree + 1) # full
|
|
209
|
+
elif parity == 0:
|
|
210
|
+
cols = np.arange(0, degree + 1, 2) # even T_0, T_2, ...
|
|
211
|
+
elif parity == 1:
|
|
212
|
+
cols = np.arange(1, degree + 1, 2) # odd T_1, T_3, ...
|
|
213
|
+
else:
|
|
214
|
+
raise ValueError("parity must be None, 0 (even), or 1 (odd)")
|
|
215
|
+
|
|
216
|
+
con_mat = con_mat[:, cols]
|
|
217
|
+
obj_mat = obj_mat[:, cols]
|
|
218
|
+
|
|
219
|
+
# Define optimization variables
|
|
220
|
+
c = cp.Variable(len(cols)) # Coefficients for Chebyshev polynomials
|
|
221
|
+
f_values_full = con_mat @ c
|
|
222
|
+
f_values_obj = obj_mat @ c
|
|
223
|
+
|
|
224
|
+
# Define the optimization problem
|
|
225
|
+
objective = cp.Minimize(cp.max(cp.abs(f_values_obj - yj_obj)))
|
|
226
|
+
constraints = [cp.abs(f_values_full) <= bound] # global bound
|
|
227
|
+
prob = cp.Problem(objective, constraints)
|
|
228
|
+
|
|
229
|
+
# Solve the optimization problem
|
|
230
|
+
prob.solve()
|
|
231
|
+
|
|
232
|
+
# Return coefficients, optimal value, and grid points
|
|
233
|
+
pcoeffs = np.zeros(degree + 1)
|
|
234
|
+
pcoeffs[cols] = c.value
|
|
235
|
+
|
|
236
|
+
if plot:
|
|
237
|
+
_plot_qsp_approx(pcoeffs, f_target, interval)
|
|
238
|
+
|
|
239
|
+
return pcoeffs, prob.value
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _gqsp_complementary_polynomial(poly_coeffs: np.ndarray) -> np.ndarray:
|
|
243
|
+
"""
|
|
244
|
+
Given polynomial coefficients of a wanted P such that |P(e^{i*theta})| <= 1,
|
|
245
|
+
calculates the complementary polynomial Q for the GQSP protocol. The polynomials
|
|
246
|
+
should fulfil |P(e^{i*theta})|^2 + |Q(e^{i*theta})|^2<= 1
|
|
247
|
+
|
|
248
|
+
The Implementation is based on the paper https://arxiv.org/abs/2308.01501 Theorem 4.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
poly_coeffs: polynomial coefficients of the P polynomial in the monomial basis.
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
Q: the coefficient of the complementary Q polynomial in monomial basis.
|
|
255
|
+
"""
|
|
256
|
+
degree = len(poly_coeffs) - 1
|
|
257
|
+
|
|
258
|
+
grid = np.exp(1j * np.linspace(0.0, 2.0 * np.pi, 2000))
|
|
259
|
+
p_z = Polynomial(poly_coeffs)(grid)
|
|
260
|
+
assert (
|
|
261
|
+
np.max(np.abs(p_z)) <= 1 + 1e-10
|
|
262
|
+
), "P violates |P(e^{i*theta})| <= 1; cannot construct a complementary Q."
|
|
263
|
+
|
|
264
|
+
r = Polynomial.basis(degree) - Polynomial(poly_coeffs) * Polynomial(
|
|
265
|
+
np.conj(poly_coeffs[::-1])
|
|
266
|
+
)
|
|
267
|
+
roots = r.roots()
|
|
268
|
+
|
|
269
|
+
roots_circle = roots[np.isclose(np.abs(roots), 1)]
|
|
270
|
+
roots_out = roots[~np.isclose(np.abs(roots), 1)]
|
|
271
|
+
roots_large = roots_out[np.abs(roots_out) > 1]
|
|
272
|
+
roots_small = roots_out[np.abs(roots_out) < 1]
|
|
273
|
+
|
|
274
|
+
assert len(roots_small) + len(roots_large) + len(roots_circle) == 2 * (degree)
|
|
275
|
+
|
|
276
|
+
# assume the unit roots are with even multiplicity
|
|
277
|
+
roots_circle_halved = sorted(roots_circle)[::2]
|
|
278
|
+
q_roots = np.concatenate([roots_small, roots_circle_halved])
|
|
279
|
+
|
|
280
|
+
scale = np.sqrt(np.abs(np.prod(roots_large) * r.coef[-1]))
|
|
281
|
+
q = Polynomial.fromroots(q_roots) * scale
|
|
282
|
+
|
|
283
|
+
# verify the completion
|
|
284
|
+
q_z = q(grid)
|
|
285
|
+
if not np.allclose(np.square(np.abs(p_z)) + np.square(np.abs(q_z)), 1, atol=1e-3):
|
|
286
|
+
raise ValueError("Failed to Complete P")
|
|
287
|
+
|
|
288
|
+
return q.coef
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _r_rot(theta: float, phi: float) -> np.ndarray:
|
|
292
|
+
return np.array(
|
|
293
|
+
[
|
|
294
|
+
[np.exp(1j * (phi)) * np.cos(theta), np.exp(1j * (phi)) * np.sin(theta)],
|
|
295
|
+
[np.sin(theta), -np.cos(theta)],
|
|
296
|
+
],
|
|
297
|
+
dtype=complex,
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def gqsp_phases(
|
|
302
|
+
poly_coeffs: np.ndarray, cheb_basis: bool = False
|
|
303
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
304
|
+
"""
|
|
305
|
+
Compute GQSP phases for a polynomial in the monomial (power) basis.
|
|
306
|
+
|
|
307
|
+
The returned phases are compatible with Classiq's `gqsp` function and use the Wz signal
|
|
308
|
+
operator convention.
|
|
309
|
+
|
|
310
|
+
Notes:
|
|
311
|
+
- The polynomial must be bounded on the unit circle:
|
|
312
|
+
|P(e^{i*theta})| <= 1 for all theta in [0, 2*pi).
|
|
313
|
+
- Laurent polynomials are supported by degree shifting. If
|
|
314
|
+
P(z) = sum_{k=m}^n c_k * z^k with m < 0, the phases correspond to the
|
|
315
|
+
degree-shifted polynomial z^{-m} * P(z) (so the minimal degree is zero).
|
|
316
|
+
- The phase finiding works in the monomial basis. If the a Chebyshev basis polynomial is provided,
|
|
317
|
+
it will be converted to the monomial basis (and introduce an additional overhead).
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
poly: array-like of complex, shape (d+1). Monomial coefficients in ascending
|
|
321
|
+
order: [c_0, c_1, ..., c_d].
|
|
322
|
+
cheb_basis: Whether the poly coefficients are given in Chebyshev (True) or Monomial(False). Defaults to Monomial.
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
phases: tuple of np.ndarray (thetas, phis, lambdas), ready to use with `gqsp`.
|
|
326
|
+
|
|
327
|
+
Raises:
|
|
328
|
+
ValueError: if |P(e^{i*theta})| > 1 anywhere on the unit circle.
|
|
329
|
+
"""
|
|
330
|
+
# remove redundant zeros at the end
|
|
331
|
+
poly_coeffs = np.array(np.trim_zeros(poly_coeffs, "b"))
|
|
332
|
+
|
|
333
|
+
# move to monomial basis if needed
|
|
334
|
+
if cheb_basis:
|
|
335
|
+
poly_coeffs = cheb2poly(poly_coeffs)
|
|
336
|
+
|
|
337
|
+
# verify the normalization
|
|
338
|
+
grid = np.exp(1j * np.linspace(0.0, 2.0 * np.pi, 2000))
|
|
339
|
+
p_z = Polynomial(poly_coeffs)(grid)
|
|
340
|
+
assert (
|
|
341
|
+
np.max(np.abs(p_z)) < 1 + 1e-10
|
|
342
|
+
), "P violates |P(e^{i*theta})| <= 1; cannot create calculate gqsp phases."
|
|
343
|
+
|
|
344
|
+
# get complementary gqsp polynomial
|
|
345
|
+
comp = _gqsp_complementary_polynomial(poly_coeffs)
|
|
346
|
+
|
|
347
|
+
s = np.array([poly_coeffs, comp])
|
|
348
|
+
thetas, phis, lambdas = np.zeros((3, len(poly_coeffs)))
|
|
349
|
+
|
|
350
|
+
for i in reversed(range(len(poly_coeffs))):
|
|
351
|
+
p_i, q_i = s[:, i]
|
|
352
|
+
thetas[i] = np.arctan2(np.abs(q_i), np.abs(p_i))
|
|
353
|
+
|
|
354
|
+
phis[i] = (
|
|
355
|
+
0
|
|
356
|
+
if np.isclose(np.abs([q_i, p_i]), 0, atol=1e-10).any()
|
|
357
|
+
else np.angle(p_i * np.conj(q_i))
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
if i == 0:
|
|
361
|
+
lambdas[i] = 0 if np.allclose(np.abs(q_i), 0) else np.angle(q_i)
|
|
362
|
+
else:
|
|
363
|
+
s = _r_rot(thetas[i], phis[i]).conj().T @ s
|
|
364
|
+
s = np.array([s[0][1 : i + 1], s[1][:i]])
|
|
365
|
+
|
|
366
|
+
return thetas, phis, lambdas
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING, Any, NoReturn, TypeVar, Union
|
|
1
|
+
from typing import TYPE_CHECKING, Any, NoReturn, Optional, TypeVar, Union
|
|
2
2
|
|
|
3
3
|
import sympy
|
|
4
4
|
|
|
@@ -377,15 +377,21 @@ def _eval_expr(
|
|
|
377
377
|
val = get_sympy_val(val)
|
|
378
378
|
if expected_type is int and isinstance(val, float) and int(val) == val:
|
|
379
379
|
val = int(val)
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
380
|
+
|
|
381
|
+
failing_type: Optional[str] = None
|
|
382
|
+
if isinstance(val, QmodAnnotatedExpression):
|
|
383
|
+
val_type = val.get_type(val.root)
|
|
384
|
+
if not isinstance(val_type, expected_qmod_type):
|
|
385
|
+
failing_type = val_type.raw_qmod_type_name
|
|
386
|
+
elif not isinstance(val, expected_type):
|
|
387
|
+
failing_type = type(val).__name__
|
|
388
|
+
if failing_type is not None:
|
|
384
389
|
raise ClassiqExpansionError(
|
|
385
390
|
f"When inferring the type of parameter {param_name!r}: "
|
|
386
|
-
f"{type_name} {attr_name} must be {expected_qmod_type.
|
|
387
|
-
f"{str(val)!r}"
|
|
391
|
+
f"{type_name} {attr_name} must be {expected_qmod_type().qmod_type_name}, "
|
|
392
|
+
f"got {str(val)!r} of type {failing_type}"
|
|
388
393
|
)
|
|
394
|
+
|
|
389
395
|
expr = Expression(expr=str(val))
|
|
390
396
|
expr._evaluated_expr = EvaluatedExpression(value=val)
|
|
391
397
|
return expr
|
classiq/execution/jobs.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import warnings
|
|
1
2
|
import webbrowser
|
|
2
3
|
from datetime import datetime
|
|
3
4
|
from typing import Any, Optional, Union
|
|
@@ -167,11 +168,13 @@ class ExecutionJob:
|
|
|
167
168
|
raise ClassiqExecutionResultError("sample")
|
|
168
169
|
|
|
169
170
|
result = results[0].value
|
|
170
|
-
if isinstance(result, ExecutionDetails):
|
|
171
|
-
return result
|
|
172
171
|
if isinstance(result, MultipleExecutionDetails) and len(result.details) == 1:
|
|
173
|
-
|
|
174
|
-
|
|
172
|
+
result = result.details[0]
|
|
173
|
+
if not isinstance(result, ExecutionDetails):
|
|
174
|
+
raise ClassiqExecutionResultError("sample")
|
|
175
|
+
for warning_str in result.warnings:
|
|
176
|
+
warnings.warn(warning_str, stacklevel=2)
|
|
177
|
+
return result
|
|
175
178
|
|
|
176
179
|
def get_batch_sample_result(
|
|
177
180
|
self, _http_client: Optional[httpx.AsyncClient] = None
|
|
@@ -192,11 +195,18 @@ class ExecutionJob:
|
|
|
192
195
|
|
|
193
196
|
result = results[0].value
|
|
194
197
|
if isinstance(result, ExecutionDetails):
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
+
result_list = [result]
|
|
199
|
+
elif isinstance(result, MultipleExecutionDetails):
|
|
200
|
+
result_list = result.details
|
|
201
|
+
else:
|
|
202
|
+
raise ClassiqExecutionResultError("batch_sample")
|
|
198
203
|
|
|
199
|
-
|
|
204
|
+
warning_strs = [
|
|
205
|
+
warning for result in result_list for warning in result.warnings
|
|
206
|
+
]
|
|
207
|
+
for warning_str in warning_strs:
|
|
208
|
+
warnings.warn(warning_str, stacklevel=2)
|
|
209
|
+
return result_list
|
|
200
210
|
|
|
201
211
|
def get_estimate_result(
|
|
202
212
|
self, _http_client: Optional[httpx.AsyncClient] = None
|
classiq/interface/_version.py
CHANGED
|
@@ -97,7 +97,7 @@ class AliceBobBackendPreferences(BackendPreferences):
|
|
|
97
97
|
The API key required to access Alice&Bob's quantum hardware.
|
|
98
98
|
- **Required**: Yes.
|
|
99
99
|
|
|
100
|
-
For more details, refer to the [Alice&Bob Backend Documentation](https://docs.classiq.io/latest/reference
|
|
100
|
+
For more details, refer to the [Alice&Bob Backend Documentation](https://docs.classiq.io/latest/sdk-reference/providers/Alice%20and%20Bob/).
|
|
101
101
|
"""
|
|
102
102
|
|
|
103
103
|
backend_service_provider: ProviderTypeVendor.ALICE_BOB = pydantic.Field(
|
|
@@ -148,7 +148,7 @@ class ClassiqBackendPreferences(BackendPreferences):
|
|
|
148
148
|
This class is used to configure the backend options for executing quantum circuits on Classiq's platform.
|
|
149
149
|
The relevant backend names for Classiq targets are specified in `ClassiqSimulatorBackendNames` & `ClassiqNvidiaBackendNames`.
|
|
150
150
|
|
|
151
|
-
For more details, refer to the [Classiq Backend Documentation](https://docs.classiq.io/latest/reference
|
|
151
|
+
For more details, refer to the [Classiq Backend Documentation](https://docs.classiq.io/latest/sdk-reference/providers/Classiq/).
|
|
152
152
|
"""
|
|
153
153
|
|
|
154
154
|
backend_service_provider: ProviderTypeVendor.CLASSIQ = pydantic.Field(
|
|
@@ -187,7 +187,7 @@ class AwsBackendPreferences(BackendPreferences):
|
|
|
187
187
|
|
|
188
188
|
|
|
189
189
|
For more details, refer to:
|
|
190
|
-
[AwsBackendPreferences examples](https://docs.classiq.io/latest/reference
|
|
190
|
+
[AwsBackendPreferences examples](https://docs.classiq.io/latest/sdk-reference/providers/AWS/)
|
|
191
191
|
"""
|
|
192
192
|
|
|
193
193
|
backend_service_provider: ProviderTypeVendor.AMAZON_BRAKET = pydantic.Field(
|
|
@@ -223,7 +223,7 @@ class IBMBackendPreferences(BackendPreferences):
|
|
|
223
223
|
instance_crn (str): The IBM Cloud instance CRN (Cloud Resource Name) for the IBM Quantum service.
|
|
224
224
|
run_through_classiq (bool): Run through Classiq's credentials. Defaults to `False`.
|
|
225
225
|
|
|
226
|
-
See examples in the [IBM Quantum Backend Documentation](https://docs.classiq.io/latest/reference
|
|
226
|
+
See examples in the [IBM Quantum Backend Documentation](https://docs.classiq.io/latest/sdk-reference/providers/IBM/).
|
|
227
227
|
"""
|
|
228
228
|
|
|
229
229
|
backend_service_provider: ProviderTypeVendor.IBM_CLOUD = pydantic.Field(
|
|
@@ -285,7 +285,7 @@ class AzureBackendPreferences(BackendPreferences):
|
|
|
285
285
|
"""
|
|
286
286
|
This class inherits from BackendPreferences.
|
|
287
287
|
This is where you specify Azure Quantum preferences.
|
|
288
|
-
See usage in the [Azure Backend Documentation](https://docs.classiq.io/latest/reference
|
|
288
|
+
See usage in the [Azure Backend Documentation](https://docs.classiq.io/latest/sdk-reference/providers/Azure/).
|
|
289
289
|
|
|
290
290
|
Attributes:
|
|
291
291
|
location (str): Azure personal resource region. Defaults to `"East US"`.
|
|
@@ -337,7 +337,7 @@ class IonqBackendPreferences(BackendPreferences):
|
|
|
337
337
|
error_mitigation (bool): A configuration option to enable or disable error mitigation during execution. Defaults to `False`.
|
|
338
338
|
run_through_classiq (bool): Running through Classiq's credentials while using user's allocated budget.
|
|
339
339
|
|
|
340
|
-
See examples in the [IonQ Backend Documentation](https://docs.classiq.io/latest/reference
|
|
340
|
+
See examples in the [IonQ Backend Documentation](https://docs.classiq.io/latest/sdk-reference/providers/IonQ/).
|
|
341
341
|
"""
|
|
342
342
|
|
|
343
343
|
backend_service_provider: ProviderTypeVendor.IONQ = pydantic.Field(
|
|
@@ -366,7 +366,7 @@ class GCPBackendPreferences(BackendPreferences):
|
|
|
366
366
|
backend_service_provider (ProviderTypeVendor.GOOGLE): Indicates the backend service provider as Google,
|
|
367
367
|
specifically for quantum computing services on Google Cloud Platform (GCP).
|
|
368
368
|
|
|
369
|
-
See examples in the [Google Cloud Backend Documentation](https://docs.classiq.io/latest/reference
|
|
369
|
+
See examples in the [Google Cloud Backend Documentation](https://docs.classiq.io/latest/sdk-reference/providers/GCP/).
|
|
370
370
|
"""
|
|
371
371
|
|
|
372
372
|
backend_service_provider: ProviderTypeVendor.GOOGLE = pydantic.Field(
|
|
@@ -402,7 +402,7 @@ class IntelBackendPreferences(BackendPreferences):
|
|
|
402
402
|
This class is used to configure the backend options for executing quantum circuits on Classiq's platform.
|
|
403
403
|
The relevant backend names for Classiq targets are specified in `ClassiqSimulatorBackendNames` & `ClassiqNvidiaBackendNames`.
|
|
404
404
|
|
|
405
|
-
For more details, refer to the [Classiq Backend Documentation](https://docs.classiq.io/latest/
|
|
405
|
+
For more details, refer to the [Classiq Backend Documentation](https://docs.classiq.io/latest/user-guide/execution/cloud-providers/intel-backends/?h=intel).
|
|
406
406
|
"""
|
|
407
407
|
|
|
408
408
|
backend_service_provider: ProviderTypeVendor.INTEL = pydantic.Field(
|
classiq/interface/exceptions.py
CHANGED
|
@@ -247,6 +247,10 @@ class ExecutionDetails(BaseModel, QmodPyObject):
|
|
|
247
247
|
|
|
248
248
|
output_type_map: RegisterQuantumTypeDict = pydantic.Field(default_factory=dict)
|
|
249
249
|
|
|
250
|
+
warnings: list[str] = pydantic.Field(
|
|
251
|
+
default_factory=list, description="A list of warning messages"
|
|
252
|
+
)
|
|
253
|
+
|
|
250
254
|
@pydantic.field_validator("counts", mode="after")
|
|
251
255
|
@classmethod
|
|
252
256
|
def _clean_spaces_from_counts_keys(cls, v: Counts) -> Counts:
|