classiq 0.36.1__py3-none-any.whl → 0.37.1__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.
Files changed (70) hide show
  1. classiq/__init__.py +1 -0
  2. classiq/_internals/api_wrapper.py +24 -6
  3. classiq/_internals/authentication/device.py +6 -3
  4. classiq/_internals/authentication/token_manager.py +21 -5
  5. classiq/_internals/client.py +7 -2
  6. classiq/_internals/config.py +12 -0
  7. classiq/_internals/host_checker.py +1 -1
  8. classiq/_internals/jobs.py +3 -1
  9. classiq/_internals/type_validation.py +3 -6
  10. classiq/analyzer/analyzer.py +1 -0
  11. classiq/analyzer/rb.py +3 -5
  12. classiq/applications_model_constructors/chemistry_model_constructor.py +0 -1
  13. classiq/applications_model_constructors/grover_model_constructor.py +27 -18
  14. classiq/execution/jobs.py +13 -4
  15. classiq/executor.py +3 -2
  16. classiq/interface/_version.py +1 -1
  17. classiq/interface/analyzer/analysis_params.py +0 -6
  18. classiq/interface/analyzer/result.py +0 -4
  19. classiq/interface/backend/backend_preferences.py +2 -2
  20. classiq/interface/backend/quantum_backend_providers.py +1 -1
  21. classiq/interface/execution/resource_estimator.py +7 -0
  22. classiq/interface/execution/result.py +5 -0
  23. classiq/interface/generator/ansatz_library.py +3 -3
  24. classiq/interface/generator/arith/binary_ops.py +1 -3
  25. classiq/interface/generator/expressions/atomic_expression_functions.py +2 -0
  26. classiq/interface/generator/expressions/qmod_qnum_proxy.py +22 -0
  27. classiq/interface/generator/expressions/qmod_sized_proxy.py +2 -12
  28. classiq/interface/generator/functions/core_lib_declarations/quantum_functions/std_lib_functions.py +140 -14
  29. classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +3 -20
  30. classiq/interface/generator/functions/native_function_definition.py +3 -3
  31. classiq/interface/generator/model/constraints.py +3 -3
  32. classiq/interface/generator/model/preferences/preferences.py +10 -8
  33. classiq/interface/generator/noise_properties.py +5 -5
  34. classiq/interface/generator/qpe.py +5 -5
  35. classiq/interface/generator/quantum_function_call.py +5 -3
  36. classiq/interface/generator/visitor.py +1 -2
  37. classiq/interface/hardware.py +1 -1
  38. classiq/interface/model/native_function_definition.py +2 -24
  39. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +2 -2
  40. classiq/interface/model/quantum_expressions/arithmetic_operation.py +2 -2
  41. classiq/interface/model/quantum_expressions/control_state.py +38 -0
  42. classiq/interface/model/quantum_expressions/quantum_expression.py +12 -9
  43. classiq/interface/model/quantum_function_call.py +3 -0
  44. classiq/interface/model/quantum_function_declaration.py +3 -3
  45. classiq/interface/model/quantum_if_operation.py +95 -0
  46. classiq/interface/model/validations/handles_validator.py +7 -15
  47. classiq/interface/server/routes.py +10 -6
  48. classiq/model/function_handler.pyi +84 -84
  49. classiq/model/model.py +1 -0
  50. classiq/qmod/__init__.py +4 -1
  51. classiq/qmod/builtins/__init__.py +13 -1
  52. classiq/qmod/builtins/classical_execution_primitives.py +109 -0
  53. classiq/qmod/builtins/classical_functions.py +68 -0
  54. classiq/qmod/builtins/functions.py +47 -21
  55. classiq/qmod/builtins/operations.py +15 -29
  56. classiq/qmod/classical_function.py +40 -0
  57. classiq/qmod/declaration_inferrer.py +5 -2
  58. classiq/qmod/qmod_variable.py +15 -3
  59. classiq/qmod/quantum_callable.py +24 -3
  60. classiq/qmod/quantum_expandable.py +99 -17
  61. classiq/qmod/quantum_function.py +12 -2
  62. classiq/qmod/symbolic.py +109 -107
  63. classiq/qmod/symbolic_expr.py +1 -4
  64. classiq/qmod/symbolic_type.py +8 -0
  65. classiq/quantum_functions/decorators.py +2 -4
  66. classiq/quantum_functions/function_library.py +1 -0
  67. {classiq-0.36.1.dist-info → classiq-0.37.1.dist-info}/METADATA +1 -1
  68. {classiq-0.36.1.dist-info → classiq-0.37.1.dist-info}/RECORD +69 -61
  69. classiq/interface/model/local_variable_declaration.py +0 -7
  70. {classiq-0.36.1.dist-info → classiq-0.37.1.dist-info}/WHEEL +0 -0
