classiq 0.80.1__py3-none-any.whl → 0.82.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 (49) hide show
  1. classiq/analyzer/show_interactive_hack.py +10 -4
  2. classiq/analyzer/url_utils.py +4 -3
  3. classiq/applications/qnn/qlayer.py +1 -1
  4. classiq/interface/_version.py +1 -1
  5. classiq/interface/backend/backend_preferences.py +0 -3
  6. classiq/interface/debug_info/debug_info.py +0 -1
  7. classiq/interface/executor/execution_preferences.py +2 -1
  8. classiq/interface/generator/compiler_keywords.py +2 -2
  9. classiq/interface/generator/expressions/atomic_expression_functions.py +11 -7
  10. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +6 -2
  11. classiq/interface/generator/function_params.py +1 -1
  12. classiq/interface/generator/functions/classical_type.py +8 -0
  13. classiq/interface/generator/generated_circuit_data.py +1 -2
  14. classiq/interface/generator/quantum_program.py +13 -0
  15. classiq/interface/generator/types/compilation_metadata.py +14 -2
  16. classiq/interface/model/handle_binding.py +12 -2
  17. classiq/interface/model/quantum_type.py +12 -1
  18. classiq/interface/server/routes.py +0 -1
  19. classiq/model_expansions/atomic_expression_functions_defs.py +1 -1
  20. classiq/model_expansions/capturing/captured_vars.py +123 -9
  21. classiq/model_expansions/closure.py +2 -0
  22. classiq/model_expansions/evaluators/quantum_type_utils.py +3 -18
  23. classiq/model_expansions/function_builder.py +1 -17
  24. classiq/model_expansions/interpreters/base_interpreter.py +2 -0
  25. classiq/model_expansions/quantum_operations/allocate.py +18 -7
  26. classiq/model_expansions/quantum_operations/assignment_result_processor.py +4 -0
  27. classiq/model_expansions/quantum_operations/bind.py +2 -1
  28. classiq/model_expansions/quantum_operations/call_emitter.py +27 -21
  29. classiq/model_expansions/quantum_operations/emitter.py +28 -0
  30. classiq/model_expansions/quantum_operations/function_calls_cache.py +1 -16
  31. classiq/model_expansions/transformers/type_qualifier_inference.py +220 -50
  32. classiq/model_expansions/visitors/symbolic_param_inference.py +0 -6
  33. classiq/open_library/functions/amplitude_amplification.py +3 -5
  34. classiq/open_library/functions/state_preparation.py +9 -0
  35. classiq/qmod/builtins/functions/__init__.py +3 -1
  36. classiq/qmod/builtins/functions/exponentiation.py +41 -3
  37. classiq/qmod/builtins/operations.py +65 -37
  38. classiq/qmod/declaration_inferrer.py +2 -1
  39. classiq/qmod/native/pretty_printer.py +12 -10
  40. classiq/qmod/pretty_print/pretty_printer.py +9 -9
  41. classiq/qmod/qfunc.py +11 -11
  42. classiq/qmod/quantum_expandable.py +4 -0
  43. classiq/qmod/semantics/error_manager.py +8 -2
  44. classiq/synthesis.py +6 -3
  45. {classiq-0.80.1.dist-info → classiq-0.82.0.dist-info}/METADATA +1 -1
  46. {classiq-0.80.1.dist-info → classiq-0.82.0.dist-info}/RECORD +47 -49
  47. classiq/interface/execution/resource_estimator.py +0 -7
  48. classiq/interface/execution/result.py +0 -5
  49. {classiq-0.80.1.dist-info → classiq-0.82.0.dist-info}/WHEEL +0 -0
@@ -29,14 +29,7 @@ from classiq.model_expansions.capturing.captured_vars import (
29
29
  validate_captured_directions,
30
30
  validate_end_state,
31
31
  )
32
- from classiq.model_expansions.closure import (
33
- Closure,
34
- FunctionClosure,
35
- GenerativeFunctionClosure,
36
- )
37
- from classiq.model_expansions.evaluators.quantum_type_utils import (
38
- is_signature_monomorphic,
39
- )
32
+ from classiq.model_expansions.closure import Closure, FunctionClosure
40
33
  from classiq.model_expansions.scope import Scope
41
34
  from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
42
35
 
@@ -225,15 +218,6 @@ class OperationBuilder:
225
218
  if name == MAIN_FUNCTION_NAME:
226
219
  return name
227
220
 
228
- if name in self.current_scope:
229
- orig_func = self.current_scope[name].value
230
- if (
231
- isinstance(orig_func, FunctionClosure)
232
- and not isinstance(orig_func, GenerativeFunctionClosure)
233
- and is_signature_monomorphic(orig_func.positional_arg_declarations)
234
- ):
235
- return name
236
-
237
221
  for _ in self.current_scope:
238
222
  name = self._counted_name_allocator.allocate(
239
223
  f"{name}_{LAMBDA_KEYWORD + '_0_0_' if function_context.is_lambda else ''}{EXPANDED_KEYWORD}"
@@ -75,6 +75,8 @@ from classiq.qmod.utilities import qmod_val_to_expr_str
75
75
 
76
76
 
77
77
  class BaseInterpreter:
78
+ skip_type_modifier_validation: bool = False
79
+
78
80
  def __init__(self, model: Model) -> None:
79
81
  validate_model(model)
80
82
  self._model = model
@@ -76,11 +76,15 @@ class AllocateEmitter(Emitter[Allocate]):
76
76
  target: QuantumSymbol,
77
77
  op_update_dict: dict[str, Expression],
78
78
  ) -> None:
79
- if not target.quantum_type.is_evaluated:
79
+ if target.quantum_type.is_evaluated:
80
+ expr = str(target.quantum_type.size_in_bits)
81
+ elif self._allow_symbolic_attrs:
82
+ expr = f"{target.handle}.size"
83
+ else:
80
84
  raise ClassiqValueError(
81
85
  f"Could not infer the size of variable {str(target.handle)!r}"
82
86
  )
83
- op_update_dict["size"] = Expression(expr=str(target.quantum_type.size_in_bits))
87
+ op_update_dict["size"] = Expression(expr=expr)
84
88
 
85
89
  def _handle_with_size(
86
90
  self,
@@ -88,7 +92,7 @@ class AllocateEmitter(Emitter[Allocate]):
88
92
  size: Expression,
89
93
  op_update_dict: dict[str, Expression],
90
94
  ) -> None:
91
- size_value = self._interpret_size(size)
95
+ size_value = self._interpret_size(size, str(target.handle))
92
96
  op_update_dict["size"] = Expression(expr=str(size_value))
93
97
 
94
98
  if not isinstance(size_value, sympy.Basic):
@@ -106,12 +110,13 @@ class AllocateEmitter(Emitter[Allocate]):
106
110
  fraction_digits: Expression,
107
111
  op_update_dict: dict[str, Expression],
108
112
  ) -> None:
113
+ var_name = str(target.handle)
109
114
  if not isinstance(target.quantum_type, QuantumNumeric):
110
115
  raise ClassiqValueError(
111
- f"Non-numeric variable {str(target.handle)!r} cannot be allocated with numeric attributes"
116
+ f"Non-numeric variable {var_name!r} cannot be allocated with numeric attributes"
112
117
  )
113
118
 
114
- size_value = self._interpret_size(size)
119
+ size_value = self._interpret_size(size, var_name)
115
120
  op_update_dict["size"] = Expression(expr=str(size_value))
116
121
  is_signed_value = self._interpret_is_signed(is_signed)
117
122
  op_update_dict["is_signed"] = Expression(expr=str(is_signed_value))
@@ -130,12 +135,18 @@ class AllocateEmitter(Emitter[Allocate]):
130
135
  fraction_digits=op_update_dict["fraction_digits"],
131
136
  ),
132
137
  target.quantum_type,
133
- str(target.handle),
138
+ var_name,
134
139
  )
135
140
 
136
- def _interpret_size(self, size: Expression) -> Union[int, float, sympy.Basic]:
141
+ def _interpret_size(
142
+ self, size: Expression, var_name: str
143
+ ) -> Union[int, float, sympy.Basic]:
137
144
  size_value = self._interpreter.evaluate(size).value
138
145
  if not self._allow_symbolic_attrs and not isinstance(size_value, (int, float)):
