classiq 0.70.0__py3-none-any.whl → 0.71.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 (56) hide show
  1. classiq/interface/_version.py +1 -1
  2. classiq/interface/debug_info/debug_info.py +4 -0
  3. classiq/interface/generator/expressions/expression_constants.py +0 -3
  4. classiq/interface/generator/expressions/expression_types.py +4 -2
  5. classiq/interface/generator/expressions/proxies/classical/classical_array_proxy.py +4 -0
  6. classiq/interface/generator/expressions/proxies/classical/classical_struct_proxy.py +5 -1
  7. classiq/interface/generator/expressions/proxies/classical/utils.py +34 -0
  8. classiq/interface/generator/functions/classical_type.py +1 -1
  9. classiq/interface/generator/functions/type_name.py +16 -0
  10. classiq/interface/generator/functions/type_qualifier.py +7 -0
  11. classiq/interface/generator/generated_circuit_data.py +1 -1
  12. classiq/interface/generator/quantum_function_call.py +8 -1
  13. classiq/interface/generator/synthesis_execution_parameter.py +1 -0
  14. classiq/interface/generator/types/compilation_metadata.py +1 -0
  15. classiq/interface/ide/visual_model.py +1 -0
  16. classiq/interface/interface_version.py +1 -1
  17. classiq/interface/model/allocate.py +7 -0
  18. classiq/interface/model/block.py +12 -0
  19. classiq/interface/model/classical_if.py +4 -0
  20. classiq/interface/model/inplace_binary_operation.py +4 -0
  21. classiq/interface/model/model.py +3 -1
  22. classiq/interface/model/phase_operation.py +4 -0
  23. classiq/interface/model/port_declaration.py +3 -0
  24. classiq/interface/model/power.py +4 -0
  25. classiq/interface/model/quantum_expressions/quantum_expression.py +4 -0
  26. classiq/interface/model/quantum_function_call.py +4 -0
  27. classiq/interface/model/quantum_function_declaration.py +1 -1
  28. classiq/interface/model/quantum_statement.py +5 -0
  29. classiq/interface/model/quantum_type.py +22 -0
  30. classiq/interface/model/repeat.py +4 -0
  31. classiq/interface/model/statement_block.py +3 -0
  32. classiq/interface/model/variable_declaration_statement.py +5 -0
  33. classiq/model_expansions/capturing/captured_vars.py +156 -34
  34. classiq/model_expansions/evaluators/classical_type_inference.py +70 -0
  35. classiq/model_expansions/evaluators/parameter_types.py +14 -0
  36. classiq/model_expansions/expression_evaluator.py +0 -11
  37. classiq/model_expansions/function_builder.py +2 -8
  38. classiq/model_expansions/generative_functions.py +7 -30
  39. classiq/model_expansions/interpreters/base_interpreter.py +3 -4
  40. classiq/model_expansions/quantum_operations/call_emitter.py +46 -6
  41. classiq/model_expansions/quantum_operations/emitter.py +41 -0
  42. classiq/model_expansions/quantum_operations/expression_evaluator.py +4 -0
  43. classiq/model_expansions/scope.py +0 -8
  44. classiq/model_expansions/scope_initialization.py +20 -28
  45. classiq/qmod/__init__.py +3 -1
  46. classiq/qmod/declaration_inferrer.py +52 -24
  47. classiq/qmod/native/pretty_printer.py +25 -3
  48. classiq/qmod/pretty_print/pretty_printer.py +31 -14
  49. classiq/qmod/python_classical_type.py +12 -1
  50. classiq/qmod/qfunc.py +33 -8
  51. classiq/qmod/qmod_variable.py +188 -147
  52. classiq/qmod/quantum_function.py +3 -4
  53. classiq/qmod/semantics/validation/type_hints.py +19 -10
  54. {classiq-0.70.0.dist-info → classiq-0.71.0.dist-info}/METADATA +1 -1
  55. {classiq-0.70.0.dist-info → classiq-0.71.0.dist-info}/RECORD +56 -52
  56. {classiq-0.70.0.dist-info → classiq-0.71.0.dist-info}/WHEEL +0 -0
