classiq 0.94.2__py3-none-any.whl → 0.95.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.
Potentially problematic release.
This version of classiq might be problematic. Click here for more details.
- classiq/_internals/authentication/auth0.py +32 -3
- classiq/_internals/authentication/authorization_code.py +9 -0
- classiq/_internals/authentication/authorization_flow.py +41 -0
- classiq/_internals/authentication/device.py +31 -50
- classiq/_internals/authentication/hybrid_flow.py +19 -0
- classiq/_internals/authentication/token_manager.py +5 -4
- classiq/applications/__init__.py +2 -2
- classiq/applications/qsp/qsp.py +6 -5
- classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +10 -0
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +11 -6
- classiq/interface/exceptions.py +0 -4
- classiq/interface/generator/application_apis/__init__.py +0 -1
- classiq/interface/generator/function_param_list.py +0 -2
- classiq/interface/generator/generated_circuit_data.py +1 -6
- classiq/interface/generator/quantum_program.py +0 -4
- classiq/interface/generator/transpiler_basis_gates.py +3 -0
- classiq/interface/generator/types/builtin_enum_declarations.py +0 -9
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/block.py +4 -0
- classiq/interface/model/classical_if.py +4 -0
- classiq/interface/model/control.py +7 -0
- classiq/interface/model/invert.py +4 -0
- classiq/interface/model/model_visitor.py +40 -1
- classiq/interface/model/power.py +4 -0
- classiq/interface/model/quantum_statement.py +8 -1
- classiq/interface/model/repeat.py +4 -0
- classiq/interface/model/skip_control.py +4 -0
- classiq/interface/model/within_apply_operation.py +4 -0
- classiq/model_expansions/interpreters/base_interpreter.py +1 -1
- classiq/model_expansions/interpreters/frontend_generative_interpreter.py +2 -1
- classiq/model_expansions/visitors/symbolic_param_inference.py +3 -3
- classiq/model_expansions/visitors/uncomputation_signature_inference.py +14 -3
- classiq/open_library/functions/__init__.py +4 -1
- classiq/open_library/functions/amplitude_loading.py +66 -0
- classiq/open_library/functions/lookup_table.py +22 -9
- classiq/open_library/functions/modular_exponentiation.py +5 -8
- classiq/open_library/functions/qsvt.py +4 -4
- classiq/qmod/builtins/classical_execution_primitives.py +0 -12
- classiq/qmod/builtins/enums.py +15 -17
- classiq/qmod/builtins/functions/__init__.py +3 -5
- classiq/qmod/builtins/functions/mcx.py +7 -0
- classiq/qmod/builtins/operations.py +60 -22
- classiq/qmod/builtins/structs.py +22 -33
- classiq/qmod/semantics/annotation/call_annotation.py +3 -3
- classiq/qmod/semantics/error_manager.py +7 -8
- {classiq-0.94.2.dist-info → classiq-0.95.0.dist-info}/METADATA +1 -1
- {classiq-0.94.2.dist-info → classiq-0.95.0.dist-info}/RECORD +50 -51
- {classiq-0.94.2.dist-info → classiq-0.95.0.dist-info}/WHEEL +1 -1
- classiq/applications/qsvm/__init__.py +0 -8
- classiq/applications/qsvm/qsvm.py +0 -11
- classiq/interface/applications/qsvm.py +0 -114
- classiq/interface/generator/application_apis/qsvm_declarations.py +0 -6
- classiq/interface/generator/qsvm.py +0 -96
- classiq/qmod/builtins/functions/qsvm.py +0 -24
- {classiq-0.94.2.dist-info → classiq-0.95.0.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import urllib.parse
|
|
2
|
+
import warnings
|
|
2
3
|
from dataclasses import dataclass
|
|
3
4
|
from typing import Any
|
|
4
5
|
|
|
@@ -20,6 +21,7 @@ class AuthSettings(BaseSettings):
|
|
|
20
21
|
default="f6721qMOVoDAOVkzrv8YaWassRKSFX6Y",
|
|
21
22
|
validation_alias="CLASSIQ_AUTH_CLIENT_ID",
|
|
22
23
|
)
|
|
24
|
+
organization: str = Field(default="", validation_alias="CLASSIQ_AUTH_ORGANIZATION")
|
|
23
25
|
|
|
24
26
|
model_config = SettingsConfigDict(extra="allow")
|
|
25
27
|
|
|
@@ -53,6 +55,10 @@ class Auth0:
|
|
|
53
55
|
def _client_id(self) -> str:
|
|
54
56
|
return self._auth_settings.client_id
|
|
55
57
|
|
|
58
|
+
@property
|
|
59
|
+
def _organization(self) -> str:
|
|
60
|
+
return self._auth_settings.organization
|
|
61
|
+
|
|
56
62
|
async def _make_request(
|
|
57
63
|
self,
|
|
58
64
|
url: str,
|
|
@@ -76,13 +82,20 @@ class Auth0:
|
|
|
76
82
|
f"Request to Auth0 failed with error code {code}: {data.get('error')}"
|
|
77
83
|
)
|
|
78
84
|
|
|
79
|
-
async def get_device_data(
|
|
85
|
+
async def get_device_data(
|
|
86
|
+
self, require_refresh_token: bool = True
|
|
87
|
+
) -> dict[str, Any]:
|
|
80
88
|
payload = {
|
|
81
|
-
"client_id": self.
|
|
89
|
+
"client_id": self._client_id,
|
|
82
90
|
"audience": self._auth_settings.audience,
|
|
83
91
|
}
|
|
84
|
-
if
|
|
92
|
+
if require_refresh_token:
|
|
85
93
|
payload["scope"] = "offline_access"
|
|
94
|
+
if self._organization:
|
|
95
|
+
warnings.warn(
|
|
96
|
+
"Organizations are not supported in device auth flow.",
|
|
97
|
+
stacklevel=1,
|
|
98
|
+
)
|
|
86
99
|
|
|
87
100
|
return await self._make_request(
|
|
88
101
|
url="/oauth/device/code",
|
|
@@ -102,6 +115,22 @@ class Auth0:
|
|
|
102
115
|
allow_error=codes.FORBIDDEN,
|
|
103
116
|
)
|
|
104
117
|
|
|
118
|
+
def get_authorize_url(
|
|
119
|
+
self, redirect_uri: str, require_refresh_token: bool = True
|
|
120
|
+
) -> str:
|
|
121
|
+
params = {
|
|
122
|
+
"client_id": self._client_id,
|
|
123
|
+
"response_type": "code",
|
|
124
|
+
"audience": self._auth_settings.audience,
|
|
125
|
+
"redirect_uri": redirect_uri,
|
|
126
|
+
}
|
|
127
|
+
if require_refresh_token:
|
|
128
|
+
params["scope"] = "offline_access"
|
|
129
|
+
if self._organization:
|
|
130
|
+
# Otherwise, let the Auth0 handle
|
|
131
|
+
params["organization"] = self._organization
|
|
132
|
+
return f"{self._base_url}/authorize?{urllib.parse.urlencode(params)}"
|
|
133
|
+
|
|
105
134
|
async def refresh_access_token(self, refresh_token: str) -> Tokens:
|
|
106
135
|
# TODO handle failure
|
|
107
136
|
payload = {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from classiq._internals.authentication.authorization_flow import AuthorizationFlow
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AuthorizationCodeFlow(AuthorizationFlow):
|
|
5
|
+
async def authorize(self, redirect_uri: str) -> None:
|
|
6
|
+
auth_url = self.auth0_client.get_authorize_url(
|
|
7
|
+
redirect_uri, self.require_refresh_token
|
|
8
|
+
)
|
|
9
|
+
self.open_url(auth_url)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import webbrowser
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from classiq.interface.exceptions import ClassiqAuthenticationError
|
|
5
|
+
|
|
6
|
+
from classiq._internals.authentication.auth0 import Auth0, Tokens
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AuthorizationFlow:
|
|
10
|
+
def __init__(self, require_refresh_token: bool = True, text_only: bool = False):
|
|
11
|
+
self.require_refresh_token = require_refresh_token
|
|
12
|
+
self.text_only = text_only
|
|
13
|
+
self.auth0_client = Auth0()
|
|
14
|
+
|
|
15
|
+
async def get_tokens(self) -> Tokens:
|
|
16
|
+
raise NotImplementedError
|
|
17
|
+
|
|
18
|
+
def handle_ready_data(self, data: dict[str, Any]) -> Tokens:
|
|
19
|
+
access_token: str | None = data.get("access_token") or None
|
|
20
|
+
# If refresh token was not requested, this would be None
|
|
21
|
+
refresh_token: str | None = data.get("refresh_token") or None
|
|
22
|
+
|
|
23
|
+
if access_token is None or (
|
|
24
|
+
self.require_refresh_token is True and refresh_token is None
|
|
25
|
+
):
|
|
26
|
+
raise ClassiqAuthenticationError(
|
|
27
|
+
"Token generation failed for unknown reason (missing access token or refresh token)."
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
return Tokens(access_token=access_token, refresh_token=refresh_token)
|
|
31
|
+
|
|
32
|
+
def open_url(self, url: str) -> None:
|
|
33
|
+
if self.text_only:
|
|
34
|
+
print( # noqa: T201
|
|
35
|
+
f"Please visit this URL from any trusted device to authenticate: {url}"
|
|
36
|
+
)
|
|
37
|
+
else:
|
|
38
|
+
webbrowser.open(url)
|
|
39
|
+
print( # noqa: T201
|
|
40
|
+
f"If a browser doesn't automatically open, please visit this URL from any trusted device to authenticate: {url}"
|
|
41
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import webbrowser
|
|
3
2
|
from collections.abc import Iterable
|
|
3
|
+
from dataclasses import dataclass
|
|
4
4
|
from datetime import timedelta
|
|
5
5
|
from typing import Any, TypeVar
|
|
6
6
|
|
|
@@ -10,74 +10,55 @@ from classiq.interface.exceptions import (
|
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
from classiq._internals.async_utils import poll_for
|
|
13
|
-
from classiq._internals.authentication.auth0 import
|
|
13
|
+
from classiq._internals.authentication.auth0 import Tokens
|
|
14
|
+
from classiq._internals.authentication.authorization_flow import AuthorizationFlow
|
|
14
15
|
|
|
15
16
|
T = TypeVar("T")
|
|
16
17
|
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
@dataclass
|
|
20
|
+
class DeviceData:
|
|
21
|
+
user_code: str
|
|
22
|
+
device_code: str
|
|
23
|
+
interval: float
|
|
24
|
+
expires_in: float
|
|
25
|
+
verification_uri: str
|
|
26
|
+
verification_uri_complete: str
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class DeviceCodeFlow(AuthorizationFlow):
|
|
19
30
|
_TIMEOUT_ERROR = (
|
|
20
31
|
"Device registration timed out. Please re-initiate the flow and "
|
|
21
32
|
"authorize the device within the timeout."
|
|
22
33
|
)
|
|
23
34
|
_TIMEOUT_SEC: float = timedelta(minutes=15).total_seconds()
|
|
24
35
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
) -> Tokens:
|
|
29
|
-
auth0_client = Auth0()
|
|
30
|
-
data: dict[str, Any] = await auth0_client.get_device_data(
|
|
31
|
-
get_refresh_token=get_refresh_token
|
|
36
|
+
async def get_device_data(self) -> DeviceData:
|
|
37
|
+
device_data: dict[str, Any] = await self.auth0_client.get_device_data(
|
|
38
|
+
require_refresh_token=self.require_refresh_token
|
|
32
39
|
)
|
|
40
|
+
return DeviceData(**device_data)
|
|
33
41
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
webbrowser.open(verification_url)
|
|
41
|
-
timeout = min(data["expires_in"], cls._TIMEOUT_SEC)
|
|
42
|
-
return await cls._poll_tokens(
|
|
43
|
-
auth0_client=auth0_client,
|
|
44
|
-
device_code=data["device_code"],
|
|
45
|
-
interval=data["interval"],
|
|
42
|
+
async def poll_tokens(self, device_data: DeviceData) -> Tokens:
|
|
43
|
+
interval = device_data.interval
|
|
44
|
+
timeout = min(device_data.expires_in, self._TIMEOUT_SEC)
|
|
45
|
+
return await self._poll_tokens(
|
|
46
|
+
device_code=device_data.device_code,
|
|
47
|
+
interval=interval,
|
|
46
48
|
timeout=timeout,
|
|
47
|
-
get_refresh_token=get_refresh_token,
|
|
48
49
|
)
|
|
49
50
|
|
|
50
|
-
@classmethod
|
|
51
|
-
def _handle_ready_data(
|
|
52
|
-
cls, data: dict[str, Any], get_refresh_token: bool
|
|
53
|
-
) -> Tokens:
|
|
54
|
-
access_token: str | None = data.get("access_token")
|
|
55
|
-
# If refresh token was not requested, this would be None
|
|
56
|
-
refresh_token: str | None = data.get("refresh_token")
|
|
57
|
-
|
|
58
|
-
if access_token is None or (
|
|
59
|
-
get_refresh_token is True and refresh_token is None
|
|
60
|
-
):
|
|
61
|
-
raise ClassiqAuthenticationError(
|
|
62
|
-
"Token generation failed for unknown reason."
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
return Tokens(access_token=access_token, refresh_token=refresh_token)
|
|
66
|
-
|
|
67
|
-
@classmethod
|
|
68
51
|
async def _poll_tokens(
|
|
69
|
-
|
|
70
|
-
auth0_client: Auth0,
|
|
52
|
+
self,
|
|
71
53
|
device_code: str,
|
|
72
|
-
interval:
|
|
54
|
+
interval: float,
|
|
73
55
|
timeout: float,
|
|
74
|
-
get_refresh_token: bool = True,
|
|
75
56
|
) -> Tokens:
|
|
76
57
|
async def poller() -> dict[str, Any]:
|
|
77
58
|
nonlocal device_code
|
|
78
|
-
return await auth0_client.poll_tokens(device_code=device_code)
|
|
59
|
+
return await self.auth0_client.poll_tokens(device_code=device_code)
|
|
79
60
|
|
|
80
|
-
def interval_coro() -> Iterable[
|
|
61
|
+
def interval_coro() -> Iterable[float]:
|
|
81
62
|
nonlocal interval
|
|
82
63
|
while True:
|
|
83
64
|
yield interval
|
|
@@ -88,14 +69,14 @@ class DeviceRegistrar:
|
|
|
88
69
|
):
|
|
89
70
|
error_code: str | None = data.get("error")
|
|
90
71
|
if error_code is None:
|
|
91
|
-
return
|
|
72
|
+
return self.handle_ready_data(data)
|
|
92
73
|
elif error_code == "authorization_pending":
|
|
93
74
|
pass
|
|
94
75
|
elif error_code == "slow_down":
|
|
95
76
|
# This value is used by poll_for via interval_coro
|
|
96
77
|
interval *= 2
|
|
97
78
|
elif error_code == "expired_token":
|
|
98
|
-
raise ClassiqExpiredTokenError(
|
|
79
|
+
raise ClassiqExpiredTokenError(self._TIMEOUT_ERROR)
|
|
99
80
|
elif error_code == "access_denied":
|
|
100
81
|
error_description = data.get("error_description")
|
|
101
82
|
if error_description is None:
|
|
@@ -109,4 +90,4 @@ class DeviceRegistrar:
|
|
|
109
90
|
f"Device registration failed with an unknown error: {error_code}."
|
|
110
91
|
)
|
|
111
92
|
else:
|
|
112
|
-
raise ClassiqAuthenticationError(
|
|
93
|
+
raise ClassiqAuthenticationError(self._TIMEOUT_ERROR)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from classiq._internals.authentication.auth0 import Tokens
|
|
2
|
+
from classiq._internals.authentication.authorization_code import AuthorizationCodeFlow
|
|
3
|
+
from classiq._internals.authentication.authorization_flow import AuthorizationFlow
|
|
4
|
+
from classiq._internals.authentication.device import DeviceCodeFlow
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class HybridFlow(AuthorizationFlow):
|
|
8
|
+
def __init__(
|
|
9
|
+
self, require_refresh_token: bool = True, text_only: bool = False
|
|
10
|
+
) -> None:
|
|
11
|
+
super().__init__(require_refresh_token, text_only)
|
|
12
|
+
self.device_flow = DeviceCodeFlow(require_refresh_token, text_only)
|
|
13
|
+
self.auth_code_flow = AuthorizationCodeFlow(require_refresh_token, text_only)
|
|
14
|
+
|
|
15
|
+
async def get_tokens(self) -> Tokens:
|
|
16
|
+
device_data = await self.device_flow.get_device_data()
|
|
17
|
+
await self.auth_code_flow.authorize(device_data.verification_uri_complete)
|
|
18
|
+
print(f"Your user code: {device_data.user_code}") # noqa: T201
|
|
19
|
+
return await self.device_flow.poll_tokens(device_data)
|
|
@@ -10,8 +10,8 @@ from classiq.interface.exceptions import (
|
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
from classiq._internals.authentication import password_manager as pm
|
|
13
|
-
from classiq._internals.authentication.auth0 import Auth0
|
|
14
|
-
from classiq._internals.authentication.
|
|
13
|
+
from classiq._internals.authentication.auth0 import Auth0, Tokens
|
|
14
|
+
from classiq._internals.authentication.hybrid_flow import HybridFlow
|
|
15
15
|
from classiq._internals.config import Configuration
|
|
16
16
|
|
|
17
17
|
PASSWORD_MANAGERS: Sequence[type[pm.PasswordManager]] = [
|
|
@@ -126,7 +126,8 @@ class TokenManager:
|
|
|
126
126
|
async def _authentication_helper(self) -> None:
|
|
127
127
|
# TODO: consider using refresh token rotation
|
|
128
128
|
# (https://auth0.com/docs/tokens/refresh-tokens/refresh-token-rotation)
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
authorization_flow = HybridFlow(
|
|
130
|
+
require_refresh_token=True, text_only=self._config.text_only
|
|
131
131
|
)
|
|
132
|
+
tokens = await authorization_flow.get_tokens()
|
|
132
133
|
self._save_tokens(tokens, force_override_refresh_token=True)
|
classiq/applications/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from classiq.applications import chemistry, combinatorial_optimization, qsp
|
|
1
|
+
from classiq.applications import chemistry, combinatorial_optimization, qsp
|
|
2
2
|
|
|
3
|
-
__all__ = ["chemistry", "combinatorial_optimization", "qsp"
|
|
3
|
+
__all__ = ["chemistry", "combinatorial_optimization", "qsp"]
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
_NON_IMPORTED_PUBLIC_SUBMODULES = ["qnn"]
|
classiq/applications/qsp/qsp.py
CHANGED
|
@@ -50,7 +50,7 @@ def qsvt_phases(
|
|
|
50
50
|
) -> np.ndarray:
|
|
51
51
|
r"""
|
|
52
52
|
Get QSVT phases that will generate the given Chebyshev polynomial.
|
|
53
|
-
The phases are ready to be used in `qsvt` and `qsvt_lcu` functions in the classiq library. The
|
|
53
|
+
The phases are ready to be used in `qsvt` and `qsvt_lcu` functions in the classiq library. The convention
|
|
54
54
|
is the reflection signal operator, and the measurement basis is the hadamard basis (see https://arxiv.org/abs/2105.02859
|
|
55
55
|
APPENDIX A.).
|
|
56
56
|
The current implementation is using the pyqsp package, based on techniques in https://arxiv.org/abs/2003.02831.
|
|
@@ -118,7 +118,7 @@ def qsvt_phases(
|
|
|
118
118
|
|
|
119
119
|
def _plot_qsp_approx(
|
|
120
120
|
poly_cheb: np.ndarray,
|
|
121
|
-
f_target: Callable[[
|
|
121
|
+
f_target: Callable[[float], complex],
|
|
122
122
|
interval: tuple[float, float] = (-1, 1),
|
|
123
123
|
) -> None:
|
|
124
124
|
from matplotlib import pyplot as plt
|
|
@@ -126,7 +126,7 @@ def _plot_qsp_approx(
|
|
|
126
126
|
grid_full = np.linspace(-1, 1, 3000)
|
|
127
127
|
grid_interval = np.linspace(interval[0], interval[1], 3000)
|
|
128
128
|
|
|
129
|
-
y_target = f_target(grid_interval)
|
|
129
|
+
y_target = np.vectorize(f_target, otypes=[float])(grid_interval)
|
|
130
130
|
y_approx = np.polynomial.Chebyshev(poly_cheb)(grid_full)
|
|
131
131
|
|
|
132
132
|
# Plot
|
|
@@ -153,7 +153,7 @@ def _plot_qsp_approx(
|
|
|
153
153
|
|
|
154
154
|
|
|
155
155
|
def qsp_approximate(
|
|
156
|
-
f_target: Callable[[
|
|
156
|
+
f_target: Callable[[float], complex],
|
|
157
157
|
degree: int,
|
|
158
158
|
parity: int | None = None,
|
|
159
159
|
interval: tuple[float, float] = (-1, 1),
|
|
@@ -193,7 +193,8 @@ def qsp_approximate(
|
|
|
193
193
|
# Select grid points for the objective in [w_min, w_max]
|
|
194
194
|
xj_obj = xj_full[(xj_full >= interval[0]) & (xj_full <= interval[1])]
|
|
195
195
|
|
|
196
|
-
yj_obj = f_target(xj_obj)
|
|
196
|
+
yj_obj = np.vectorize(f_target, otypes=[float])(xj_obj)
|
|
197
|
+
|
|
197
198
|
# heuristic verification
|
|
198
199
|
bound = min(1, bound)
|
|
199
200
|
assert (
|
|
@@ -7,6 +7,7 @@ import sympy
|
|
|
7
7
|
from classiq.interface.exceptions import (
|
|
8
8
|
ClassiqExpansionError,
|
|
9
9
|
ClassiqInternalExpansionError,
|
|
10
|
+
ClassiqValueError,
|
|
10
11
|
)
|
|
11
12
|
from classiq.interface.generator.functions.classical_function_declaration import (
|
|
12
13
|
ClassicalFunctionDeclaration,
|
|
@@ -208,6 +209,15 @@ def try_eval_sympy_function(
|
|
|
208
209
|
|
|
209
210
|
def try_eval_builtin_function(
|
|
210
211
|
expr_val: QmodAnnotatedExpression, node: ast.Call, func_name: str
|
|
212
|
+
) -> bool:
|
|
213
|
+
try:
|
|
214
|
+
return _try_eval_builtin_function(expr_val, node, func_name)
|
|
215
|
+
except ValueError as e:
|
|
216
|
+
raise ClassiqValueError(str(e)) from e
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _try_eval_builtin_function(
|
|
220
|
+
expr_val: QmodAnnotatedExpression, node: ast.Call, func_name: str
|
|
211
221
|
) -> bool:
|
|
212
222
|
args_are_int = all(isinstance(expr_val.get_type(arg), Integer) for arg in node.args)
|
|
213
223
|
args_are_real = all(
|
classiq/interface/_version.py
CHANGED
|
@@ -172,10 +172,11 @@ class AwsBackendPreferences(BackendPreferences):
|
|
|
172
172
|
backend_service_provider (ProviderTypeVendor.AMAZON_BRAKET):
|
|
173
173
|
The service provider for the backend, which is Amazon Braket.
|
|
174
174
|
|
|
175
|
-
|
|
176
|
-
The
|
|
177
|
-
|
|
178
|
-
|
|
175
|
+
aws_access_key_id (str):
|
|
176
|
+
The access key id of AWS user with full braket access
|
|
177
|
+
|
|
178
|
+
aws_secret_access_key (str):
|
|
179
|
+
The secret key assigned to the access key id for the user with full braket access.
|
|
179
180
|
|
|
180
181
|
s3_bucket_name (str):
|
|
181
182
|
The name of the S3 bucket where results and other related data will be stored.
|
|
@@ -193,9 +194,13 @@ class AwsBackendPreferences(BackendPreferences):
|
|
|
193
194
|
backend_service_provider: ProviderTypeVendor.AMAZON_BRAKET = pydantic.Field(
|
|
194
195
|
default=ProviderVendor.AMAZON_BRAKET
|
|
195
196
|
)
|
|
196
|
-
|
|
197
|
+
aws_access_key_id: str | None = pydantic.Field(
|
|
198
|
+
default=None,
|
|
199
|
+
description="Key id assigned to user with credentials to access Braket service",
|
|
200
|
+
)
|
|
201
|
+
aws_secret_access_key: str | None = pydantic.Field(
|
|
197
202
|
default=None,
|
|
198
|
-
description="
|
|
203
|
+
description="Secret access key assigned to user with credentials to access Braket service",
|
|
199
204
|
)
|
|
200
205
|
s3_bucket_name: str | None = pydantic.Field(
|
|
201
206
|
default=None, description="S3 Bucket Name"
|
classiq/interface/exceptions.py
CHANGED
|
@@ -4,6 +4,5 @@ from classiq.interface.generator.builtin_api_builder import (
|
|
|
4
4
|
|
|
5
5
|
from .arithmetic_declarations import * # noqa: F403
|
|
6
6
|
from .combinatorial_optimization_declarations import * # noqa: F403
|
|
7
|
-
from .qsvm_declarations import * # noqa: F403
|
|
8
7
|
|
|
9
8
|
populate_builtin_declarations(vars().values())
|
|
@@ -42,7 +42,6 @@ from classiq.interface.generator.hardware_efficient_ansatz import (
|
|
|
42
42
|
from classiq.interface.generator.identity import Identity
|
|
43
43
|
from classiq.interface.generator.mcu import Mcu
|
|
44
44
|
from classiq.interface.generator.mcx import Mcx
|
|
45
|
-
from classiq.interface.generator.qsvm import QSVMFeatureMap
|
|
46
45
|
from classiq.interface.generator.randomized_benchmarking import RandomizedBenchmarking
|
|
47
46
|
from classiq.interface.generator.reset import Reset
|
|
48
47
|
from classiq.interface.generator.standard_gates.standard_gates_param_list import (
|
|
@@ -108,7 +107,6 @@ function_param_library: FunctionParamLibrary = FunctionParamLibrary(
|
|
|
108
107
|
RandomizedBenchmarking,
|
|
109
108
|
UGate,
|
|
110
109
|
AmplitudeLoading,
|
|
111
|
-
QSVMFeatureMap,
|
|
112
110
|
HadamardTransform,
|
|
113
111
|
Copy,
|
|
114
112
|
Reset,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import re
|
|
3
|
-
from typing import Literal,
|
|
3
|
+
from typing import Literal, TypeAlias
|
|
4
4
|
from uuid import UUID
|
|
5
5
|
|
|
6
6
|
import pydantic
|
|
@@ -188,8 +188,6 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
|
188
188
|
back_refs: StatementBlock = Field(default_factory=list)
|
|
189
189
|
|
|
190
190
|
model_config = ConfigDict(extra="allow")
|
|
191
|
-
# Temporary field to store the override debug info for parallel old/new visualization
|
|
192
|
-
override_debug_info: Optional["FunctionDebugInfoInterface"] = None
|
|
193
191
|
|
|
194
192
|
@property
|
|
195
193
|
def is_allocate_or_free(self) -> bool:
|
|
@@ -317,9 +315,6 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
|
317
315
|
)
|
|
318
316
|
|
|
319
317
|
def inverse(self) -> "FunctionDebugInfoInterface":
|
|
320
|
-
if self.override_debug_info is not None:
|
|
321
|
-
self.override_debug_info = self.override_debug_info.inverse()
|
|
322
|
-
return self
|
|
323
318
|
inverse_generated_function = (
|
|
324
319
|
self.generated_function.model_copy(
|
|
325
320
|
update=dict(registers=self._inverse_registers)
|
|
@@ -65,7 +65,6 @@ class QuantumProgram(VersionedModel, CircuitCodeInterface):
|
|
|
65
65
|
transpiled_circuit: TranspiledCircuitData | None = pydantic.Field(default=None)
|
|
66
66
|
creation_time: str = pydantic.Field(default_factory=_get_formatted_utc_current_time)
|
|
67
67
|
synthesis_duration: SynthesisStepDurations | None = pydantic.Field(default=None)
|
|
68
|
-
debug_info: list[FunctionDebugInfoInterface] | None = pydantic.Field(default=None)
|
|
69
68
|
compressed_debug_info: bytes | None = pydantic.Field(default=None)
|
|
70
69
|
program_id: str = pydantic.Field(default_factory=get_uuid_as_str)
|
|
71
70
|
execution_primitives_input: PrimitivesInput | None = pydantic.Field(default=None)
|
|
@@ -169,9 +168,6 @@ class QuantumProgram(VersionedModel, CircuitCodeInterface):
|
|
|
169
168
|
)
|
|
170
169
|
|
|
171
170
|
def get_debug_info(self) -> list[FunctionDebugInfoInterface] | None:
|
|
172
|
-
# Support legacy uncompressed debug info
|
|
173
|
-
if self.debug_info is not None:
|
|
174
|
-
return self.debug_info
|
|
175
171
|
if self.compressed_debug_info is None:
|
|
176
172
|
return None
|
|
177
173
|
decompressed_debug_info_dict_list = decompress(self.compressed_debug_info)
|
|
@@ -64,6 +64,9 @@ DEFAULT_BASIS_GATES: BasisGates = SINGLE_QUBIT_GATES | BASIC_TWO_QUBIT_GATES
|
|
|
64
64
|
ALL_GATES: BasisGates = (
|
|
65
65
|
SINGLE_QUBIT_GATES | TWO_QUBIT_GATES | THREE_QUBIT_GATES | NON_UNITARY_GATES
|
|
66
66
|
)
|
|
67
|
+
ALL_NON_3_QBIT_GATES: BasisGates = (
|
|
68
|
+
SINGLE_QUBIT_GATES | TWO_QUBIT_GATES | NON_UNITARY_GATES
|
|
69
|
+
)
|
|
67
70
|
|
|
68
71
|
ROUTING_TWO_QUBIT_BASIS_GATES: BasisGates = frozenset(
|
|
69
72
|
("cx", "ecr", "rzx", "ryy", "rxx", "rzz", "cy", "cz", "cp", "swap")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
INTERFACE_VERSION = "
|
|
1
|
+
INTERFACE_VERSION = "14"
|
classiq/interface/model/block.py
CHANGED
|
@@ -31,6 +31,10 @@ class ClassicalIf(QuantumOperation):
|
|
|
31
31
|
def expressions(self) -> list[Expression]:
|
|
32
32
|
return [self.condition]
|
|
33
33
|
|
|
34
|
+
@property
|
|
35
|
+
def blocks(self) -> dict[str, "StatementBlock"]:
|
|
36
|
+
return {"then": self.then, "else_": self.else_}
|
|
37
|
+
|
|
34
38
|
@property
|
|
35
39
|
def wiring_inputs(self) -> Mapping[str, HandleBinding]:
|
|
36
40
|
return functools.reduce(
|
|
@@ -47,3 +47,10 @@ class Control(QuantumExpressionOperation):
|
|
|
47
47
|
|
|
48
48
|
def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
|
|
49
49
|
return reset_lists(self, ["body", "else_block"])
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def blocks(self) -> dict[str, "StatementBlock"]:
|
|
53
|
+
blocks = {"body": self.body}
|
|
54
|
+
if self.else_block is not None:
|
|
55
|
+
blocks["else_block"] = self.else_block
|
|
56
|
+
return blocks
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
from collections.abc import Collection
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
1
5
|
from classiq.interface.debug_info.debug_info import DebugInfoCollection
|
|
2
|
-
from classiq.interface.generator.visitor import Transformer, Visitor
|
|
6
|
+
from classiq.interface.generator.visitor import RetType, Transformer, Visitor
|
|
7
|
+
from classiq.interface.model.model import Model
|
|
8
|
+
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
|
9
|
+
from classiq.interface.model.quantum_statement import QuantumStatement
|
|
3
10
|
|
|
4
11
|
|
|
5
12
|
class ModelVisitor(Visitor):
|
|
@@ -7,8 +14,40 @@ class ModelVisitor(Visitor):
|
|
|
7
14
|
return
|
|
8
15
|
|
|
9
16
|
|
|
17
|
+
class ModelStatementsVisitor(ModelVisitor):
|
|
18
|
+
def visit_BaseModel(self, node: BaseModel) -> RetType | None:
|
|
19
|
+
if isinstance(node, Model):
|
|
20
|
+
return self.visit(node.functions)
|
|
21
|
+
if isinstance(node, NativeFunctionDefinition):
|
|
22
|
+
return self.visit(node.body)
|
|
23
|
+
if isinstance(node, QuantumStatement):
|
|
24
|
+
for block in node.blocks.values():
|
|
25
|
+
self.visit(block)
|
|
26
|
+
return None
|
|
27
|
+
return super().visit_BaseModel(node)
|
|
28
|
+
|
|
29
|
+
|
|
10
30
|
class ModelTransformer(Transformer):
|
|
11
31
|
def visit_DebugInfoCollection(
|
|
12
32
|
self, debug_info: DebugInfoCollection
|
|
13
33
|
) -> DebugInfoCollection:
|
|
14
34
|
return debug_info
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ModelStatementsTransformer(ModelTransformer):
|
|
38
|
+
def visit_BaseModel(
|
|
39
|
+
self, node: BaseModel, fields_to_skip: Collection[str] | None = None
|
|
40
|
+
) -> RetType:
|
|
41
|
+
if isinstance(node, Model):
|
|
42
|
+
new_functions = self.visit(node.functions)
|
|
43
|
+
return node.model_copy(update=dict(functions=new_functions))
|
|
44
|
+
if isinstance(node, NativeFunctionDefinition):
|
|
45
|
+
new_body = self.visit(node.body)
|
|
46
|
+
return node.model_copy(update=dict(body=new_body))
|
|
47
|
+
if isinstance(node, QuantumStatement):
|
|
48
|
+
new_blocks = {
|
|
49
|
+
block_name: self.visit(block)
|
|
50
|
+
for block_name, block in node.blocks.items()
|
|
51
|
+
}
|
|
52
|
+
return node.model_copy(update=new_blocks)
|
|
53
|
+
return super().visit_BaseModel(node, fields_to_skip)
|