classiq 0.56.1__py3-none-any.whl → 0.57.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 (31) hide show
  1. classiq/analyzer/show_interactive_hack.py +16 -4
  2. classiq/executor.py +1 -1
  3. classiq/interface/_version.py +1 -1
  4. classiq/interface/executor/execution_request.py +5 -5
  5. classiq/interface/generator/arith/arithmetic_expression_validator.py +28 -9
  6. classiq/interface/generator/types/builtin_enum_declarations.py +1 -0
  7. classiq/model_expansions/closure.py +24 -6
  8. classiq/model_expansions/quantum_operations/call_emitter.py +207 -0
  9. classiq/model_expansions/quantum_operations/classicalif.py +2 -2
  10. classiq/model_expansions/quantum_operations/control.py +7 -5
  11. classiq/model_expansions/quantum_operations/emitter.py +1 -186
  12. classiq/model_expansions/quantum_operations/expression_operation.py +26 -189
  13. classiq/model_expansions/quantum_operations/inplace_binary_operation.py +2 -2
  14. classiq/model_expansions/quantum_operations/invert.py +2 -2
  15. classiq/model_expansions/quantum_operations/phase.py +3 -1
  16. classiq/model_expansions/quantum_operations/power.py +2 -2
  17. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +7 -9
  18. classiq/model_expansions/quantum_operations/quantum_function_call.py +2 -2
  19. classiq/model_expansions/quantum_operations/repeat.py +2 -2
  20. classiq/model_expansions/transformers/__init__.py +0 -0
  21. classiq/model_expansions/transformers/var_splitter.py +237 -0
  22. classiq/qmod/builtins/classical_functions.py +1 -0
  23. classiq/qmod/builtins/functions/state_preparation.py +1 -1
  24. classiq/qmod/create_model_function.py +25 -20
  25. classiq/qmod/pretty_print/pretty_printer.py +53 -28
  26. classiq/qmod/qfunc.py +18 -16
  27. classiq/qmod/quantum_function.py +30 -24
  28. classiq/synthesis.py +3 -1
  29. {classiq-0.56.1.dist-info → classiq-0.57.0.dist-info}/METADATA +1 -1
  30. {classiq-0.56.1.dist-info → classiq-0.57.0.dist-info}/RECORD +31 -28
  31. {classiq-0.56.1.dist-info → classiq-0.57.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,237 @@
1
+ import ast
2
+ from dataclasses import dataclass
3
+ from itertools import chain
4
+ from typing import TYPE_CHECKING, Callable, TypeVar
5
+
6
+ from classiq.interface.exceptions import (
7
+ ClassiqExpansionError,
8
+ ClassiqInternalExpansionError,
9
+ )
10
+ from classiq.interface.generator.expressions.expression import Expression
11
+ from classiq.interface.generator.visitor import NodeType, Transformer
12
+ from classiq.interface.model.bind_operation import BindOperation
13
+ from classiq.interface.model.handle_binding import (
14
+ HandleBinding,
15
+ NestedHandleBinding,
16
+ SlicedHandleBinding,
17
+ )
18
+ from classiq.interface.model.quantum_type import (
19
+ QuantumBitvector,
20
+ QuantumScalar,
21
+ QuantumType,
22
+ )
23
+ from classiq.interface.model.variable_declaration_statement import (
24
+ VariableDeclarationStatement,
25
+ )
26
+
27
+ from classiq.model_expansions.scope import QuantumSymbol, Scope
28
+ from classiq.model_expansions.visitors.variable_references import VarRefCollector
29
+
30
+ AST_NODE = TypeVar("AST_NODE", bound=NodeType)
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class SymbolPart:
35
+ source_handle: HandleBinding
36
+ target_var_name: str
37
+ target_var_type: QuantumType
38
+
39
+ @property
40
+ def target_var_handle(self) -> HandleBinding:
41
+ return HandleBinding(name=self.target_var_name)
42
+
43
+
44
+ SymbolParts = dict[QuantumSymbol, list[SymbolPart]]
45
+ PartNamer = Callable[[str], str]
46
+
47
+
48
+ class VarSplitter:
49
+ def __init__(self, scope: Scope):
50
+ self._scope = scope
51
+
52
+ def split_symbols(self, expression: Expression, namer: PartNamer) -> SymbolParts:
53
+ vrc = VarRefCollector(ignore_duplicated_handles=True)
54
+ vrc.visit(ast.parse(expression.expr))
55
+ symbol_names_to_split = dict.fromkeys(
56
+ handle.name
57
+ for handle in self._get_handles(vrc)
58
+ if isinstance(handle, NestedHandleBinding)
59
+ )
60
+
61
+ symbol_handles = {
62
+ symbol: list(
63
+ dict.fromkeys(
64
+ handle.collapse()
65
+ for handle in vrc.var_handles
66
+ if handle.name == symbol.handle.name
67
+ )
68
+ )
69
+ for symbol_name in symbol_names_to_split
70
+ if isinstance(
71
+ symbol := self._scope[symbol_name].value,
72
+ QuantumSymbol,
73
+ )
74
+ }
75
+
76
+ return {
77
+ symbol: [
78
+ SymbolPart(
79
+ source_handle=part.handle,
80
+ target_var_name=namer(part.handle.identifier),
81
+ target_var_type=part.quantum_type,
82
+ )
83
+ for part in self._get_symbol_parts(symbol, handles)
84
+ ]
85
+ for symbol, handles in symbol_handles.items()
86
+ }
87
+
88
+ def _get_handles(self, collector: VarRefCollector) -> list[HandleBinding]:
89
+ return [
90
+ handle
91
+ for handle in collector.var_handles
92
+ if isinstance(self._scope[handle.name].value, QuantumSymbol)
93
+ ]
94
+
95
+ def _get_symbol_parts(
96
+ self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
97
+ ) -> list[QuantumSymbol]:
98
+ for i in range(len(target_parts)):
99
+ for j in range(i + 1, len(target_parts)):
100
+ if target_parts[i].overlaps(target_parts[j]):
101
+ raise ClassiqInternalExpansionError(
102
+ f"Handles {str(target_parts[i])!r} and "
103
+ f"{str(target_parts[j])!r} overlapping in expression"
104
+ )
105
+ return self._get_symbol_parts_unsafe(symbol, target_parts)
106
+
107
+ def _get_symbol_parts_unsafe(
108
+ self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
109
+ ) -> list[QuantumSymbol]:
110
+ if all(
111
+ symbol.handle == target_part or symbol.handle not in target_part.prefixes()
112
+ for target_part in target_parts
113
+ ) or isinstance(symbol.quantum_type, QuantumScalar):
114
+ return [symbol]
115
+
116
+ if isinstance(symbol.quantum_type, QuantumBitvector):
117
+ return self._get_array_parts(symbol, target_parts)
118
+
119
+ return self._get_struct_parts(symbol, target_parts)
120
+
121
+ def _get_array_parts(
122
+ self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
123
+ ) -> list[QuantumSymbol]:
124
+ if TYPE_CHECKING:
125
+ assert isinstance(symbol.quantum_type, QuantumBitvector)
126
+
127
+ if not symbol.quantum_type.has_length:
128
+ raise ClassiqExpansionError(
129
+ f"Could not determine the length of quantum array " f"{symbol.handle}."
130
+ )
131
+ target_slices = {
132
+ target_part.start.to_int_value(): target_part.end.to_int_value()
133
+ for target_part in target_parts
134
+ if isinstance(target_part, SlicedHandleBinding)
135
+ and symbol.handle == target_part.base_handle
136
+ }
137
+
138
+ symbol_parts: list[QuantumSymbol] = []
139
+ idx = 0
140
+ while idx < symbol.quantum_type.length_value:
141
+ if idx in target_slices:
142
+ stop = target_slices[idx]
143
+ if stop <= idx:
144
+ raise ClassiqInternalExpansionError(
145
+ f"Illegal sliced handle {str(symbol[idx: stop].handle)!r}"
146
+ )
147
+ symbol_parts.append(symbol[idx:stop])
148
+ idx = stop
149
+ else:
150
+ symbol_parts.extend(
151
+ self._get_symbol_parts_unsafe(symbol[idx], target_parts)
152
+ )
153
+ idx += 1
154
+
155
+ return symbol_parts
156
+
157
+ def _get_struct_parts(
158
+ self, symbol: QuantumSymbol, target_parts: list[HandleBinding]
159
+ ) -> list[QuantumSymbol]:
160
+ return list(
161
+ chain.from_iterable(
162
+ self._get_symbol_parts_unsafe(field_symbol, target_parts)
163
+ for field_symbol in symbol.fields.values()
164
+ )
165
+ )
166
+
167
+ @staticmethod
168
+ def get_bind_ops(symbol_parts: SymbolParts) -> list[BindOperation]:
169
+ return [
170
+ BindOperation(
171
+ in_handles=[symbol.handle],
172
+ out_handles=[part.target_var_handle for part in parts],
173
+ )
174
+ for symbol, parts in symbol_parts.items()
175
+ ]
176
+
177
+ @staticmethod
178
+ def get_var_decls(symbol_parts: SymbolParts) -> list[VariableDeclarationStatement]:
179
+ return [
180
+ VariableDeclarationStatement(
181
+ name=part.target_var_name,
182
+ quantum_type=part.target_var_type,
183
+ )
184
+ for part in chain.from_iterable(symbol_parts.values())
185
+ ]
186
+
187
+ def _check_all_handles_were_replaced(self, new_expr_str: str) -> None:
188
+ vrc = VarRefCollector(ignore_duplicated_handles=True)
189
+ vrc.visit(ast.parse(new_expr_str))
190
+ for handle in self._get_handles(vrc):
191
+ if isinstance(handle, NestedHandleBinding):
192
+ raise ClassiqInternalExpansionError(f"Did not replace handle {handle}")
193
+
194
+ def rewrite(self, subject: AST_NODE, symbol_mapping: SymbolParts) -> AST_NODE:
195
+ handle_replacements = {
196
+ part.source_handle: part.target_var_handle
197
+ for parts in symbol_mapping.values()
198
+ for part in parts
199
+ }
200
+
201
+ class ReplaceSplitVars(Transformer):
202
+ @staticmethod
203
+ def visit_HandleBinding(handle: HandleBinding) -> HandleBinding:
204
+ handle = handle.collapse()
205
+ for handle_to_replace, replacement in handle_replacements.items():
206
+ handle = handle.replace_prefix(handle_to_replace, replacement)
207
+ return handle
208
+
209
+ @staticmethod
210
+ def visit_Expression(expr: Expression) -> Expression:
211
+ return self._rewrite_expression(symbol_mapping, expr)
212
+
213
+ return ReplaceSplitVars().visit(subject)
214
+
215
+ def _rewrite_expression(
216
+ self,
217
+ symbol_mapping: SymbolParts,
218
+ expression: Expression,
219
+ ) -> Expression:
220
+ vrc = VarRefCollector(ignore_duplicated_handles=True)
221
+ vrc.visit(ast.parse(expression.expr))
222
+
223
+ handle_names = {
224
+ part.source_handle: part.target_var_name
225
+ for parts in symbol_mapping.values()
226
+ for part in parts
227
+ }
228
+ new_expr_str = expression.expr
229
+ for handle in vrc.var_handles:
230
+ collapsed_handle = handle.collapse()
231
+ if collapsed_handle in handle_names:
232
+ new_expr_str = new_expr_str.replace(
233
+ str(handle), handle_names[collapsed_handle]
234
+ )
235
+ self._check_all_handles_were_replaced(new_expr_str)
236
+
237
+ return Expression(expr=new_expr_str)
@@ -1,5 +1,6 @@
1
1
  # This file was generated automatically - do not edit manually
2
2
 
3
+
3
4
  from classiq.qmod.qmod_parameter import CArray, CBool, CInt, CReal
4
5
  from classiq.qmod.symbolic import symbolic_function
5
6
 
@@ -415,7 +415,7 @@ def inplace_prepare_int(value: CInt, target: QArray[QBit]) -> None:
415
415
  @qfunc(external=True)
416
416
  def prepare_int(
417
417
  value: CInt,
418
- out: Output[QNum[Literal["floor(log(value, 2)) + 1"], Literal[False], Literal[0]]],
418
+ out: Output[QNum[Literal["floor(log(value, 2)) + 1"]]],
419
419
  ) -> None:
420
420
  """
421
421
  [Qmod Classiq-library function]
@@ -71,7 +71,6 @@ def create_model(
71
71
  constraints,
72
72
  execution_preferences,
73
73
  preferences,
74
- classical_execution_function,
75
74
  )
76
75
  else:
77
76
  if TYPE_CHECKING:
@@ -95,26 +94,21 @@ def _expand_generative_model(
95
94
  constraints: Optional[Constraints] = None,
96
95
  execution_preferences: Optional[ExecutionPreferences] = None,
97
96
  preferences: Optional[Preferences] = None,
98
- classical_execution_function: Optional[CFunc] = None,
99
97
  ) -> Model:
100
98
  @QFunc
101
99
  def _dummy() -> None:
102
100
  pass
103
101
 
104
- functions_compilation_metadata = {
105
- dec_func._py_callable.__name__: dec_func.compilation_metadata
106
- for dec_func in DEC_QFUNCS
107
- if dec_func.compilation_metadata is not None
108
- }
109
102
  model = _dummy.create_model(
110
103
  constraints,
111
104
  execution_preferences,
112
105
  preferences,
113
- classical_execution_function,
114
- functions_compilation_metadata,
115
106
  )
116
- generative_functions = _get_generative_functions(gen_main, preferences)
117
- model.functions = generative_functions
107
+ gen_expand_model = _get_generative_functions(gen_main, preferences)
108
+ model.functions = gen_expand_model.functions
109
+ model.functions_compilation_metadata = (
110
+ gen_expand_model.functions_compilation_metadata
111
+ )
118
112
  model.types = list(QMODULE.type_decls.values())
119
113
  model.enums = list(QMODULE.enum_decls.values())
120
114
  model.qstructs = list(QMODULE.qstruct_decls.values())
@@ -122,8 +116,9 @@ def _expand_generative_model(
122
116
 
123
117
 
124
118
  def _get_generative_functions(
125
- gen_main: QFunc, preferences: Optional[Preferences]
126
- ) -> list[NativeFunctionDefinition]:
119
+ gen_main: QFunc,
120
+ preferences: Optional[Preferences],
121
+ ) -> Model:
127
122
  # The Interpreter accepts a model and a list of generative functions.
128
123
  # Since the main function is generative, it can only be expanded using the
129
124
  # Interpreter.
@@ -132,14 +127,21 @@ def _get_generative_functions(
132
127
  # passing them to the Interpreter.
133
128
  gen_model = _get_wrapper_main(gen_main, preferences)
134
129
  gen_functions = _get_all_model_functions_as_generative_functions()
135
- functions_dict = _interpret_generative_model(gen_model, gen_functions)
136
- return list(functions_dict.values())
130
+ return _interpret_generative_model(gen_model, gen_functions)
137
131
 
138
132
 
139
- def _get_wrapper_main(gen_main: QFunc, preferences: Optional[Preferences]) -> Model:
133
+ def _get_wrapper_main(
134
+ gen_main: QFunc,
135
+ preferences: Optional[Preferences],
136
+ ) -> Model:
140
137
  extra_args = {}
141
138
  if preferences is not None:
142
139
  extra_args["preferences"] = preferences
140
+ functions_compilation_metadata = {
141
+ qfunc._py_callable.__name__: qfunc.compilation_metadata
142
+ for qfunc in DEC_QFUNCS + GEN_QFUNCS
143
+ if qfunc.compilation_metadata is not None
144
+ }
143
145
  return Model(
144
146
  functions=[
145
147
  NativeFunctionDefinition(
@@ -157,6 +159,7 @@ def _get_wrapper_main(gen_main: QFunc, preferences: Optional[Preferences]) -> Mo
157
159
  ],
158
160
  ),
159
161
  ],
162
+ functions_compilation_metadata=functions_compilation_metadata,
160
163
  **extra_args,
161
164
  )
162
165
 
@@ -164,7 +167,9 @@ def _get_wrapper_main(gen_main: QFunc, preferences: Optional[Preferences]) -> Mo
164
167
  def _get_all_model_functions_as_generative_functions() -> list[GenerativeQFunc]:
165
168
 
166
169
  gen_functions = list(GEN_QFUNCS) + [
167
- GenerativeQFunc(dec_func._py_callable, dec_func.func_decl)
170
+ GenerativeQFunc(
171
+ dec_func._py_callable, dec_func.func_decl, dec_func.compilation_metadata
172
+ )
168
173
  for dec_func in DEC_QFUNCS
169
174
  ]
170
175
  return [
@@ -174,6 +179,7 @@ def _get_all_model_functions_as_generative_functions() -> list[GenerativeQFunc]:
174
179
  else GenerativeQFunc(
175
180
  gen_func._py_callable,
176
181
  gen_func.func_decl.model_copy(update={"name": GEN_MAIN_NAME}),
182
+ gen_func.compilation_metadata,
177
183
  )
178
184
  )
179
185
  for gen_func in gen_functions
@@ -183,7 +189,7 @@ def _get_all_model_functions_as_generative_functions() -> list[GenerativeQFunc]:
183
189
 
184
190
  def _interpret_generative_model(
185
191
  gen_model: Model, gen_functions: list[GenerativeQFunc]
186
- ) -> dict[str, NativeFunctionDefinition]:
192
+ ) -> Model:
187
193
  resolve_function_calls(
188
194
  gen_model,
189
195
  {gen_func.func_decl.name: gen_func.func_decl for gen_func in gen_functions},
@@ -201,5 +207,4 @@ def _interpret_generative_model(
201
207
  expanded_gen_main_name
202
208
  ].model_copy(update={"name": MAIN_FUNCTION_NAME})
203
209
  functions_dict.pop(expanded_gen_main_name)
204
-
205
- return functions_dict
210
+ return expand_model.model_copy(update={"functions": list(functions_dict.values())})
@@ -3,6 +3,7 @@ from typing import Optional, Union, cast
3
3
 
4
4
  import black
5
5
 
6
+ from classiq.interface.exceptions import ClassiqInternalError
6
7
  from classiq.interface.generator.constant import Constant
7
8
  from classiq.interface.generator.expressions.expression import Expression
8
9
  from classiq.interface.generator.functions.classical_type import (
@@ -86,16 +87,15 @@ class VariableDeclarationAssignment(Visitor):
86
87
  def __init__(self, pretty_printer: "PythonPrettyPrinter") -> None:
87
88
  self.pretty_printer = pretty_printer
88
89
 
89
- def visit(self, node: NodeType) -> tuple[str, Optional[list[str]]]:
90
+ def visit(self, node: NodeType) -> tuple[str, list[str]]:
90
91
  res = super().visit(node)
91
92
  if not isinstance(res, tuple):
92
93
  raise AssertionError(f"Pretty printing for {type(node)} is not supported ")
93
94
  return res # type: ignore[return-value]
94
95
 
95
- def visit_QuantumBit(self, qtype: QuantumBit) -> tuple[str, Optional[list[str]]]:
96
+ def visit_QuantumBit(self, qtype: QuantumBit) -> tuple[str, list[str]]:
96
97
  self.pretty_printer._imports["QBit"] = 1
97
-
98
- return "QBit", None
98
+ return "QBit", []
99
99
 
100
100
  def visit_QuantumBitvector(
101
101
  self, qtype: QuantumBitvector
@@ -111,19 +111,10 @@ class VariableDeclarationAssignment(Visitor):
111
111
  self, qtype: QuantumNumeric
112
112
  ) -> tuple[str, Optional[list[str]]]:
113
113
  self.pretty_printer._imports["QNum"] = 1
114
+ return "QNum", self.pretty_printer._get_qnum_properties(qtype)
114
115
 
115
- params = []
116
- if qtype.size is not None:
117
- params = [
118
- self.pretty_printer.visit(param)
119
- for param in [qtype.size, qtype.is_signed, qtype.fraction_digits]
120
- if param is not None
121
- ]
122
-
123
- return "QNum", params
124
-
125
- def visit_TypeName(self, qtype: TypeName) -> tuple[str, None]:
126
- return qtype.name, None
116
+ def visit_TypeName(self, qtype: TypeName) -> tuple[str, list[str]]:
117
+ return qtype.name, []
127
118
 
128
119
 
129
120
  class PythonPrettyPrinter(Visitor):
@@ -269,21 +260,46 @@ class PythonPrettyPrinter(Visitor):
269
260
  return f"QArray[{element_type}]"
270
261
 
271
262
  def visit_QuantumNumeric(self, qtype: QuantumNumeric) -> str:
272
- params = ""
273
263
  self._imports["QNum"] = 1
274
- if qtype.size is not None:
275
- assert qtype.is_signed is not None
276
- assert qtype.fraction_digits is not None
277
-
264
+ params = ""
265
+ qnum_properties = self._get_qnum_properties(qtype)
266
+ if len(qnum_properties) > 0:
278
267
  params = "[{}]".format(
279
- ", ".join(
280
- _add_quotes(self.visit(param))
281
- for param in [qtype.size, qtype.is_signed, qtype.fraction_digits]
282
- )
268
+ ", ".join(_add_quotes(param) for param in qnum_properties)
283
269
  )
284
-
285
270
  return f"QNum{params}"
286
271
 
272
+ def _get_qnum_properties(self, qtype: QuantumNumeric) -> list[str]:
273
+ params: list[str] = []
274
+ if qtype.size is None:
275
+ return params
276
+ params.append(self.visit(qtype.size))
277
+
278
+ is_signed_expr = qtype.is_signed
279
+ fraction_digits_expr = qtype.fraction_digits
280
+ if is_signed_expr is None:
281
+ if fraction_digits_expr is not None:
282
+ raise ClassiqInternalError
283
+ return params
284
+ if fraction_digits_expr is None:
285
+ raise ClassiqInternalError
286
+
287
+ is_unsigned = (
288
+ is_signed_expr.is_evaluated()
289
+ and not is_signed_expr.to_bool_value()
290
+ or is_signed_expr.expr == "UNSIGNED"
291
+ )
292
+ is_integer = (
293
+ fraction_digits_expr.is_evaluated()
294
+ and fraction_digits_expr.to_int_value() == 0
295
+ )
296
+ if is_unsigned and is_integer:
297
+ return params
298
+ params.append(self.visit(is_signed_expr))
299
+ params.append(self.visit(fraction_digits_expr))
300
+
301
+ return params
302
+
287
303
  def visit_AnonClassicalParameterDeclaration(
288
304
  self, cparam: AnonClassicalParameterDeclaration
289
305
  ) -> str:
@@ -325,7 +341,7 @@ class PythonPrettyPrinter(Visitor):
325
341
  type_name, params = VariableDeclarationAssignment(self).visit(
326
342
  local_decl.quantum_type
327
343
  )
328
- params = [f'"{local_decl.name}"'] + (params or [])
344
+ params = [f'"{local_decl.name}"'] + params
329
345
  param_args = ", ".join(params)
330
346
  return f"{self._indent}{self.visit_QuantumVariableDeclaration(local_decl)} = {type_name}({param_args})\n"
331
347
 
@@ -516,7 +532,12 @@ class PythonPrettyPrinter(Visitor):
516
532
 
517
533
 
518
534
  def _add_quotes(exp: str) -> str:
519
- if exp.lower() == "true" or exp.lower() == "false" or _convertible_to_number(exp):
535
+ if (
536
+ exp.lower() == "true"
537
+ or exp.lower() == "false"
538
+ or _convertible_to_number(exp)
539
+ or _is_constant(exp)
540
+ ):
520
541
  return exp
521
542
 
522
543
  return f'"{exp}"'
@@ -531,3 +552,7 @@ def _convertible_to_number(exp: str) -> bool:
531
552
  else:
532
553
  return True
533
554
  return False
555
+
556
+
557
+ def _is_constant(exp: str) -> bool:
558
+ return exp in ("SIGNED", "UNSIGNED")
classiq/qmod/qfunc.py CHANGED
@@ -1,9 +1,14 @@
1
1
  from typing import Callable, Literal, Optional, Union, overload
2
2
 
3
- from classiq.interface.exceptions import ClassiqError
3
+ from classiq.interface.exceptions import ClassiqInternalError
4
4
 
5
5
  from classiq.qmod.quantum_callable import QCallable
6
- from classiq.qmod.quantum_function import ExternalQFunc, GenerativeQFunc, QFunc
6
+ from classiq.qmod.quantum_function import (
7
+ BaseQFunc,
8
+ ExternalQFunc,
9
+ GenerativeQFunc,
10
+ QFunc,
11
+ )
7
12
 
8
13
  GEN_QFUNCS: list[GenerativeQFunc] = []
9
14
  DEC_QFUNCS: list[QFunc] = []
@@ -54,25 +59,22 @@ def qfunc(
54
59
  synthesize_separately: bool = False,
55
60
  ) -> Union[Callable[[Callable], QCallable], QCallable]:
56
61
  def wrapper(func: Callable) -> QCallable:
62
+ qfunc: BaseQFunc
57
63
  if generative:
58
- gen_qfunc = GenerativeQFunc(func)
64
+ qfunc = GenerativeQFunc(func)
65
+ GEN_QFUNCS.append(qfunc)
66
+ elif external:
59
67
  if synthesize_separately:
60
- raise ClassiqError(
61
- "Generative functions can not be synthesized separately"
62
- )
63
- GEN_QFUNCS.append(gen_qfunc)
64
- return gen_qfunc
65
- if external:
66
- if synthesize_separately:
67
- raise ClassiqError(
68
- "External functions can not be synthesized separately"
68
+ raise ClassiqInternalError(
69
+ "External functions can't be marked as synthesized separately"
69
70
  )
70
71
  return ExternalQFunc(func)
71
- dec_qfunc = QFunc(func)
72
+ else:
73
+ qfunc = QFunc(func)
74
+ DEC_QFUNCS.append(qfunc)
72
75
  if synthesize_separately:
73
- dec_qfunc.should_synthesize_separately = True
74
- DEC_QFUNCS.append(dec_qfunc)
75
- return dec_qfunc
76
+ qfunc.update_compilation_metadata(should_synthesize_separately=True)
77
+ return qfunc
76
78
 
77
79
  if func is not None:
78
80
  return wrapper(func)
@@ -28,12 +28,35 @@ from classiq.qmod.quantum_expandable import QExpandable, QTerminalCallable
28
28
  from classiq.qmod.utilities import mangle_keyword
29
29
 
30
30
 
31
- class QFunc(QExpandable):
31
+ class BaseQFunc(QExpandable):
32
+ def __init__(
33
+ self,
34
+ py_callable: Callable,
35
+ compilation_metadata: Optional[CompilationMetadata] = None,
36
+ ) -> None:
37
+ super().__init__(py_callable)
38
+ self.compilation_metadata = compilation_metadata
39
+
40
+ def update_compilation_metadata(self, **kwargs: Any) -> None:
41
+ self.compilation_metadata = self._compilation_metadata.model_copy(update=kwargs)
42
+
43
+ @property
44
+ def _compilation_metadata(self) -> CompilationMetadata:
45
+ if self.compilation_metadata is None:
46
+ return CompilationMetadata()
47
+ return self.compilation_metadata
48
+
49
+
50
+ class QFunc(BaseQFunc):
32
51
  FRAME_DEPTH = 2
33
52
 
34
- def __init__(self, py_callable: Callable) -> None:
53
+ def __init__(
54
+ self,
55
+ py_callable: Callable,
56
+ compilation_metadata: Optional[CompilationMetadata] = None,
57
+ ) -> None:
35
58
  _validate_no_gen_params(py_callable.__annotations__)
36
- super().__init__(py_callable)
59
+ super().__init__(py_callable, compilation_metadata)
37
60
  functools.update_wrapper(self, py_callable)
38
61
  self.compilation_metadata: Optional[CompilationMetadata] = None
39
62
 
@@ -44,21 +67,6 @@ class QFunc(QExpandable):
44
67
  infer_func_decl(self._py_callable, qmodule=self._qmodule),
45
68
  )
46
69
 
47
- @property
48
- def should_synthesize_separately(self) -> bool:
49
- if self.compilation_metadata is None:
50
- return False
51
- return self.compilation_metadata.should_synthesize_separately
52
-
53
- @should_synthesize_separately.setter
54
- def should_synthesize_separately(self, value: bool) -> None:
55
- if self.compilation_metadata is None:
56
- self.compilation_metadata = CompilationMetadata(
57
- should_synthesize_separately=value
58
- )
59
- else:
60
- self.compilation_metadata.should_synthesize_separately = value
61
-
62
70
  def __call__(self, *args: Any, **kwargs: Any) -> None:
63
71
  super().__call__(*args, **kwargs)
64
72
  self.expand()
@@ -69,16 +77,13 @@ class QFunc(QExpandable):
69
77
  execution_preferences: Optional[ExecutionPreferences] = None,
70
78
  preferences: Optional[Preferences] = None,
71
79
  classical_execution_function: Optional[CFunc] = None,
72
- functions_compilation_metadata: Optional[dict[str, CompilationMetadata]] = None,
73
80
  ) -> Model:
74
- if functions_compilation_metadata is None:
75
- functions_compilation_metadata = dict()
76
81
  self._qmodule.enum_decls = dict()
77
82
  self._qmodule.type_decls = dict()
78
83
  self._qmodule.qstruct_decls = dict()
79
84
  self._qmodule.native_defs = dict()
80
85
  self._qmodule.constants = dict()
81
- self._qmodule.functions_compilation_metadata = functions_compilation_metadata
86
+ self._qmodule.functions_compilation_metadata = dict()
82
87
  QConstant.set_current_model(self._qmodule)
83
88
  self.expand()
84
89
  model_extra_settings: list[tuple[str, Any]] = [
@@ -167,13 +172,14 @@ class ExternalQFunc(QTerminalCallable):
167
172
  ][0]
168
173
 
169
174
 
170
- class GenerativeQFunc(QExpandable):
175
+ class GenerativeQFunc(BaseQFunc):
171
176
  def __init__(
172
177
  self,
173
178
  py_callable: Callable,
174
179
  func_decl: Optional[NamedParamsQuantumFunctionDeclaration] = None,
180
+ compilation_metadata: Optional[CompilationMetadata] = None,
175
181
  ) -> None:
176
- super().__init__(py_callable)
182
+ super().__init__(py_callable, compilation_metadata)
177
183
  self._func_decl = func_decl
178
184
 
179
185
  @property
classiq/synthesis.py CHANGED
@@ -20,13 +20,15 @@ CANT_PARSE_QUANTUM_PROGRAM_MSG = (
20
20
  )
21
21
 
22
22
 
23
- def show(quantum_program: SerializedQuantumProgram) -> None:
23
+ def show(quantum_program: SerializedQuantumProgram, display_url: bool = True) -> None:
24
24
  """
25
25
  Displays the interactive representation of the quantum program in the Classiq IDE.
26
26
 
27
27
  Args:
28
28
  quantum_program:
29
29
  The serialized quantum program to be displayed.
30
+ display_url:
31
+ Whether to print the url
30
32
 
31
33
  Links:
32
34
  [Visualization tool](https://docs.classiq.io/latest/reference-manual/analyzer/quantum-program-visualization-tool/)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: classiq
3
- Version: 0.56.1
3
+ Version: 0.57.0
4
4
  Summary: Classiq's Python SDK for quantum computing
5
5
  Home-page: https://classiq.io
6
6
  License: Proprietary