classiq 0.43.2__py3-none-any.whl → 0.44.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 (112) hide show
  1. classiq/__init__.py +7 -1
  2. classiq/_internals/client.py +4 -7
  3. classiq/_internals/host_checker.py +34 -12
  4. classiq/_internals/jobs.py +2 -2
  5. classiq/applications/chemistry/chemistry_model_constructor.py +12 -6
  6. classiq/applications/combinatorial_helpers/allowed_constraints.py +4 -1
  7. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
  8. classiq/applications/finance/finance_model_constructor.py +3 -2
  9. classiq/applications/grover/grover_model_constructor.py +7 -5
  10. classiq/applications/hamiltonian/__init__.py +0 -0
  11. classiq/applications/hamiltonian/pauli_decomposition.py +113 -0
  12. classiq/applications/qnn/qlayer.py +1 -1
  13. classiq/exceptions.py +4 -0
  14. classiq/interface/_version.py +1 -1
  15. classiq/interface/ast_node.py +1 -18
  16. classiq/interface/backend/backend_preferences.py +15 -16
  17. classiq/interface/backend/ionq/ionq_quantum_program.py +1 -1
  18. classiq/interface/backend/pydantic_backend.py +0 -5
  19. classiq/interface/backend/quantum_backend_providers.py +3 -2
  20. classiq/interface/chemistry/operator.py +5 -1
  21. classiq/interface/debug_info/__init__.py +0 -0
  22. classiq/interface/debug_info/debug_info.py +32 -0
  23. classiq/interface/executor/execution_preferences.py +1 -45
  24. classiq/interface/executor/result.py +25 -12
  25. classiq/interface/generator/application_apis/arithmetic_declarations.py +8 -5
  26. classiq/interface/generator/application_apis/chemistry_declarations.py +78 -60
  27. classiq/interface/generator/application_apis/combinatorial_optimization_declarations.py +19 -10
  28. classiq/interface/generator/application_apis/entangler_declarations.py +11 -6
  29. classiq/interface/generator/application_apis/finance_declarations.py +36 -22
  30. classiq/interface/generator/application_apis/qsvm_declarations.py +21 -15
  31. classiq/interface/generator/arith/arithmetic_expression_abc.py +21 -1
  32. classiq/interface/generator/arith/binary_ops.py +5 -4
  33. classiq/interface/generator/arith/extremum_operations.py +43 -19
  34. classiq/interface/generator/constant.py +1 -1
  35. classiq/interface/generator/expressions/atomic_expression_functions.py +1 -0
  36. classiq/interface/generator/expressions/expression_constants.py +3 -1
  37. classiq/interface/generator/expressions/qmod_qarray_proxy.py +52 -66
  38. classiq/interface/generator/expressions/qmod_qstruct_proxy.py +35 -0
  39. classiq/interface/generator/expressions/sympy_supported_expressions.py +2 -1
  40. classiq/interface/generator/functions/builtins/core_library/__init__.py +4 -2
  41. classiq/interface/generator/functions/builtins/core_library/atomic_quantum_functions.py +41 -41
  42. classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +52 -42
  43. classiq/interface/generator/functions/builtins/open_lib_functions.py +1095 -3347
  44. classiq/interface/generator/functions/builtins/quantum_operators.py +9 -22
  45. classiq/interface/generator/functions/classical_function_declaration.py +14 -6
  46. classiq/interface/generator/functions/classical_type.py +7 -76
  47. classiq/interface/generator/functions/concrete_types.py +55 -0
  48. classiq/interface/generator/functions/function_declaration.py +10 -10
  49. classiq/interface/generator/functions/type_name.py +104 -0
  50. classiq/interface/generator/generated_circuit_data.py +3 -3
  51. classiq/interface/generator/model/model.py +11 -0
  52. classiq/interface/generator/model/preferences/preferences.py +5 -0
  53. classiq/interface/generator/quantum_function_call.py +3 -0
  54. classiq/interface/generator/quantum_program.py +2 -2
  55. classiq/interface/generator/register_role.py +7 -1
  56. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +1 -3
  57. classiq/interface/generator/types/builtin_struct_declarations/pauli_struct_declarations.py +1 -2
  58. classiq/interface/generator/types/qstruct_declaration.py +17 -0
  59. classiq/interface/generator/types/struct_declaration.py +1 -1
  60. classiq/interface/helpers/validation_helpers.py +1 -17
  61. classiq/interface/ide/visual_model.py +9 -2
  62. classiq/interface/interface_version.py +1 -0
  63. classiq/interface/model/bind_operation.py +25 -5
  64. classiq/interface/model/classical_parameter_declaration.py +8 -5
  65. classiq/interface/model/control.py +5 -5
  66. classiq/interface/model/handle_binding.py +185 -12
  67. classiq/interface/model/inplace_binary_operation.py +16 -4
  68. classiq/interface/model/model.py +28 -5
  69. classiq/interface/model/native_function_definition.py +8 -4
  70. classiq/interface/model/parameter.py +14 -0
  71. classiq/interface/model/port_declaration.py +20 -2
  72. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +21 -6
  73. classiq/interface/model/quantum_expressions/arithmetic_operation.py +30 -6
  74. classiq/interface/model/quantum_expressions/quantum_expression.py +4 -9
  75. classiq/interface/model/quantum_function_call.py +135 -192
  76. classiq/interface/model/quantum_function_declaration.py +147 -165
  77. classiq/interface/model/quantum_lambda_function.py +24 -6
  78. classiq/interface/model/quantum_statement.py +34 -8
  79. classiq/interface/model/quantum_type.py +61 -10
  80. classiq/interface/model/quantum_variable_declaration.py +1 -1
  81. classiq/interface/model/statement_block.py +2 -0
  82. classiq/interface/model/validation_handle.py +7 -0
  83. classiq/interface/server/global_versions.py +4 -4
  84. classiq/interface/server/routes.py +2 -0
  85. classiq/interface/source_reference.py +59 -0
  86. classiq/qmod/__init__.py +2 -3
  87. classiq/qmod/builtins/functions.py +39 -11
  88. classiq/qmod/builtins/operations.py +171 -40
  89. classiq/qmod/declaration_inferrer.py +99 -56
  90. classiq/qmod/expression_query.py +1 -1
  91. classiq/qmod/model_state_container.py +2 -0
  92. classiq/qmod/native/pretty_printer.py +71 -53
  93. classiq/qmod/pretty_print/pretty_printer.py +98 -52
  94. classiq/qmod/qfunc.py +11 -5
  95. classiq/qmod/qmod_parameter.py +1 -2
  96. classiq/qmod/qmod_variable.py +364 -172
  97. classiq/qmod/quantum_callable.py +3 -3
  98. classiq/qmod/quantum_expandable.py +119 -65
  99. classiq/qmod/quantum_function.py +15 -3
  100. classiq/qmod/semantics/annotation.py +12 -13
  101. classiq/qmod/semantics/error_manager.py +36 -10
  102. classiq/qmod/semantics/static_semantics_visitor.py +163 -75
  103. classiq/qmod/semantics/validation/func_call_validation.py +42 -96
  104. classiq/qmod/semantics/validation/handle_validation.py +85 -0
  105. classiq/qmod/semantics/validation/types_validation.py +108 -1
  106. classiq/qmod/type_attribute_remover.py +32 -0
  107. classiq/qmod/utilities.py +26 -5
  108. {classiq-0.43.2.dist-info → classiq-0.44.0.dist-info}/METADATA +3 -3
  109. {classiq-0.43.2.dist-info → classiq-0.44.0.dist-info}/RECORD +111 -99
  110. classiq/qmod/qmod_struct.py +0 -13
  111. /classiq/{interface/ide/show.py → show.py} +0 -0
  112. {classiq-0.43.2.dist-info → classiq-0.44.0.dist-info}/WHEEL +0 -0
