classiq 0.68.0__py3-none-any.whl → 0.70.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. classiq/_internals/api_wrapper.py +4 -8
  2. classiq/analyzer/analyzer.py +0 -18
  3. classiq/analyzer/url_utils.py +9 -4
  4. classiq/applications/combinatorial_helpers/pyomo_utils.py +2 -0
  5. classiq/applications/combinatorial_optimization/combinatorial_problem.py +8 -11
  6. classiq/applications/qnn/gradients/quantum_gradient.py +1 -1
  7. classiq/applications/qnn/gradients/simple_quantum_gradient.py +1 -1
  8. classiq/applications/qnn/torch_utils.py +1 -1
  9. classiq/execution/jobs.py +2 -5
  10. classiq/interface/_version.py +1 -1
  11. classiq/interface/backend/quantum_backend_providers.py +8 -3
  12. classiq/interface/chemistry/operator.py +12 -28
  13. classiq/interface/debug_info/back_ref_util.py +22 -0
  14. classiq/interface/debug_info/debug_info.py +11 -21
  15. classiq/interface/executor/optimizer_preferences.py +1 -0
  16. classiq/interface/executor/quantum_instruction_set.py +1 -0
  17. classiq/interface/generator/arith/arithmetic.py +21 -6
  18. classiq/interface/generator/arith/arithmetic_param_getters.py +3 -3
  19. classiq/interface/generator/circuit_code/circuit_code.py +4 -0
  20. classiq/interface/generator/circuit_code/types_and_constants.py +1 -0
  21. classiq/interface/generator/expressions/atomic_expression_functions.py +1 -2
  22. classiq/interface/generator/expressions/expression_types.py +8 -2
  23. classiq/interface/generator/expressions/proxies/__init__.py +0 -0
  24. classiq/interface/generator/expressions/proxies/classical/__init__.py +0 -0
  25. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +75 -0
  26. classiq/interface/generator/expressions/proxies/classical/classical_proxy.py +26 -0
  27. classiq/interface/generator/expressions/proxies/classical/classical_scalar_proxy.py +32 -0
  28. classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +31 -0
  29. classiq/interface/generator/expressions/proxies/quantum/__init__.py +0 -0
  30. classiq/interface/generator/expressions/{qmod_qarray_proxy.py → proxies/quantum/qmod_qarray_proxy.py} +3 -1
  31. classiq/interface/generator/expressions/{qmod_qscalar_proxy.py → proxies/quantum/qmod_qscalar_proxy.py} +3 -1
  32. classiq/interface/generator/expressions/{qmod_qstruct_proxy.py → proxies/quantum/qmod_qstruct_proxy.py} +3 -1
  33. classiq/interface/generator/functions/classical_type.py +34 -29
  34. classiq/interface/generator/functions/type_name.py +26 -2
  35. classiq/interface/generator/generated_circuit_data.py +84 -27
  36. classiq/interface/generator/model/preferences/preferences.py +1 -0
  37. classiq/interface/generator/quantum_program.py +0 -1
  38. classiq/interface/generator/types/builtin_enum_declarations.py +1 -0
  39. classiq/interface/generator/types/enum_declaration.py +12 -1
  40. classiq/interface/ide/visual_model.py +0 -2
  41. classiq/interface/model/native_function_definition.py +0 -10
  42. classiq/interface/model/quantum_statement.py +1 -1
  43. classiq/interface/model/quantum_type.py +15 -3
  44. classiq/interface/server/routes.py +0 -6
  45. classiq/model_expansions/atomic_expression_functions_defs.py +9 -3
  46. classiq/model_expansions/evaluators/arg_type_match.py +4 -2
  47. classiq/model_expansions/evaluators/classical_expression.py +2 -2
  48. classiq/model_expansions/evaluators/control.py +1 -1
  49. classiq/model_expansions/evaluators/parameter_types.py +58 -16
  50. classiq/model_expansions/evaluators/quantum_type_utils.py +7 -57
  51. classiq/model_expansions/expression_evaluator.py +3 -1
  52. classiq/model_expansions/generative_functions.py +67 -7
  53. classiq/model_expansions/quantum_operations/arithmetic/__init__.py +0 -0
  54. classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +60 -0
  55. classiq/model_expansions/quantum_operations/assignment_result_processor.py +9 -0
  56. classiq/model_expansions/quantum_operations/call_emitter.py +0 -13
  57. classiq/model_expansions/quantum_operations/quantum_function_call.py +0 -22
  58. classiq/model_expansions/scope.py +7 -6
  59. classiq/model_expansions/scope_initialization.py +20 -33
  60. classiq/model_expansions/transformers/model_renamer.py +13 -4
  61. classiq/model_expansions/visitors/variable_references.py +8 -4
  62. classiq/open_library/functions/__init__.py +2 -0
  63. classiq/open_library/functions/amplitude_amplification.py +3 -7
  64. classiq/open_library/functions/discrete_sine_cosine_transform.py +4 -4
  65. classiq/open_library/functions/grover.py +2 -2
  66. classiq/open_library/functions/hea.py +3 -3
  67. classiq/open_library/functions/lookup_table.py +58 -0
  68. classiq/open_library/functions/modular_exponentiation.py +10 -20
  69. classiq/open_library/functions/qft_functions.py +2 -2
  70. classiq/open_library/functions/qsvt.py +8 -8
  71. classiq/open_library/functions/utility_functions.py +2 -2
  72. classiq/qmod/builtins/classical_functions.py +24 -7
  73. classiq/qmod/builtins/enums.py +1 -0
  74. classiq/qmod/builtins/functions/__init__.py +2 -0
  75. classiq/qmod/builtins/functions/exponentiation.py +24 -0
  76. classiq/qmod/builtins/operations.py +26 -11
  77. classiq/qmod/cparam.py +32 -5
  78. classiq/qmod/declaration_inferrer.py +3 -1
  79. classiq/qmod/python_classical_type.py +10 -4
  80. classiq/qmod/qmod_parameter.py +8 -0
  81. classiq/qmod/qmod_variable.py +11 -14
  82. classiq/qmod/quantum_callable.py +2 -1
  83. classiq/qmod/quantum_function.py +3 -2
  84. classiq/qmod/semantics/annotation/call_annotation.py +0 -28
  85. classiq/qmod/semantics/annotation/qstruct_annotator.py +21 -1
  86. classiq/qmod/semantics/error_manager.py +1 -1
  87. classiq/qmod/semantics/validation/main_validation.py +1 -1
  88. classiq/qmod/semantics/validation/type_hints.py +29 -0
  89. classiq/qmod/utilities.py +67 -2
  90. classiq/synthesis.py +9 -6
  91. {classiq-0.68.0.dist-info → classiq-0.70.0.dist-info}/METADATA +10 -12
  92. {classiq-0.68.0.dist-info → classiq-0.70.0.dist-info}/RECORD +95 -84
  93. {classiq-0.68.0.dist-info → classiq-0.70.0.dist-info}/WHEEL +1 -1
  94. classiq/interface/execution/jobs.py +0 -31
  95. /classiq/interface/generator/expressions/{qmod_struct_instance.py → proxies/classical/qmod_struct_instance.py} +0 -0
  96. /classiq/interface/generator/expressions/{qmod_sized_proxy.py → proxies/quantum/qmod_sized_proxy.py} +0 -0
