classiq 0.68.0__py3-none-any.whl → 0.70.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 (96) hide show
  1. classiq/_internals/api_wrapper.py +4 -8
  2. classiq/analyzer/analyzer.py +0 -18
  3. classiq/analyzer/url_utils.py +9 -4
  4. classiq/applications/combinatorial_helpers/pyomo_utils.py +2 -0
  5. classiq/applications/combinatorial_optimization/combinatorial_problem.py +8 -11
  6. classiq/applications/qnn/gradients/quantum_gradient.py +1 -1
  7. classiq/applications/qnn/gradients/simple_quantum_gradient.py +1 -1
  8. classiq/applications/qnn/torch_utils.py +1 -1
  9. classiq/execution/jobs.py +2 -5
  10. classiq/interface/_version.py +1 -1
  11. classiq/interface/backend/quantum_backend_providers.py +8 -3
  12. classiq/interface/chemistry/operator.py +12 -28
  13. classiq/interface/debug_info/back_ref_util.py +22 -0
  14. classiq/interface/debug_info/debug_info.py +11 -21
  15. classiq/interface/executor/optimizer_preferences.py +1 -0
  16. classiq/interface/executor/quantum_instruction_set.py +1 -0
  17. classiq/interface/generator/arith/arithmetic.py +21 -6
  18. classiq/interface/generator/arith/arithmetic_param_getters.py +3 -3
  19. classiq/interface/generator/circuit_code/circuit_code.py +4 -0
  20. classiq/interface/generator/circuit_code/types_and_constants.py +1 -0
  21. classiq/interface/generator/expressions/atomic_expression_functions.py +1 -2
  22. classiq/interface/generator/expressions/expression_types.py +8 -2
  23. classiq/interface/generator/expressions/proxies/__init__.py +0 -0
  24. classiq/interface/generator/expressions/proxies/classical/__init__.py +0 -0
  25. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +75 -0
  26. classiq/interface/generator/expressions/proxies/classical/classical_proxy.py +26 -0
  27. classiq/interface/generator/expressions/proxies/classical/classical_scalar_proxy.py +32 -0
  28. classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +31 -0
  29. classiq/interface/generator/expressions/proxies/quantum/__init__.py +0 -0
  30. classiq/interface/generator/expressions/{qmod_qarray_proxy.py → proxies/quantum/qmod_qarray_proxy.py} +3 -1
  31. classiq/interface/generator/expressions/{qmod_qscalar_proxy.py → proxies/quantum/qmod_qscalar_proxy.py} +3 -1
  32. classiq/interface/generator/expressions/{qmod_qstruct_proxy.py → proxies/quantum/qmod_qstruct_proxy.py} +3 -1
  33. classiq/interface/generator/functions/classical_type.py +34 -29
  34. classiq/interface/generator/functions/type_name.py +26 -2
  35. classiq/interface/generator/generated_circuit_data.py +84 -27
  36. classiq/interface/generator/model/preferences/preferences.py +1 -0
  37. classiq/interface/generator/quantum_program.py +0 -1
  38. classiq/interface/generator/types/builtin_enum_declarations.py +1 -0
  39. classiq/interface/generator/types/enum_declaration.py +12 -1
  40. classiq/interface/ide/visual_model.py +0 -2
  41. classiq/interface/model/native_function_definition.py +0 -10
  42. classiq/interface/model/quantum_statement.py +1 -1
  43. classiq/interface/model/quantum_type.py +15 -3
  44. classiq/interface/server/routes.py +0 -6
  45. classiq/model_expansions/atomic_expression_functions_defs.py +9 -3
  46. classiq/model_expansions/evaluators/arg_type_match.py +4 -2
  47. classiq/model_expansions/evaluators/classical_expression.py +2 -2
  48. classiq/model_expansions/evaluators/control.py +1 -1
  49. classiq/model_expansions/evaluators/parameter_types.py +58 -16
  50. classiq/model_expansions/evaluators/quantum_type_utils.py +7 -57
  51. classiq/model_expansions/expression_evaluator.py +3 -1
  52. classiq/model_expansions/generative_functions.py +67 -7
  53. classiq/model_expansions/quantum_operations/arithmetic/__init__.py +0 -0
  54. classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +60 -0
  55. classiq/model_expansions/quantum_operations/assignment_result_processor.py +9 -0
  56. classiq/model_expansions/quantum_operations/call_emitter.py +0 -13
  57. classiq/model_expansions/quantum_operations/quantum_function_call.py +0 -22
  58. classiq/model_expansions/scope.py +7 -6
  59. classiq/model_expansions/scope_initialization.py +20 -33
  60. classiq/model_expansions/transformers/model_renamer.py +13 -4
  61. classiq/model_expansions/visitors/variable_references.py +8 -4
  62. classiq/open_library/functions/__init__.py +2 -0
  63. classiq/open_library/functions/amplitude_amplification.py +3 -7
  64. classiq/open_library/functions/discrete_sine_cosine_transform.py +4 -4
  65. classiq/open_library/functions/grover.py +2 -2
  66. classiq/open_library/functions/hea.py +3 -3
  67. classiq/open_library/functions/lookup_table.py +58 -0
  68. classiq/open_library/functions/modular_exponentiation.py +10 -20
  69. classiq/open_library/functions/qft_functions.py +2 -2
  70. classiq/open_library/functions/qsvt.py +8 -8
  71. classiq/open_library/functions/utility_functions.py +2 -2
  72. classiq/qmod/builtins/classical_functions.py +24 -7
  73. classiq/qmod/builtins/enums.py +1 -0
  74. classiq/qmod/builtins/functions/__init__.py +2 -0
  75. classiq/qmod/builtins/functions/exponentiation.py +24 -0
  76. classiq/qmod/builtins/operations.py +26 -11
  77. classiq/qmod/cparam.py +32 -5
  78. classiq/qmod/declaration_inferrer.py +3 -1
  79. classiq/qmod/python_classical_type.py +10 -4
  80. classiq/qmod/qmod_parameter.py +8 -0
  81. classiq/qmod/qmod_variable.py +11 -14
  82. classiq/qmod/quantum_callable.py +2 -1
  83. classiq/qmod/quantum_function.py +3 -2
  84. classiq/qmod/semantics/annotation/call_annotation.py +0 -28
  85. classiq/qmod/semantics/annotation/qstruct_annotator.py +21 -1
  86. classiq/qmod/semantics/error_manager.py +1 -1
  87. classiq/qmod/semantics/validation/main_validation.py +1 -1
  88. classiq/qmod/semantics/validation/type_hints.py +29 -0
  89. classiq/qmod/utilities.py +67 -2
  90. classiq/synthesis.py +9 -6
  91. {classiq-0.68.0.dist-info → classiq-0.70.0.dist-info}/METADATA +10 -12
  92. {classiq-0.68.0.dist-info → classiq-0.70.0.dist-info}/RECORD +95 -84
  93. {classiq-0.68.0.dist-info → classiq-0.70.0.dist-info}/WHEEL +1 -1
  94. classiq/interface/execution/jobs.py +0 -31
  95. /classiq/interface/generator/expressions/{qmod_struct_instance.py → proxies/classical/qmod_struct_instance.py} +0 -0
  96. /classiq/interface/generator/expressions/{qmod_sized_proxy.py → proxies/quantum/qmod_sized_proxy.py} +0 -0