146
+ if size.expr == f"{var_name}.size":
147
+ raise ClassiqValueError(
148
+ f"Could not infer the size of variable {var_name!r}"
149
+ )
139
150
  raise ClassiqValueError(
140
151
  f"The number of allocated qubits must be an integer. Got "
141
152
  f"{str(size_value)!r}"
@@ -78,7 +78,11 @@ class AssignmentResultProcessor(Emitter[QuantumAssignmentOperation]):
78
78
  expr = self._evaluate_expression(op.expression)
79
79
  if len(self._get_classical_vars_in_expression(expr)):
80
80
  return None
81
+
81
82
  symbols = self._get_symbols_in_expression(expr)
83
+ if any(not symbol.quantum_type.is_instantiated for symbol in symbols):
84
+ return None
85
+
82
86
  expr_str = rename_variables(
83
87
  expr.expr,
84
88
  {str(symbol.handle): symbol.handle.identifier for symbol in symbols}
@@ -31,7 +31,8 @@ class BindEmitter(Emitter[BindOperation]):
31
31
 
32
32
  def emit(self, bind: BindOperation, /) -> bool:
33
33
  inputs, outputs = self._get_inputs_outputs(bind)
34
- validate_bind_targets(bind, self._current_scope, self._allow_symbolic_size)
34
+ if not self._allow_symbolic_size:
35
+ validate_bind_targets(bind, self._current_scope)
35
36
  self._process_var_sizes(bind, inputs, outputs)
36
37
 
37
38
  for symbol in chain(inputs, outputs):
@@ -29,6 +29,7 @@ from classiq.interface.generator.functions.port_declaration import (
29
29
  PortDeclarationDirection,
30
30
  )
31
31
  from classiq.interface.generator.functions.type_qualifier import TypeQualifier
32
+ from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
32
33
  from classiq.interface.helpers.backward_compatibility import zip_strict
33
34
  from classiq.interface.helpers.text_utils import are, readable_list, s
34
35
  from classiq.interface.model.classical_parameter_declaration import (
@@ -80,7 +81,7 @@ from classiq.model_expansions.scope import (
80
81
  Scope,
81
82
  )
82
83
  from classiq.model_expansions.transformers.type_qualifier_inference import (
83
- TypeQualifierInference,
84
+ TypeQualifierValidation,
84
85
  )
85
86
  from classiq.model_expansions.transformers.var_splitter import VarSplitter
86
87
  from classiq.qmod.pretty_print.expression_to_python import transform_expression
@@ -290,6 +291,7 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
290
291
  context = self._expand_operation(function)
291
292
  function_context = cast(FunctionContext, context)
292
293
  function_def = self._create_function_definition(function_context, args)
294
+ self._validate_type_qualifiers(function_context, function_def)
293
295
  self._expanded_functions[cache_key] = function_def
294
296
  self._top_level_scope[function_def.name] = Evaluated(
295
297
  value=function_context.closure.with_new_declaration(function_def)
@@ -320,20 +322,14 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
320
322
  )
321
323
  captured_ports = captured_vars.get_captured_parameters()
322
324
  if len(captured_ports) == 0:
323
- self._override_type_qualifier(function_context, func_def)
324
325
  return func_def
325
326
  func_def.positional_arg_declarations = list(
326
327
  chain.from_iterable((func_def.positional_arg_declarations, captured_ports))
327
328
  )
328
329
 
329
- rewrite_mapping = dict(captured_vars.get_propagated_captured_mapping())
330
- if function_context.is_lambda:
331
- rewrite_mapping |= captured_vars.get_immediate_captured_mapping()
332
- rewrite_mapping |= captured_vars.get_classical_captured_mapping()
330
+ rewrite_mapping = captured_vars.get_captured_mapping(function_context.is_lambda)
333
331
  func_def.body = self.rewrite(func_def.body, rewrite_mapping)
334
332
 
335
- self._override_type_qualifier(function_context, func_def)
336
-
337
333
  return func_def
338
334
 
339
335
  @staticmethod
@@ -419,9 +415,21 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
419
415
  if var_state and param.direction == PortDeclarationDirection.Output:
420
416
  raise ClassiqExpansionError(INITIALIZED_VAR_MESSAGE.format(var_name))
421
417
 
422
- def _override_type_qualifier(
418
+ def _validate_type_qualifiers(
423
419
  self, func_context: FunctionContext, func_def: NativeFunctionDefinition
424
420
  ) -> None:
421
+ if self._should_override_type_qualifiers(func_context):
422
+ self._override_type_qualifiers(func_def)
423
+
424
+ unchecked = self._functions_compilation_metadata.get(
425
+ func_context.name, CompilationMetadata()
426
+ ).unchecked
427
+ TypeQualifierValidation(
428
+ skip_validation=self._interpreter.skip_type_modifier_validation
429
+ ).run(func_def.port_declarations, func_def.body, unchecked)
430
+
431
+ @staticmethod
432
+ def _should_override_type_qualifiers(func_context: FunctionContext) -> bool:
425
433
  """
426
434
  The type qualifier can be changed according to the operand passed to the
427
435
  function. For example,
@@ -434,29 +442,27 @@ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT], VarSpl
434
442
  """
435
443
 
436
444
  if func_context.is_lambda:
437
- self._update_type_qualifiers(func_def)
438
- return
445
+ return True
439
446
 
440
447
  orig_name = func_context.name
441
448
  if (
442
449
  orig_name == MAIN_FUNCTION_NAME
443
450
  or orig_name not in func_context.closure.scope
444
451
  ):
445
- return
452
+ return False
446
453
 
447
454
  orig_func = func_context.closure.scope[orig_name].value
448
- if isinstance(orig_func, Closure) and not any(
449
- isinstance(param_decl, QuantumOperandDeclaration)
450
- for param_decl in orig_func.positional_arg_declarations
451
- ):
452
- return
453
-
454
- self._update_type_qualifiers(func_def)
455
+ return not (
456
+ isinstance(orig_func, Closure)
457
+ and not any(
458
+ isinstance(param_decl, QuantumOperandDeclaration)
459
+ for param_decl in orig_func.positional_arg_declarations
460
+ )
461
+ )
455
462
 
456
463
  @staticmethod
457
- def _update_type_qualifiers(func_def: NativeFunctionDefinition) -> None:
464
+ def _override_type_qualifiers(func_def: NativeFunctionDefinition) -> None:
458
465
  # only override the qualifier if it's unspecified (not QFree or Const)
459
466
  for port in func_def.port_declarations:
460
467
  if port.type_qualifier is TypeQualifier.Quantum:
461
468
  port.type_qualifier = TypeQualifier.Inferred
462
- TypeQualifierInference().run(func_def.port_declarations, func_def.body)
@@ -184,6 +184,8 @@ class Emitter(Generic[QuantumStatementT], ABC):
184
184
  def _update_captured_classical_vars_in_expression(self, expr: Expression) -> None:
185
185
  for var_name, var_type in self._get_classical_vars_in_expression(expr):
186
186
  self._capture_classical_var(var_name, var_type)
187
+ for handle in self._get_quantum_type_attributes_in_expression(expr):
188
+ self._capture_quantum_type_attribute(handle)
187
189
 
188
190
  def _update_captured_vars(self, op: QuantumOperation) -> None:
189
191
  for handle, direction in op.handles_with_directions:
@@ -223,6 +225,17 @@ class Emitter(Generic[QuantumStatementT], ABC):
223
225
  defining_function=defining_function,
224
226
  )
225
227
 
228
+ def _capture_quantum_type_attribute(self, handle: FieldHandleBinding) -> None:
229
+ if handle.name not in self._current_scope:
230
+ return
231
+ defining_function = self._current_scope[handle.name].defining_function
232
+ if defining_function is None:
233
+ raise ClassiqInternalExpansionError
234
+ self._builder.current_block.captured_vars.capture_quantum_type_attribute(
235
+ handle=handle,
236
+ defining_function=defining_function,
237
+ )
238
+
226
239
  def _get_symbols_in_expression(self, expr: Expression) -> list[QuantumSymbol]:
227
240
  vrc = VarRefCollector(ignore_duplicated_handles=True, unevaluated=True)
228
241
  vrc.visit(ast.parse(expr.expr))
@@ -258,3 +271,18 @@ class Emitter(Generic[QuantumStatementT], ABC):
258
271
  )
259
272
  }.items()
260
273
  )