@@ -1,9 +1,9 @@
1
- from typing import List, Literal, Mapping
1
+ from typing import List, Literal, Mapping, Sequence
2
2
 
3
3
  import pydantic
4
4
 
5
- from classiq.interface.model.handle_binding import HandleBinding
6
- from classiq.interface.model.quantum_statement import QuantumOperation
5
+ from classiq.interface.model.handle_binding import ConcreteHandleBinding, HandleBinding
6
+ from classiq.interface.model.quantum_statement import HandleMetadata, QuantumOperation
7
7
 
8
8
  from classiq.exceptions import ClassiqValueError
9
9
 
@@ -14,8 +14,8 @@ BIND_OUTPUT_NAME = "bind_output"
14
14
  class BindOperation(QuantumOperation):
15
15
  kind: Literal["BindOperation"]
16
16
 
17
- in_handles: List[HandleBinding]
18
- out_handles: List[HandleBinding]
17
+ in_handles: List[ConcreteHandleBinding]
18
+ out_handles: List[ConcreteHandleBinding]
19
19
 
20
20
  @property
21
21
  def wiring_inputs(self) -> Mapping[str, HandleBinding]:
@@ -23,6 +23,16 @@ class BindOperation(QuantumOperation):
23
23
  f"{BIND_INPUT_NAME}_{i}": handle for i, handle in enumerate(self.in_handles)
24
24
  }
25
25
 