@@ -3,5 +3,5 @@ from packaging.version import Version
3
3
  # This file was generated automatically
4
4
  # Please don't track in version control (DONTTRACK)
5
5
 
6
- SEMVER_VERSION = '0.70.0'
6
+ SEMVER_VERSION = '0.71.0'
7
7
  VERSION = str(Version(SEMVER_VERSION))
@@ -10,6 +10,7 @@ from classiq.interface.generator.generated_circuit_data import (
10
10
  OperationLevel,
11
11
  StatementType,
12
12
  )
13
+ from classiq.interface.model.block import Block
13
14
  from classiq.interface.model.statement_block import ConcreteQuantumStatement
14
15
 
15
16
  ParameterValue = Union[float, int, str, None]
@@ -84,6 +85,9 @@ def get_back_refs(
84
85
  ) -> list[ConcreteQuantumStatement]:
85
86
  back_refs: list[ConcreteQuantumStatement] = []
86
87
  while (node := debug_info.node) is not None:
88
+ # For backwards compatibility, we make sure that the back_ref is not a block
89
+ # Remove this check when we start saving blocks in the debug info collection.
90
+ assert not isinstance(node, Block)
87
91
  back_refs.insert(0, node)
88
92
  if node.back_ref is None:
89
93
  break
@@ -20,6 +20,3 @@ BOOLEAN_LITERALS = {"True", "False"}
20
20
  FORBIDDEN_LITERALS: set[str] = set(keyword.kwlist) - SUPPORTED_FUNC_NAMES
21
21
  CPARAM_EXECUTION_SUFFIX: Final[str] = "_param"
22
22
  RESERVED_EXPRESSIONS: frozenset[str] = frozenset({"i"})
23
- CPARAM_EXECUTION_SUFFIX_PATTERN = (
24
- rf"({CPARAM_EXECUTION_SUFFIX}|^({'|'.join(RESERVED_EXPRESSIONS)}))(_\d+)*$"
25
- )
@@ -21,10 +21,12 @@ RuntimeConstant = Union[
21
21
  list,
22
22
  bool,
23
23
  QmodStructInstance,
24
- QmodSizedProxy,
25
24
  TypeProxy,
26
25
  HandleIdentifier,
26
+ ]
27
+ Proxies = Union[
28
+ QmodSizedProxy,
27
29
  ClassicalProxy,
28
30
  ]
29
31
  RuntimeExpression = Union[Expr, Boolean]
30
- ExpressionValue = Union[RuntimeConstant, RuntimeExpression]
32
+ ExpressionValue = Union[RuntimeConstant, Proxies, RuntimeExpression]
@@ -38,6 +38,10 @@ class ClassicalArrayProxy(NonSymbolicExpr, ClassicalProxy):
38
38
  def type_name(self) -> str:
39
39
  return "Array"
40
40
 
41
+ @property
42
+ def length(self) -> int:
43
+ return self._length
44
+
41
45
  def __getitem__(self, key: Union[slice, int, Integer]) -> ClassicalProxy:
