classiq 0.75.0__py3-none-any.whl → 0.77.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 (101) hide show
  1. classiq/_internals/api_wrapper.py +36 -0
  2. classiq/analyzer/show_interactive_hack.py +58 -2
  3. classiq/applications/chemistry/chemistry_model_constructor.py +15 -7
  4. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +11 -1
  5. classiq/applications/combinatorial_optimization/combinatorial_problem.py +8 -7
  6. classiq/applications/qnn/gradients/quantum_gradient.py +3 -5
  7. classiq/applications/qnn/gradients/simple_quantum_gradient.py +2 -2
  8. classiq/applications/qnn/qlayer.py +14 -19
  9. classiq/applications/qnn/types.py +1 -4
  10. classiq/execution/__init__.py +3 -0
  11. classiq/execution/execution_session.py +3 -16
  12. classiq/execution/qnn.py +2 -2
  13. classiq/execution/user_budgets.py +38 -0
  14. classiq/executor.py +7 -19
  15. classiq/interface/_version.py +1 -1
  16. classiq/interface/debug_info/debug_info.py +16 -2
  17. classiq/interface/executor/user_budget.py +56 -0
  18. classiq/interface/generator/application_apis/finance_declarations.py +3 -0
  19. classiq/interface/generator/expressions/atomic_expression_functions.py +3 -0
  20. classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +30 -124
  21. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +45 -21
  22. classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
  23. classiq/interface/generator/expressions/proxies/classical/utils.py +12 -11
  24. classiq/interface/generator/expressions/proxies/quantum/qmod_qarray_proxy.py +6 -15
  25. classiq/interface/generator/expressions/proxies/quantum/qmod_qscalar_proxy.py +22 -6
  26. classiq/interface/generator/expressions/proxies/quantum/qmod_sized_proxy.py +9 -4
  27. classiq/interface/generator/expressions/sympy_supported_expressions.py +1 -0
  28. classiq/interface/generator/functions/classical_type.py +6 -1
  29. classiq/interface/generator/functions/type_name.py +7 -2
  30. classiq/interface/generator/functions/type_qualifier.py +15 -0
  31. classiq/interface/generator/model/preferences/preferences.py +7 -0
  32. classiq/interface/generator/quantum_program.py +5 -19
  33. classiq/interface/helpers/backward_compatibility.py +9 -0
  34. classiq/interface/helpers/datastructures.py +6 -0
  35. classiq/interface/model/handle_binding.py +8 -0
  36. classiq/interface/model/model.py +3 -6
  37. classiq/interface/model/port_declaration.py +1 -2
  38. classiq/interface/model/quantum_function_call.py +31 -1
  39. classiq/interface/model/quantum_lambda_function.py +2 -1
  40. classiq/interface/model/quantum_statement.py +14 -1
  41. classiq/interface/server/routes.py +6 -0
  42. classiq/interface/source_reference.py +7 -2
  43. classiq/model_expansions/atomic_expression_functions_defs.py +62 -19
  44. classiq/model_expansions/capturing/captured_vars.py +18 -6
  45. classiq/model_expansions/closure.py +5 -0
  46. classiq/model_expansions/evaluators/arg_type_match.py +2 -2
  47. classiq/model_expansions/evaluators/argument_types.py +3 -3
  48. classiq/model_expansions/evaluators/classical_expression.py +9 -9
  49. classiq/model_expansions/evaluators/classical_type_inference.py +17 -6
  50. classiq/model_expansions/evaluators/parameter_types.py +45 -24
  51. classiq/model_expansions/expression_evaluator.py +21 -12
  52. classiq/model_expansions/function_builder.py +45 -0
  53. classiq/model_expansions/generative_functions.py +62 -35
  54. classiq/model_expansions/interpreters/base_interpreter.py +32 -7
  55. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +9 -3
  56. classiq/model_expansions/interpreters/generative_interpreter.py +17 -5
  57. classiq/model_expansions/quantum_operations/allocate.py +8 -3
  58. classiq/model_expansions/quantum_operations/assignment_result_processor.py +221 -20
  59. classiq/model_expansions/quantum_operations/bind.py +54 -30
  60. classiq/model_expansions/quantum_operations/block_evaluator.py +42 -0
  61. classiq/model_expansions/quantum_operations/call_emitter.py +35 -18
  62. classiq/model_expansions/quantum_operations/composite_emitter.py +1 -1
  63. classiq/model_expansions/quantum_operations/declarative_call_emitter.py +23 -9
  64. classiq/model_expansions/quantum_operations/emitter.py +21 -9
  65. classiq/model_expansions/quantum_operations/quantum_function_call.py +4 -3
  66. classiq/model_expansions/scope.py +63 -10
  67. classiq/model_expansions/sympy_conversion/arithmetics.py +18 -0
  68. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +2 -0
  69. classiq/model_expansions/sympy_conversion/sympy_to_python.py +10 -1
  70. classiq/model_expansions/transformers/model_renamer.py +45 -7
  71. classiq/model_expansions/utils/handles_collector.py +1 -1
  72. classiq/model_expansions/visitors/symbolic_param_inference.py +3 -3
  73. classiq/model_expansions/visitors/variable_references.py +45 -9
  74. classiq/open_library/functions/lookup_table.py +1 -1
  75. classiq/open_library/functions/state_preparation.py +1 -1
  76. classiq/qmod/builtins/functions/allocation.py +2 -2
  77. classiq/qmod/builtins/functions/arithmetic.py +14 -12
  78. classiq/qmod/builtins/functions/standard_gates.py +23 -23
  79. classiq/qmod/create_model_function.py +21 -3
  80. classiq/qmod/declaration_inferrer.py +19 -7
  81. classiq/qmod/generative.py +9 -1
  82. classiq/qmod/global_declarative_switch.py +19 -0
  83. classiq/qmod/native/expression_to_qmod.py +4 -0
  84. classiq/qmod/native/pretty_printer.py +12 -3
  85. classiq/qmod/pretty_print/pretty_printer.py +5 -1
  86. classiq/qmod/python_classical_type.py +4 -5
  87. classiq/qmod/qfunc.py +31 -23
  88. classiq/qmod/qmod_constant.py +15 -7
  89. classiq/qmod/qmod_variable.py +7 -1
  90. classiq/qmod/quantum_expandable.py +29 -1
  91. classiq/qmod/quantum_function.py +45 -25
  92. classiq/qmod/semantics/lambdas.py +6 -2
  93. classiq/qmod/semantics/validation/main_validation.py +17 -4
  94. classiq/qmod/symbolic.py +8 -19
  95. classiq/qmod/symbolic_expr.py +26 -0
  96. classiq/qmod/write_qmod.py +36 -10
  97. classiq/synthesis.py +24 -37
  98. classiq/visualization.py +35 -0
  99. {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/METADATA +1 -1
  100. {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/RECORD +101 -96
  101. {classiq-0.75.0.dist-info → classiq-0.77.0.dist-info}/WHEEL +0 -0
@@ -28,6 +28,7 @@ from classiq.interface.generator.functions.concrete_types import (
28
28
  from classiq.interface.model.classical_parameter_declaration import (
29
29
  AnonClassicalParameterDeclaration,
30
30
  )
31
+ from classiq.interface.model.handle_binding import GeneralHandle, HandlesList
31
32
  from classiq.interface.model.port_declaration import AnonPortDeclaration
32
33
  from classiq.interface.model.quantum_function_call import (
33
34
  ArgValue,
@@ -52,6 +53,7 @@ from classiq.interface.model.variable_declaration_statement import (
52
53
  from classiq.interface.source_reference import SourceReference
53
54
 
54
55
  from classiq.qmod.generative import generative_mode_context, is_generative_mode
56
+ from classiq.qmod.global_declarative_switch import get_global_declarative_switch
55
57
  from classiq.qmod.model_state_container import QMODULE, ModelStateContainer
56
58
  from classiq.qmod.qmod_constant import QConstant
57
59
  from classiq.qmod.qmod_parameter import (
@@ -84,6 +86,11 @@ class QExpandable(QCallable, QExpandableInterface, ABC):
84
86
  self._py_callable: Callable = py_callable
85
87
  self._body: list[QuantumStatement] = list()
86
88
 
89
+ def __eq__(self, other: Any) -> bool:
90
+ return (
91
+ isinstance(other, QExpandable) and self._py_callable is other._py_callable
92
+ )
93
+
87
94
  @property
88
95
  def body(self) -> list[QuantumStatement]:
89
96
  return self._body
@@ -317,8 +324,10 @@ def prepare_arg(
317
324
  def prepare_arg(
318
325
  arg_decl: AnonPositionalArg, val: Any, func_name: Optional[str], param_name: str
319
326
  ) -> ArgValue:
320
- from classiq.qmod.quantum_function import BaseQFunc, GenerativeQFunc
327
+ from classiq.qmod.quantum_function import BaseQFunc, GenerativeQFunc, QFunc
321
328
 
329
+ if get_global_declarative_switch() and isinstance(val, GenerativeQFunc):
330
+ val = QFunc(val._py_callable)
322
331
  if isinstance(val, BaseQFunc):
323
332
  val.add_function_dependencies()
324
333
  if isinstance(val, GenerativeQFunc):
@@ -330,6 +339,9 @@ def prepare_arg(
330
339
  _validate_classical_arg(val, arg_decl, func_name)
331
340
  return Expression(expr=qmod_val_to_expr_str(val))
332
341
  elif isinstance(arg_decl, AnonPortDeclaration):
342
+ handles_list = _try_preparing_handles_list(val)
343
+ if handles_list is not None:
344
+ return handles_list
333
345
  if not isinstance(val, QVar):
334
346
  func_name_message = (
335
347
  "" if func_name is None else f" of function {func_name!r}"
@@ -505,3 +517,19 @@ def _is_legal_iterable_element(arg: Any) -> bool:
505
517
  if isinstance(arg, Iterable):
506
518
  return all(_is_legal_iterable_element(e) for e in arg)
507
519
  return True
520
+
521
+
522
+ def _try_preparing_handles_list(val: Any) -> Optional[HandlesList]:
523
+ if not isinstance(val, list):
524
+ return None
525
+ items = [
526
+ (
527
+ item.get_handle_binding()
528
+ if isinstance(item, QVar)
529
+ else _try_preparing_handles_list(item)
530
+ )
531
+ for item in val
532
+ ]
533
+ if any(item is None for item in items):
534
+ return None
535
+ return HandlesList(handles=cast(list[GeneralHandle], items))
@@ -14,7 +14,7 @@ from classiq.interface.generator.functions.port_declaration import (
14
14
  from classiq.interface.generator.model.constraints import Constraints
15
15
  from classiq.interface.generator.model.preferences.preferences import Preferences
16
16
  from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
17
- from classiq.interface.model.model import Model
17
+ from classiq.interface.model.model import MAIN_FUNCTION_NAME, Model
18
18
  from classiq.interface.model.native_function_definition import (
19
19
  NativeFunctionDefinition,
20
20
  )
@@ -26,11 +26,13 @@ from classiq.qmod.classical_function import CFunc
26
26
  from classiq.qmod.cparam import CParamAbstract
27
27
  from classiq.qmod.declaration_inferrer import infer_func_decl, is_qvar
28
28
  from classiq.qmod.generative import set_frontend_interpreter
29
+ from classiq.qmod.global_declarative_switch import get_global_declarative_switch
29
30
  from classiq.qmod.qmod_constant import QConstant
30
31
  from classiq.qmod.qmod_parameter import CArray
31
32
  from classiq.qmod.quantum_callable import QCallable, QCallableList
32
33
  from classiq.qmod.quantum_expandable import QExpandable, QTerminalCallable
33
34
  from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
35
+ from classiq.qmod.semantics.validation.main_validation import validate_main_function
34
36
  from classiq.qmod.utilities import mangle_keyword
35
37
 
36
38
 
@@ -48,6 +50,18 @@ class BaseQFunc(QExpandable):
48
50
  def func_decl(self) -> NamedParamsQuantumFunctionDeclaration:
49
51
  raise NotImplementedError
50
52
 
53
+ @property
54
+ def pure_decl(self) -> NamedParamsQuantumFunctionDeclaration:
55
+ if type(self.func_decl) is NamedParamsQuantumFunctionDeclaration:
56
+ return self.func_decl
57
+ return NamedParamsQuantumFunctionDeclaration(
58
+ **{
59
+ k: v
60
+ for k, v in self.func_decl.model_dump().items()
61
+ if k in NamedParamsQuantumFunctionDeclaration.model_fields
62
+ }
63
+ )
64
+
51
65
  @property
52
66
  def _has_inputs(self) -> bool:
53
67
  return any(
@@ -89,22 +103,10 @@ class QFunc(BaseQFunc):
89
103
  super().__init__(py_callable, compilation_metadata)
90
104
  self.compilation_metadata: Optional[CompilationMetadata] = None
91
105
 
92
- @property
93
- def pure_decl(self) -> NamedParamsQuantumFunctionDeclaration:
94
- if type(self.func_decl) is NamedParamsQuantumFunctionDeclaration:
95
- return self.func_decl
96
- return NamedParamsQuantumFunctionDeclaration(
97
- **{
98
- k: v
99
- for k, v in self.func_decl.model_dump().items()
100
- if k in NamedParamsQuantumFunctionDeclaration.model_fields
101
- }
102
- )
103
-
104
106
  @property
105
107
  def func_decl(self) -> NamedParamsQuantumFunctionDeclaration:
106
108
  name = self._py_callable.__name__
107
- if hasattr(self._qmodule, "native_defs") and name in self._qmodule.native_defs:
109
+ if name in self._qmodule.native_defs:
108
110
  return self._qmodule.native_defs[name]
109
111
  return infer_func_decl(self._py_callable, qmodule=self._qmodule)
110
112
 
@@ -141,7 +143,10 @@ class QFunc(BaseQFunc):
141
143
  functions_compilation_metadata=self._qmodule.functions_compilation_metadata,
142
144
  **{key: value for key, value in model_extra_settings if value},
143
145
  )
144
- if len(self._qmodule.generative_functions) > 0:
146
+ if (
147
+ not get_global_declarative_switch()
148
+ and len(self._qmodule.generative_functions) > 0
149
+ ):
145
150
  return self._create_generative_model(model)
146
151
  return model
147
152
 
@@ -232,12 +237,6 @@ class ExternalQFunc(QTerminalCallable):
232
237
  def pure_decl(self) -> NamedParamsQuantumFunctionDeclaration:
233
238
  return self.func_decl
234
239
 
235
- def get_implementation(self) -> NativeFunctionDefinition:
236
- model = QFunc(self._py_callable).create_model()
237
- return [
238
- func for func in model.functions if func.name == self._py_callable.__name__
239
- ][0]
240
-
241
240
 
242
241
  class GenerativeQFunc(BaseQFunc):
243
242
  FRAME_DEPTH = 3
@@ -250,15 +249,25 @@ class GenerativeQFunc(BaseQFunc):
250
249
  ) -> None:
251
250
  super().__init__(py_callable, compilation_metadata)
252
251
  self._func_decl = func_decl
252
+ self._inferred_func_decl: Optional[NamedParamsQuantumFunctionDeclaration] = None
253
253
 
254
254
  @property
255
255
  def func_decl(self) -> NamedParamsQuantumFunctionDeclaration:
256
- if self._func_decl is None:
257
- self._func_decl = infer_func_decl(self._py_callable, self._qmodule)
258
- return self._func_decl
256
+ if self._func_decl is not None:
257
+ return self._func_decl
258
+ if self._inferred_func_decl is None:
259
+ self._inferred_func_decl = infer_func_decl(self._py_callable, self._qmodule)
260
+ return self._inferred_func_decl
259
261
 
260
262
  def __call__(self, *args: Any, **kwargs: Any) -> None:
261
- self._qmodule.generative_functions[self.func_decl.name] = self
263
+ if get_global_declarative_switch():
264
+ return QFunc(self._py_callable)(*args, **kwargs)
265
+ if self.func_decl.name not in self._qmodule.generative_functions:
266
+ self._qmodule.generative_functions[self.func_decl.name] = self
267
+ if self._func_decl is None:
268
+ self._inferred_func_decl = infer_func_decl(
269
+ self._py_callable, self._qmodule
270
+ )
262
271
  super().__call__(*args, **kwargs)
263
272
 
264
273
  def create_model(
@@ -268,6 +277,17 @@ class GenerativeQFunc(BaseQFunc):
268
277
  preferences: Optional[Preferences] = None,
269
278
  classical_execution_function: Optional[CFunc] = None,
270
279
  ) -> Model:
280
+ if get_global_declarative_switch():
281
+ return QFunc(self._py_callable).create_model(
282
+ constraints,
283
+ execution_preferences,
284
+ preferences,
285
+ classical_execution_function,
286
+ )
287
+ self._qmodule.reset()
288
+ if self.func_decl.name == MAIN_FUNCTION_NAME:
289
+ validate_main_function(self.func_decl)
290
+
271
291
  def _dec_main(*args: Any, **kwargs: Any) -> None:
272
292
  self(*args, **kwargs)
273
293
 
@@ -1,3 +1,4 @@
1
+ from classiq.interface.helpers.backward_compatibility import zip_strict
1
2
  from classiq.interface.model.port_declaration import PortDeclaration
2
3
  from classiq.interface.model.quantum_function_declaration import (
3
4
  AnonQuantumOperandDeclaration,
@@ -14,8 +15,11 @@ def get_renamed_parameters(
14
15
  renamed_parameters: list[str] = []
15
16
  renamed_operands: dict[str, QuantumOperandDeclaration] = {}
16
17
  renamed_ports: list[PortDeclaration] = []
17
- for idx, param in enumerate(lambda_func.func_decl.positional_arg_declarations):
18
- param_name = lambda_func.pos_rename_params[idx]
18
+ for param, param_name in zip_strict(
19
+ lambda_func.func_decl.positional_arg_declarations,
20
+ lambda_func.pos_rename_params,
21
+ strict=False,
22
+ ):
19
23
  if isinstance(param, AnonClassicalParameterDeclaration):
20
24
  renamed_parameters.append(param_name)
21
25
  elif isinstance(param, AnonQuantumOperandDeclaration):
@@ -1,16 +1,22 @@
1
- from classiq.interface.exceptions import ClassiqExpansionError
1
+ from classiq.interface.exceptions import ClassiqExpansionError, ClassiqValueError
2
2
  from classiq.interface.generator.functions.classical_type import (
3
3
  ClassicalArray,
4
4
  ClassicalList,
5
5
  )
6
6
  from classiq.interface.generator.functions.concrete_types import ConcreteClassicalType
7
- from classiq.interface.model.native_function_definition import NativeFunctionDefinition
8
- from classiq.interface.model.quantum_function_declaration import PositionalArg
7
+ from classiq.interface.generator.functions.port_declaration import (
8
+ PortDeclarationDirection,
9
+ )
10
+ from classiq.interface.model.port_declaration import PortDeclaration
11
+ from classiq.interface.model.quantum_function_declaration import (
12
+ NamedParamsQuantumFunctionDeclaration,
13
+ PositionalArg,
14
+ )
9
15
 
10
16
  from classiq import ClassicalParameterDeclaration
11
17
 
12
18
 
13
- def validate_main_function(func: NativeFunctionDefinition) -> None:
19
+ def validate_main_function(func: NamedParamsQuantumFunctionDeclaration) -> None:
14
20
  for param in func.positional_arg_declarations:
15
21
  _validate_main_param(param)
16
22
 
@@ -18,6 +24,8 @@ def validate_main_function(func: NativeFunctionDefinition) -> None:
18
24
  def _validate_main_param(param: PositionalArg) -> None:
19
25
  if isinstance(param, ClassicalParameterDeclaration):
20
26
  _validate_main_classical_param_type(param.classical_type, param.name)
27
+ if isinstance(param, PortDeclaration):
28
+ _validate_main_quantum_param_type(param)
21
29
 
22
30
 
23
31
  def _validate_main_classical_param_type(
@@ -30,3 +38,8 @@ def _validate_main_classical_param_type(
30
38
  )
31
39
  if isinstance(param, ClassicalArray):
32
40
  _validate_main_classical_param_type(param.element_type, param_name)
41
+
42
+
43
+ def _validate_main_quantum_param_type(param: PortDeclaration) -> None:
44
+ if param.direction != PortDeclarationDirection.Output:
45
+ raise ClassiqValueError("Function 'main' cannot declare quantum inputs")
classiq/qmod/symbolic.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import sys
2
- from collections.abc import Sequence
3
2
  from typing import (
4
3
  TYPE_CHECKING,
5
4
  Any,
@@ -12,6 +11,9 @@ from typing import (
12
11
  import numpy as np
13
12
 
14
13
  from classiq.interface.exceptions import ClassiqValueError
14
+ from classiq.interface.generator.expressions.proxies.classical.any_classical_value import (
15
+ subscript_to_str,
16
+ )
15
17
 
16
18
  from classiq.qmod import model_state_container
17
19
  from classiq.qmod.declaration_inferrer import python_type_to_qmod
@@ -24,6 +26,7 @@ from classiq.qmod.qmod_parameter import (
24
26
  )
25
27
  from classiq.qmod.symbolic_expr import SymbolicExpr
26
28
  from classiq.qmod.symbolic_type import SymbolicTypes
29
+ from classiq.qmod.utilities import qmod_val_to_expr_str
27
30
 
28
31
  pi = SymbolicExpr("pi", False)
29
32
  E = SymbolicExpr("E", False)
@@ -306,27 +309,13 @@ def sum(arr: SymbolicTypes) -> CParamScalar:
306
309
  return symbolic_function(arr)
307
310
 
308
311
 
309
- def _subscript_to_str(index: Any) -> str:
310
- if not isinstance(index, slice):
311
- return str(index)
312
- expr = ""
313
- if index.start is not None:
314
- expr += str(index.start)
315
- expr += ":"
316
- if index.stop is not None:
317
- expr += str(index.stop)
318
- if index.step is not None:
319
- expr += f":{index.step}"
320
- return expr
321
-
322
-
323
312
  def subscript(
324
- array: Union[
325
- Sequence[Union[float, CReal, CParamScalar]], CArray[CReal], np.ndarray
326
- ],
313
+ array: Union[list, CArray[CReal], np.ndarray],
327
314
  index: Any,
328
315
  ) -> CParamScalar:
329
- return CParamScalar(expr=f"{_unwrap_numpy(array)}[{_subscript_to_str(index)}]")
316
+ return CParamScalar(
317
+ expr=f"{qmod_val_to_expr_str(_unwrap_numpy(array))}[{subscript_to_str(index)}]"
318
+ )
330
319
 
331
320
 
332
321
  __all__ = [
@@ -177,3 +177,29 @@ class SymbolicEquality(SymbolicExpr):
177
177
  super().__init__(sym_expr._expr, sym_expr.is_quantum)
178
178
  self.lhs = lhs
179
179
  self.rhs = rhs
180
+
181
+
182
+ class SymbolicSubscriptAndField(SymbolicExpr):
183
+ def __getattr__(self, item: str) -> SymbolicSubscriptAndField:
184
+ return SymbolicSubscriptAndField(f"{self}.{item}", is_quantum=self.is_quantum)
185
+
186
+ def __getitem__(self, item: Any) -> SymbolicExpr:
187
+ if isinstance(item, slice):
188
+ start = item.start
189
+ stop = item.stop
190
+ step = item.step
191
+ item_is_quantum = (
192
+ (isinstance(start, SymbolicExpr) and start.is_quantum)
193
+ or (isinstance(stop, SymbolicExpr) and stop.is_quantum)
194
+ or (isinstance(step, SymbolicExpr) and step.is_quantum)
195
+ )
196
+ start_str = "" if start is None else qmod_val_to_expr_str(start)
197
+ stop_str = "" if stop is None else qmod_val_to_expr_str(stop)
198
+ step_str = "" if step is None else qmod_val_to_expr_str(step)
199
+ item_str = f"{start_str}:{stop_str}:{step_str}"
200
+ else:
201
+ item_is_quantum = isinstance(item, SymbolicExpr) and item.is_quantum
202
+ item_str = qmod_val_to_expr_str(item)
203
+ return SymbolicSubscriptAndField(
204
+ f"{self}[{item_str}]", is_quantum=self.is_quantum or item_is_quantum
205
+ )
@@ -1,9 +1,10 @@
1
1
  import json
2
2
  from pathlib import Path
3
- from typing import Any, Optional, Union
3
+ from typing import Optional, Union
4
4
 
5
5
  from classiq.interface.model.model import Model, SerializedModel
6
6
 
7
+ from classiq.qmod.global_declarative_switch import set_global_declarative_switch
7
8
  from classiq.qmod.native.pretty_printer import DSLPrettyPrinter
8
9
  from classiq.qmod.quantum_function import GenerativeQFunc, QFunc
9
10
  from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
@@ -17,8 +18,7 @@ def write_qmod(
17
18
  name: str,
18
19
  directory: Optional[Path] = None,
19
20
  decimal_precision: int = DEFAULT_DECIMAL_PRECISION,
20
- *args: Any,
21
- **kwargs: Any,
21
+ symbolic_only: bool = True,
22
22
  ) -> None:
23
23
  """
24
24
  Creates a native Qmod file from a serialized model and outputs the synthesis options (Preferences and Constraints) to a file.
@@ -29,20 +29,15 @@ def write_qmod(
29
29
  name: The name to save the file by.
30
30
  directory: The directory to save the files in. If None, the current working directory is used.
31
31
  decimal_precision: The number of decimal places to use for numbers, set to 4 by default.
32
- args: (placeholder)
33
- kwargs: (placeholder)
32
+ symbolic_only: If True keep function definitions un-expanded and symbolic (note that Qmod functions with parameters of Python types are not supported in this mode)
34
33
 
35
34
  Returns:
36
35
  None
37
36
  """
38
- if isinstance(model, (QFunc, GenerativeQFunc)):
39
- model_obj = model.create_model()
40
- else:
41
- model_obj = Model.model_validate_json(model)
37
+ model_obj = prepare_write_qmod_model(model, symbolic_only)
42
38
  pretty_printed_model = DSLPrettyPrinter(decimal_precision=decimal_precision).visit(
43
39
  model_obj
44
40
  )
45
-
46
41
  synthesis_options = model_obj.model_dump(
47
42
  include={"constraints", "preferences"}, exclude_none=True
48
43
  )
@@ -58,3 +53,34 @@ def write_qmod(
58
53
  native_qmod_path = directory / native_qmod_path
59
54
 
60
55
  native_qmod_path.write_text(pretty_printed_model)
56
+
57
+
58
+ def prepare_write_qmod_model(
59
+ model: Union[SerializedModel, QFunc, GenerativeQFunc], symbolic_only: bool
60
+ ) -> Model:
61
+ if isinstance(model, str) and hasattr(model, "entry_point") and symbolic_only:
62
+ model_obj = Model.model_validate_json(model)
63
+ with set_global_declarative_switch():
64
+ dec_model_obj = model.entry_point.create_model(
65
+ constraints=model_obj.constraints,
66
+ execution_preferences=model_obj.execution_preferences,
67
+ preferences=model_obj.preferences,
68
+ )
69
+ dec_constant_names = {const.name for const in dec_model_obj.constants}
70
+ all_constants = dec_model_obj.constants + [
71
+ const
72
+ for const in model_obj.constants
73
+ if const.name not in dec_constant_names
74
+ ]
75
+ return dec_model_obj.model_copy(
76
+ update={
77
+ "constants": all_constants,
78
+ "classical_execution_code": model_obj.classical_execution_code,
79
+ }
80
+ )
81
+ if isinstance(model, (QFunc, GenerativeQFunc)):
82
+ if symbolic_only:
83
+ with set_global_declarative_switch():
84
+ return model.create_model()
85
+ return model.create_model()
86
+ return Model.model_validate_json(model)
classiq/synthesis.py CHANGED
@@ -1,6 +1,4 @@
1
- from typing import Any, NewType, Optional, Union
2
-
3
- import pydantic
1
+ from typing import Any, Optional, Union
4
2
 
5
3
  from classiq.interface.analyzer.result import QasmCode
6
4
  from classiq.interface.exceptions import ClassiqError, ClassiqValueError
@@ -12,43 +10,34 @@ from classiq.interface.model.model import MAIN_FUNCTION_NAME, Model, SerializedM
12
10
  from classiq import QuantumProgram
13
11
  from classiq._internals import async_utils
14
12
  from classiq._internals.api_wrapper import ApiWrapper
15
- from classiq.qmod.quantum_function import GenerativeQFunc, QFunc
16
-
17
- SerializedQuantumProgram = NewType("SerializedQuantumProgram", str)
13
+ from classiq.qmod.create_model_function import add_entry_point
14
+ from classiq.qmod.quantum_function import BaseQFunc
18
15
 
19
- CANT_PARSE_QUANTUM_PROGRAM_MSG = (
20
- "Can not parse quantum_program into GeneratedCircuit, \n"
21
- )
16
+ SerializedQuantumProgram = QuantumProgram
22
17
 
23
18
 
24
- def show(quantum_program: SerializedQuantumProgram, display_url: bool = True) -> None:
19
+ def show(quantum_program: QuantumProgram, display_url: bool = True) -> None:
25
20
  """
26
21
  Displays the interactive representation of the quantum program in the Classiq IDE.
27
22
 
28
23
  Args:
29
24
  quantum_program:
30
- The serialized quantum program to be displayed.
25
+ The quantum program to be displayed.
31
26
  display_url:
32
27
  Whether to print the url
33
28
 
34
29
  Links:
35
30
  [Visualization tool](https://docs.classiq.io/latest/reference-manual/analyzer/quantum-program-visualization-tool/)
36
31
  """
37
- try:
38
- circuit = QuantumProgram.model_validate_json(quantum_program)
39
- except pydantic.ValidationError as exc:
40
- raise ClassiqValueError(CANT_PARSE_QUANTUM_PROGRAM_MSG) from exc
41
- circuit.show() # type: ignore[attr-defined]
32
+ QuantumProgram.model_validate(quantum_program)
33
+ quantum_program.show() # type: ignore[attr-defined]
42
34
 
43
35
 
44
- async def quantum_program_from_qasm_async(qasm: str) -> SerializedQuantumProgram:
45
- quantum_program = await ApiWrapper.get_generated_circuit_from_qasm(
46
- QasmCode(code=qasm)
47
- )
48
- return SerializedQuantumProgram(quantum_program.model_dump_json())
36
+ async def quantum_program_from_qasm_async(qasm: str) -> QuantumProgram:
37
+ return await ApiWrapper.get_generated_circuit_from_qasm(QasmCode(code=qasm))
49
38
 
50
39
 
51
- def quantum_program_from_qasm(qasm: str) -> SerializedQuantumProgram:
40
+ def quantum_program_from_qasm(qasm: str) -> QuantumProgram:
52
41
  """
53
42
  generate a quantum program from a QASM file.
54
43
 
@@ -56,25 +45,24 @@ def quantum_program_from_qasm(qasm: str) -> SerializedQuantumProgram:
56
45
  qasm: A QASM2/3 string.
57
46
 
58
47
  Returns:
59
- SerializedQuantumProgram: Quantum program serialized as a string. (See: QuantumProgram)
48
+ QuantumProgram: Quantum program. (See: QuantumProgram)
60
49
  """
61
50
  return async_utils.run(quantum_program_from_qasm_async(qasm))
62
51
 
63
52
 
64
53
  async def synthesize_async(
65
54
  serialized_model: SerializedModel,
66
- ) -> SerializedQuantumProgram:
55
+ ) -> QuantumProgram:
67
56
  model = Model.model_validate_json(serialized_model)
68
- quantum_program = await ApiWrapper.call_generation_task(model)
69
- return SerializedQuantumProgram(quantum_program.model_dump_json(indent=2))
57
+ return await ApiWrapper.call_generation_task(model)
70
58
 
71
59
 
72
60
  def synthesize(
73
- model: Union[SerializedModel, QFunc, GenerativeQFunc],
61
+ model: Union[SerializedModel, BaseQFunc],
74
62
  auto_show: bool = False,
75
63
  constraints: Optional[Constraints] = None,
76
64
  preferences: Optional[Preferences] = None,
77
- ) -> SerializedQuantumProgram:
65
+ ) -> QuantumProgram:
78
66
  """
79
67
  Synthesize a model with the Classiq engine to receive a quantum program.
80
68
  [More details](https://docs.classiq.io/latest/reference-manual/synthesis/)
@@ -86,9 +74,9 @@ def synthesize(
86
74
  preferences: Preferences for the synthesis of the model. See Preferences (Optional).
87
75
 
88
76
  Returns:
89
- SerializedQuantumProgram: Quantum program serialized as a string. (See: QuantumProgram)
77
+ QuantumProgram: Quantum program. (See: QuantumProgram)
90
78
  """
91
- if isinstance(model, (QFunc, GenerativeQFunc)):
79
+ if isinstance(model, BaseQFunc):
92
80
  func_name = model._py_callable.__name__
93
81
  if func_name != MAIN_FUNCTION_NAME:
94
82
  raise ClassiqError(
@@ -129,7 +117,7 @@ def set_preferences(
129
117
 
130
118
  model = Model.model_validate_json(serialized_model)
131
119
  model.preferences = preferences
132
- return model.get_model()
120
+ return add_entry_point(model.get_model(), serialized_model)
133
121
 
134
122
 
135
123
  def update_preferences(
@@ -149,7 +137,7 @@ def update_preferences(
149
137
 
150
138
  for key, value in kwargs.items():
151
139
  setattr(model.preferences, key, value)
152
- return model.get_model()
140
+ return add_entry_point(model.get_model(), serialized_model)
153
141
 
154
142
 
155
143
  def set_constraints(
@@ -177,7 +165,7 @@ def set_constraints(
177
165
 
178
166
  model = Model.model_validate_json(serialized_model)
179
167
  model.constraints = constraints
180
- return model.get_model()
168
+ return add_entry_point(model.get_model(), serialized_model)
181
169
 
182
170
 
183
171
  def update_constraints(
@@ -197,7 +185,7 @@ def update_constraints(
197
185
 
198
186
  for key, value in kwargs.items():
199
187
  setattr(model.constraints, key, value)
200
- return model.get_model()
188
+ return add_entry_point(model.get_model(), serialized_model)
201
189
 
202
190
 
203
191
  def set_execution_preferences(
@@ -226,7 +214,7 @@ def set_execution_preferences(
226
214
 
227
215
  model = Model.model_validate_json(serialized_model)
228
216
  model.execution_preferences = execution_preferences
229
- return model.get_model()
217
+ return add_entry_point(model.get_model(), serialized_model)
230
218
 
231
219
 
232
220
  def update_execution_preferences(
@@ -247,12 +235,11 @@ def update_execution_preferences(
247
235
  for key, value in kwargs.items():
248
236
  setattr(model.execution_preferences, key, value)
249
237
 
250
- return model.get_model()
238
+ return add_entry_point(model.get_model(), serialized_model)
251
239
 
252
240
 
253
241
  __all__ = [
254
242
  "SerializedModel",
255
- "SerializedQuantumProgram",
256
243
  "set_constraints",
257
244
  "set_execution_preferences",
258
245
  "set_preferences",
@@ -0,0 +1,35 @@
1
+ from typing import NewType
2
+
3
+ from classiq.interface.analyzer.result import DataID
4
+ from classiq.interface.exceptions import ClassiqAPIError
5
+
6
+ from classiq._internals import async_utils
7
+ from classiq._internals.api_wrapper import ApiWrapper
8
+
9
+ SerializedVisualModel = NewType("SerializedVisualModel", str)
10
+
11
+
12
+ async def visualize_async(
13
+ data_id: DataID,
14
+ ) -> SerializedVisualModel:
15
+ try:
16
+ visual_model = await ApiWrapper.call_get_visual_model(data_id.id)
17
+ except ClassiqAPIError as error:
18
+ if error.status_code != 404:
19
+ raise error
20
+ analyzer_data = await ApiWrapper.get_analyzer_app_data(data_id)
21
+ visual_model = await ApiWrapper.call_visualization_task(analyzer_data)
22
+ return SerializedVisualModel(visual_model.model_dump_json(indent=2))
23
+
24
+
25
+ def visualize(
26
+ data_id: DataID,
27
+ ) -> SerializedVisualModel:
28
+ result = async_utils.run(visualize_async(data_id))
29
+ return result
30
+
31
+
32
+ __all__ = [
33
+ "SerializedVisualModel",
34
+ "visualize",
35
+ ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: classiq
3
- Version: 0.75.0
3
+ Version: 0.77.0
4
4
  Summary: Classiq's Python SDK for quantum computing
5
5
  License: Proprietary
6
6
  Keywords: quantum computing,quantum circuits,quantum algorithms,QAD,QDL