@@ -22,10 +22,6 @@ from classiq.interface.execution.iqcc import (
22
22
  IQCCProbeAuthData,
23
23
  IQCCProbeAuthResponse,
24
24
  )
25
- from classiq.interface.execution.jobs import (
26
- ExecutionJobDetailsV1,
27
- ExecutionJobsQueryResultsV1,
28
- )
29
25
  from classiq.interface.execution.primitives import PrimitivesInput
30
26
  from classiq.interface.executor import execution_request
31
27
  from classiq.interface.generator import quantum_program as generator_result
@@ -218,7 +214,7 @@ class ApiWrapper:
218
214
  job_id: JobID,
219
215
  name: str,
220
216
  http_client: Optional[httpx.AsyncClient] = None,
221
- ) -> ExecutionJobDetailsV1:
217
+ ) -> execution_request.ExecutionJobDetails:
222
218
  data = await cls._call_task(
223
219
  http_method=HTTPMethod.PATCH,
224
220
  url=f"{routes.EXECUTION_JOBS_FULL_PATH}/{job_id.job_id}",
@@ -227,7 +223,7 @@ class ApiWrapper:
227
223
  },
228
224
  http_client=http_client,
229
225
  )
230
- return ExecutionJobDetailsV1.model_validate(data)
226
+ return execution_request.ExecutionJobDetails.model_validate(data)
231
227
 
232
228
  @classmethod