26
+ @property
27
+ def readable_inputs(self) -> Sequence[HandleMetadata]:
28
+ return [
29
+ HandleMetadata(
30
+ handle=handle,
31
+ readable_location="on the left-hand side of a bind statement",
32
+ )
33
+ for handle in self.in_handles
34
+ ]
35
+
26
36
  @property
27
37
  def wiring_outputs(self) -> Mapping[str, HandleBinding]:
28
38
  return {
@@ -30,6 +40,16 @@ class BindOperation(QuantumOperation):
30
40
  for i, handle in enumerate(self.out_handles)
31
41
  }
32
42
 
43
+ @property
44
+ def readable_outputs(self) -> Sequence[HandleMetadata]:
45
+ return [
46
+ HandleMetadata(
47
+ handle=handle,
48
+ readable_location="on the right-hand side of a bind statement",
49
+ )
50
+ for handle in self.out_handles
51
+ ]
52
+
33
53
  @pydantic.validator("in_handles", "out_handles")
34
54
  def validate_handle(cls, handles: List[HandleBinding]) -> List[HandleBinding]:
35
55
  for handle in handles:
@@ -2,15 +2,14 @@ from typing import Any, Dict, Literal
2
2
 
3
3
  import pydantic
4
4
 
5
- from classiq.interface.ast_node import ASTNode
6
- from classiq.interface.generator.functions.classical_type import ConcreteClassicalType
5
+ from classiq.interface.generator.functions.concrete_types import ConcreteClassicalType
7
6
  from classiq.interface.helpers.pydantic_model_helpers import values_with_discriminator
7
+ from classiq.interface.model.parameter import Parameter
8
8
 
9
9
 
10
- class ClassicalParameterDeclaration(ASTNode):
10
+ class AnonClassicalParameterDeclaration(Parameter):
11
11
  kind: Literal["ClassicalParameterDeclaration"]
12
12
 
13
- name: str
14
13
  classical_type: ConcreteClassicalType
15
14
 
16
15
  @pydantic.root_validator(pre=True)
@@ -20,4 +19,8 @@ class ClassicalParameterDeclaration(ASTNode):
20
19
  )
21
20
 
22
21
  def rename(self, new_name: str) -> "ClassicalParameterDeclaration":
23
- return self.copy(update=dict(name=new_name))
22
+ return ClassicalParameterDeclaration(**{**self.__dict__, "name": new_name})
23
+
24
+
25
+ class ClassicalParameterDeclaration(AnonClassicalParameterDeclaration):
26
+ name: str
@@ -14,11 +14,11 @@ class Control(QuantumExpressionOperation):
14
14
  kind: Literal["Control"]
15
15
  body: "StatementBlock"
16
16
 
17
- _ctrl_state: str = pydantic.PrivateAttr(default="")
17
+ _ctrl_size: int = pydantic.PrivateAttr(default=0)
18
18
 
19
19
  @property
20
- def ctrl_state(self) -> str:
21
- return self._ctrl_state
20
+ def ctrl_size(self) -> int:
21
+ return self._ctrl_size
22
22
 
23
- def set_ctrl_state(self, ctrl_state: str) -> None:
24
- self._ctrl_state = ctrl_state
23
+ def set_ctrl_size(self, ctrl_size: int) -> None:
24
+ self._ctrl_size = ctrl_size
@@ -1,13 +1,17 @@
1
- from typing import Union
1
+ from itertools import chain
2
+ from typing import TYPE_CHECKING, Any, Dict, Sequence, Union
2
3
 
3
- from pydantic import Extra
4
+ import pydantic
5
+ from pydantic import Extra, Field
4
6
 
5
7
  from classiq.interface.ast_node import ASTNode
6
8
  from classiq.interface.generator.expressions.expression import Expression
7
9
 
10
+ HANDLE_ID_SEPARATOR = "___"
11
+
8
12
 
9
13
  class HandleBinding(ASTNode):
10
- name: str
14
+ name: str = Field(default=None)
11
15
 
12
16
  class Config:
13
17
  frozen = True
@@ -19,8 +23,49 @@ class HandleBinding(ASTNode):
19
23
  def is_bindable(self) -> bool:
20
24
  return True
21
25
 