classiq/__init__.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Classiq SDK."""
2
+
2
3
  import sys
3
4
  from typing import List
4
5
 
@@ -24,6 +24,10 @@ from classiq._internals.jobs import JobPoller
24
24
  from classiq.exceptions import ClassiqAPIError, ClassiqValueError
25
25
 
26
26
  ResultType = TypeVar("ResultType", bound=pydantic.BaseModel)
27
+ CLASSIQ_ACCEPT_HEADER = "X-Classiq-Accept-Version"
28
+
29
+ _ACCEPT_HEADER = "X-Classiq-Accept-Version"
30
+ _CONTENT_TYPE_HEADER = "X-Classiq-Content-Type-Version"
27
31
 
28
32
 
29
33
  class HTTPMethod(StrEnum):
@@ -72,11 +76,13 @@ class ApiWrapper:
72
76
  body: Optional[Dict] = None,
73
77
  params: Optional[Dict] = None,
74
78
  use_versioned_url: bool = True,
79
+ headers: Optional[Dict[str, str]] = None,
75
80
  ) -> dict:
76
81
  res = await client().call_api(
77
82
  http_method=http_method,
78
83
  url=url,
79
84
  body=body,
85
+ headers=headers,
80
86
  params=params,
81
87
  use_versioned_url=use_versioned_url,
82
88
  )
@@ -101,10 +107,16 @@ class ApiWrapper:
101
107
  url=routes.CONVERSION_GENERATED_CIRCUIT_TO_EXECUTION_INPUT_FULL,
102
108
  model=circuit,
103
109
  )
110
+ headers = {
111
+ _ACCEPT_HEADER: "v1",
112
+ _CONTENT_TYPE_HEADER: execution_input["version"],
113
+ }
104
114
  data = await cls._call_task(
105
115
  http_method=HTTPMethod.POST,
106
- url=routes.EXECUTION_JOBS_FULL_PATH,
116
+ headers=headers,
117
+ url=routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH,
107
118
  body=execution_input,
119
+ use_versioned_url=False,
108
120
  )
109
121
  return execution_request.ExecutionJobDetails.parse_obj(data)
110
122
 
@@ -113,9 +125,12 @@ class ApiWrapper:
113
125
  cls,
114
126
  job_id: JobID,
115
127
  ) -> execution_request.ExecutionJobDetails:
128
+ headers = {_CONTENT_TYPE_HEADER: "v1"}
116
129
  data = await cls._call_task(
117
130
  http_method=HTTPMethod.GET,
118
- url=f"{routes.EXECUTION_JOBS_FULL_PATH }/{job_id.job_id}",
131
+ headers=headers,
132
+ url=f"{routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH}/{job_id.job_id}",
133
+ use_versioned_url=False,
119
134
  )
120
135
  return execution_request.ExecutionJobDetails.parse_obj(data)
121
136
 
@@ -123,11 +138,13 @@ class ApiWrapper:
123
138
  async def call_get_execution_job_result(
124
139
  cls,
125
140
  job_id: JobID,
141
+ version: str,
126
142
  ) -> classiq.interface.executor.execution_result.ExecuteGeneratedCircuitResults:
127
143
  data = await cls._call_task(
128
144
  http_method=HTTPMethod.GET,
129
- url=f"{routes.EXECUTION_SERVICE_JOBS_FULL_PATH}/{job_id.job_id}/result",
145
+ url=f"{routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH}/{job_id.job_id}/result",
130
146
  use_versioned_url=False,
147
+ headers={CLASSIQ_ACCEPT_HEADER: version},
131
148
  )
132
149
  return classiq.interface.executor.execution_result.ExecuteGeneratedCircuitResults.parse_obj(
133
150
  data
@@ -141,7 +158,7 @@ class ApiWrapper:
141
158
  ) -> ExecutionJobDetailsV1:
142
159
  data = await cls._call_task(
143
160
  http_method=HTTPMethod.PATCH,
144
- url=f"{routes.EXECUTION_SERVICE_JOBS_FULL_PATH}/{job_id.job_id}",
161
+ url=f"{routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH}/{job_id.job_id}",
145
162
  params={
146
163
  "name": name,
147
164
  },
@@ -157,7 +174,7 @@ class ApiWrapper:
157
174
  ) -> ExecutionJobsQueryResultsV1:
158
175
  data = await cls._call_task(
159
176
  http_method=HTTPMethod.GET,
160
- url=f"{routes.EXECUTION_SERVICE_JOBS_FULL_PATH}",
177
+ url=f"{routes.EXECUTION_JOBS_NON_VERSIONED_FULL_PATH}",
161
178
  params={
162
179
  "offset": offset,
163
180
  "limit": limit,
@@ -277,7 +294,8 @@ class ApiWrapper:
277
294
  cls, problem: ground_state_problem.CHEMISTRY_PROBLEMS_TYPE
278
295
  ) -> operator.PauliOperator:
279
296
  poller = JobPoller(
280
- base_url=routes.GENERATE_HAMILTONIAN_FULL_PATH, use_versioned_url=False
297
+ base_url=routes.GENERATE_HAMILTONIAN_FULL_PATH,
298
+ use_versioned_url=False,
281
299
  )
282
300
  result = await poller.run_pydantic(problem, timeout_sec=None)
283
301
  return _parse_job_response(result, operator.PauliOperator)
@@ -18,7 +18,9 @@ class DeviceRegistrar:
18
18
  _TIMEOUT_SEC: float = timedelta(minutes=15).total_seconds()
19
19
 
20
20
  @classmethod
21
- async def register(cls, get_refresh_token: bool = True) -> Tokens:
21
+ async def register(
22
+ cls, get_refresh_token: bool = True, text_only: bool = False
23
+ ) -> Tokens:
22
24
  auth0_client = Auth0()
23
25
  data: Dict[str, Any] = await auth0_client.get_device_data(
24
26
  get_refresh_token=get_refresh_token
@@ -27,9 +29,10 @@ class DeviceRegistrar:
27
29
  print(f"Your user code: {data['user_code']}")
28
30
  verification_url = data["verification_uri_complete"]
29
31
  print(
30
- f"If a browser doesn't automatically open, please visit the url: {verification_url}"
32
+ f"If a browser doesn't automatically open, please visit this URL from any trusted device: {verification_url}"
31
33
  )
32
- webbrowser.open(verification_url)
34
+ if not text_only:
35
+ webbrowser.open(verification_url)
33
36
  timeout = min(data["expires_in"], cls._TIMEOUT_SEC)
34
37
  return await cls._poll_tokens(
35
38
  auth0_client=auth0_client,
@@ -7,6 +7,7 @@ from typing import Optional
7
7
  from classiq._internals.authentication import password_manager as pm
8
8
  from classiq._internals.authentication.auth0 import Auth0
9
9
  from classiq._internals.authentication.device import DeviceRegistrar, Tokens
10
+ from classiq._internals.config import Configuration
10
11
  from classiq.exceptions import (
11
12
  ClassiqAuthenticationError,
12
13
  ClassiqPasswordManagerSelectionError,
@@ -17,9 +18,14 @@ _logger = logging.getLogger(__name__)
17
18
 
18
19
 
19
20
  class TokenManager:
20
- def __init__(self, password_manager: Optional[pm.PasswordManager] = None) -> None:
21
+ def __init__(
22
+ self,
23
+ config: Configuration,
24
+ password_manager: Optional[pm.PasswordManager] = None,
25
+ ) -> None:
26
+ self._config = config
21
27
  if password_manager is None:
22
- password_manager = self._make_password_manager()
28
+ password_manager = self._make_password_manager(self._config)
23
29
  # We use threading.Lock instead of asyncio.Lock because the latter is coupled
24
30
  # to a specific event loop, which is undesirable
25
31
  self._lock = threading.Lock()
@@ -32,15 +38,23 @@ class TokenManager:
32
38
  )
33
39
 
34
40
  @classmethod
35
- def _make_password_manager(cls) -> pm.PasswordManager:
41
+ def _should_skip_authentication(cls) -> bool:
36
42
  parser = argparse.ArgumentParser()
37
43
  parser.add_argument(
38
44
  "--skip-authentication", action="store_true", required=False
39
45
  )
40
46
  args, _ = parser.parse_known_args()
41
47
 
42
- if args.skip_authentication:
48
+ return args.skip_authentication
49
+
50
+ @classmethod
51
+ def _make_password_manager(cls, config: Configuration) -> pm.PasswordManager:
52
+ should_skip_authentication = cls._should_skip_authentication()
53
+
54
+ if should_skip_authentication:
43
55
  return pm.DummyPasswordManager()
56
+ if config.text_only:
57
+ return pm.FilePasswordManager()
44
58
  for password_manager in PASSWORD_MANAGERS:
45
59
  if password_manager.is_supported():
46
60
  return password_manager
@@ -108,5 +122,7 @@ class TokenManager:
108
122
  async def _authentication_helper(self) -> None:
109
123
  # TODO: consider using refresh token rotation
110
124
  # (https://auth0.com/docs/tokens/refresh-tokens/refresh-token-rotation)
111
- tokens = await DeviceRegistrar.register(get_refresh_token=True)
125
+ tokens = await DeviceRegistrar.register(
126
+ get_refresh_token=True, text_only=self._config.text_only
127
+ )
112
128
  self._save_tokens(tokens, force_override_refresh_token=True)
@@ -135,7 +135,7 @@ class Client:
135
135
 
136
136
  def __init__(self, conf: config.Configuration) -> None:
137
137
  self._config = conf
138
- self._token_manager = token_manager.TokenManager()
138
+ self._token_manager = token_manager.TokenManager(config=self._config)
139
139
  self._ssl_context = ssl.create_default_context()
140
140
  self._HTTP_TIMEOUT_SECONDS = (
141
141
  3600 # Needs to be synced with load-balancer timeout
@@ -205,6 +205,7 @@ class Client:
205
205
  body: Optional[Dict] = None,
206
206
  params: Optional[Dict] = None,
207
207
  use_versioned_url: bool = True,
208
+ headers: Optional[Dict[str, str]] = None,
208
209
  ) -> Union[Dict, str]:
209
210
  if use_versioned_url:
210
211
  url = self.make_versioned_url(url)
@@ -214,6 +215,7 @@ class Client:
214
215
  url=url,
215
216
  json=body,
216
217
  params=params,
218
+ headers=headers,
217
219
  )
218
220
  self.handle_response(response)
219
221
  return response.json()
@@ -223,12 +225,15 @@ class Client:
223
225
  http_method: str,
224
226
  url: str,
225
227
  body: Optional[Dict] = None,
228
+ headers: Optional[Dict] = None,
226
229
  use_versioned_url: bool = True,
227
230
  ) -> Union[Dict, str]:
228
231
  if use_versioned_url:
229
232
  url = self.make_versioned_url(url)
230
233
  with httpx.Client(**self._make_client_args()) as sync_client:
231
- response = sync_client.request(method=http_method, url=url, json=body)
234
+ response = sync_client.request(
235
+ method=http_method, url=url, json=body, headers=headers
236
+ )
232
237
  self.handle_response(response)
233
238
  return response.json()
234
239
 
@@ -1,4 +1,5 @@
1
1
  """Configuration of the SDK module."""
2
+
2
3
  import os
3
4
  import pathlib
4
5
  from typing import List, Optional, Union
@@ -32,6 +33,9 @@ class Configuration(BaseModel):
32
33
  mode: SDKMode = pydantic.Field(
33
34
  default=SDKMode.PROD, description="The operational mode of the SDK"
34
35
  )
36
+ text_only: bool = pydantic.Field(
37
+ default=False, description="Should classiq avoid relying on GUI"
38
+ )
35
39
 
36
40
 
37
41
  _DEFAULT_CONFIG_FILES = [str(pathlib.Path("classiq", "config.ini"))]
@@ -94,6 +98,13 @@ def init(args: Optional[Union[str, List[str]]] = None) -> Configuration:
94
98
  type=SDKMode,
95
99
  default=SDKMode.PROD,
96
100
  )
101
+ arg_parser.add_argument(
102
+ "--text-only",
103
+ dest="classiq_text_only",
104
+ help="Should classiq avoid relying on GUI",
105
+ env_var="CLASSIQ_TEXT_ONLY",
106
+ action="store_true",
107
+ )
97
108
 
98
109
  parsed_args, _ = arg_parser.parse_known_args(args=args)
99
110
  return Configuration(
@@ -101,4 +112,5 @@ def init(args: Optional[Union[str, List[str]]] = None) -> Configuration:
101
112
  ide=parsed_args.classiq_ide,
102
113
  should_check_host=not parsed_args.classiq_skip_check_host,
103
114
  mode=parsed_args.classiq_mode,
115
+ text_only=parsed_args.classiq_text_only,
104
116
  )
@@ -45,7 +45,7 @@ class HostChecker:
45
45
  if lhs_version == cls._UNKNOWN_VERSION or rhs_version == cls._UNKNOWN_VERSION:
46
46
  # In case one of those versions is unknown, they are considered equal
47
47
  _logger.debug(
48
- "Either {} or {} is an unknown version. Assuming both versions are equal.",
48
+ "Either %s or %s is an unknown version. Assuming both versions are equal.",
49
49
  lhs_version,
50
50
  rhs_version,
51
51
  )
@@ -56,8 +56,10 @@ class JobPoller:
56
56
  base_url: str,
57
57
  required_headers: Optional[Set[str]] = None,
58
58
  use_versioned_url: bool = True,
59
+ additional_headers: Optional[Dict[str, str]] = None,
59
60
  ) -> None:
60
61
  self._required_headers = required_headers or set()
62
+ self._additional_headers = additional_headers
61
63
  client_instance = client()
62
64
  self._base_url = (
63
65
  client_instance.make_versioned_url(base_url)
@@ -93,7 +95,7 @@ class JobPoller:
93
95
  # Update headers in case they change
94
96
  self._async_client.headers.update(client().get_headers())
95
97
  response = await self._async_client.request(
96
- method=http_method, url=url, json=body
98
+ method=http_method, url=url, json=body, headers=self._additional_headers
97
99
  )
98
100
  client().handle_response(response)
99
101
  return response
@@ -11,8 +11,7 @@ U = TypeVar("U")
11
11
  @overload
12
12
  def validate_type(
13
13
  obj: Any, expected_type: Type[T], operation: str, exception_type: Type[Exception]
14
- ) -> T:
15
- ...
14
+ ) -> T: ...
16
15
 
17
16
 
18
17
  @overload
@@ -21,8 +20,7 @@ def validate_type(
21
20
  expected_type: Tuple[Type[T], Type[U]],
22
21
  operation: str,
23
22
  exception_type: Type[Exception],
24
- ) -> Union[T, U]:
25
- ...
23
+ ) -> Union[T, U]: ...
26
24
 
27
25
 
28
26
  @overload
@@ -31,8 +29,7 @@ def validate_type(
31
29
  expected_type: Tuple[Type[T], ...],
32
30
  operation: str,
33
31
  exception_type: Type[Exception],
34
- ) -> Any:
35
- ...
32
+ ) -> Any: ...
36
33
 
37
34
 
38
35
  def validate_type(
@@ -1,4 +1,5 @@
1
1
  """Analyzer module, implementing facilities for analyzing circuits using Classiq platform."""
2
+
2
3
  import json
3
4
  import webbrowser
4
5
  from importlib.util import find_spec
classiq/analyzer/rb.py CHANGED
@@ -12,7 +12,7 @@ from classiq.interface.analyzer.result import RbResults
12
12
  from classiq._internals.api_wrapper import ApiWrapper
13
13
  from classiq._internals.async_utils import Asyncify
14
14
  from classiq.exceptions import ClassiqAnalyzerError
15
- from classiq.executor import BackendPreferencesProgramAndResult
15
+ from classiq.executor import BackendPreferencesAndResult
16
16
 
17
17
 
18
18
  class RBAnalysis(metaclass=Asyncify):
@@ -86,8 +86,7 @@ def _strict_string(arg: Union[Enum, str]) -> str:
86
86
 
87
87
 
88
88
  def order_executor_data_by_hardware(
89
- mixed_data: List[BackendPreferencesProgramAndResult],
90
- clifford_numbers_per_program: Dict[str, int],
89
+ mixed_data: List[BackendPreferencesAndResult],
91
90
  ) -> List[AnalysisRBParams]:
92
91
  hardware_names: Set[str] = {
93
92
  _strict_string(hardware.backend_name) for hardware, _, _ in mixed_data
@@ -96,9 +95,8 @@ def order_executor_data_by_hardware(
96
95
  name: list() for name in hardware_names
97
96
  }
98
97
  cliffords_dicts: Dict[str, List[int]] = {name: list() for name in hardware_names}
99
- for hardware, program, result in mixed_data:
98
+ for hardware, num_clifford, result in mixed_data:
100
99
  hw_name: str = _strict_string(hardware.backend_name)
101
- num_clifford: int = clifford_numbers_per_program[program.code] # type: ignore[index]
102
100
  counts_dicts[hw_name].append(result.counts) # type: ignore[union-attr]
103
101
  cliffords_dicts[hw_name].append(num_clifford)
104
102
 
@@ -27,7 +27,6 @@ from classiq.interface.generator.functions.port_declaration import (
27
27
  PortDeclarationDirection,
28
28
  )
29
29
  from classiq.interface.model.handle_binding import HandleBinding
30
- from classiq.interface.model.local_variable_declaration import LocalVariableDeclaration
31
30
  from classiq.interface.model.model import Model, SerializedModel
32
31
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
33
32
  from classiq.interface.model.port_declaration import PortDeclaration
@@ -6,9 +6,11 @@ from classiq.interface.generator.functions.port_declaration import (
6
6
  )
7
7
  from classiq.interface.model.bind_operation import BindOperation
8
8
  from classiq.interface.model.handle_binding import HandleBinding, SlicedHandleBinding
9
- from classiq.interface.model.local_variable_declaration import LocalVariableDeclaration
10
9
  from classiq.interface.model.model import Model, SerializedModel
11
10
  from classiq.interface.model.native_function_definition import NativeFunctionDefinition
11
+ from classiq.interface.model.numeric_reinterpretation import (
12
+ NumericReinterpretationOperation,
13
+ )
12
14
  from classiq.interface.model.port_declaration import PortDeclaration
13
15
  from classiq.interface.model.quantum_expressions.arithmetic_operation import (
14
16
  ArithmeticOperation,
@@ -18,10 +20,9 @@ from classiq.interface.model.quantum_function_call import (
18
20
  QuantumLambdaFunction,
19
21
  )
20
22
  from classiq.interface.model.quantum_statement import QuantumStatement
21
- from classiq.interface.model.quantum_type import (
22
- QuantumFixedReal,
23
- QuantumInteger,
24
- QuantumType,
23
+ from classiq.interface.model.quantum_type import QuantumNumeric
24
+ from classiq.interface.model.variable_declaration_statement import (
25
+ VariableDeclarationStatement,
25
26
  )
26
27
 
27
28
  from classiq import RegisterUserInput
@@ -108,13 +109,6 @@ def _construct_arithmetic_oracle(
108
109
  )
109
110
 
110
111
 
111
- def get_quantum_type(fraction_places: int) -> QuantumType:
112
- if fraction_places > 0:
113
- return QuantumFixedReal(fraction_places=Expression(expr=f"{fraction_places}"))
114
-
115
- return QuantumInteger()
116
-
117
-
118
112
  def grover_main_port_declarations(
119
113
  definitions: List[Tuple[str, RegisterUserInput]],
120
114
  direction: PortDeclarationDirection,
@@ -123,25 +117,38 @@ def grover_main_port_declarations(
123
117
  name: PortDeclaration(
124
118
  name=name,
125
119
  size=Expression(expr=f"{reg.size}"),
126
- quantum_type=get_quantum_type(reg.fraction_places),
120
+ quantum_type=QuantumNumeric(),
127
121
  direction=direction,
128
122
  )
129
123
  for name, reg in definitions
130
124
  }
131
125
 
132
126
 
133
- def _generate_local_variable_declarations(
127
+ def _generate_variable_declaration_statements(
134
128
  definitions: List[Tuple[str, RegisterUserInput]]
135
- ) -> List[LocalVariableDeclaration]:
136
- ret = [LocalVariableDeclaration(name="gsq")]
129
+ ) -> List[VariableDeclarationStatement]:
130
+ ret = [VariableDeclarationStatement(name="gsq")]
137
131
  if len(definitions) >= 2:
138
132
  ret += [
139
- LocalVariableDeclaration(name=f"split{i}")
133
+ VariableDeclarationStatement(name=f"split{i}")
140
134
  for i in range(len(definitions) - 2)
141
135
  ]
142
136
  return ret
143
137
 
144
138
 
139
+ def reinterpret_registers(
140
+ definitions: List[Tuple[str, RegisterUserInput]]
141
+ ) -> List[QuantumStatement]:
142
+ return [
143
+ NumericReinterpretationOperation(
144
+ target=HandleBinding(name=name),
145
+ fraction_digits=Expression(expr=f"{definition.fraction_places}"),
146
+ is_signed=Expression(expr=f"{definition.is_signed}"),
147
+ )
148
+ for name, definition in definitions
149
+ ]
150
+
151
+
145
152
  def construct_grover_model(
146
153
  definitions: List[Tuple[str, RegisterUserInput]],
147
154
  expression: str,
@@ -163,6 +170,7 @@ def construct_grover_model(
163
170
  name=_PREDICATE_FUNCTION_NAME,
164
171
  port_declarations=predicate_port_decls,
165
172
  body=[
173
+ *reinterpret_registers(definitions),
166
174
  ArithmeticOperation(
167
175
  expression=Expression(expr=expression),
168
176
  result_var=HandleBinding(name="res"),
@@ -175,8 +183,8 @@ def construct_grover_model(
175
183
  port_declarations=grover_main_port_declarations(
176
184
  definitions, PortDeclarationDirection.Output
177
185
  ),
178
- local_handles=_generate_local_variable_declarations(definitions),
179
186
  body=[
187
+ *_generate_variable_declaration_statements(definitions),
180
188
  QuantumFunctionCall(
181
189
  function="allocate",
182
190
  positional_args=[
@@ -207,6 +215,7 @@ def construct_grover_model(
207
215
  [reg.size for _, reg in definitions],
208
216
  "gsq",
209
217
  ),
218
+ *reinterpret_registers(definitions),
210
219
  ],
211
220
  ),
212
221
  ],
classiq/execution/jobs.py CHANGED
@@ -7,9 +7,9 @@ from classiq.interface.execution.jobs import ExecutionJobDetailsV1
7
7
  from classiq.interface.executor.execution_request import ExecutionJobDetails
8
8
  from classiq.interface.executor.execution_result import ResultsCollection
9
9
  from classiq.interface.jobs import JobStatus, JSONObject
10
- from classiq.interface.server.routes import EXECUTION_JOBS_FULL_PATH
10
+ from classiq.interface.server.routes import EXECUTION_JOBS_NON_VERSIONED_FULL_PATH
11
11
 
12
- from classiq._internals.api_wrapper import ApiWrapper
12
+ from classiq._internals.api_wrapper import CLASSIQ_ACCEPT_HEADER, ApiWrapper
13
13
  from classiq._internals.async_utils import syncify_function
14
14
  from classiq._internals.client import client
15
15
  from classiq._internals.jobs import JobID, JobPoller
@@ -17,6 +17,9 @@ from classiq.exceptions import ClassiqAPIError
17
17
 
18
18
  _JobDetails = Union[ExecutionJobDetails, ExecutionJobDetailsV1]
19
19
 
20
+ _JOB_DETAILS_VERSION = "v1"
21
+ _JOB_RESULT_VERSION = "v1"
22
+
20
23
 
21
24
  class ExecutionJob:
22
25
  _details: _JobDetails
@@ -98,7 +101,9 @@ class ExecutionJob:
98
101
 
99
102
  if self._result is None:
100
103
  self._result = (
101
- await ApiWrapper.call_get_execution_job_result(self._job_id)
104
+ await ApiWrapper.call_get_execution_job_result(
105
+ job_id=self._job_id, version=_JOB_RESULT_VERSION
106
+ )
102
107
  ).results
103
108
  return self._result
104
109
 
@@ -117,7 +122,11 @@ class ExecutionJob:
117
122
  return True
118
123
  return None
119
124
 
120
- poller = JobPoller(base_url=EXECUTION_JOBS_FULL_PATH)
125
+ poller = JobPoller(
126
+ base_url=EXECUTION_JOBS_NON_VERSIONED_FULL_PATH,
127
+ use_versioned_url=False,
128
+ additional_headers={CLASSIQ_ACCEPT_HEADER: _JOB_DETAILS_VERSION},
129
+ )
121
130
  await poller.poll(
122
131
  job_id=self._job_id,
123
132
  response_parser=response_parser,
classiq/executor.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Executor module, implementing facilities for executing quantum programs using Classiq platform."""
2
+
2
3
  import functools
