classiq 0.64.0__py3-none-any.whl → 0.65.1__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 (51) hide show
  1. classiq/_internals/api_wrapper.py +30 -0
  2. classiq/applications/chemistry/chemistry_model_constructor.py +8 -9
  3. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +4 -6
  4. classiq/applications/combinatorial_optimization/combinatorial_problem.py +2 -5
  5. classiq/applications/finance/finance_model_constructor.py +7 -12
  6. classiq/applications/grover/grover_model_constructor.py +4 -6
  7. classiq/applications/qsvm/qsvm_model_constructor.py +6 -4
  8. classiq/execution/execution_session.py +14 -13
  9. classiq/interface/_version.py +1 -1
  10. classiq/interface/backend/backend_preferences.py +1 -9
  11. classiq/interface/generator/expressions/qmod_qarray_proxy.py +11 -13
  12. classiq/interface/generator/functions/type_name.py +6 -0
  13. classiq/interface/model/allocate.py +16 -0
  14. classiq/interface/model/quantum_type.py +26 -0
  15. classiq/interface/model/statement_block.py +2 -0
  16. classiq/interface/server/routes.py +1 -0
  17. classiq/model_expansions/evaluators/quantum_type_utils.py +10 -0
  18. classiq/model_expansions/function_builder.py +35 -11
  19. classiq/model_expansions/generative_functions.py +6 -4
  20. classiq/model_expansions/interpreters/base_interpreter.py +37 -138
  21. classiq/model_expansions/interpreters/frontend_generative_interpreter.py +28 -0
  22. classiq/model_expansions/interpreters/generative_interpreter.py +144 -3
  23. classiq/model_expansions/quantum_operations/call_emitter.py +43 -91
  24. classiq/model_expansions/quantum_operations/declarative_call_emitter.py +87 -0
  25. classiq/model_expansions/quantum_operations/emitter.py +5 -0
  26. classiq/model_expansions/quantum_operations/quantum_function_call.py +9 -0
  27. classiq/model_expansions/quantum_operations/shallow_emitter.py +20 -1
  28. classiq/model_expansions/scope.py +15 -15
  29. classiq/model_expansions/scope_initialization.py +3 -5
  30. classiq/open_library/functions/discrete_sine_cosine_transform.py +8 -2
  31. classiq/open_library/functions/grover.py +1 -1
  32. classiq/open_library/functions/modular_exponentiation.py +8 -2
  33. classiq/open_library/functions/state_preparation.py +23 -13
  34. classiq/open_library/functions/swap_test.py +1 -2
  35. classiq/open_library/functions/variational.py +1 -2
  36. classiq/qmod/builtins/__init__.py +1 -1
  37. classiq/qmod/builtins/operations.py +51 -0
  38. classiq/qmod/native/pretty_printer.py +9 -1
  39. classiq/qmod/pretty_print/pretty_printer.py +12 -1
  40. classiq/qmod/qmod_variable.py +38 -38
  41. classiq/qmod/quantum_function.py +4 -4
  42. classiq/qmod/semantics/annotation/__init__.py +0 -0
  43. classiq/qmod/semantics/annotation/call_annotation.py +92 -0
  44. classiq/qmod/semantics/lambdas.py +25 -0
  45. classiq/qmod/semantics/static_semantics_visitor.py +8 -46
  46. classiq/qmod/utilities.py +16 -0
  47. {classiq-0.64.0.dist-info → classiq-0.65.1.dist-info}/METADATA +1 -1
  48. {classiq-0.64.0.dist-info → classiq-0.65.1.dist-info}/RECORD +50 -45
  49. classiq/qmod/semantics/annotation.py +0 -36
  50. /classiq/qmod/semantics/{qstruct_annotator.py → annotation/qstruct_annotator.py} +0 -0
  51. {classiq-0.64.0.dist-info → classiq-0.65.1.dist-info}/WHEEL +0 -0
@@ -23,11 +23,8 @@ from typing_extensions import ParamSpec, Self, _AnnotatedAlias
23
23
  from classiq.interface.exceptions import ClassiqValueError
24
24
  from classiq.interface.generator.expressions.expression import Expression
25
25
  from classiq.interface.generator.expressions.qmod_qarray_proxy import (
26
- ILLEGAL_SLICE_BOUNDS_MSG,
27
26
  ILLEGAL_SLICE_MSG,
28
27
  ILLEGAL_SLICING_STEP_MSG,
29
- SLICE_OUT_OF_BOUNDS_MSG,
30
- SUBSCRIPT_OUT_OF_BOUNDS_MSG,
31
28
  )