42
46
  return (
43
47
  self._get_slice(key) if isinstance(key, slice) else self._get_subscript(key)
@@ -17,11 +17,15 @@ class ClassicalStructProxy(NonSymbolicExpr, ClassicalProxy):
17
17
  super().__init__(handle)
18
18
  self._decl = decl
19
19
 
20
+ @property
21
+ def struct_declaration(self) -> "StructDeclaration":
22
+ return self._decl
23
+
20
24
  @property
21
25
  def fields(self) -> Mapping[str, "ExpressionValue"]:
22
26
  return {
23
27
  field_name: field_type.get_classical_proxy(
24
- FieldHandleBinding(base_handle=self.handle, field_name=field_name)
28
+ FieldHandleBinding(base_handle=self.handle, field=field_name)
25
29
  )
26
30
  for field_name, field_type in self._decl.variables.items()
27
31
  }
@@ -0,0 +1,34 @@
1
+ from classiq.interface.exceptions import ClassiqInternalExpansionError
2
+ from classiq.interface.generator.expressions.proxies.classical.classical_array_proxy import (
3
+ ClassicalArrayProxy,
4
+ )
5
+ from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
6
+ ClassicalProxy,
7
+ )
8
+ from classiq.interface.generator.expressions.proxies.classical.classical_scalar_proxy import (
9
+ ClassicalScalarProxy,
10
+ )
11
+ from classiq.interface.generator.expressions.proxies.classical.classical_struct_proxy import (
12
+ ClassicalStructProxy,
13
+ )
14
+ from classiq.interface.generator.functions.classical_type import (
15
+ ClassicalArray,
16
+ ClassicalType,
17
+ )
18
+ from classiq.interface.generator.functions.type_name import TypeName
19
+
20
+
21
+ def get_proxy_type(proxy: ClassicalProxy) -> ClassicalType:
22
+ if isinstance(proxy, ClassicalScalarProxy):
23
+ classical_type = proxy._classical_type
24
+ elif isinstance(proxy, ClassicalArrayProxy):
25
+ classical_type = ClassicalArray(
26
+ element_type=proxy._element_type, size=proxy.length
27
+ )
28
+ elif isinstance(proxy, ClassicalStructProxy):
29
+ classical_type = TypeName(name=proxy._decl.name)
30
+ else:
31
+ raise ClassiqInternalExpansionError(
32
+ f"Unrecognized classical proxy {type(proxy).__name__}"
33
+ )
34
+ return classical_type
@@ -98,7 +98,7 @@ class StructMetaType(ClassicalType):
98
98
  class ClassicalArray(ClassicalType):
99
99
  kind: Literal["array"]
100
100
  element_type: "ConcreteClassicalType"
101
- size: pydantic.PositiveInt
101
+ size: int
102
102
 
103
103
  @pydantic.model_validator(mode="before")
104
104
  @classmethod
@@ -1,9 +1,11 @@
1
1
  from collections.abc import Mapping
2
+ from itertools import chain
2
3
  from typing import TYPE_CHECKING, Any, Literal, Optional
3
4
 
4
5
  import pydantic
5
6
 
6
7
  from classiq.interface.exceptions import ClassiqExpansionError
8
+ from classiq.interface.generator.expressions.expression import Expression
7
9
  from classiq.interface.generator.expressions.proxies.classical.classical_proxy import (
8
10
  ClassicalProxy,
9
11
  )
@@ -97,6 +99,12 @@ class TypeName(ClassicalType, QuantumType):
97
99
  def has_classical_struct_decl(self) -> bool:
98
100
  return self._classical_struct_decl is not None
99
101
 
102
+ @property
103
+ def classical_struct_decl(self) -> "StructDeclaration":
104
+ if self._classical_struct_decl is None:
105
+ raise ClassiqExpansionError(f"Type {self.name!r} is undefined")
106
+ return self._classical_struct_decl
107
+
100
108
  def set_classical_struct_decl(self, decl: "StructDeclaration") -> None:
101
109
  self._classical_struct_decl = decl
102
110
 
@@ -105,6 +113,14 @@ class TypeName(ClassicalType, QuantumType):
105
113
  raise ClassiqExpansionError(f"Type {self.name!r} is undefined")
106
114
  return ClassicalStructProxy(handle, self._classical_struct_decl)
107
115
 
116
+ @property
117
+ def expressions(self) -> list[Expression]:
118
+ return list(
119
+ chain.from_iterable(
120
+ field_type.expressions for field_type in self.fields.values()
121
+ )
122
+ )
123
+
108
124
 
109
125
  class Enum(TypeName):
110
126
  pass
@@ -0,0 +1,7 @@
1
+ from classiq.interface.enum_utils import StrEnum
2
+
3
+
4
+ class TypeQualifier(StrEnum):
5
+ Const = "const"
6
+ QFree = "qfree"
7
+ Quantum = "quantum"
@@ -83,7 +83,7 @@ class GeneratedRegister(pydantic.BaseModel):
83
83
  @staticmethod
84
84
  def demangle_name(name: str) -> str:
85
85
  if is_captured_var_name(name):
86
- return demangle_capture_name(name)
86
+ name = demangle_capture_name(name)
87
87
  return demangle_name(name)