26
+ @property
27
+ def identifier(self) -> str:
28
+ return self.name
29
+
30
+ def collapse(self) -> "HandleBinding":
31
+ return self
32
+
33
+ def prefixes(self) -> Sequence["HandleBinding"]:
34
+ """
35
+ Split the handle into prefixes:
36
+ a.b[0].c --> [a, a.b, a.b[0], a.b[0].c]
37
+ """
38
+ return [self]
39
+
40
+ def _tail_overlaps(self, other_handle: "HandleBinding") -> bool:
41
+ return self.name == other_handle.name
42
+
43
+ def overlaps(self, other_handle: "HandleBinding") -> bool:
44
+ self_prefixes = self.collapse().prefixes()
45
+ other_prefixes = other_handle.collapse().prefixes()
46
+ return all(
47
+ self_prefix._tail_overlaps(other_prefix)
48
+ for self_prefix, other_prefix in zip(self_prefixes, other_prefixes)
49
+ )
50
+
51
+
52
+ class NestedHandleBinding(HandleBinding):
53
+ base_handle: "ConcreteHandleBinding"
54
+
55
+ @pydantic.root_validator(pre=False)
56
+ def _set_name(cls, values: Dict[str, Any]) -> Dict[str, Any]:
57
+ if "base_handle" in values:
58
+ values["name"] = values["base_handle"].name
59
+ return values
60
+
61
+ def is_bindable(self) -> bool:
62
+ return False
63
+
64
+ def prefixes(self) -> Sequence["HandleBinding"]:
65
+ return list(chain.from_iterable([self.base_handle.prefixes(), [self]]))
66
+
22
67
 
23
- class SubscriptHandleBinding(HandleBinding):
68
+ class SubscriptHandleBinding(NestedHandleBinding):
24
69
  index: Expression
25
70
 
26
71
  class Config:
@@ -28,13 +73,52 @@ class SubscriptHandleBinding(HandleBinding):
28
73
  extra = Extra.forbid
29
74
 
30
75
  def __str__(self) -> str:
31
- return f"{self.name}[{self.index}]"
32
-
33
- def is_bindable(self) -> bool:
76
+ return f"{self.base_handle}[{self.index}]"
77
+
78
+ @property
79
+ def identifier(self) -> str:
80
+ return f"{self.base_handle.identifier}{HANDLE_ID_SEPARATOR}{self.index}"
81
+
82
+ def collapse(self) -> HandleBinding:
83
+ if isinstance(self.base_handle, SlicedHandleBinding):
84
+ return SubscriptHandleBinding(
85
+ base_handle=self.base_handle.base_handle,
86
+ index=self._get_collapsed_index(),
87
+ ).collapse()
88
+ return SubscriptHandleBinding(
89
+ base_handle=self.base_handle.collapse(),
90
+ index=self.index,
91
+ )
92
+
93
+ def _get_collapsed_index(self) -> Expression:
94
+ if TYPE_CHECKING:
95
+ assert isinstance(self.base_handle, SlicedHandleBinding)
96
+ if self.index.is_evaluated() and self.base_handle.start.is_evaluated():
97
+ return Expression(
98
+ expr=str(
99
+ self.base_handle.start.to_int_value() + self.index.to_int_value()
100
+ )
101
+ )
102
+ return Expression(expr=f"({self.base_handle.start})+({self.index})")
103
+
104
+ def _tail_overlaps(self, other_handle: "HandleBinding") -> bool:
105
+ if isinstance(other_handle, SubscriptHandleBinding):
106
+ return self.index == other_handle.index
107
+ if (
108
+ isinstance(other_handle, SlicedHandleBinding)
109
+ and self.index.is_evaluated()
110
+ and other_handle.start.is_evaluated()
111
+ and other_handle.end.is_evaluated()
112
+ ):
113
+ return (
114
+ other_handle.start.to_int_value()
115
+ <= self.index.to_int_value()
116
+ < other_handle.end.to_int_value()
117
+ )
34
118
  return False
35
119
 
36
120
 
37
- class SlicedHandleBinding(HandleBinding):
121
+ class SlicedHandleBinding(NestedHandleBinding):
38
122
  start: Expression
39
123
  end: Expression
40
124
 
@@ -43,12 +127,101 @@ class SlicedHandleBinding(HandleBinding):
43
127
  extra = Extra.forbid
44
128
 
45
129
  def __str__(self) -> str:
46
- return f"{self.name}[{self.start}:{self.end}]"
47
-
48
- def is_bindable(self) -> bool:
130
+ return f"{self.base_handle}[{self.start}:{self.end}]"
131
+
132
+ @property
133
+ def identifier(self) -> str:
134
+ return (
135
+ f"{self.base_handle.identifier}{HANDLE_ID_SEPARATOR}{self.start}_{self.end}"
136
+ )
137
+
138
+ def collapse(self) -> HandleBinding:
139
+ if isinstance(self.base_handle, SlicedHandleBinding):
140
+ return SubscriptHandleBinding(
141
+ base_handle=self.base_handle.base_handle,
142
+ start=self._get_collapsed_start(),
143
+ end=self._get_collapsed_stop(),
144
+ ).collapse()
145
+ return SlicedHandleBinding(
146
+ base_handle=self.base_handle.collapse(),
147
+ start=self.start,
148
+ end=self.end,
149
+ )
150
+
151
+ def _tail_overlaps(self, other_handle: "HandleBinding") -> bool:
152
+ if not self.start.is_evaluated() or not self.end.is_evaluated():
153
+ return False
154
+ start = self.start.to_int_value()
155
+ end = self.end.to_int_value()
156
+ if (
157
+ isinstance(other_handle, SubscriptHandleBinding)
158
+ and other_handle.index.is_evaluated()
159
+ ):
160
+ return start <= other_handle.index.to_int_value() < end
161
+ if (
162
+ isinstance(other_handle, SlicedHandleBinding)
163
+ and other_handle.start.is_evaluated()
164
+ and other_handle.end.is_evaluated()
165
+ ):
166
+ other_start = other_handle.start.to_int_value()
167
+ other_end = other_handle.end.to_int_value()
168
+ return start <= other_start < end or other_start <= start < other_end
49
169
  return False
50
170
 
171
+ def _get_collapsed_start(self) -> Expression:
172
+ if TYPE_CHECKING:
173
+ assert isinstance(self.base_handle, SlicedHandleBinding)
174
+ if self.start.is_evaluated() and self.base_handle.start.is_evaluated():
175
+ return Expression(
176
+ expr=str(
177
+ self.base_handle.start.to_int_value() + self.start.to_int_value()
178
+ )
179
+ )
180
+ return Expression(expr=f"({self.base_handle.start})+({self.start})")
181
+
182
+ def _get_collapsed_stop(self) -> Expression:
183
+ if TYPE_CHECKING:
184
+ assert isinstance(self.base_handle, SlicedHandleBinding)
185
+ if self.end.is_evaluated() and self.base_handle.end.is_evaluated():
186
+ return Expression(
187
+ expr=str(self.base_handle.end.to_int_value() - self.end.to_int_value())
188
+ )
189
+ return Expression(expr=f"({self.base_handle.end})-({self.end})")
190
+
191
+
192
+ class FieldHandleBinding(NestedHandleBinding):
193
+ field: str
194
+
195
+ class Config:
196
+ frozen = True
197
+ extra = Extra.forbid
198
+
199
+ def __str__(self) -> str:
200
+ return f"{self.base_handle}.{self.field}"
201
+
202
+ @property
203
+ def identifier(self) -> str:
204
+ return f"{self.base_handle.identifier}{HANDLE_ID_SEPARATOR}{self.field}"
205
+
206
+ def collapse(self) -> HandleBinding:
207
+ return FieldHandleBinding(
208
+ base_handle=self.base_handle.collapse(),
209
+ field=self.field,
210
+ )
211
+
212
+ def _tail_overlaps(self, other_handle: "HandleBinding") -> bool:
213
+ return (
214
+ isinstance(other_handle, FieldHandleBinding)
215
+ and self.field == other_handle.field
216
+ )
217
+
51
218
 
52
219
  ConcreteHandleBinding = Union[
53
- HandleBinding, SubscriptHandleBinding, SlicedHandleBinding
220
+ HandleBinding,
221
+ SubscriptHandleBinding,
222
+ SlicedHandleBinding,
223
+ FieldHandleBinding,
54
224
  ]
225
+ SubscriptHandleBinding.update_forward_refs(ConcreteHandleBinding=ConcreteHandleBinding)
226
+ SlicedHandleBinding.update_forward_refs(ConcreteHandleBinding=ConcreteHandleBinding)
227
+ FieldHandleBinding.update_forward_refs(ConcreteHandleBinding=ConcreteHandleBinding)
@@ -1,4 +1,4 @@
1
- from typing import Literal, Mapping
1
+ from typing import Literal, Mapping, Sequence
2
2
 