32
29
  from classiq.interface.generator.functions.port_declaration import (
33
30
  PortDeclarationDirection,
@@ -69,6 +66,7 @@ from classiq.qmod.symbolic_type import SymbolicTypes
69
66
  from classiq.qmod.utilities import (
70
67
  get_source_ref,
71
68
  unwrap_forward_ref,
69
+ varname,
72
70
  version_portable_get_args,
73
71
  )
74
72
 
@@ -98,28 +96,42 @@ def _no_current_expandable() -> Iterator[None]:
98
96
  QCallable.CURRENT_EXPANDABLE = current_expandable
99
97
 
100
98
 
99
+ def _infer_variable_name(name: Any, depth: int) -> Any:
100
+ if name is not None:
101
+ return name
102
+ name = varname(depth + 1)
103
+ if name is None:
104
+ raise ClassiqValueError(
105
+ "Could not infer variable name. Please specify the variable name explicitly"
106
+ )
107
+ return name
108
+
109
+
101
110
  class QVar(Symbolic):
111
+ CONSTRUCTOR_DEPTH: int = 1
112
+
102
113
  def __init__(
103
114
  self,
104
- origin: Union[str, HandleBinding],
115
+ origin: Union[None, str, HandleBinding] = None,
105
116
  *,
106
117
  expr_str: Optional[str] = None,
107
118
  depth: int = 2,
108
119
  ) -> None:
109
- super().__init__(str(origin), True)
120
+ name = _infer_variable_name(origin, self.CONSTRUCTOR_DEPTH)
121
+ super().__init__(str(name), True)
110
122
  source_ref = (
111
123
  get_source_ref(sys._getframe(depth))
112
- if isinstance(origin, str)
113
- else origin.source_ref
124
+ if isinstance(name, str)
125
+ else name.source_ref
114
126
  )
115
127
  self._base_handle: HandleBinding = (
116
- HandleBinding(name=origin) if isinstance(origin, str) else origin
128
+ HandleBinding(name=name) if isinstance(name, str) else name
117
129
  )
118
- if isinstance(origin, str) and QCallable.CURRENT_EXPANDABLE is not None:
130
+ if isinstance(name, str) and QCallable.CURRENT_EXPANDABLE is not None:
119
131
  QCallable.CURRENT_EXPANDABLE.add_local_handle(
120
- origin, self.get_qmod_type(), source_ref
132
+ name, self.get_qmod_type(), source_ref
121
133
  )
122
- self._expr_str = expr_str if expr_str is not None else str(origin)
134
+ self._expr_str = expr_str if expr_str is not None else str(name)
123
135
 
124
136
  def get_handle_binding(self) -> HandleBinding:
125
137
  return self._base_handle
@@ -180,13 +192,16 @@ Input = Annotated[_Q, PortDeclarationDirection.Input]
180
192
 
181
193
 
182
194
  class QScalar(QVar, SymbolicExpr):
195
+ CONSTRUCTOR_DEPTH: int = 2
196
+
183
197
  def __init__(
184
198
  self,
185
- origin: Union[str, HandleBinding],
199
+ origin: Union[None, str, HandleBinding] = None,
186
200
  *,
187
201
  _expr_str: Optional[str] = None,
188
202
  depth: int = 2,
189
203
  ) -> None:
204
+ origin = _infer_variable_name(origin, self.CONSTRUCTOR_DEPTH)
190
205
  QVar.__init__(self, origin, expr_str=_expr_str, depth=depth)
191
206
  SymbolicExpr.__init__(self, str(origin), True)
192
207
 
@@ -286,9 +301,11 @@ _P = ParamSpec("_P")
286
301
 
287
302
 
288
303
  class QNum(Generic[_P], QScalar):
304
+ CONSTRUCTOR_DEPTH: int = 3
305
+
289
306
  def __init__(
290
307
  self,
291
- name: Union[str, HandleBinding],
308
+ name: Union[None, str, HandleBinding] = None,
292
309
  size: Union[int, CInt, Expression, SymbolicExpr, None] = None,
293
310
  is_signed: Union[bool, Expression, SymbolicExpr, None] = None,
294
311
  fraction_digits: Union[int, CInt, Expression, None] = None,
@@ -398,10 +415,12 @@ class QNum(Generic[_P], QScalar):
398
415
 
399
416
 
400
417
  class QArray(ArrayBase[_P], QVar):
418
+ CONSTRUCTOR_DEPTH: int = 3
419
+
401
420
  # TODO [CAD-18620]: improve type hints
402
421
  def __init__(
403
422
  self,
404
- name: Union[str, HandleBinding],
423
+ name: Union[None, str, HandleBinding] = None,
405
424
  element_type: Union[_GenericAlias, QuantumType] = QBit,
406
425
  length: Optional[Union[int, SymbolicExpr, Expression]] = None,
407
426
  _expr_str: Optional[str] = None,
@@ -425,14 +444,6 @@ class QArray(ArrayBase[_P], QVar):
425
444
  def _get_subscript(self, index: Union[slice, int, SymbolicExpr]) -> Any:
426
445
  if isinstance(index, SymbolicExpr) and index.is_quantum:
427
446
  raise ClassiqValueError("Non-classical parameter for slicing")
428
- if (
429
- isinstance(index, int)
430
- and self._length is not None
431
- and self._length.is_evaluated()
432
- ):
433
- length = self._length.to_int_value()
434
- if index < 0 or index >= length:
435
- raise ClassiqValueError(SUBSCRIPT_OUT_OF_BOUNDS_MSG)
436
447
 
437
448
  return _create_qvar_for_qtype(
438
449
  self.get_qmod_type().element_type,
@@ -450,20 +461,6 @@ class QArray(ArrayBase[_P], QVar):
450
461
  slice_.stop, (int, SymbolicExpr)
451
462
  ):
452
463
  raise ClassiqValueError(ILLEGAL_SLICE_MSG)
453
- if (
454
- isinstance(slice_.start, int)
455
- and isinstance(slice_.stop, int)
456
- and slice_.start >= slice_.stop
457
- ):
458
- raise ClassiqValueError(
459
- ILLEGAL_SLICE_BOUNDS_MSG.format(slice_.start, slice_.stop)
460
- )
461
- if self._length is not None and self._length.is_evaluated():
462
- length = self._length.to_int_value()
463
- if (isinstance(slice_.start, int) and slice_.start < 0) or (
464
- isinstance(slice_.stop, int) and slice_.stop > length
465
- ):
466
- raise ClassiqValueError(SLICE_OUT_OF_BOUNDS_MSG)
467
464
 
468
465
  return QArray(
469
466
  name=SlicedHandleBinding(
@@ -547,16 +544,19 @@ class QArray(ArrayBase[_P], QVar):
547
544
 
548
545
 
549
546
  class QStruct(QVar):
547
+ CONSTRUCTOR_DEPTH: int = 2
548
+
550
549
  _struct_name: str
551
550
  _fields: Mapping[str, QVar]
552
551
 
553
552
  def __init__(
554
553
  self,
555
- name: Union[str, HandleBinding],
554
+ origin: Union[None, str, HandleBinding] = None,
556
555
  _struct_name: Optional[str] = None,
557
556
  _fields: Optional[Mapping[str, QVar]] = None,
558
557
  _expr_str: Optional[str] = None,
559
558
  ) -> None:
559
+ name = _infer_variable_name(origin, self.CONSTRUCTOR_DEPTH)
560
560
  if _struct_name is None or _fields is None:
561
561
  with _no_current_expandable():
562
562
  temp_var = QStruct.to_qvar(name, type(self), _expr_str)
@@ -611,7 +611,7 @@ class QStruct(QVar):
611
611
  for field_name, (field_class, field_type) in field_types.items()
612
612
  }
613
613
  return QStruct(
614
- name=origin,
614
+ origin,
615
615
  _struct_name=type_hint.__name__,
616
616
  _fields=field_vars,
617
617
  _expr_str=expr_str,
@@ -153,10 +153,10 @@ class QFunc(BaseQFunc):
153
153
  return model
154
154
 
155
155
  def _create_generative_model(self, model_stub: Model) -> Model:
156
- from classiq.model_expansions.interpreters.generative_interpreter import (
157
- GenerativeInterpreter,
156
+ from classiq.model_expansions.interpreters.frontend_generative_interpreter import (
157
+ FrontendGenerativeInterpreter,
158
158
  )
159
- from classiq.qmod.semantics.static_semantics_visitor import (
159
+ from classiq.qmod.semantics.annotation.call_annotation import (
160
160
  resolve_function_calls,
161
161
  )
162
162
 
@@ -169,7 +169,7 @@ class QFunc(BaseQFunc):
169
169
  for gen_func in generative_functions
170
170
  },
171
171
  )
172
- interpreter = GenerativeInterpreter(model_stub, generative_functions)
172
+ interpreter = FrontendGenerativeInterpreter(model_stub, generative_functions)
173
173
  set_frontend_interpreter(interpreter)
174
174
  return interpreter.expand()
175
175
 
File without changes
@@ -0,0 +1,92 @@
1
+ from collections.abc import Iterator, Mapping
2
+ from contextlib import contextmanager
3
+ from typing import Any
4
+
5
+ from classiq.interface.exceptions import ClassiqError
6
+ from classiq.interface.generator.visitor import Visitor
7
+ from classiq.interface.model.native_function_definition import NativeFunctionDefinition
8
+ from classiq.interface.model.quantum_function_call import QuantumFunctionCall
9
+ from classiq.interface.model.quantum_function_declaration import (
10
+ AnonQuantumOperandDeclaration,
11
+ QuantumFunctionDeclaration,
12
+ QuantumOperandDeclaration,
13
+ )
14
+ from classiq.interface.model.quantum_lambda_function import (
15
+ QuantumLambdaFunction,
16
+ )
17
+
18
+ from classiq.qmod.builtins.functions import BUILTIN_FUNCTION_DECLARATIONS
19
+ from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
20
+ from classiq.qmod.semantics.error_manager import ErrorManager
21
+ from classiq.qmod.semantics.lambdas import get_renamed_parameters
22
+
23
+
24
+ def _annotate_function_call_decl(
25
+ fc: QuantumFunctionCall,
26
+ function_dict: Mapping[str, QuantumFunctionDeclaration],
27
+ ) -> None:
28
+ if fc._func_decl is None:
29
+ func_decl = function_dict.get(fc.func_name)
30
+ if func_decl is None:
31
+ raise ClassiqError(
32
+ f"Error resolving function {fc.func_name}, the function is not found in included library."
33
+ )
34
+ fc.set_func_decl(func_decl)
35
+
36
+ for arg, param in zip(fc.positional_args, fc.func_decl.positional_arg_declarations):
37
+ if not isinstance(param, AnonQuantumOperandDeclaration):
38
+ continue
39
+ args: list
40
+ if isinstance(arg, list):
41
+ args = arg
42
+ else:
43
+ args = [arg]
44
+ for qlambda in args:
45
+ if isinstance(qlambda, QuantumLambdaFunction):
46
+ qlambda.set_op_decl(param)
47
+
48
+
49
+ class _CallLambdaAnnotator(Visitor):
50
+ def __init__(
51
+ self, quantum_functions: Mapping[str, QuantumFunctionDeclaration]
52
+ ) -> None:
53
+ self._quantum_functions = dict(quantum_functions)
54
+ self._current_operands: dict[str, QuantumOperandDeclaration] = {}
55
+
56
+ @contextmanager
57
+ def set_operands(
58
+ self, operands: dict[str, QuantumOperandDeclaration]
59
+ ) -> Iterator[None]:
60
+ previous_operands = self._current_operands
61
+ self._current_operands = operands
62
+ yield
63
+ self._current_operands = previous_operands
64
+
65
+ def visit_NativeFunctionDefinition(self, func: NativeFunctionDefinition) -> None:
66
+ with self.set_operands(func.operand_declarations_dict):
67
+ self.generic_visit(func)
68
+
69
+ def visit_QuantumLambdaFunction(self, lambda_func: QuantumLambdaFunction) -> None:
70
+ lambda_operands = get_renamed_parameters(lambda_func)[1]
71
+ with self.set_operands(self._current_operands | lambda_operands):
72
+ self.generic_visit(lambda_func)
73
+
74
+ def visit_QuantumFunctionCall(self, call: QuantumFunctionCall) -> None:
75
+ _annotate_function_call_decl(
76
+ call, self._quantum_functions | self._current_operands
77
+ )
78
+ self.generic_visit(call)
79
+
80
+
81
+ def resolve_function_calls(
82
+ root: Any,
83
+ quantum_function_dict: Mapping[str, QuantumFunctionDeclaration],
84
+ ) -> None:
85
+ all_functions: Mapping[str, QuantumFunctionDeclaration] = {
86
+ **BUILTIN_FUNCTION_DECLARATIONS,
87
+ **quantum_function_dict,
88
+ }
89
+ with ErrorManager().ignore_errors_context():
90
+ QStructAnnotator().visit(quantum_function_dict)
91
+ QStructAnnotator().visit(root)
92
+ _CallLambdaAnnotator(all_functions).visit(root)
@@ -0,0 +1,25 @@
1
+ from classiq.interface.model.port_declaration import PortDeclaration
2
+ from classiq.interface.model.quantum_function_declaration import (
3
+ AnonQuantumOperandDeclaration,
4
+ QuantumOperandDeclaration,
5
+ )
6
+ from classiq.interface.model.quantum_lambda_function import QuantumLambdaFunction
7
+
8
+ from classiq import AnonClassicalParameterDeclaration
9
+
10
+
11
+ def get_renamed_parameters(
12
+ lambda_func: QuantumLambdaFunction,
13
+ ) -> tuple[list[str], dict[str, QuantumOperandDeclaration], list[PortDeclaration]]:
14
+ renamed_parameters: list[str] = []
15
+ renamed_operands: dict[str, QuantumOperandDeclaration] = {}
16
+ 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]
19
+ if isinstance(param, AnonClassicalParameterDeclaration):
20
+ renamed_parameters.append(param_name)
21
+ elif isinstance(param, AnonQuantumOperandDeclaration):
22
+ renamed_operands[param_name] = param.rename(param_name)
23
+ else:
24
+ renamed_ports.append(param.rename(param_name))
25
+ return renamed_parameters, renamed_operands, renamed_ports
@@ -2,7 +2,6 @@ import ast
2
2
  from collections.abc import Iterator, Mapping, Sequence
3
3
  from contextlib import contextmanager
4
4
  from typing import (
5
- Any,
6
5
  Optional,
7
6
  )
8
7
 
@@ -15,9 +14,6 @@ from classiq.interface.generator.functions.port_declaration import (
15
14
  PortDeclarationDirection,
16
15
  )
17
16
  from classiq.interface.generator.visitor import Visitor
18
- from classiq.interface.model.classical_parameter_declaration import (
19
- AnonClassicalParameterDeclaration,
20
- )
21
17
  from classiq.interface.model.handle_binding import (
22
18
  FieldHandleBinding,
23
19
  HandleBinding,
@@ -33,7 +29,6 @@ from classiq.interface.model.quantum_expressions.quantum_expression import (
33
29
  )
34
30
  from classiq.interface.model.quantum_function_call import QuantumFunctionCall
35
31
  from classiq.interface.model.quantum_function_declaration import (
36
- AnonQuantumOperandDeclaration,
37
32
  QuantumFunctionDeclaration,
38
33
  QuantumOperandDeclaration,
39
34
  )
@@ -51,9 +46,10 @@ from classiq.model_expansions.visitors.variable_references import VarRefCollecto
51
46
  from classiq.qmod.builtins.functions import (
52
47
  BUILTIN_FUNCTION_DECLARATIONS,
53
48
  )
54
- from classiq.qmod.semantics.annotation import annotate_function_call_decl
49
+ from classiq.qmod.semantics.annotation.call_annotation import resolve_function_calls
50
+ from classiq.qmod.semantics.annotation.qstruct_annotator import QStructAnnotator
55
51
  from classiq.qmod.semantics.error_manager import ErrorManager
56
- from classiq.qmod.semantics.qstruct_annotator import QStructAnnotator
52
+ from classiq.qmod.semantics.lambdas import get_renamed_parameters
57
53
  from classiq.qmod.semantics.validation.constants_validation import (
58
54
  check_duplicate_constants,
59
55
  )
@@ -202,13 +198,6 @@ class StaticSemanticsVisitor(Visitor):
202
198
  def visit_QuantumOperation(self, op: QuantumOperation) -> None:
203
199
  with self._error_manager.node_context(op):
204
200
  if isinstance(op, QuantumFunctionCall):
205
- annotate_function_call_decl(
206
- op,
207
- {
208
- **self._functions_dict,
209
- **self.current_scope.operands,
210
- },
211
- )
212
201
  validate_call_arguments(
213
202
  op,
214
203
  {
@@ -243,8 +232,8 @@ class StaticSemanticsVisitor(Visitor):
243
232
  )
244
233
 
245
234
  def visit_QuantumLambdaFunction(self, lambda_func: QuantumLambdaFunction) -> None:
246
- renamed_parameters, renamed_operands, renamed_ports = (
247
- self._get_renamed_parameters(lambda_func)
235
+ renamed_parameters, renamed_operands, renamed_ports = get_renamed_parameters(
236
+ lambda_func
248
237
  )
249
238
  scope = StaticScope(
250
239
  parameters=self.current_scope.parameters + renamed_parameters,
@@ -259,22 +248,6 @@ class StaticSemanticsVisitor(Visitor):
259
248
  with self.scoped_visit(scope):
260
249
  self.generic_visit(lambda_func)
261
250
 
262
- def _get_renamed_parameters(
263
- self, lambda_func: QuantumLambdaFunction
264
- ) -> tuple[list[str], dict[str, QuantumOperandDeclaration], list[PortDeclaration]]:
265
- renamed_parameters: list[str] = []
266
- renamed_operands: dict[str, QuantumOperandDeclaration] = {}
267
- renamed_ports: list[PortDeclaration] = []
268
- for idx, param in enumerate(lambda_func.func_decl.positional_arg_declarations):
269
- param_name = lambda_func.pos_rename_params[idx]
270
- if isinstance(param, AnonClassicalParameterDeclaration):
271
- renamed_parameters.append(param_name)
272
- elif isinstance(param, AnonQuantumOperandDeclaration):
273
- renamed_operands[param_name] = param.rename(param_name)
274
- else:
275
- renamed_ports.append(param.rename(param_name))
276
- return renamed_parameters, renamed_operands, renamed_ports
277
-
278
251
  def visit_HandleBinding(self, handle: HandleBinding) -> None:
279
252
  resolve_handle(self.current_scope, handle)
280
253
 
@@ -375,26 +348,15 @@ class StaticSemanticsVisitor(Visitor):
375
348
  )
376
349
 
377
350
 
378
- def resolve_function_calls(
379
- root: Any,
380
- quantum_function_dict: Mapping[str, QuantumFunctionDeclaration],
381
- ) -> None:
382
- with ErrorManager().ignore_errors_context():
383
- QStructAnnotator().visit(quantum_function_dict)
384
- QStructAnnotator().visit(root)
385
- StaticSemanticsVisitor(
386
- {**BUILTIN_FUNCTION_DECLARATIONS, **quantum_function_dict},
387
- [],
388
- ).visit(root)
389
-
390
-
391
351
  def static_semantics_analysis_pass(
392
352
  model: Model, error_type: Optional[type[Exception]] = ClassiqSemanticError
393
353
  ) -> None:
394
354
  _check_function_name_collisions(model, error_type)
395
355
  QStructAnnotator().visit(model)
356
+ functions = {**BUILTIN_FUNCTION_DECLARATIONS, **model.function_dict}
357
+ resolve_function_calls(model, functions)
396
358
  StaticSemanticsVisitor(
397
- {**BUILTIN_FUNCTION_DECLARATIONS, **model.function_dict},
359
+ functions,
398
360
  [const.name for const in model.constants],
399
361
  ).visit(model)
400
362
  if error_type is not None:
classiq/qmod/utilities.py CHANGED
@@ -103,3 +103,19 @@ def unwrap_forward_ref(x: Any) -> Any:
103
103
  if isinstance(x, ForwardRef):
104
104
  return x.__forward_arg__
105
105
  return x
106
+
107
+
108
+ def varname(depth: int) -> Optional[str]:
109
+ frame = sys._getframe(depth)
110
+ codes = inspect.getframeinfo(frame).code_context
111
+ if codes is None or len(codes) != 1:
112
+ return None
113
+ code = codes[0]
114
+ if "=" not in code:
115
+ return None
116
+ var_name = code.split("=")[0].strip()
117
+ if ":" in var_name:
118
+ var_name = var_name.split(":")[0].strip()
119
+ if not var_name.isidentifier():
120
+ return None
121
+ return var_name
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: classiq
3
- Version: 0.64.0
3
+ Version: 0.65.1
4
4
  Summary: Classiq's Python SDK for quantum computing
5
5
  Home-page: https://classiq.io
6
6
  License: Proprietary