@@ -0,0 +1,75 @@
1
+ from collections.abc import Mapping
2
+ from typing import TYPE_CHECKING, Union
3
+
4
+ from sympy import Integer
5
+
6
+ from classiq.interface.exceptions import ClassiqValueError
7
+ from classiq.interface.generator.expressions.expression import Expression
8
+ from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
9
+ from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
10
+ ClassicalProxy,
11
+ )
12
+ from classiq.interface.model.handle_binding import (
13
+ HandleBinding,
14
+ SlicedHandleBinding,
15
+ SubscriptHandleBinding,
16
+ )
17
+
18
+ if TYPE_CHECKING:
19
+ from classiq.interface.generator.expressions.expression_types import ExpressionValue
20
+ from classiq.interface.generator.functions.concrete_types import (
21
+ ConcreteClassicalType,
22
+ )
23
+
24
+
25
+ class ClassicalArrayProxy(NonSymbolicExpr, ClassicalProxy):
26
+ def __init__(
27
+ self, handle: HandleBinding, element_type: "ConcreteClassicalType", length: int
28
+ ) -> None:
29
+ super().__init__(handle)
30
+ self._element_type = element_type
31
+ self._length = length
32
+
33
+ @property
34
+ def fields(self) -> Mapping[str, "ExpressionValue"]:
35
+ return {"len": self._length}
36
+
37
+ @property
38
+ def type_name(self) -> str:
39
+ return "Array"
40
+
41
+ def __getitem__(self, key: Union[slice, int, Integer]) -> ClassicalProxy:
42
+ return (
43
+ self._get_slice(key) if isinstance(key, slice) else self._get_subscript(key)
44
+ )
45
+
46
+ def _get_slice(self, slice_: slice) -> ClassicalProxy:
47
+ start = int(slice_.start)
48
+ stop = int(slice_.stop)
49
+ if start >= stop:
50
+ raise ClassiqValueError("Array slice has non-positive length")
51
+ if start < 0 or stop > self._length:
52
+ raise ClassiqValueError("Array slice is out of bounds")
53
+ return ClassicalArrayProxy(
54
+ SlicedHandleBinding(
55
+ base_handle=self.handle,
56
+ start=Expression(expr=str(start)),
57
+ end=Expression(expr=str(stop)),
58
+ ),
59
+ self._element_type,
60
+ stop - start,
61
+ )
62
+
63
+ def _get_subscript(self, index_: Union[int, Integer]) -> ClassicalProxy:
64
+ index = int(index_)
65
+ if index < 0:
66
+ raise ClassiqValueError(
67
+ "Array index is out of bounds (negative indices are not supported)"
68
+ )
69
+ if index >= self._length:
70
+ raise ClassiqValueError("Array index is out of bounds")
71
+ return self._element_type.get_classical_proxy(
72
+ SubscriptHandleBinding(
73
+ base_handle=self._handle, index=Expression(expr=str(index_))
74
+ )
75
+ )
@@ -0,0 +1,26 @@
1
+ from collections.abc import Mapping
2
+ from typing import TYPE_CHECKING
3
+
4
+ if TYPE_CHECKING:
5
+ from classiq.interface.generator.expressions.expression_types import ExpressionValue
6
+ from classiq.interface.model.handle_binding import HandleBinding
7
+
8
+
9
+ class ClassicalProxy:
10
+ def __init__(self, handle: "HandleBinding") -> None:
11
+ self._handle = handle
12
+
13
+ @property
14
+ def handle(self) -> "HandleBinding":
15
+ return self._handle
16
+
17
+ def __str__(self) -> str:
18
+ return str(self.handle)
19
+
20
+ @property
21
+ def fields(self) -> Mapping[str, "ExpressionValue"]:
22
+ raise NotImplementedError
23
+
24
+ @property
25
+ def type_name(self) -> str:
26
+ raise NotImplementedError
@@ -0,0 +1,32 @@
1
+ from collections.abc import Mapping
2
+ from typing import TYPE_CHECKING, Any
3
+
4
+ from sympy import Symbol
5
+
6
+ from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
7
+ ClassicalProxy,
8
+ )
9
+ from classiq.interface.model.handle_binding import HandleBinding
10
+
11
+ if TYPE_CHECKING:
12
+ from classiq.interface.generator.expressions.expression_types import ExpressionValue
13
+ from classiq.interface.generator.functions.classical_type import ClassicalType
14
+
15
+
16
+ class ClassicalScalarProxy(Symbol, ClassicalProxy):
17
+ def __new__(
18
+ cls, handle: HandleBinding, *args: Any, **assumptions: bool
19
+ ) -> "ClassicalScalarProxy":
20
+ return super().__new__(cls, str(handle), **assumptions)
21
+
22
+ def __init__(self, handle: HandleBinding, classical_type: "ClassicalType") -> None:
23
+ super().__init__(handle)
24
+ self._classical_type = classical_type
25
+
26
+ @property
27
+ def fields(self) -> Mapping[str, "ExpressionValue"]:
28
+ return {}
29
+
30
+ @property
31
+ def type_name(self) -> str:
32
+ return type(self._classical_type).__name__
@@ -0,0 +1,31 @@
1
+ from collections.abc import Mapping
2
+ from typing import TYPE_CHECKING
3
+
4
+ from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
5
+ from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
6
+ ClassicalProxy,
7
+ )
8
+ from classiq.interface.model.handle_binding import FieldHandleBinding, HandleBinding
9
+
10
+ if TYPE_CHECKING:
11
+ from classiq.interface.generator.expressions.expression_types import ExpressionValue
12
+ from classiq.interface.generator.types.struct_declaration import StructDeclaration
13
+
14
+
15
+ class ClassicalStructProxy(NonSymbolicExpr, ClassicalProxy):
16
+ def __init__(self, handle: HandleBinding, decl: "StructDeclaration") -> None:
17
+ super().__init__(handle)
18
+ self._decl = decl
19
+
20
+ @property
21
+ def fields(self) -> Mapping[str, "ExpressionValue"]:
22
+ return {
23
+ field_name: field_type.get_classical_proxy(
24
+ FieldHandleBinding(base_handle=self.handle, field_name=field_name)
25
+ )
26
+ for field_name, field_type in self._decl.variables.items()
27
+ }
28
+
29
+ @property
30
+ def type_name(self) -> str:
31
+ return f"Struct {self._decl.name}"
@@ -6,7 +6,9 @@ from sympy import Integer
6
6
  from classiq.interface.exceptions import ClassiqValueError
7
7
  from classiq.interface.generator.expressions.expression import Expression
8
8
  from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
9
- from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
9
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_sized_proxy import (
10
+ QmodSizedProxy,
11
+ )
10
12
  from classiq.interface.model.handle_binding import (
11
13
  HandleBinding,
12
14
  SlicedHandleBinding,
@@ -4,7 +4,9 @@ from typing import Any
4
4
  from sympy import Symbol
5
5
 
6
6
  from classiq.interface.exceptions import ClassiqValueError
7
- from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
7
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_sized_proxy import (
8
+ QmodSizedProxy,
9
+ )
8
10
  from classiq.interface.model.handle_binding import HandleBinding
9
11
 
10
12
 
@@ -2,7 +2,9 @@ from collections.abc import Mapping
2
2
  from typing import TYPE_CHECKING
3
3
 
4
4
  from classiq.interface.generator.expressions.non_symbolic_expr import NonSymbolicExpr
5
- from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
5
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_sized_proxy import (
6
+ QmodSizedProxy,
7
+ )
6
8
  from classiq.interface.model.handle_binding import (
7
9
  FieldHandleBinding,
8
10
  HandleBinding,
@@ -1,39 +1,51 @@
1
- from typing import TYPE_CHECKING, Any, Literal, Union
1
+ from typing import TYPE_CHECKING, Any, Literal
2
2
 
3
3
  import pydantic
4
- from pydantic import ConfigDict
5
- from sympy import IndexedBase, Symbol
4
+ from pydantic import ConfigDict, PrivateAttr
5
+ from typing_extensions import Self
6
6
 
7
7
  from classiq.interface.ast_node import HashableASTNode
8
- from classiq.interface.generator.expressions.expression_types import RuntimeExpression
8
+ from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
9
+ ClassicalArrayProxy,
10
+ )
11
+ from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
12
+ ClassicalProxy,
13
+ )
14
+ from classiq.interface.generator.expressions.proxies.classical.classical_scalar_proxy import (
15
+ ClassicalScalarProxy,
16
+ )
9
17
  from classiq.interface.helpers.pydantic_model_helpers import values_with_discriminator
18
+ from classiq.interface.model.handle_binding import HandleBinding
10
19
 
11
20
  if TYPE_CHECKING:
12
21
  from classiq.interface.generator.functions.concrete_types import (
13
22
  ConcreteClassicalType,
14
23
  )
15
24
 
16
- CLASSICAL_ATTRIBUTES = {"len", "size", "is_signed", "fraction_digits"}
17
-
18
- NamedSymbol = Union[IndexedBase, Symbol]
19
-
20
25
 
21
26
  class ClassicalType(HashableASTNode):
22
- def as_symbolic(self, name: str) -> Union[NamedSymbol, list[NamedSymbol]]:
23
- return Symbol(name)
27
+ _is_generative: bool = PrivateAttr(default=False)
24
28
 
25
29
  model_config = ConfigDict(extra="forbid")
26
30
 
27
31
  def __str__(self) -> str:
28
32
  return str(type(self).__name__)
29
33
 
34
+ def set_generative(self) -> Self:
35
+ self._is_generative = True
36
+ return self
37
+
38
+ @property
39
+ def is_generative(self) -> bool:
40
+ return self._is_generative
41
+
42
+ def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
43
+ return ClassicalScalarProxy(handle, self)
44
+
30
45
 
31
46
  class Integer(ClassicalType):
32
47
  kind: Literal["int"]
33
48
 
34
- def as_symbolic(self, name: str) -> Symbol:
35
- return Symbol(name, integer=True)
36
-
37
49
  @pydantic.model_validator(mode="before")
38
50
  @classmethod
39
51
  def _set_kind(cls, values: Any) -> dict[str, Any]:
@@ -43,9 +55,6 @@ class Integer(ClassicalType):
43
55
  class Real(ClassicalType):
44
56
  kind: Literal["real"]
45
57
 
46
- def as_symbolic(self, name: str) -> Symbol:
47
- return Symbol(name, real=True)
48
-
49
58
  @pydantic.model_validator(mode="before")
50
59
  @classmethod
51
60
  def _set_kind(cls, values: Any) -> dict[str, Any]:
@@ -65,14 +74,14 @@ class ClassicalList(ClassicalType):
65
74
  kind: Literal["list"]
66
75
  element_type: "ConcreteClassicalType"
67
76
 
68
- def as_symbolic(self, name: str) -> Symbol:
69
- return IndexedBase(name)
70
-
71
77
  @pydantic.model_validator(mode="before")
72
78
  @classmethod
73
79
  def _set_kind(cls, values: Any) -> dict[str, Any]:
74
80
  return values_with_discriminator(values, "kind", "list")
75
81
 
82
+ def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
83
+ raise NotImplementedError
84
+
76
85
 
77
86
  class StructMetaType(ClassicalType):
78
87
  kind: Literal["type_proxy"]
@@ -82,20 +91,23 @@ class StructMetaType(ClassicalType):
82
91
  def _set_kind(cls, values: Any) -> dict[str, Any]:
83
92
  return values_with_discriminator(values, "kind", "type_proxy")
84
93
 
94
+ def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
95
+ raise NotImplementedError
96
+
85
97
 
86
98
  class ClassicalArray(ClassicalType):
87
99
  kind: Literal["array"]
88
100
  element_type: "ConcreteClassicalType"
89
101
  size: pydantic.PositiveInt
90
102
 
91
- def as_symbolic(self, name: str) -> list:
92
- return [self.element_type.as_symbolic(f"{name}_{i}") for i in range(self.size)]
93
-
94
103
  @pydantic.model_validator(mode="before")
95
104
  @classmethod
96
105
  def _set_kind(cls, values: Any) -> dict[str, Any]:
97
106
  return values_with_discriminator(values, "kind", "array")
98
107
 
108
+ def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
109
+ return ClassicalArrayProxy(handle, self.element_type, self.size)
110
+
99
111
 
100
112
  class OpaqueHandle(ClassicalType):
101
113
  pass
@@ -137,12 +149,5 @@ class IQAERes(OpaqueHandle):
137
149
  return values_with_discriminator(values, "kind", "iqae_result")
138
150
 
139
151
 
140
- def as_symbolic(symbols: dict[str, ClassicalType]) -> dict[str, RuntimeExpression]:
141
- return {
142
- param_name: param_type.as_symbolic(param_name)
143
- for param_name, param_type in symbols.items()
144
- }
145
-
146
-
147
152
  class QmodPyObject:
148
153
  pass
@@ -4,7 +4,15 @@ from typing import TYPE_CHECKING, Any, Literal, Optional
4
4
  import pydantic
5
5
 
6
6
  from classiq.interface.exceptions import ClassiqExpansionError
7
- from classiq.interface.generator.expressions.qmod_qstruct_proxy import QmodQStructProxy
7
+ from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
8
+ ClassicalProxy,
9
+ )
10
+ from classiq.interface.generator.expressions.proxies.classical.classical_struct_proxy import (
11
+ ClassicalStructProxy,
12
+ )
13
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_qstruct_proxy import (
14
+ QmodQStructProxy,
15
+ )
8
16
  from classiq.interface.generator.functions.classical_type import (
9
17
  ClassicalType,
10
18
  )
@@ -16,6 +24,7 @@ from classiq.interface.model.quantum_type import (
16
24
 
17
25
  if TYPE_CHECKING:
18
26
  from classiq.interface.generator.functions.concrete_types import ConcreteQuantumType
27
+ from classiq.interface.generator.types.struct_declaration import StructDeclaration
19
28
 
20
29
 
21
30
  class TypeName(ClassicalType, QuantumType):
@@ -24,6 +33,9 @@ class TypeName(ClassicalType, QuantumType):
24
33
  _assigned_fields: Optional[Mapping[str, "ConcreteQuantumType"]] = (
25
34
  pydantic.PrivateAttr(default=None)
26
35
  )
36
+ _classical_struct_decl: Optional["StructDeclaration"] = pydantic.PrivateAttr(
37
+ default=None
38
+ )
27
39
 
28
40
  @pydantic.model_validator(mode="before")
29
41
  @classmethod
@@ -40,7 +52,7 @@ class TypeName(ClassicalType, QuantumType):
40
52
  )
41
53
 
42
54
  def get_proxy(self, handle: "HandleBinding") -> "QmodQStructProxy":
