classiq 0.36.0__py3-none-any.whl → 0.37.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.
Files changed (91) 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 +42 -67
  13. classiq/applications_model_constructors/grover_model_constructor.py +27 -18
  14. classiq/exceptions.py +5 -0
  15. classiq/execution/jobs.py +13 -4
  16. classiq/executor.py +3 -2
  17. classiq/interface/_version.py +1 -1
  18. classiq/interface/analyzer/analysis_params.py +0 -6
  19. classiq/interface/analyzer/result.py +0 -4
  20. classiq/interface/backend/backend_preferences.py +2 -2
  21. classiq/interface/backend/quantum_backend_providers.py +1 -1
  22. classiq/interface/execution/resource_estimator.py +7 -0
  23. classiq/interface/execution/result.py +5 -0
  24. classiq/interface/executor/register_initialization.py +3 -1
  25. classiq/interface/executor/vqe_result.py +1 -0
  26. classiq/interface/generator/ansatz_library.py +3 -3
  27. classiq/interface/generator/arith/argument_utils.py +4 -4
  28. classiq/interface/generator/arith/arithmetic.py +4 -2
  29. classiq/interface/generator/arith/arithmetic_arg_type_validator.py +11 -5
  30. classiq/interface/generator/arith/arithmetic_expression_parser.py +8 -7
  31. classiq/interface/generator/arith/arithmetic_operations.py +7 -0
  32. classiq/interface/generator/arith/arithmetic_param_getters.py +97 -16
  33. classiq/interface/generator/arith/arithmetic_result_builder.py +13 -3
  34. classiq/interface/generator/arith/binary_ops.py +8 -10
  35. classiq/interface/generator/arith/extremum_operations.py +2 -2
  36. classiq/interface/generator/arith/number_utils.py +20 -23
  37. classiq/interface/generator/arith/register_user_input.py +3 -1
  38. classiq/interface/generator/arith/unary_ops.py +9 -13
  39. classiq/interface/generator/expressions/atomic_expression_functions.py +2 -0
  40. classiq/interface/generator/expressions/expression.py +7 -2
  41. classiq/interface/generator/expressions/qmod_qnum_proxy.py +22 -0
  42. classiq/interface/generator/expressions/qmod_sized_proxy.py +2 -12
  43. classiq/interface/generator/functions/core_lib_declarations/quantum_functions/atomic_quantum_functions.py +63 -3
  44. classiq/interface/generator/functions/core_lib_declarations/quantum_functions/std_lib_functions.py +143 -17
  45. classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +41 -16
  46. classiq/interface/generator/functions/native_function_definition.py +3 -3
  47. classiq/interface/generator/model/constraints.py +3 -3
  48. classiq/interface/generator/model/preferences/preferences.py +13 -9
  49. classiq/interface/generator/noise_properties.py +5 -5
  50. classiq/interface/generator/qpe.py +5 -5
  51. classiq/interface/generator/quantum_function_call.py +5 -3
  52. classiq/interface/generator/randomized_benchmarking.py +5 -3
  53. classiq/interface/generator/visitor.py +1 -2
  54. classiq/interface/hardware.py +1 -1
  55. classiq/interface/helpers/custom_pydantic_types.py +6 -0
  56. classiq/interface/model/{modular_addition_operation.py → inplace_binary_operation.py} +16 -2
  57. classiq/interface/model/native_function_definition.py +2 -24
  58. classiq/interface/model/operator_synthesis_data.py +6 -0
  59. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +8 -4
  60. classiq/interface/model/quantum_expressions/arithmetic_operation.py +9 -5
  61. classiq/interface/model/quantum_expressions/control_state.py +38 -0
  62. classiq/interface/model/quantum_expressions/quantum_expression.py +21 -11
  63. classiq/interface/model/quantum_function_call.py +81 -6
  64. classiq/interface/model/quantum_function_declaration.py +3 -3
  65. classiq/interface/model/quantum_if_operation.py +95 -0
  66. classiq/interface/model/resolvers/function_call_resolver.py +1 -1
  67. classiq/interface/model/validations/handles_validator.py +42 -15
  68. classiq/interface/server/routes.py +10 -6
  69. classiq/model/function_handler.pyi +86 -86
  70. classiq/model/model.py +1 -0
  71. classiq/qmod/__init__.py +6 -1
  72. classiq/qmod/builtins/__init__.py +13 -1
  73. classiq/qmod/builtins/classical_execution_primitives.py +109 -0
  74. classiq/qmod/builtins/classical_functions.py +68 -0
  75. classiq/qmod/builtins/functions.py +88 -18
  76. classiq/qmod/builtins/operations.py +60 -35
  77. classiq/qmod/classical_function.py +40 -0
  78. classiq/qmod/declaration_inferrer.py +5 -2
  79. classiq/qmod/qmod_variable.py +17 -10
  80. classiq/qmod/quantum_callable.py +24 -3
  81. classiq/qmod/quantum_expandable.py +131 -21
  82. classiq/qmod/quantum_function.py +12 -2
  83. classiq/qmod/symbolic.py +182 -107
  84. classiq/qmod/symbolic_expr.py +11 -10
  85. classiq/qmod/symbolic_type.py +8 -0
  86. classiq/quantum_functions/decorators.py +2 -4
  87. classiq/quantum_functions/function_library.py +1 -0
  88. {classiq-0.36.0.dist-info → classiq-0.37.0.dist-info}/METADATA +1 -1
  89. {classiq-0.36.0.dist-info → classiq-0.37.0.dist-info}/RECORD +90 -82
  90. classiq/interface/model/local_variable_declaration.py +0 -7
  91. {classiq-0.36.0.dist-info → classiq-0.37.0.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
@@ -325,70 +324,49 @@ def _get_hartree_fock(
325
324
  )
326
325
 
327
326
 
328
- def _get_hea_function(
329
- hea_parameters: HEAParameters, use_hartree_fock: bool
330
- ) -> List[QuantumFunctionCall]:
331
- result = []
332
- if not use_hartree_fock:
333
- result.append(
334
- QuantumFunctionCall(
335
- function="allocate",
336
- positional_args=[
337
- Expression(expr=f"{hea_parameters.num_qubits}"),
338
- HandleBinding(name="x"),
339
- ],
340
- )
341
- )
342
- result.append(
343
- QuantumFunctionCall(
344
- function="full_hea",
345
- params={
346
- "num_qubits": Expression(expr=f"{hea_parameters.num_qubits}"),
347
- "is_parametrized": Expression(
348
- expr=f"{[int(_is_parametric_gate(_HAE_GATE_MAPPING[gate])) for gate in hea_parameters.one_qubit_gates+hea_parameters.two_qubit_gates]}"
349
- ),
350
- "connectivity_map": Expression(
351
- expr=f"{[list(connectivity_pair) for connectivity_pair in hea_parameters.connectivity_map]}"
352
- ),
353
- "reps": Expression(expr=f"{hea_parameters.reps}"),
354
- "angle_params": Expression(expr="t"),
355
- },
356
- operands={
357
- "operands_1qubit": [
358
- QuantumLambdaFunction(body=[_HAE_GATE_MAPPING[gate]])
359
- for gate in hea_parameters.one_qubit_gates
360
- ],
361
- "operands_2qubit": [
362
- QuantumLambdaFunction(body=[_HAE_GATE_MAPPING[gate]])
363
- for gate in hea_parameters.two_qubit_gates
364
- ],
365
- },
366
- inouts={"x": HandleBinding(name="qbv")}
367
- if use_hartree_fock
368
- else {"x": HandleBinding(name="x")},
369
- )
327
+ def _get_hea_function(hea_parameters: HEAParameters) -> QuantumFunctionCall:
328
+ return QuantumFunctionCall(
329
+ function="full_hea",
330
+ params={
331
+ "num_qubits": Expression(expr=f"{hea_parameters.num_qubits}"),
332
+ "is_parametrized": Expression(
333
+ expr=f"{[int(_is_parametric_gate(_HAE_GATE_MAPPING[gate])) for gate in hea_parameters.one_qubit_gates+hea_parameters.two_qubit_gates]}"
334
+ ),
335
+ "connectivity_map": Expression(
336
+ expr=f"{[list(connectivity_pair) for connectivity_pair in hea_parameters.connectivity_map]}"
337
+ ),
338
+ "reps": Expression(expr=f"{hea_parameters.reps}"),
339
+ "angle_params": Expression(expr="t"),
340
+ },
341
+ operands={
342
+ "operands_1qubit": [
343
+ QuantumLambdaFunction(body=[_HAE_GATE_MAPPING[gate]])
344
+ for gate in hea_parameters.one_qubit_gates
345
+ ],
346
+ "operands_2qubit": [
347
+ QuantumLambdaFunction(body=[_HAE_GATE_MAPPING[gate]])
348
+ for gate in hea_parameters.two_qubit_gates
349
+ ],
350
+ },
351
+ inouts={"x": HandleBinding(name="qbv")},
370
352
  )
371
- return result
372
353
 
373
354
 
374
355
  def _get_ansatz(
375
356
  chemistry_problem: CHEMISTRY_PROBLEMS_TYPE,
376
- use_hartree_fock: bool,
377
357
  ansatz_parameters: AnsatzParameters,
378
- ) -> List[QuantumFunctionCall]:
358
+ ) -> QuantumFunctionCall:
379
359
  if isinstance(ansatz_parameters, HEAParameters):
380
- return _get_hea_function(ansatz_parameters, use_hartree_fock)
381
- return [
382
- _get_chemistry_function(
383
- chemistry_problem,
384
- _ANSATZ_PARAMETERS_FUNCTION_NAME_MAPPING[type(ansatz_parameters)],
385
- {"qbv": HandleBinding(name="qbv")},
386
- {
387
- param_name: Expression(expr=str(param_value))
388
- for param_name, param_value in ansatz_parameters.__dict__.items()
389
- },
390
- )
391
- ]
360
+ return _get_hea_function(ansatz_parameters)
361
+ return _get_chemistry_function(
362
+ chemistry_problem,
363
+ _ANSATZ_PARAMETERS_FUNCTION_NAME_MAPPING[type(ansatz_parameters)],
364
+ {"qbv": HandleBinding(name="qbv")},
365
+ {
366
+ param_name: Expression(expr=str(param_value))
367
+ for param_name, param_value in ansatz_parameters.__dict__.items()
368
+ },
369
+ )
392
370
 
393
371
 
394
372
  def _get_chemistry_vqe_additional_params(
@@ -485,20 +463,17 @@ def _get_chemistry_quantum_main(
485
463
  if use_hartree_fock:
486
464
  body.append(_get_hartree_fock(chemistry_problem))
487
465
 
488
- body += _get_ansatz(chemistry_problem, use_hartree_fock, ansatz_parameters)
466
+ body.append(_get_ansatz(chemistry_problem, ansatz_parameters))
489
467
 
490
468
  return NativeFunctionDefinition(
491
469
  name="main",
492
470
  param_decls=_get_chemistry_quantum_main_params(ansatz_parameters),
493
471
  port_declarations=(
494
- {"x": PortDeclaration(name="x", direction=PortDeclarationDirection.Output)}
495
- if isinstance(ansatz_parameters, HEAParameters) and not use_hartree_fock
496
- else {}
497
- ),
498
- local_handles=(
499
- [
500
- LocalVariableDeclaration(name="qbv"),
501
- ]
472
+ {
473
+ "qbv": PortDeclaration(
474
+ name="qbv", direction=PortDeclarationDirection.Output
475
+ )
476
+ }
502
477
  ),
503
478
  body=body,
504
479
  )
@@ -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/exceptions.py CHANGED
@@ -13,6 +13,11 @@ class ClassiqError(Exception):
13
13
  def __init__(self, message: str) -> None:
14
14
  message_with_link = message + CLASSIQ_SLACK_COMMUNITY_LINK
15
15
  super().__init__(message_with_link)
16
+ self._raw_message = message
17
+
18
+ @property
19
+ def raw_message(self) -> str:
20
+ return self._raw_message
16
21
 
17
22
 
18
23
  class ClassiqExecutionError(ClassiqError):