88
88
 
89
89
 
@@ -40,7 +40,10 @@ from classiq.interface.generator.slice_parsing_utils import (
40
40
  SLICING,
41
41
  parse_io_slicing,
42
42
  )
43
- from classiq.interface.generator.synthesis_execution_parameter import PydanticPowerType
43
+ from classiq.interface.generator.synthesis_execution_parameter import (
44
+ ClassicalArg,
45
+ PydanticPowerType,
46
+ )
44
47
  from classiq.interface.generator.user_defined_function_params import CustomFunction
45
48
  from classiq.interface.helpers.custom_pydantic_types import PydanticNonEmptyString
46
49
  from classiq.interface.helpers.hashable_pydantic_base_model import (
@@ -148,6 +151,10 @@ class SynthesisQuantumFunctionCall(BaseModel):
148
151
  description="The name of the function instance. "
149
152
  "If not set, determined automatically.",
150
153
  )
154
+ caller_parameters: Optional[list[str]] = pydantic.Field(default=None)
155
+ parameter_assignments: Optional[dict[str, ClassicalArg]] = pydantic.Field(
156
+ default=None
157
+ )
151
158
  source_id: Optional[UUID] = pydantic.Field(default=None)
152
159
  arithmetic_id: Optional[str] = pydantic.Field(default=None)
153
160
  inverse_op_id: Optional[UUID] = pydantic.Field(default=None)
@@ -6,3 +6,4 @@ from classiq.interface.backend.pydantic_backend import PydanticExecutionParamete
6
6
 
7
7
  PydanticIntSynthesisExecutionParameter = Union[PydanticExecutionParameter, int]
8
8
  PydanticPowerType: TypeAlias = PydanticIntSynthesisExecutionParameter
9
+ ClassicalArg = Union[float, str]
@@ -4,3 +4,4 @@ from pydantic import BaseModel, Field, NonNegativeInt
4
4
  class CompilationMetadata(BaseModel):
5
5
  should_synthesize_separately: bool = Field(default=False)
6
6
  occurrences_number: NonNegativeInt = Field(default=1)
7
+ atomic_qualifiers: list[str] = Field(default_factory=list)
@@ -97,6 +97,7 @@ class AtomicGate(StrEnum):
97
97
  SWAP = "SWAP"
98
98
  IDENTITY = "IDENTITY"
99
99
  U = "U"
100
+ RESET = "RESET"
100
101
 
101
102
  @property
102
103
  def is_control_gate(self) -> bool:
@@ -1 +1 @@
1
- INTERFACE_VERSION = "8"
1
+ INTERFACE_VERSION = "9"
@@ -14,3 +14,10 @@ class Allocate(QuantumOperation):
14
14
  @property
15
15
  def wiring_outputs(self) -> Mapping[str, HandleBinding]:
16
16
  return {"out": self.target}
17
+
18
+ @property
19
+ def expressions(self) -> list[Expression]:
20
+ exprs = []
21
+ if self.size is not None:
22
+ exprs.append(self.size)
23
+ return exprs
@@ -0,0 +1,12 @@
1
+ from typing import TYPE_CHECKING, Literal
2
+
3
+ from classiq.interface.model.quantum_statement import QuantumOperation
4
+
5
+ if TYPE_CHECKING:
6
+ from classiq.interface.model.statement_block import StatementBlock
7
+
8
+
9
+ class Block(QuantumOperation):
10
+ kind: Literal["Block"]
11
+
12
+ statements: "StatementBlock"
@@ -17,3 +17,7 @@ class ClassicalIf(QuantumOperation):
17
17
 
18
18
  def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
19
19
  return reset_lists(self, ["then", "else_"])
20
+
21
+ @property
22
+ def expressions(self) -> list[Expression]:
23
+ return [self.condition]
@@ -41,3 +41,7 @@ class InplaceBinaryOperation(QuantumOperation):
41
41
  )
42
42
  )
43
43
  return readable_inouts
44
+
45
+ @property
46
+ def expressions(self) -> list[Expression]:
47
+ return [self.value] if isinstance(self.value, Expression) else []
@@ -103,7 +103,9 @@ class Model(VersionedModel, ASTNode):
103
103
  functions_compilation_metadata: dict[str, CompilationMetadata] = pydantic.Field(
104
104
  default_factory=dict
105
105
  )
106
- execution_parameters: Optional[dict[str, ConcreteClassicalType]] = None
106
+ execution_parameters: Optional[dict[str, ConcreteClassicalType]] = pydantic.Field(
107
+ default=None, exclude=True
108
+ )
107
109
 
108
110
  @property
109
111
  def main_func(self) -> NativeFunctionDefinition:
@@ -9,3 +9,7 @@ from classiq.interface.model.quantum_expressions.quantum_expression import (
9
9
  class PhaseOperation(QuantumExpressionOperation):
10
10
  kind: Literal["PhaseOperation"]
11
11
  theta: Expression
12
+
13
+ @property
14
+ def expressions(self) -> list[Expression]:
15
+ return super().expressions + [self.theta]
@@ -8,6 +8,7 @@ from classiq.interface.generator.functions.concrete_types import ConcreteQuantum
8
8
  from classiq.interface.generator.functions.port_declaration import (
9
9
  PortDeclarationDirection,
10
10
  )
11
+ from classiq.interface.generator.functions.type_qualifier import TypeQualifier
11
12
  from classiq.interface.helpers.pydantic_model_helpers import values_with_discriminator
12
13
  from classiq.interface.model.parameter import Parameter
13
14
 
@@ -15,6 +16,8 @@ from classiq.interface.model.parameter import Parameter
15
16
  class AnonPortDeclaration(Parameter):
16
17
  quantum_type: ConcreteQuantumType
17
18
  direction: PortDeclarationDirection
19
+ # TODO remove default after BWC-breaking version
20
+ type_qualifier: TypeQualifier = pydantic.Field(default=TypeQualifier.Quantum)
18
21
  kind: Literal["PortDeclaration"]
19
22
 
20
23
  @pydantic.model_validator(mode="before")
@@ -16,3 +16,7 @@ class Power(QuantumOperation):
16
16
 
17
17
  def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
18
18
  return reset_lists(self, ["body"])
19
+
20
+ @property
21
+ def expressions(self) -> list[Expression]:
22
+ return [self.power]
@@ -46,6 +46,10 @@ class QuantumExpressionOperation(QuantumOperation):
46
46
  def wiring_inouts(self) -> Mapping[str, ConcreteHandleBinding]:
47
47
  return nameables_to_dict(self.var_handles)
48
48
 
49
+ @property
50
+ def expressions(self) -> list[Expression]:
51
+ return [self.expression]
52
+
49
53
 
50
54
  class QuantumAssignmentOperation(QuantumExpressionOperation):
51
55
  result_var: ConcreteHandleBinding = pydantic.Field(
@@ -170,6 +170,10 @@ class QuantumFunctionCall(QuantumOperation):
170
170
  param for param in self.positional_args if isinstance(param, HandleBinding)
171
171
  ]
172
172
 
173
+ @property
174
+ def expressions(self) -> list[Expression]:
175
+ return [arg for arg in self.positional_args if isinstance(arg, Expression)]
176
+
173
177
  def _get_handles_by_direction(
174
178
  self, direction: PortDeclarationDirection
175
179
  ) -> list[tuple[int, AnonPortDeclaration, HandleBinding]]:
@@ -87,7 +87,7 @@ class AnonQuantumFunctionDeclaration(FunctionDeclaration):
87
87
  ]
88
88
 
89
89
  @property
90
- def param_names(self) -> Sequence[str]:
90
+ def param_names(self) -> list[str]:
91
91
  return [param.get_name() for param in self.param_decls]
92
92
 
93
93
  @property
@@ -8,6 +8,7 @@ from pydantic import ConfigDict
8
8
  from typing_extensions import Self
9
9
 
10
10
  from classiq.interface.ast_node import ASTNode
11
+ from classiq.interface.generator.expressions.expression import Expression
11
12
  from classiq.interface.helpers.pydantic_model_helpers import values_with_discriminator