43
- from classiq.interface.generator.expressions.qmod_qstruct_proxy import (
55
+ from classiq.interface.generator.expressions.proxies.quantum.qmod_qstruct_proxy import (
44
56
  QmodQStructProxy,
45
57
  )
46
58
 
@@ -81,6 +93,18 @@ class TypeName(ClassicalType, QuantumType):
81
93
  field_type.is_evaluated for field_type in self.fields.values()
82
94
  )
83
95
 
96
+ @property
97
+ def has_classical_struct_decl(self) -> bool:
98
+ return self._classical_struct_decl is not None
99
+
100
+ def set_classical_struct_decl(self, decl: "StructDeclaration") -> None:
101
+ self._classical_struct_decl = decl
102
+
103
+ def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
104
+ if self._classical_struct_decl is None:
105
+ raise ClassiqExpansionError(f"Type {self.name!r} is undefined")
106
+ return ClassicalStructProxy(handle, self._classical_struct_decl)
107
+
84
108
 
85
109
  class Enum(TypeName):
86
110
  pass
@@ -1,17 +1,29 @@
1
1
  import logging
2
+ import re
2
3
  from typing import Literal, Optional, Union
3
4
 
4
5
  import pydantic
5
- from pydantic import ConfigDict
6
+ from pydantic import ConfigDict, Field
6
7
  from typing_extensions import TypeAlias
7
8
 
9
+ from classiq.interface.debug_info.back_ref_util import is_allocate_or_free_by_backref
8
10
  from classiq.interface.enum_utils import StrEnum
11
+ from classiq.interface.generator.compiler_keywords import (
12
+ generate_original_function_name,
13
+ )
9
14
  from classiq.interface.generator.control_state import ControlState
10
15
  from classiq.interface.generator.register_role import RegisterRole
11
16
  from classiq.interface.generator.synthesis_metadata.synthesis_execution_data import (
12
17
  ExecutionData,
13
18
  )
14
- from classiq.interface.model.statement_block import StatementBlock
19
+ from classiq.interface.model.quantum_expressions.quantum_expression import (
20
+ QuantumExpressionOperation,
21
+ )
22
+ from classiq.interface.model.statement_block import (
23
+ ConcreteQuantumStatement,
24
+ QuantumFunctionCall,
25
+ StatementBlock,
26
+ )
15
27
 