3
4
  from typing import Optional, Tuple, Union
4
5
 
@@ -32,8 +33,8 @@ from classiq.synthesis import SerializedQuantumProgram
32
33
 
33
34
  BatchExecutionResult: TypeAlias = Union[ExecutionDetails, BaseException]
34
35
  ProgramAndResult: TypeAlias = Tuple[QuantumProgram, BatchExecutionResult]
35
- BackendPreferencesProgramAndResult: TypeAlias = Tuple[
36
- BackendPreferencesTypes, QuantumProgram, BatchExecutionResult
36
+ BackendPreferencesAndResult: TypeAlias = Tuple[
37
+ BackendPreferencesTypes, int, BatchExecutionResult
37
38
  ]
38
39
  _MAX_ARGUMENTS_SIZE = 1024
39
40
 
@@ -3,5 +3,5 @@ from packaging.version import Version
3
3
  # This file was generated automatically
4
4
  # Please don't track in version control (DONTTRACK)
5
5
 
6
- SEMVER_VERSION = '0.36.1'
6
+ SEMVER_VERSION = '0.37.1'
7
7
  VERSION = str(Version(SEMVER_VERSION))
@@ -51,12 +51,6 @@ class AnalysisHardwareListParams(AnalysisParams, HardwareListParams):
51
51
  transpilation_params: AnalysisHardwareTranspilationParams
52
52
 
53
53
 
54
- class ResourceEstimatorParams(pydantic.BaseModel):
55
- circuit_id: str
56
- error_budget: float
57
- physical_error_rate: float
58
-
59
-
60
54
  class HardwareParams(pydantic.BaseModel):
61
55
  device: PydanticNonEmptyString = pydantic.Field(default=None, description="Devices")
62
56
  provider: AnalyzerProviderVendor
@@ -136,10 +136,6 @@ class DevicesResult(VersionedModel):
136
136
  status: GraphStatus
137
137
 
138
138
 
139
- class ResourceEstimatorResult(VersionedModel):
140
- report_json: str
141
-
142
-
143
139
  class QuantumCircuitProperties(pydantic.BaseModel):
144
140
  depth: pydantic.NonNegativeInt = pydantic.Field(
145
141
  default=..., description="Circuit depth"
@@ -204,8 +204,8 @@ class GCPBackendPreferences(BackendPreferences):
204
204
 
205
205
  class OQCBackendPreferences(BackendPreferences):
206
206
  backend_service_provider: ProviderTypeVendor.OQC
207
- oqc_email: str = pydantic.Field(description="OQC Email")
208
- oqc_password: str = pydantic.Field(description="OQC email password")
207
+ username: str = pydantic.Field(description="OQC username")
208
+ password: str = pydantic.Field(description="OQC password")
209
209
 
210
210
  @pydantic.root_validator(pre=True)
211
211
  def _set_backend_service_provider(cls, values: Dict[str, Any]) -> Dict[str, Any]:
@@ -16,7 +16,7 @@ class ProviderVendor(StrEnum):
16
16
  AMAZON_BRAKET = "Amazon Braket"
17
17
  IONQ = "IonQ"
18
18
  GOOGLE = "Google"
19
- ALICE_AND_BOB = "Alice and Bob"
19
+ ALICE_AND_BOB = "Alice & Bob"
20
20
  OQC = "OQC"
21
21
 
22
22
 
@@ -0,0 +1,7 @@
1
+ import pydantic
2
+
3
+
4
+ class ResourceEstimatorParams(pydantic.BaseModel):
5
+ circuit_id: str
6
+ error_budget: float
7
+ physical_error_rate: float
@@ -0,0 +1,5 @@
1
+ from classiq.interface.helpers.versioned_model import VersionedModel
2
+
3
+
4
+ class ResourceEstimatorResult(VersionedModel):
5
+ report_json: str
@@ -52,9 +52,9 @@ class RandomTwoQubitGatesArgs(CustomAnsatzArgs):
52
52
 
53
53
 
54
54
  class TwoLocalArgs(CustomAnsatzArgs):
55
- rotation_blocks: Optional[
56
- Union[RotationBlocksType, List[RotationBlocksType]]
57
- ] = RotationBlocksType.ry
55
+ rotation_blocks: Optional[Union[RotationBlocksType, List[RotationBlocksType]]] = (
56
+ RotationBlocksType.ry
57
+ )
58
58
  entanglement_blocks: Optional[
59
59
  Union[EntanglementBlocksType, List[EntanglementBlocksType]]
60
60
  ] = EntanglementBlocksType.cx
@@ -583,9 +583,7 @@ class Modulo(EffectiveUnaryOpParams[int]):
583
583
  ) -> int:
584
584
  repr_qubits_float = math.log2(right_arg)
585
585
  repr_qubits = round(repr_qubits_float)
586
- assert (
587
- abs(repr_qubits - repr_qubits_float) < 10**-8
588
- ), NOT_POWER_OF_TWO_ERROR_MSG
586
+ assert abs(repr_qubits - repr_qubits_float) < 10**-8, NOT_POWER_OF_TWO_ERROR_MSG
589
587
  output_size = values.get("output_size")
590
588
  if output_size is not None:
591
589
  repr_qubits = min(repr_qubits, output_size)
@@ -11,6 +11,8 @@ SUPPORTED_ATOMIC_EXPRESSION_FUNCTIONS = {
11
11
  "get_type",
12
12
  "struct_literal",
13
13
  "get_field",
14
+ "fraction_digits",
15
+ "is_signed",
14
16
  "molecule_problem_to_hamiltonian",
15
17
  "fock_hamiltonian_problem_to_hamiltonian",
16
18
  "molecule_ground_state_solution_post_process",