233
229
  async def call_cancel_execution_job(
@@ -248,7 +244,7 @@ class ApiWrapper:
248
244
  offset: int,
249
245
  limit: int,
250
246
  http_client: Optional[httpx.AsyncClient] = None,
251
- ) -> ExecutionJobsQueryResultsV1:
247
+ ) -> execution_request.ExecutionJobsQueryResults:
252
248
  data = await cls._call_task(
253
249
  http_method=HTTPMethod.GET,
254
250
  url=f"{routes.EXECUTION_JOBS_FULL_PATH}",
@@ -258,7 +254,7 @@ class ApiWrapper:
258
254
  },
259
255
  http_client=http_client,
260
256
  )
261
- return ExecutionJobsQueryResultsV1.model_validate(data)
257
+ return execution_request.ExecutionJobsQueryResults.model_validate(data)
262
258
 
263
259
  @classmethod
264
260
  async def call_analysis_task(
@@ -5,7 +5,6 @@ import webbrowser
5
5
  from collections.abc import Sequence
6
6
  from importlib.util import find_spec
7
7
  from typing import Any, Optional, Union
8
- from urllib.parse import urljoin
9
8
 
10
9
  import plotly.graph_objects as go
11
10
 
@@ -21,7 +20,6 @@ from classiq.analyzer.analyzer_utilities import (
21
20
  DeviceName,
22
21
  ProviderNameEnum,
23
22
  )
24
- from classiq.analyzer.url_utils import circuit_page_uri, client_ide_base_url
25
23
 
26
24
  find_ipywidgets = find_spec("ipywidgets")
27
25
  VBox = Any
@@ -63,22 +61,6 @@ class Analyzer(AnalyzerUtilities):
63
61
  transpilation_option=self.circuit.model.execution_preferences.transpile_to_hardware,
64
62
  )
65
63
 
66
- def analyzer_app(self) -> None:
67
- """Opens the analyzer app with synthesis interactive results.
68
-
69
- Returns:
70
- None.
71
- """
72
- result = async_utils.run(ApiWrapper.call_analyzer_app(self.circuit))
73
- webbrowser.open_new_tab(
74
- urljoin(
75
- client_ide_base_url(),
76
- circuit_page_uri(
77
- circuit_id=result.id, circuit_version=self.circuit.version
78
- ),
79
- )
80
- )
81
-
82
64
  def get_available_devices(
83
65
  self, providers: Optional[list[ProviderNameEnum]] = None
84
66
  ) -> dict[ProviderNameEnum, list[DeviceName]]:
@@ -7,6 +7,7 @@ import classiq
7
7
 
8
8
  QUERY_START_MARK = "?"
9
9
  VERSION_QUERY_PARAM = "version"
10
+ LOGIN_QUERY_PARAM = "login"
10
11
 
11
12
 
12
13
  def client_ide_base_url() -> str:
@@ -14,13 +15,17 @@ def client_ide_base_url() -> str:
14
15
  return str(client.config.ide)
15
16
 
16
17
 
17
- def versioned_url_params(circuit_version: str) -> str:
18
- return QUERY_START_MARK + urllib.parse.urlencode(
19
- {VERSION_QUERY_PARAM: circuit_version}
18
+ def circuit_page_search_params(circuit_version: str) -> str:
19
+ return urllib.parse.urlencode(
20
+ {
21
+ LOGIN_QUERY_PARAM: True,
22
+ VERSION_QUERY_PARAM: circuit_version,
23
+ }
20
24
  )
21
25
 
22
26
 
23
27
  def circuit_page_uri(circuit_id: str, circuit_version: str) -> str:
24
28
  url = urljoin(f"{routes.ANALYZER_CIRCUIT_PAGE}/", circuit_id)
25
- url += versioned_url_params(circuit_version=circuit_version)
29
+ query_string = circuit_page_search_params(circuit_version)
30
+ url += QUERY_START_MARK + query_string
26
31
  return url
@@ -1,3 +1,4 @@
1
+ import copy
1
2
  import math
2
3
  import re
3
4
  from collections import defaultdict
@@ -209,6 +210,7 @@ def convert_pyomo_to_global_presentation(
209
210
  def pyomo2qmod(
210
211
  struct_name: str, pyo_model: ConcreteModel
211
212
  ) -> CombinatorialOptimizationStructDeclaration:
213
+ pyo_model = copy.deepcopy(pyo_model)
212
214
  symbols_map = PyomoSympyBimap()
213
215
 
214
216
  variables: list[sympy.Symbol] = []
@@ -24,7 +24,6 @@ from classiq.open_library.functions.utility_functions import (
24
24
  from classiq.qmod.builtins.functions import RX
25
25
  from classiq.qmod.builtins.operations import allocate, phase, repeat
26
26
  from classiq.qmod.cparam import CReal
27
- from classiq.qmod.create_model_function import create_model
28
27
  from classiq.qmod.qfunc import qfunc
29
28
  from classiq.qmod.qmod_parameter import CArray
30
29
  from classiq.qmod.qmod_variable import Output, QVar
@@ -75,17 +74,15 @@ class CombinatorialProblem:
75
74
  hadamard_transform(v)
76
75
  repeat(
77
76
  self.num_layers_,
78
- lambda i: [ # type:ignore[arg-type]
79
- phase(
80
- -self.cost_func(v), params[i]
81
- ), # type:ignore[func-returns-value]
77
+ lambda i: [
78
+ phase(-self.cost_func(v), params[i]),
82
79
  apply_to_all(lambda q: RX(params[self.num_layers_ + i], q), v),
83
80
  ],
84
81
  )
85
82
 
86
- self.model_ = create_model(
87
- main, constraints=constraints, preferences=preferences
88
- ) # type:ignore[assignment]
83
+ self.model_ = main.create_model(
84
+ constraints=constraints, preferences=preferences
85
+ ).get_model() # type:ignore[assignment]
89
86
  return self.model_ # type:ignore[return-value]
90
87
 
91
88
  def get_qprog(self) -> SerializedQuantumProgram:
@@ -199,13 +196,13 @@ def execute_qaoa(
199
196
  hadamard_transform(v)
200
197
  repeat(
201
198
  num_layers,
202
- lambda i: [ # type:ignore[arg-type]
203
- phase(-cost_func(v), params[i]), # type:ignore[func-returns-value]
199
+ lambda i: [
200
+ phase(-cost_func(v), params[i]),
204
201
  apply_to_all(lambda q: RX(params[num_layers + i], q), v),
205
202
  ],
206
203
  )
207
204
 
208
- model = create_model(main)
205
+ model = main.create_model().get_model()
209
206
  qprog = synthesize(model)
210
207
 
211
208
  with ExecutionSession(qprog, execution_preferences) as es:
@@ -18,7 +18,7 @@ class QuantumGradient(abc.ABC):
18
18
  execute: ExecuteFunction,
19
19
  post_process: PostProcessFunction,
20
20
  *args: Any,
21
- **kwargs: Any
21
+ **kwargs: Any,
22
22
  ) -> None:
23
23
  self._execute = execute
24
24
  self._post_process = post_process
@@ -71,7 +71,7 @@ class SimpleQuantumGradient(QuantumGradient):
71
71
  post_process: PostProcessFunction,
72
72
  epsilon: float = EPSILON,
73
73
  *args: Any,
74
- **kwargs: Any
74
+ **kwargs: Any,
75
75
  ) -> None:
76
76
  super().__init__(quantum_program, execute, post_process)
77
77
  self._epsilon = epsilon
@@ -94,7 +94,7 @@ def iter_inputs_weights(
94
94
  post_process: PostProcessFunction,
95
95
  *,
96
96
  expected_shape: Shape = (),
97
- requires_grad: Optional[bool] = None
97
+ requires_grad: Optional[bool] = None,
98
98
  ) -> Tensor:
99
99
  if is_single_layer_circuit(weights):
100
100
  iter_weights = torch.reshape(weights, (1, weights.shape[0]))
classiq/execution/jobs.py CHANGED
@@ -9,7 +9,6 @@ from classiq.interface.exceptions import (
9
9
  ClassiqAPIError,
10
10
  ClassiqError,
11
11
  )
12
- from classiq.interface.execution.jobs import ExecutionJobDetailsV1
13
12
  from classiq.interface.executor.execution_request import ExecutionJobDetails, JobCost
14
13
  from classiq.interface.executor.execution_result import ResultsCollection
15
14
  from classiq.interface.executor.result import (
@@ -26,8 +25,6 @@ from classiq._internals.async_utils import syncify_function
26
25
  from classiq._internals.client import client
27
26
  from classiq._internals.jobs import JobID, JobPoller
28
27
 
29
- _JobDetails = Union[ExecutionJobDetails, ExecutionJobDetailsV1]
30
-
31
28
 
32
29
  class ClassiqExecutionResultError(ClassiqError):
33
30
  def __init__(self, primitive: str) -> None:
@@ -37,10 +34,10 @@ class ClassiqExecutionResultError(ClassiqError):
37
34
 
38
35
 
39
36
  class ExecutionJob:
40
- _details: _JobDetails
37
+ _details: ExecutionJobDetails
41
38
  _result: Optional[ResultsCollection]
42
39
 
43
- def __init__(self, details: _JobDetails) -> None:
40
+ def __init__(self, details: ExecutionJobDetails) -> None:
44
41
  self._details = details
45
42
  self._result = None
46
43
 
@@ -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.68.0'
6
+ SEMVER_VERSION = '0.70.0'
7
7
  VERSION = str(Version(SEMVER_VERSION))
@@ -172,15 +172,20 @@ class ClassiqNvidiaBackendNames(StrEnum):
172
172
 
173
173
  SIMULATOR = "nvidia_simulator"
174
174
  SIMULATOR_STATEVECTOR = "nvidia_simulator_statevector"
175
+ BRAKET_NVIDIA_SIMULATOR = "braket_nvidia_simulator"
176
+ BRAKET_NVIDIA_SIMULATOR_STATEVECTOR = "braket_nvidia_simulator_statevector"
177
+
178
+ def is_braket_nvidia_backend(self) -> bool:
179
+ return self in (
180
+ self.BRAKET_NVIDIA_SIMULATOR,
181
+ self.BRAKET_NVIDIA_SIMULATOR_STATEVECTOR,
182
+ )
175
183
 
176
184
 
177
185
  class IntelBackendNames(StrEnum):
178
186
  SIMULATOR = "intel_qsdk_simulator"
179
187
 
180
188
 
181
- AllClassiqBackendNames = Union[ClassiqSimulatorBackendNames, ClassiqNvidiaBackendNames]
182
-
183
-
184
189
  class GoogleNvidiaBackendNames(StrEnum):
185
190
  """
186
191
  Google backend names which Classiq Supports running on.
@@ -1,5 +1,6 @@
1
1
  from collections.abc import Collection
2
2
  from functools import reduce
3
+ from itertools import combinations
3
4
  from typing import (
4
5
  Any,
5
6
  Optional,
@@ -84,31 +85,11 @@ class PauliOperator(HashablePydanticBaseModel, VersionedModel):
84
85
  raise ClassiqValueError("Pauli strings have incompatible lengths.")
85
86
  return pauli_list
86
87
 
87
- @staticmethod
88
- def check_if_hermitian(pauli_list: PydanticPauliList) -> bool:
89
- if all(isinstance(summand[1], (float, int, complex)) for summand in pauli_list):
90
- if all(np.isclose(summand[1].imag, 0) for summand in pauli_list): # type: ignore[union-attr]
91
- return True
92
-
93
- for pauli_string, coeff in pauli_list:
94
- reverse_string = pauli_string[::-1]
95
- reverse_found = False
96
- for other_string, other_coeff in pauli_list:
97
- if other_string == reverse_string and np.isclose(
98
- coeff, other_coeff.conjugate() # type: ignore[union-attr]
99
- ):
100
- reverse_found = True
101
- break
102
- if not reverse_found:
103
- return False
104
- return True
105
- return False
106
-
107
88
  @pydantic.model_validator(mode="before")
108
89
  @classmethod
109
90
  def _validate_hermitianity(cls, values: dict[str, Any]) -> dict[str, Any]:
110
91
  pauli_list = values.get("pauli_list", [])
111
- if PauliOperator.check_if_hermitian(pauli_list):
92
+ if all(isinstance(summand[1], (float, int, complex)) for summand in pauli_list):
112
93
  values["is_hermitian"] = all(
113
94
  np.isclose(complex(summand[1]).real, summand[1])
114
95
  for summand in pauli_list
@@ -144,16 +125,19 @@ class PauliOperator(HashablePydanticBaseModel, VersionedModel):
144
125
  @property
145
126
  def is_commutative(self) -> bool:
146
127
  return all(
147
- self._is_sub_pauli_commutative(
148
- [summand[0][qubit_num] for summand in self.pauli_list]
149
- )
150
- for qubit_num in range(self.num_qubits)
128
+ self._do_paulis_commute(first[0], second[0])
129
+ for first, second in combinations(self.pauli_list, 2)
151
130
  )
152
131
 
153
132
  @staticmethod
154
- def _is_sub_pauli_commutative(qubit_pauli_string: Union[list[str], str]) -> bool:
155
- unique_paulis = set(qubit_pauli_string) - {"I"}
156
- return len(unique_paulis) <= 1
133
+ def _do_paulis_commute(
134
+ first: PydanticPauliMonomialStr, second: PydanticPauliMonomialStr
135
+ ) -> bool:
136
+ commute = True
137
+ for c1, c2 in zip(first, second):
138
+ if (c1 != "I") and (c2 != "I") and (c1 != c2):
139
+ commute = not commute
140
+ return commute
157
141
 
158
142
  @property
159
143
  def num_qubits(self) -> int:
@@ -0,0 +1,22 @@
1
+ from classiq.interface.model.allocate import Allocate
2
+ from classiq.interface.model.quantum_function_call import QuantumFunctionCall
3
+ from classiq.interface.model.statement_block import (
4
+ ConcreteQuantumStatement,
5
+ StatementBlock,
6
+ )
7
+
8
+ """
9
+ This module contains helper functions to determine if a given quantum statement
10
+ is an allocation or free statement.
11
+ """
12
+
13
+
14
+ def is_allocate_or_free(concrete_quantum_statement: ConcreteQuantumStatement) -> bool:
15
+ return isinstance(concrete_quantum_statement, Allocate) or (
16
+ isinstance(concrete_quantum_statement, QuantumFunctionCall)
17
+ and concrete_quantum_statement.function == "free"
18
+ )
19
+
20
+
21
+ def is_allocate_or_free_by_backref(back_refs: StatementBlock) -> bool:
22
+ return len(back_refs) > 0 and is_allocate_or_free(back_refs[0])
@@ -1,34 +1,23 @@
1
- import json
2
1
  from collections.abc import Mapping
3
- from typing import Any, Optional, Union
2
+ from typing import Optional, Union
4
3
  from uuid import UUID
5
4
 
6
5
  from pydantic import BaseModel, Field
7
6
 
8
- from classiq.interface.enum_utils import StrEnum
7
+ from classiq.interface.debug_info import back_ref_util
9
8
  from classiq.interface.generator.generated_circuit_data import (
10
9
  FunctionDebugInfoInterface,
11
10
  OperationLevel,
11
+ StatementType,
12
12
  )
13
13
  from classiq.interface.model.statement_block import ConcreteQuantumStatement
14
14
 
15
15
  ParameterValue = Union[float, int, str, None]
16
16
 
17
17
 
18
- class StatementType(StrEnum):
19
- CONTROL = "control"
20
- POWER = "power"
21
- INVERT = "invert"
22
- WITHIN_APPLY = "within_apply"
23
- ASSIGNMENT = "assignment"
24
- REPEAT = "repeat"
25
-
26
-
27
18
  class FunctionDebugInfo(BaseModel):
28
19
  name: str
29
- # Parameters describe classical parameters passed to function
30
- parameters: dict[str, str]
31
- level: OperationLevel
20
+ level: OperationLevel = Field(default=OperationLevel.UNKNOWN)
32
21
  statement_type: Union[StatementType, None] = None
33
22
  is_allocate_or_free: bool = Field(default=False)
34
23
  is_inverse: bool = Field(default=False)
@@ -36,12 +25,13 @@ class FunctionDebugInfo(BaseModel):
36
25
  port_to_passed_variable_map: dict[str, str] = Field(default_factory=dict)
37
26
  node: Optional[ConcreteQuantumStatement] = None
38
27
 
39
- @staticmethod
40
- def param_controller(value: Any) -> str:
41
- try:
42
- return json.dumps(value)
43
- except TypeError:
44
- return repr(value)
28
+ @property
29
+ def is_allocate_or_free_(self) -> bool:
30
+ return (
31
+ back_ref_util.is_allocate_or_free(self.node)
32
+ if self.node is not None
33
+ else self.is_allocate_or_free
34
+ )
45
35
 
46
36
  def update_map_from_port_mapping(self, port_mapping: Mapping[str, str]) -> None:
47
37
  new_port_to_passed_variable_map = self.port_to_passed_variable_map.copy()
@@ -21,6 +21,7 @@ class OptimizerType(StrEnum):
21
21
  L_BFGS_B = "L_BFGS_B"
22
22
  NELDER_MEAD = "NELDER_MEAD"
23
23
  ADAM = "ADAM"
24
+ SLSQP = "SLSQP"
24
25
 
25
26
 
26
27
  class OptimizerPreferences(BaseModel):
@@ -6,6 +6,7 @@ class QuantumInstructionSet(StrEnum):
6
6
  QASM = "qasm"
7
7
  QSHARP = "qsharp"
8
8
  IONQ = "ionq"
9
+ INTERNAL = "_internal"
9
10
 
10
11
  @classmethod
11
12
  def from_suffix(cls, suffix: str) -> "QuantumInstructionSet":
@@ -22,6 +22,9 @@ from classiq.interface.generator.arith.arithmetic_result_builder import (
22
22
  )
23
23
  from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
24
24
  from classiq.interface.generator.expressions.expression import Expression
25
+ from classiq.interface.generator.expressions.expression_constants import (
26
+ BOOLEAN_LITERALS,
27
+ )
25
28
  from classiq.interface.model.quantum_type import (
26
29
  QuantumNumeric,
27
30
  QuantumType,
@@ -40,6 +43,10 @@ def is_zero(expr: str) -> bool:
40
43
  return is_constant(expr) and float(expr) == 0
41
44
 
42
45
 
46
+ def is_bool(expr: str) -> bool:
47
+ return expr in BOOLEAN_LITERALS
48
+
49
+
43
50
  class Arithmetic(ArithmeticExpressionABC):
44
51
  target: Optional[RegisterArithmeticInfo] = None
45
52
  inputs_to_save: set[str] = pydantic.Field(default_factory=set)
@@ -115,7 +122,7 @@ def get_arithmetic_params(
115
122
  def compute_arithmetic_result_type(
116
123
  expr_str: str, var_types: dict[str, QuantumType], machine_precision: int
117
124
  ) -> QuantumNumeric:
118
- if is_zero(expr_str):
125
+ if is_zero(expr_str) or is_bool(expr_str):
119
126
  return QuantumNumeric(
120
127
  size=Expression(expr="1"),
121
128
  is_signed=Expression(expr="False"),
@@ -153,6 +160,17 @@ def aggregate_numeric_types(
153
160
  )
154
161
 
155
162
 
163
+ def _eval_num(val: ast.AST) -> float:
164
+ if isinstance(val, ast.Num):
165
+ return cast(float, val.value)
166
+ if isinstance(val, ast.UnaryOp) and isinstance(val.op, ast.USub):
167
+ return -_eval_num(val.operand)
168
+ raise ClassiqValueError(
169
+ "Classical lists with quantum subscripts must contain compile-time classical "
170
+ "real numbers"
171
+ )
172
+
173
+
156
174
  class _QuantumSubscriptRemover(ast.NodeTransformer):
157
175
  def __init__(self, machine_precision: int) -> None:
158
176
  self._machine_precision = machine_precision
@@ -161,10 +179,7 @@ class _QuantumSubscriptRemover(ast.NodeTransformer):
161
179
  def visit_Call(self, node: ast.Call) -> ast.expr:
162
180
  if not isinstance(node.func, ast.Name) or node.func.id != "Piecewise":
163
181
  return node
164
- items = [
165
- cast(float, cast(ast.Num, cast(ast.Tuple, arg).elts[0]).value)
166
- for arg in node.args
167
- ]
182
+ items = [_eval_num(cast(ast.Tuple, arg).elts[0]) for arg in node.args]
168
183
  numeric_types = [
169
184
  compute_arithmetic_result_type(str(num), {}, self._machine_precision)
170
185
  for num in items
@@ -172,7 +187,7 @@ class _QuantumSubscriptRemover(ast.NodeTransformer):
172
187
  unified_numeric_type = register_info_to_quantum_type(
173
188
  aggregate_numeric_types(numeric_types)
174
189
  )
175
- substitution_var_name = f"__lut__{len(self.substitutions_types)}__"
190
+ substitution_var_name = f"lut__{len(self.substitutions_types)}__"
176
191
  self.substitutions_types[substitution_var_name] = unified_numeric_type
177
192
  return ast.Name(id=substitution_var_name)
178
193
 
@@ -59,7 +59,7 @@ def get_params(
59
59
  machine_precision: int,
60
60
  output_size: Optional[int] = None,
61
61
  inplace_arg: Optional[str] = None,
62
- target: Optional[RegisterArithmeticInfo] = None
62
+ target: Optional[RegisterArithmeticInfo] = None,
63
63
  ) -> ArithmeticOperationParams:
64
64
  operation = id2op(node_id)
65
65
  if target and not operation_allows_target(operation):
@@ -334,7 +334,7 @@ def logical_and_params_getter(
334
334
  machine_precision: int,
335
335
  output_size: Optional[int] = None,
336
336
  inplace_arg: Optional[str] = None,
337
- target: Optional[RegisterArithmeticInfo] = None
337
+ target: Optional[RegisterArithmeticInfo] = None,
338
338
  ) -> ArithmeticOperationParams:
339
339
  return LogicalAnd(args=arg, target=target, machine_precision=machine_precision)
340
340
 
@@ -344,7 +344,7 @@ def logical_or_params_getter(
344
344
  machine_precision: int,
345
345
  output_size: Optional[int] = None,
346
346
  inplace_arg: Optional[str] = None,
347
- target: Optional[RegisterArithmeticInfo] = None
347
+ target: Optional[RegisterArithmeticInfo] = None,
348
348
  ) -> ArithmeticOperationParams:
349
349
  return LogicalOr(args=arg, target=target, machine_precision=machine_precision)
350
350
 
@@ -49,6 +49,10 @@ class CircuitCodeInterface(pydantic.BaseModel):
49
49
  def qasm_cirq_compatible(self) -> Optional[Code]:
50
50
  return self.outputs.get(QuantumFormat.QASM_CIRQ_COMPATIBLE)
51
51
 
52
+ @property
53
+ def _execution_serialization(self) -> Optional[Code]:
54
+ return self.outputs.get(QuantumFormat.EXECUTION_SERIALIZATION)
55
+
52
56
  def get_code(self, instruction_set: QuantumInstructionSet) -> Code:
53
57
  quantum_format: QuantumFormat = INSTRUCTION_SET_TO_FORMAT[instruction_set]
54
58
  code = self.outputs.get(quantum_format)
@@ -12,6 +12,7 @@ INSTRUCTION_SET_TO_FORMAT: dict[QuantumInstructionSet, QuantumFormat] = {
12
12
  QuantumInstructionSet.QASM: QuantumFormat.QASM,
13
13
  QuantumInstructionSet.QSHARP: QuantumFormat.QSHARP,
14
14
  QuantumInstructionSet.IONQ: QuantumFormat.IONQ,
15
+ QuantumInstructionSet.INTERNAL: QuantumFormat.EXECUTION_SERIALIZATION,
15
16
  }
16
17
  VENDOR_TO_INSTRUCTION_SET: dict[Provider, QuantumInstructionSet] = {
17
18
  Provider.CLASSIQ: QuantumInstructionSet.QASM,
@@ -1,5 +1,3 @@
1
- from classiq.interface.generator.functions.classical_type import CLASSICAL_ATTRIBUTES
2
-
3
1
  SUPPORTED_PYTHON_BUILTIN_FUNCTIONS = {"len", "sum", "print"}
4
2
 
5
3
  SUPPORTED_CLASSIQ_BUILTIN_FUNCTIONS = {
@@ -33,6 +31,7 @@ SUPPORTED_ATOMIC_EXPRESSION_FUNCTIONS = {
33
31
  *SUPPORTED_PYTHON_BUILTIN_FUNCTIONS,
34
32
  }
35
33
 
34
+ CLASSICAL_ATTRIBUTES = {"len", "size", "is_signed", "fraction_digits"}
36
35
  SUPPORTED_ATOMIC_EXPRESSION_FUNCTIONS_QMOD = (
37
36
  SUPPORTED_ATOMIC_EXPRESSION_FUNCTIONS - CLASSICAL_ATTRIBUTES
38
37
  )
@@ -4,10 +4,15 @@ from sympy import Expr
4
4
  from sympy.logic.boolalg import Boolean
5
5
 
6
6
  from classiq.interface.generator.expressions.handle_identifier import HandleIdentifier
7
- from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
8
- from classiq.interface.generator.expressions.qmod_struct_instance import (
7
+ from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
8
+ ClassicalProxy,
9
+ )
10
+ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
9
11
  QmodStructInstance,
10
12
  )
13
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_sized_proxy import (
14
+ QmodSizedProxy,
15
+ )
11
16
  from classiq.interface.generator.expressions.type_proxy import TypeProxy
12
17
 
13
18
  RuntimeConstant = Union[
@@ -19,6 +24,7 @@ RuntimeConstant = Union[
19
24
  QmodSizedProxy,
20
25
  TypeProxy,
21
26
  HandleIdentifier,
27
+ ClassicalProxy,
22
28
  ]
23
29
  RuntimeExpression = Union[Expr, Boolean]
24
30
  ExpressionValue = Union[RuntimeConstant, RuntimeExpression]