16
28
  from classiq.model_expansions.capturing.mangling_utils import (
17
29
  demangle_capture_name,
@@ -24,6 +36,11 @@ ParameterName = str
24
36
  IOQubitMapping: TypeAlias = dict[str, tuple[int, ...]]
25
37
 
26
38
  CLASSIQ_HIERARCHY_SEPARATOR: Literal["__"] = "__"
39
+ QASM_SEPARATOR = "_"
40
+ SPLIT_MARKER: str = "part"
41
+ PART_SUFFIX_REGEX = re.compile(
42
+ rf".+{QASM_SEPARATOR}{SPLIT_MARKER}{QASM_SEPARATOR}(\d+)$"
43
+ )
27
44
 
28
45
  VISUALIZATION_HIDE_LIST = [
29
46
  "apply_to_all",
@@ -120,34 +137,83 @@ class OperationLevel(StrEnum):
120
137
  UNKNOWN = "UNKNOWN"
121
138
 
122
139
 
123
- class OperationParameter(pydantic.BaseModel):
124
- label: str
125
- value: Optional[str] = None
140
+ class StatementType(StrEnum):
141
+ CONTROL = "control"
142
+ POWER = "power"
143
+ INVERT = "invert"
144
+ WITHIN_APPLY = "within_apply"
145
+ ASSIGNMENT = "assignment"
146
+ REPEAT = "repeat"
126
147
 
127
148
 
128
149
  class FunctionDebugInfoInterface(pydantic.BaseModel):
129
- generated_function: Optional[GeneratedFunction] = pydantic.Field(default=None)
150
+ generated_function: Optional[GeneratedFunction] = Field(default=None)
130
151
  children: list["FunctionDebugInfoInterface"]
131
152
  relative_qubits: tuple[int, ...]
132
- absolute_qubits: Optional[tuple[int, ...]] = pydantic.Field(default=None)
133
- is_basis_gate: Optional[bool] = pydantic.Field(default=None)
134
- is_inverse: bool = pydantic.Field(default=False)
135
- is_allocate_or_free: bool = pydantic.Field(default=False)
136
- level: OperationLevel = pydantic.Field(default=OperationLevel.UNKNOWN)
137
- parameters: list[OperationParameter] = list()
138
- port_to_passed_variable_map: dict[str, str] = pydantic.Field(default={})
139
- release_by_inverse: bool = pydantic.Field(default=False)
140
- back_refs: StatementBlock = pydantic.Field(default_factory=list)
153
+ absolute_qubits: Optional[tuple[int, ...]] = Field(default=None)
154
+ is_basis_gate: Optional[bool] = Field(default=None)
155
+ is_inverse: bool = Field(default=False)
156
+ is_allocate_or_free: bool = Field(default=False)
157
+ level: OperationLevel = Field(default=OperationLevel.UNKNOWN)
158
+ port_to_passed_variable_map: dict[str, str] = Field(default={})
159
+ release_by_inverse: bool = Field(default=False)
160
+ back_refs: StatementBlock = Field(default_factory=list)
141
161
 
142
162
  model_config = ConfigDict(extra="allow")
143
163
  # Temporary field to store the override debug info for parallel old/new visualization
144
164
  override_debug_info: Optional["FunctionDebugInfoInterface"] = None
145
165
 
166
+ @property
167
+ def is_allocate_or_free_(self) -> bool:
168
+ """
169
+ temporary measure to handle the fact that in the current release we do not have
170
+ the backref, and therefore fallback to the old field of is_allocate_or_free
171
+ """
172
+ return (
173
+ is_allocate_or_free_by_backref(self.back_refs)
174
+ if bool(self.back_refs)
175
+ else self.is_allocate_or_free
176
+ )
177
+
146
178
  @property
147
179
  def name(self) -> str:
148
- if self.generated_function is None:
149
- return ""
150
- return self.generated_function.name
180
+ generated_name = self.generated_function.name if self.generated_function else ""
181
+ # Temp fix for currently "supported" statements (same as for level_ property)
182
+ if generated_name in {StatementType.CONTROL, StatementType.POWER}:
183
+ return generated_name
184
+ if self.first_back_ref and isinstance(self.first_back_ref, QuantumFunctionCall):
185
+ name = generate_original_function_name(self.first_back_ref.func_name)
186
+ if part_match := PART_SUFFIX_REGEX.match(generated_name):
187
+ suffix = f" [{part_match.group(1)}]"
188
+ else:
189
+ suffix = ""
190
+ return f"{name}{suffix}"
191
+ return generated_name
192
+
193
+ @property
194
+ def first_back_ref(self) -> Optional[ConcreteQuantumStatement]:
195
+ return self.back_refs[0] if self.back_refs else None
196
+
197
+ @property
198
+ def level_(self) -> OperationLevel:
199
+ # Temp fix for currently "supported" statements
200
+ if self.name in {StatementType.CONTROL, StatementType.POWER}:
201
+ return OperationLevel.QMOD_STATEMENT
202
+
203
+ back_ref = self.first_back_ref
204
+ if back_ref is None:
205
+ # This is the expected behavior for the case where there's not back ref.
206
+ # We will use it once we can rely on the existence of the back ref in
207
+ # all expected cases (non-engine calls)
208
+ # return OperationLevel.ENGINE_FUNCTION_CALL
209
+ return self.level
210
+ if isinstance(back_ref, QuantumFunctionCall):
211
+ return OperationLevel.QMOD_FUNCTION_CALL
212
+ # Temp "fix" for assignment statements until we have a way to fully
213
+ # distinguish them and to properly display them
214
+ if isinstance(back_ref, QuantumExpressionOperation):
215
+ return OperationLevel.ENGINE_FUNCTION_CALL
216
+ return OperationLevel.QMOD_STATEMENT
151
217
 
152
218
  @property
153
219
  def registers(self) -> list[GeneratedRegister]:
@@ -167,15 +233,6 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
167
233
  return list()
168
234
  return self.generated_function.control_states
169
235
 
170
- @staticmethod
171
- def create_parameters_from_dict(
172
- parameters: dict[str, str],
173
- ) -> list[OperationParameter]:
174
- return [
175
- OperationParameter(label=key, value=value)
176
- for key, value in parameters.items()
177
- ]
178
-
179
236
  def propagate_absolute_qubits(self) -> "FunctionDebugInfoInterface":
180
237
  if self.absolute_qubits is None:
181
238
  return self
@@ -47,6 +47,7 @@ class QuantumFormat(StrEnum):
47
47
  IONQ = "ionq"
48
48
  CIRQ_JSON = "cirq_json"
49
49
  QASM_CIRQ_COMPATIBLE = "qasm_cirq_compatible"
50
+ EXECUTION_SERIALIZATION = "_execution_serialization"
50
51
 
51
52
 
52
53
  _SERVICE_PROVIDER_TO_FORMAT: dict[Provider, QuantumFormat] = {
@@ -117,7 +117,6 @@ class QuantumProgram(VersionedModel, CircuitCodeInterface):
117
117
  instruction_set: Optional[QuantumInstructionSet] = None,
118
118
  ) -> quantum_code.QuantumCode:
119
119
  initial_values = initial_values or self.initial_values
120
-
121
120
  if instruction_set is not None:
122
121
  code, syntax = (
123
122
  self.program_circuit.get_code(instruction_set),
@@ -150,6 +150,7 @@ class Optimizer(IntEnum):
150
150
  L_BFGS_B = 3
151
151
  NELDER_MEAD = 4
152
152
  ADAM = 5
153
+ SLSQP = 6
153
154
 
154
155
 
155
156
  class Pauli(IntEnum):
@@ -75,7 +75,7 @@ class EnumDeclaration(HashableASTNode):
75
75
 
76
76
 
77
77
  def declaration_from_enum(enum_type: EnumMeta) -> EnumDeclaration:
78
- members: list[Enum] = list(enum_type)
78
+ members = _get_members(enum_type)
79
79
  return EnumDeclaration(
80
80
  name=enum_type.__name__,
81
81
  members={
@@ -83,3 +83,14 @@ def declaration_from_enum(enum_type: EnumMeta) -> EnumDeclaration:
83
83
  for member in sorted(members, key=lambda member: member.value)
84
84
  },
85
85
  )
86
+
87
+
88
+ def _get_members(enum_type: EnumMeta) -> list[Enum]:
89
+ members: list[Enum] = list(enum_type)
90
+ for member in members:
91
+ if not isinstance(member.value, int):
92
+ raise ClassiqValueError(
93
+ f"Member {member.name!r} of enum {enum_type.__name__!r} has a "
94
+ f"non-integer value {member.value!r}"
95
+ )
96
+ return members
@@ -7,7 +7,6 @@ from pydantic import ConfigDict
7
7
  from classiq.interface.enum_utils import StrEnum
8
8
  from classiq.interface.generator.generated_circuit_data import (
9
9
  OperationLevel,
10
- OperationParameter,
11
10
  )
12
11
  from classiq.interface.generator.hardware.hardware_data import SynthesisHardwareData
13
12
  from classiq.interface.helpers.versioned_model import VersionedModel
@@ -114,7 +113,6 @@ class Operation(pydantic.BaseModel):
114
113
  control_qubits: tuple[int, ...] = pydantic.Field(default_factory=tuple)
115
114
  auxiliary_qubits: tuple[int, ...]
116
115
  target_qubits: tuple[int, ...]
117
- parameters: list[OperationParameter] = pydantic.Field(default_factory=list)
118
116
  operation_level: OperationLevel
119
117
  operation_type: OperationType = pydantic.Field(
120
118
  description="Identifies unique operations that are visualized differently"
@@ -10,13 +10,6 @@ if TYPE_CHECKING:
10
10
  from classiq.interface.model.statement_block import StatementBlock
11
11
 
12
12
 
13
- class FunctionSynthesisData(pydantic.BaseModel):
14
- should_synthesize_separately: bool = pydantic.Field(
15
- default=False,
16
- description="Whether the function should be synthesized separately.",
17
- )
18
-
19
-
20
13
  class NativeFunctionDefinition(NamedParamsQuantumFunctionDeclaration):
21
14
  """
22
15
  Facilitates the creation of a user-defined composite function
@@ -28,6 +21,3 @@ class NativeFunctionDefinition(NamedParamsQuantumFunctionDeclaration):
28
21
  body: "StatementBlock" = pydantic.Field(
29
22
  default_factory=list, description="List of function calls to perform."
30
23
  )
31
- synthesis_data: FunctionSynthesisData = pydantic.Field(
32
- default_factory=FunctionSynthesisData, deprecated=True, exclude=True
33
- )
@@ -27,7 +27,7 @@ class QuantumStatement(ASTNode):
27
27
  *,
28
28
  update: Optional[dict[str, Any]] = None,
29
29
  deep: bool = False,
30
- keep_uuid: bool = False
30
+ keep_uuid: bool = False,
31
31
  ) -> Self:
32
32
  if not keep_uuid:
33
33
  update = update or dict()