3
3
  from classiq.interface.generator.functions.builtins.core_library import (
4
4
  INTEGER_XOR_FUNCTION,
@@ -7,9 +7,9 @@ from classiq.interface.generator.functions.builtins.core_library import (
7
7
  from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
8
8
  from classiq.interface.model.handle_binding import ConcreteHandleBinding, HandleBinding
9
9
  from classiq.interface.model.quantum_function_declaration import (
10
- QuantumFunctionDeclaration,
10
+ NamedParamsQuantumFunctionDeclaration,
11
11
  )
12
- from classiq.interface.model.quantum_statement import QuantumOperation
12
+ from classiq.interface.model.quantum_statement import HandleMetadata, QuantumOperation
13
13
 
14
14
  from classiq._internals.enum_utils import StrEnum
15
15
 
@@ -19,7 +19,7 @@ class BinaryOperation(StrEnum):
19
19
  Xor = "inplace_xor"
20
20
 
21
21
  @property
22
- def internal_function_declaration(self) -> QuantumFunctionDeclaration:
22
+ def internal_function_declaration(self) -> NamedParamsQuantumFunctionDeclaration:
23
23
  return {
24
24
  BinaryOperation.Addition: MODULAR_ADD_FUNCTION,
25
25
  BinaryOperation.Xor: INTEGER_XOR_FUNCTION,
@@ -36,3 +36,15 @@ class InplaceBinaryOperation(QuantumOperation):
36
36
  @property
37
37
  def wiring_inouts(self) -> Mapping[str, HandleBinding]:
38
38
  return nameables_to_dict([self.target, self.value])
39
+
40
+ @property
41
+ def readable_inouts(self) -> Sequence[HandleMetadata]:
42
+ suffix = f" of an in-place {self.operation.name.lower()} statement"
43
+ return [
44
+ HandleMetadata(
45
+ handle=self.target, readable_location=f"as the target{suffix}"
46
+ ),
47
+ HandleMetadata(
48
+ handle=self.value, readable_location=f"as the value{suffix}"
49
+ ),
50
+ ]
@@ -1,9 +1,10 @@
1
1
  from collections import Counter
2
- from typing import Dict, List, Literal, NewType, Set
2
+ from typing import List, Literal, Mapping, NewType, Set
3
3
 
4
4
  import pydantic
5
5
 
6
6
  from classiq.interface.ast_node import ASTNode
7
+ from classiq.interface.debug_info.debug_info import DebugInfoCollection
7
8
  from classiq.interface.executor.execution_preferences import ExecutionPreferences
8
9
  from classiq.interface.generator.constant import Constant
9
10
  from classiq.interface.generator.functions.port_declaration import (
@@ -13,12 +14,15 @@ from classiq.interface.generator.model.constraints import Constraints
13
14
  from classiq.interface.generator.model.preferences.preferences import Preferences
14
15
  from classiq.interface.generator.quantum_function_call import SUFFIX_RANDOMIZER
15
16
  from classiq.interface.generator.types.enum_declaration import EnumDeclaration
17
+ from classiq.interface.generator.types.qstruct_declaration import QStructDeclaration
16
18
  from classiq.interface.generator.types.struct_declaration import StructDeclaration
17
19
  from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
18
20
  from classiq.interface.helpers.versioned_model import VersionedModel
19
- from classiq.interface.model.native_function_definition import NativeFunctionDefinition
21
+ from classiq.interface.model.native_function_definition import (
22
+ NativeFunctionDefinition,
23
+ )
20
24
  from classiq.interface.model.quantum_function_declaration import (
21
- QuantumFunctionDeclaration,
25
+ NamedParamsQuantumFunctionDeclaration,
22
26
  )
23
27
  from classiq.interface.model.statement_block import StatementBlock
24
28
 
@@ -73,6 +77,11 @@ class Model(VersionedModel, ASTNode):
73
77
  description="user-defined structs",
74
78
  )
75
79
 
80
+ qstructs: List[QStructDeclaration] = pydantic.Field(
81
+ default_factory=list,
82
+ description="user-defined quantum structs",
83
+ )
84
+
76
85
  classical_execution_code: str = pydantic.Field(
77
86
  description="The classical execution code of the model", default=""
78
87
  )
@@ -88,6 +97,10 @@ class Model(VersionedModel, ASTNode):
88
97
  )
89
98
  preferences: Preferences = pydantic.Field(default_factory=Preferences)
90
99
 
100
+ debug_info: DebugInfoCollection = pydantic.Field(
101
+ default_factory=DebugInfoCollection
102
+ )
103
+
91
104
  @property
92
105
  def main_func(self) -> NativeFunctionDefinition:
93
106
  return self.function_dict[MAIN_FUNCTION_NAME] # type:ignore[return-value]
@@ -109,7 +122,7 @@ class Model(VersionedModel, ASTNode):
109
122
  return direction
110
123
 
111
124
  @property
112
- def function_dict(self) -> Dict[str, QuantumFunctionDeclaration]:
125
+ def function_dict(self) -> Mapping[str, NamedParamsQuantumFunctionDeclaration]:
113
126
  return nameables_to_dict(self.functions)
114
127
 
115
128
  @pydantic.validator("functions", always=True)
@@ -149,7 +162,7 @@ class Model(VersionedModel, ASTNode):
149
162
  raise ClassiqValueError("The model must contain a `main` function")
150
163
  if any(
151
164
  pd.direction != PortDeclarationDirection.Output
152
- for pd in function_dict[MAIN_FUNCTION_NAME].port_declarations.values()
165
+ for pd in function_dict[MAIN_FUNCTION_NAME].port_declarations
153
166
  ):
154
167
  raise ClassiqValueError("Function 'main' cannot declare quantum inputs")
155
168
 
@@ -169,3 +182,13 @@ class Model(VersionedModel, ASTNode):
169
182
  f"{multiply_defined_constants}"
170
183
  )
171
184
  return constants
185
+
186
+ def json_no_preferences_and_constraints(self) -> str:
187
+ return self.json(
188
+ indent=2,
189
+ exclude={
190
+ "constraints",
191
+ "execution_preferences",
192
+ "preferences",
193
+ },
194
+ )
@@ -1,12 +1,16 @@
1
+ from typing import TYPE_CHECKING
2
+
1
3
  import pydantic
2
4
 
3
5
  from classiq.interface.model.quantum_function_declaration import (
4
- QuantumFunctionDeclaration,
6
+ NamedParamsQuantumFunctionDeclaration,
5
7
  )
6
- from classiq.interface.model.statement_block import StatementBlock
8
+
9
+ if TYPE_CHECKING:
10
+ from classiq.interface.model.statement_block import StatementBlock
7
11
 
8
12
 
9
- class NativeFunctionDefinition(QuantumFunctionDeclaration):
13
+ class NativeFunctionDefinition(NamedParamsQuantumFunctionDeclaration):
10
14
  """
11
15
  Facilitates the creation of a user-defined composite function
12
16
 
@@ -14,6 +18,6 @@ class NativeFunctionDefinition(QuantumFunctionDeclaration):
14
18
  objects from other classes.
15
19
  """
16
20
 
17
- body: StatementBlock = pydantic.Field(
21
+ body: "StatementBlock" = pydantic.Field(
18
22
  default_factory=list, description="List of function calls to perform."
19
23
  )
@@ -0,0 +1,14 @@
1
+ from typing import Optional
2
+
3
+ from classiq.interface.ast_node import ASTNode
4
+
5
+ from classiq.exceptions import ClassiqError
6
+
7
+
8
+ class Parameter(ASTNode):
9
+ name: Optional[str]
10
+
11
+ def get_name(self) -> str:
12
+ if self.name is None:
13
+ raise ClassiqError("Cannot resolve parameter name")
14
+ return self.name
@@ -1,11 +1,14 @@
1
- from typing import Any, Dict, Literal, Mapping
1
+ from typing import Any, Dict, Literal, Mapping, Optional
2
2
 
3
3
  import pydantic
4
4
 
5
+ from classiq.interface.generator.expressions.expression import Expression
6
+ from classiq.interface.generator.functions.concrete_types import ConcreteQuantumType
5
7
  from classiq.interface.generator.functions.port_declaration import (
6
8
  PortDeclarationDirection,
7
9
  )
8
10
  from classiq.interface.helpers.pydantic_model_helpers import values_with_discriminator
11
+ from classiq.interface.model.parameter import Parameter
9
12
  from classiq.interface.model.quantum_variable_declaration import (
10
13
  QuantumVariableDeclaration,
11
14
  )
@@ -13,9 +16,11 @@ from classiq.interface.model.quantum_variable_declaration import (
13
16
  from classiq.exceptions import ClassiqValueError
14
17
 
15
18
 
16
- class PortDeclaration(QuantumVariableDeclaration):
19
+ class AnonPortDeclaration(Parameter):
17
20
  kind: Literal["PortDeclaration"]
18
21
 
22
+ quantum_type: ConcreteQuantumType
23
+ size: Optional[Expression] = pydantic.Field(default=None)
19
24
  direction: PortDeclarationDirection
20
25
 
21
26
  @pydantic.root_validator(pre=True)
@@ -32,3 +37,16 @@ class PortDeclaration(QuantumVariableDeclaration):
32
37
  raise ClassiqValueError("Port declaration is missing a type")
33
38
 
34
39
  return direction
40
+
41
+ @pydantic.validator("size")
42
+ def _propagate_size_to_type(
43
+ cls, size: Optional[Expression], values: Mapping[str, Any]
44
+ ) -> Optional[Expression]:
45
+ return QuantumVariableDeclaration._propagate_size_to_type(size, values)
46
+
47
+ def rename(self, new_name: str) -> "PortDeclaration":
48
+ return PortDeclaration(**{**self.__dict__, "name": new_name})
49
+
50
+
51
+ class PortDeclaration(AnonPortDeclaration):
52
+ name: str
@@ -1,4 +1,4 @@
1
- from typing import Dict, Literal, Mapping, Union
1
+ from typing import Dict, Literal, Mapping, Sequence
2
2
 
3
3
  import pydantic
4
4
 
@@ -7,13 +7,13 @@ from classiq.interface.generator.amplitude_loading import (
7
7
  TARGET_OUTPUT_NAME,
8
8
  )
9
9
  from classiq.interface.model.handle_binding import (
10
+ ConcreteHandleBinding,
10
11
  HandleBinding,
11
- SlicedHandleBinding,
12
- SubscriptHandleBinding,
13
12
  )
14
13
  from classiq.interface.model.quantum_expressions.quantum_expression import (
15
14
  QuantumAssignmentOperation,
16
15
  )
16
+ from classiq.interface.model.quantum_statement import HandleMetadata
17
17
  from classiq.interface.model.quantum_type import QuantumBit, QuantumNumeric, QuantumType
18
18
 
19
19
  from classiq.exceptions import ClassiqValueError
@@ -33,14 +33,29 @@ class AmplitudeLoadingOperation(QuantumAssignmentOperation):
33
33
  @property
34
34
  def wiring_inouts(
35
35
  self,
36
- ) -> Mapping[
37
- str, Union[SlicedHandleBinding, SubscriptHandleBinding, HandleBinding]
38
- ]:
36
+ ) -> Mapping[str, ConcreteHandleBinding]:
39
37
  inouts = {self.result_name(): self.result_var}
40
38
  if len(self.var_handles) == 1:
41
39
  inouts[AMPLITUDE_IO_NAME] = self.var_handles[0]
42
40
  return inouts
43
41
 
42
+ @property
43
+ def readable_inouts(self) -> Sequence[HandleMetadata]:
44
+ inouts = [
45
+ HandleMetadata(
46
+ handle=self.result_var,
47
+ readable_location="on the left-hand side of an in-place assignment",
48
+ )
49
+ ]
50
+ if len(self.var_handles) == 1:
51
+ inouts.append(
52
+ HandleMetadata(
53
+ handle=self.var_handles[0],
54
+ readable_location="in an expression",
55
+ )
56
+ )
57
+ return inouts
58
+
44
59
  @property
45
60
  def wiring_outputs(self) -> Mapping[str, HandleBinding]:
46
61
  return {}
@@ -1,4 +1,4 @@
1
- from typing import Dict, Literal, Mapping, Union
1
+ from typing import Dict, Literal, Mapping, Sequence
2
2
 
3
3
  import pydantic
4
4
 
@@ -7,13 +7,13 @@ from classiq.interface.generator.arith.arithmetic import (
7
7
  compute_arithmetic_result_type,
8
8
  )
9
9
  from classiq.interface.model.handle_binding import (
10
+ ConcreteHandleBinding,
10
11
  HandleBinding,
11
- SlicedHandleBinding,
12
- SubscriptHandleBinding,
13
12
  )
14
13
  from classiq.interface.model.quantum_expressions.quantum_expression import (
15
14
  QuantumAssignmentOperation,
16
15
  )
16
+ from classiq.interface.model.quantum_statement import HandleMetadata
17
17
  from classiq.interface.model.quantum_type import QuantumType
18
18
 
19
19
 
@@ -37,20 +37,44 @@ class ArithmeticOperation(QuantumAssignmentOperation):
37
37
  @property
38
38
  def wiring_inouts(
39
39
  self,
40
- ) -> Mapping[
41
- str, Union[SlicedHandleBinding, SubscriptHandleBinding, HandleBinding]
42
- ]:
40
+ ) -> Mapping[str, ConcreteHandleBinding]:
43
41
  inouts = dict(super().wiring_inouts)
44
42
  if self.inplace_result:
45
43
  inouts[self.result_name()] = self.result_var
46
44
  return inouts
47
45
 
46
+ @property
47
+ def readable_inouts(self) -> Sequence[HandleMetadata]:
48
+ inouts = [
49
+ HandleMetadata(handle=handle, readable_location="in an expression")
50
+ for handle in self.var_handles
51
+ ]
52
+ if self.inplace_result:
53
+ inouts.append(
54
+ HandleMetadata(
55
+ handle=self.result_var,
56
+ readable_location="on the left-hand side of an in-place assignment",
57
+ )
58
+ )
59
+ return inouts
60
+
48
61
  @property
49
62
  def wiring_outputs(self) -> Mapping[str, HandleBinding]:
50
63
  if self.inplace_result:
51
64
  return {}
52
65
  return super().wiring_outputs
53
66
 
67
+ @property
68
+ def readable_outputs(self) -> Sequence[HandleMetadata]:
69
+ if self.inplace_result:
70
+ return []
71
+ return [
72
+ HandleMetadata(
73
+ handle=self.result_var,
74
+ readable_location="on the left-hand side of an assignment",
75
+ )
76
+ ]
77
+
54
78
  @classmethod
55
79
  def result_name(cls) -> str:
56
80
  return ARITHMETIC_EXPRESSION_RESULT_NAME