12
13
  from classiq.interface.model.handle_binding import (
13
14
  ConcreteHandleBinding,
@@ -39,6 +40,10 @@ class QuantumStatement(ASTNode):
39
40
  def _set_kind(cls, values: Any) -> dict[str, Any]:
40
41
  return values_with_discriminator(values, "kind", cls.__name__)
41
42
 
43
+ @property
44
+ def expressions(self) -> list[Expression]:
45
+ return []
46
+
42
47
 
43
48
  @dataclass
44
49
  class HandleMetadata:
@@ -71,6 +71,10 @@ class QuantumType(HashableASTNode):
71
71
  def is_evaluated(self) -> bool:
72
72
  raise NotImplementedError
73
73
 
74
+ @property
75
+ def expressions(self) -> list[Expression]:
76
+ return []
77
+
74
78
 
75
79
  class QuantumScalar(QuantumType):
76
80
  def get_proxy(self, handle: "HandleBinding") -> QmodQScalarProxy:
@@ -174,6 +178,13 @@ class QuantumBitvector(QuantumType):
174
178
  and self.element_type.is_evaluated
175
179
  )
176
180
 
181
+ @property
182
+ def expressions(self) -> list[Expression]:
183
+ exprs = self.element_type.expressions
184
+ if self.length is not None:
185
+ exprs.append(self.length)
186
+ return exprs
187
+
177
188
 
178
189
  class QuantumNumeric(QuantumScalar):
179
190
  kind: Literal["qnum"]
@@ -265,6 +276,17 @@ class QuantumNumeric(QuantumScalar):
265
276
  self.fraction_digits is not None and not self.fraction_digits.is_evaluated()
266
277
  )
267
278
 
279
+ @property
280
+ def expressions(self) -> list[Expression]:
281
+ exprs = []
282
+ if self.size is not None:
283
+ exprs.append(self.size)
284
+ if self.is_signed is not None:
285
+ exprs.append(self.is_signed)
286
+ if self.fraction_digits is not None:
287
+ exprs.append(self.fraction_digits)
288
+ return exprs
289
+
268
290
 
269
291
  class RegisterQuantumType(BaseModel):
270
292
  quantum_types: "ConcreteQuantumType"
@@ -17,3 +17,7 @@ class Repeat(QuantumOperation):
17
17
 
18
18
  def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
19
19
  return reset_lists(self, ["body"])
20
+
21
+ @property
22
+ def expressions(self) -> list[Expression]:
23
+ return [self.count]
@@ -4,6 +4,7 @@ from pydantic import Field
4
4
 
5
5
  from classiq.interface.model.allocate import Allocate
6
6
  from classiq.interface.model.bind_operation import BindOperation
7
+ from classiq.interface.model.block import Block
7
8
  from classiq.interface.model.classical_if import ClassicalIf
8
9
  from classiq.interface.model.control import Control
9
10
  from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
@@ -41,12 +42,14 @@ ConcreteQuantumStatement = Annotated[
41
42
  Control,
42
43
  WithinApply,
43
44
  PhaseOperation,
45
+ Block,
44
46
  ],
45
47
  Field(..., discriminator="kind"),
46
48
  ]
47
49
 
48
50
  StatementBlock = list[ConcreteQuantumStatement]
49
51
 
52
+ Block.model_rebuild()
50
53
  Control.model_rebuild()
51
54
  QuantumLambdaFunction.model_rebuild()
52
55
  Repeat.model_rebuild()
@@ -1,5 +1,6 @@
1
1
  from typing import Literal
2
2
 
3
+ from classiq.interface.generator.expressions.expression import Expression
3
4
  from classiq.interface.model.quantum_statement import QuantumStatement
4
5
  from classiq.interface.model.quantum_variable_declaration import (
5
6
  QuantumVariableDeclaration,
@@ -8,3 +9,7 @@ from classiq.interface.model.quantum_variable_declaration import (
8
9
 
9
10
  class VariableDeclarationStatement(QuantumStatement, QuantumVariableDeclaration):
10
11
  kind: Literal["VariableDeclarationStatement"]
12
+
13
+ @property
14
+ def expressions(self) -> list[Expression]:
15
+ return self.quantum_type.expressions