274
+
275
+ def _get_quantum_type_attributes_in_expression(
276
+ self, expr: Expression
277
+ ) -> list[FieldHandleBinding]:
278
+ vrc = VarRefCollector(ignore_duplicated_handles=True, unevaluated=True)
279
+ vrc.visit(ast.parse(expr.expr))
280
+ return list(
281
+ dict.fromkeys(
282
+ handle
283
+ for handle in vrc.var_handles
284
+ if isinstance(handle, FieldHandleBinding)
285
+ and handle.field in CLASSICAL_ATTRIBUTES
286
+ and isinstance(self._current_scope[handle.name].value, QuantumSymbol)
287
+ )
288
+ )
@@ -11,10 +11,6 @@ from classiq.interface.generator.expressions.proxies.classical.classical_struct_
11
11
  from classiq.interface.generator.expressions.proxies.classical.utils import (
12
12
  get_proxy_type,
13
13
  )
14
- from classiq.interface.generator.functions.port_declaration import (
15
- PortDeclarationDirection,
16
- )
17
- from classiq.interface.model.port_declaration import PortDeclaration
18
14
  from classiq.interface.model.quantum_function_declaration import (
19
15
  NamedParamsQuantumFunctionDeclaration,
20
16
  )
@@ -36,18 +32,7 @@ def get_func_call_cache_key(
36
32
  raise ClassiqInternalExpansionError(
37
33
  "Mismatch between number of args to number of arg declarations"
38
34
  )
39
-
40
- # output arguments cannot affect the morphization of the function, as their
41
- # attributes are defined locally by the function, according to other arguments
42
- non_outputs_args = [
43
- arg
44
- for arg_decl, arg in zip(decl.positional_arg_declarations, args)
45
- if not (
46
- isinstance(arg_decl, PortDeclaration)
47
- and arg_decl.direction is PortDeclarationDirection.Output
48
- )
49
- ]
50
- return f"{decl.name}__{_evaluated_args_to_str(non_outputs_args)}"
35
+ return f"{decl.name}__{_evaluated_args_to_str(args)}"
51
36
 
52
37
 
53
38
  def _evaluated_args_to_str(evaluated_args: list[Evaluated]) -> str: