classiq 0.86.1__py3-none-any.whl → 0.88.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.

Potentially problematic release.


This version of classiq might be problematic. Click here for more details.

Files changed (131) hide show
  1. classiq/__init__.py +2 -0
  2. classiq/applications/__init__.py +1 -2
  3. classiq/applications/chemistry/hartree_fock.py +5 -1
  4. classiq/applications/chemistry/op_utils.py +2 -2
  5. classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +1 -1
  6. classiq/applications/combinatorial_helpers/encoding_mapping.py +11 -15
  7. classiq/applications/combinatorial_helpers/encoding_utils.py +6 -6
  8. classiq/applications/combinatorial_helpers/memory.py +4 -4
  9. classiq/applications/combinatorial_helpers/optimization_model.py +5 -5
  10. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +6 -10
  11. classiq/applications/combinatorial_helpers/pyomo_utils.py +27 -26
  12. classiq/applications/combinatorial_helpers/sympy_utils.py +2 -2
  13. classiq/applications/combinatorial_helpers/transformations/encoding.py +4 -6
  14. classiq/applications/combinatorial_helpers/transformations/fixed_variables.py +4 -4
  15. classiq/applications/combinatorial_helpers/transformations/ising_converter.py +2 -2
  16. classiq/applications/combinatorial_helpers/transformations/penalty_support.py +3 -3
  17. classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -0
  18. classiq/applications/hamiltonian/pauli_decomposition.py +34 -2
  19. classiq/evaluators/argument_types.py +15 -6
  20. classiq/evaluators/parameter_types.py +43 -39
  21. classiq/evaluators/qmod_annotated_expression.py +117 -17
  22. classiq/evaluators/qmod_expression_visitors/out_of_place_node_transformer.py +19 -0
  23. classiq/evaluators/qmod_expression_visitors/qmod_expression_bwc.py +0 -5
  24. classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +66 -16
  25. classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +48 -26
  26. classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +65 -72
  27. classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +13 -6
  28. classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +175 -28
  29. classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +36 -19
  30. classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +17 -5
  31. classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +24 -2
  32. classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +97 -0
  33. classiq/evaluators/qmod_node_evaluators/name_evaluation.py +11 -26
  34. classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +56 -0
  35. classiq/evaluators/qmod_node_evaluators/piecewise_evaluation.py +40 -0
  36. classiq/evaluators/qmod_node_evaluators/struct_instantiation_evaluation.py +3 -4
  37. classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +51 -24
  38. classiq/evaluators/qmod_node_evaluators/unary_op_evaluation.py +53 -9
  39. classiq/evaluators/qmod_node_evaluators/utils.py +28 -6
  40. classiq/evaluators/qmod_type_inference/classical_type_inference.py +188 -0
  41. classiq/evaluators/qmod_type_inference/quantum_type_inference.py +330 -0
  42. classiq/evaluators/quantum_type_utils.py +0 -131
  43. classiq/evaluators/type_type_match.py +1 -1
  44. classiq/execution/execution_session.py +18 -3
  45. classiq/execution/qnn.py +4 -1
  46. classiq/execution/user_budgets.py +1 -1
  47. classiq/interface/_version.py +1 -1
  48. classiq/interface/backend/backend_preferences.py +10 -30
  49. classiq/interface/backend/quantum_backend_providers.py +63 -52
  50. classiq/interface/execution/primitives.py +1 -0
  51. classiq/interface/generator/application_apis/__init__.py +0 -1
  52. classiq/interface/generator/arith/binary_ops.py +107 -115
  53. classiq/interface/generator/arith/extremum_operations.py +33 -45
  54. classiq/interface/generator/arith/number_utils.py +4 -1
  55. classiq/interface/generator/circuit_code/types_and_constants.py +0 -9
  56. classiq/interface/generator/compiler_keywords.py +2 -0
  57. classiq/interface/generator/expressions/atomic_expression_functions.py +0 -2
  58. classiq/interface/generator/function_param_list.py +129 -5
  59. classiq/interface/generator/functions/classical_type.py +67 -2
  60. classiq/interface/generator/functions/qmod_python_interface.py +15 -0
  61. classiq/interface/generator/functions/type_name.py +12 -0
  62. classiq/interface/generator/model/preferences/preferences.py +1 -17
  63. classiq/interface/generator/quantum_program.py +1 -13
  64. classiq/interface/generator/transpiler_basis_gates.py +5 -1
  65. classiq/interface/generator/types/builtin_enum_declarations.py +0 -8
  66. classiq/interface/helpers/model_normalizer.py +2 -2
  67. classiq/interface/helpers/text_utils.py +7 -2
  68. classiq/interface/interface_version.py +1 -1
  69. classiq/interface/model/classical_if.py +48 -0
  70. classiq/interface/model/classical_parameter_declaration.py +4 -0
  71. classiq/interface/model/handle_binding.py +28 -16
  72. classiq/interface/model/port_declaration.py +12 -0
  73. classiq/interface/model/quantum_function_declaration.py +12 -0
  74. classiq/interface/model/quantum_type.py +117 -2
  75. classiq/interface/pretty_print/expression_to_qmod.py +7 -8
  76. classiq/interface/pyomo_extension/__init__.py +0 -4
  77. classiq/interface/pyomo_extension/pyomo_sympy_bimap.py +2 -2
  78. classiq/model_expansions/arithmetic.py +43 -1
  79. classiq/model_expansions/arithmetic_compute_result_attrs.py +255 -0
  80. classiq/model_expansions/capturing/captured_vars.py +2 -5
  81. classiq/model_expansions/quantum_operations/allocate.py +23 -16
  82. classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +1 -3
  83. classiq/model_expansions/quantum_operations/assignment_result_processor.py +52 -71
  84. classiq/model_expansions/quantum_operations/bind.py +15 -7
  85. classiq/model_expansions/quantum_operations/call_emitter.py +2 -10
  86. classiq/model_expansions/quantum_operations/classical_var_emitter.py +6 -0
  87. classiq/model_expansions/quantum_operations/handle_evaluator.py +2 -8
  88. classiq/open_library/functions/__init__.py +4 -0
  89. classiq/open_library/functions/lcu.py +117 -0
  90. classiq/open_library/functions/state_preparation.py +47 -5
  91. classiq/qmod/builtins/__init__.py +0 -3
  92. classiq/qmod/builtins/classical_functions.py +0 -28
  93. classiq/qmod/builtins/enums.py +26 -20
  94. classiq/qmod/builtins/functions/__init__.py +0 -5
  95. classiq/qmod/builtins/operations.py +142 -0
  96. classiq/qmod/builtins/structs.py +33 -29
  97. classiq/qmod/native/pretty_printer.py +1 -1
  98. classiq/qmod/pretty_print/expression_to_python.py +1 -6
  99. classiq/qmod/pretty_print/pretty_printer.py +4 -1
  100. classiq/qmod/qmod_variable.py +94 -2
  101. classiq/qmod/semantics/annotation/call_annotation.py +4 -2
  102. classiq/qmod/semantics/annotation/qstruct_annotator.py +20 -5
  103. {classiq-0.86.1.dist-info → classiq-0.88.0.dist-info}/METADATA +5 -5
  104. {classiq-0.86.1.dist-info → classiq-0.88.0.dist-info}/RECORD +106 -124
  105. classiq/applications/finance/__init__.py +0 -15
  106. classiq/interface/finance/finance_modelling_params.py +0 -11
  107. classiq/interface/finance/function_input.py +0 -102
  108. classiq/interface/finance/gaussian_model_input.py +0 -50
  109. classiq/interface/finance/log_normal_model_input.py +0 -40
  110. classiq/interface/finance/model_input.py +0 -22
  111. classiq/interface/generator/amplitude_estimation.py +0 -34
  112. classiq/interface/generator/application_apis/finance_declarations.py +0 -108
  113. classiq/interface/generator/expressions/enums/__init__.py +0 -0
  114. classiq/interface/generator/expressions/enums/finance_functions.py +0 -12
  115. classiq/interface/generator/finance.py +0 -107
  116. classiq/interface/generator/function_param_list_without_self_reference.py +0 -160
  117. classiq/interface/generator/grover_diffuser.py +0 -93
  118. classiq/interface/generator/grover_operator.py +0 -106
  119. classiq/interface/generator/oracles/__init__.py +0 -3
  120. classiq/interface/generator/oracles/arithmetic_oracle.py +0 -82
  121. classiq/interface/generator/oracles/custom_oracle.py +0 -65
  122. classiq/interface/generator/oracles/oracle_abc.py +0 -76
  123. classiq/interface/generator/oracles/oracle_function_param_list.py +0 -6
  124. classiq/interface/generator/piecewise_linear_amplitude_loading.py +0 -165
  125. classiq/interface/generator/qpe.py +0 -169
  126. classiq/interface/grover/__init__.py +0 -0
  127. classiq/interface/grover/grover_modelling_params.py +0 -13
  128. classiq/model_expansions/transformers/var_splitter.py +0 -224
  129. classiq/qmod/builtins/functions/finance.py +0 -34
  130. /classiq/{interface/finance → evaluators/qmod_type_inference}/__init__.py +0 -0
  131. {classiq-0.86.1.dist-info → classiq-0.88.0.dist-info}/WHEEL +0 -0
@@ -58,6 +58,14 @@ class ClassicalType(HashableASTNode):
58
58
  def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
59
59
  return ClassicalScalarProxy(handle, self)
60
60
 
61
+ @property
62
+ def qmod_type_name(self) -> str:
63
+ raise NotImplementedError
64
+
65
+ @property
66
+ def raw_qmod_type_name(self) -> str:
67
+ return self.qmod_type_name
68
+
61
69
  @property
62
70
  def expressions(self) -> list[Expression]:
63
71
  return []
@@ -79,6 +87,10 @@ class Integer(ClassicalType):
79
87
  def _set_kind(cls, values: Any) -> dict[str, Any]:
80
88
  return values_with_discriminator(values, "kind", "int")
81
89
 
90
+ @property
91
+ def qmod_type_name(self) -> str:
92
+ return "CInt"
93
+
82
94
 
83
95
  class Real(ClassicalType):
84
96
  kind: Literal["real"]
@@ -88,6 +100,10 @@ class Real(ClassicalType):
88
100
  def _set_kind(cls, values: Any) -> dict[str, Any]:
89
101
  return values_with_discriminator(values, "kind", "real")
90
102
 
103
+ @property
104
+ def qmod_type_name(self) -> str:
105
+ return "CReal"
106
+
91
107
 
92
108
  class Bool(ClassicalType):
93
109
  kind: Literal["bool"]
@@ -97,6 +113,10 @@ class Bool(ClassicalType):
97
113
  def _set_kind(cls, values: Any) -> dict[str, Any]:
98
114
  return values_with_discriminator(values, "kind", "bool")
99
115
 
116
+ @property
117
+ def qmod_type_name(self) -> str:
118
+ return "CBool"
119
+
100
120
 
101
121
  class StructMetaType(ClassicalType):
102
122
  kind: Literal["type_proxy"]
@@ -124,6 +144,14 @@ class ClassicalArray(ClassicalType):
124
144
  def has_length(self) -> bool:
125
145
  return self.length is not None and self.length.is_evaluated()
126
146
 
147
+ @property
148
+ def has_constant_length(self) -> bool:
149
+ return (
150
+ self.length is not None
151
+ and self.length.is_evaluated()
152
+ and self.length.is_constant()
153
+ )
154
+
127
155
  @property
128
156
  def length_value(self) -> int:
129
157
  if not self.has_length:
@@ -163,6 +191,18 @@ class ClassicalArray(ClassicalType):
163
191
  raw_type.set_generative()
164
192
  return raw_type
165
193
 
194
+ @property
195
+ def qmod_type_name(self) -> str:
196
+ if self.length is None:
197
+ length = ""
198
+ else:
199
+ length = f", {self.length.expr}"
200
+ return f"CArray[{self.element_type.qmod_type_name}{length}]"
201
+
202
+ @property
203
+ def raw_qmod_type_name(self) -> str:
204
+ return "CArray"
205
+
166
206
 
167
207
  class ClassicalTuple(ClassicalType):
168
208
  kind: Literal["tuple"]
@@ -196,10 +236,24 @@ class ClassicalTuple(ClassicalType):
196
236
  element_type.is_purely_generative for element_type in self.element_types
197
237
  )
198
238
 
199
- def get_raw_type(self) -> "ConcreteClassicalType":
239
+ def get_raw_type(self, *, preserve_length: bool = False) -> "ConcreteClassicalType":
200
240
  if len(self.element_types) == 0:
201
241
  return self
202
- raw_type = ClassicalArray(element_type=self.element_types[0].get_raw_type())
242
+ chosen_element = self.element_types[0]
243
+ for element in self.element_types:
244
+ if (
245
+ not isinstance(element, ClassicalTuple)
246
+ or len(element.element_types) > 0
247
+ ):
248
+ chosen_element = element
249
+ break
250
+ if preserve_length:
251
+ length = Expression(expr=str(len(self.element_types)))
252
+ else:
253
+ length = None
254
+ raw_type = ClassicalArray(
255
+ element_type=chosen_element.get_raw_type(), length=length
256
+ )
203
257
  if self._is_generative:
204
258
  raw_type.set_generative()
205
259
  return raw_type
@@ -208,6 +262,17 @@ class ClassicalTuple(ClassicalType):
208
262
  def length(self) -> int:
209
263
  return len(self.element_types)
210
264
 
265
+ @property
266
+ def qmod_type_name(self) -> str:
267
+ raw_type = self.get_raw_type(preserve_length=True)
268
+ if isinstance(raw_type, ClassicalTuple):
269
+ return "CArray[0]"
270
+ return raw_type.qmod_type_name
271
+
272
+ @property
273
+ def raw_qmod_type_name(self) -> str:
274
+ return "CArray"
275
+
211
276
 
212
277
  class OpaqueHandle(ClassicalType):
213
278
  pass
@@ -1,4 +1,19 @@
1
1
  from collections.abc import Mapping
2
2
  from typing import Any
3
3
 
4
+ from classiq.interface.generator.expressions.proxies.classical.qmod_struct_instance import (
5
+ QmodStructInstance,
6
+ )
7
+
4
8
  QmodPyStruct = Mapping[str, Any]
9
+
10
+
11
+ def qmod_value_to_dict(qmod_value: Any) -> Any:
12
+ if isinstance(qmod_value, QmodStructInstance):
13
+ return {
14
+ field_name: qmod_value_to_dict(field_value)
15
+ for field_name, field_value in qmod_value.fields.items()
16
+ }
17
+ if isinstance(qmod_value, list):
18
+ return [qmod_value_to_dict(item) for item in qmod_value]
19
+ return qmod_value
@@ -101,6 +101,12 @@ class TypeName(ClassicalType, QuantumType):
101
101
  field_type.is_evaluated for field_type in self.fields.values()
102
102
  )
103
103
 
104
+ @property
105
+ def is_constant(self) -> bool:
106
+ return self.has_fields and all(
107
+ field_type.is_constant for field_type in self.fields.values()
108
+ )
109
+
104
110
  @property
105
111
  def has_classical_struct_decl(self) -> bool:
106
112
  return self._classical_struct_decl is not None
@@ -177,6 +183,12 @@ class TypeName(ClassicalType, QuantumType):
177
183
  raw_type.set_classical_struct_decl(raw_decl)
178
184
  return raw_type
179
185
 
186
+ @property
187
+ def minimal_size_in_bits(self) -> int:
188
+ return sum(
189
+ field_type.minimal_size_in_bits for field_type in self.fields.values()
190
+ )
191
+
180
192
 
181
193
  class Enum(TypeName):
182
194
  pass
@@ -50,14 +50,6 @@ class QuantumFormat(StrEnum):
50
50
  EXECUTION_SERIALIZATION = "_execution_serialization"
51
51
 
52
52
 
53
- _SERVICE_PROVIDER_TO_FORMAT: dict[Provider, QuantumFormat] = {
54
- Provider.CLASSIQ: QuantumFormat.QASM,
55
- Provider.IONQ: QuantumFormat.IONQ,
56
- Provider.AZURE_QUANTUM: QuantumFormat.QSHARP,
57
- Provider.IBM_QUANTUM: QuantumFormat.QASM,
58
- Provider.AMAZON_BRAKET: QuantumFormat.QASM,
59
- }
60
-
61
53
  if TYPE_CHECKING:
62
54
  PydanticConstrainedQuantumFormatList = list[QuantumFormat]
63
55
  else:
@@ -176,7 +168,7 @@ class Preferences(pydantic.BaseModel, extra="forbid"):
176
168
  deprecated=True,
177
169
  )
178
170
  optimization_level: OptimizationLevel = pydantic.Field(
179
- default=OptimizationLevel.HIGH,
171
+ default=OptimizationLevel.LIGHT,
180
172
  description="The optimization level used during synthesis; determines the trade-off between synthesis speed and the quality of the results",
181
173
  )
182
174
  output_format: PydanticConstrainedQuantumFormatList = pydantic.Field(
@@ -260,14 +252,6 @@ class Preferences(pydantic.BaseModel, extra="forbid"):
260
252
  "has at least one format that appears twice or more"
261
253
  )
262
254
 
263
- service_provider = info.data.get("backend_service_provider")
264
- if service_provider is None:
265
- return output_format
266
-
267
- provider_format = _SERVICE_PROVIDER_TO_FORMAT.get(service_provider)
268
- if provider_format is not None and provider_format not in output_format:
269
- output_format.append(provider_format)
270
-
271
255
  return output_format
272
256
 
273
257
  @pydantic.field_validator("backend_name")
@@ -19,9 +19,7 @@ from classiq.interface.executor.quantum_instruction_set import QuantumInstructio
19
19
  from classiq.interface.executor.register_initialization import RegisterInitialization
20
20
  from classiq.interface.generator.circuit_code.circuit_code import CircuitCodeInterface
21
21
  from classiq.interface.generator.circuit_code.types_and_constants import (
22
- DEFAULT_INSTRUCTION_SET,
23
22
  INSTRUCTION_SET_TO_FORMAT,
24
- VENDOR_TO_INSTRUCTION_SET,
25
23
  CodeAndSyntax,
26
24
  )
27
25
  from classiq.interface.generator.generated_circuit_data import (
@@ -82,7 +80,7 @@ class QuantumProgram(VersionedModel, CircuitCodeInterface):
82
80
  def __str__(self) -> str:
83
81
  return self.model_dump_json(indent=2)
84
82
 
85
- def _hardware_agnostic_program_code(self) -> CodeAndSyntax:
83
+ def _default_program_code(self) -> CodeAndSyntax:
86
84
  circuit_code = self.program_circuit.get_code_by_priority()
87
85
  if circuit_code is not None:
88
86
  return circuit_code
@@ -91,16 +89,6 @@ class QuantumProgram(VersionedModel, CircuitCodeInterface):
91
89
  missing_formats=list(INSTRUCTION_SET_TO_FORMAT.values())
92
90
  )
93
91
 
94
- def _default_program_code(self) -> CodeAndSyntax:
95
- if self.hardware_data.backend_data is None:
96
- return self._hardware_agnostic_program_code()
97
-
98
- backend_provider = self.hardware_data.backend_data.hw_provider
99
- instruction_set: QuantumInstructionSet = VENDOR_TO_INSTRUCTION_SET.get(
100
- backend_provider, DEFAULT_INSTRUCTION_SET
101
- )
102
- return self.program_circuit.get_code(instruction_set), instruction_set
103
-
104
92
  def to_base_program(self) -> quantum_code.QuantumBaseCode:
105
93
  code, syntax = self._default_program_code()
106
94
  return quantum_code.QuantumBaseCode(code=code, syntax=syntax)
@@ -57,11 +57,15 @@ EXTRA_TWO_QUBIT_GATES: BasisGates = frozenset(
57
57
  )
58
58
  )
59
59
 
60
+ NON_UNITARY_GATES: BasisGates = frozenset(("if_else",))
61
+
60
62
  TWO_QUBIT_GATES = BASIC_TWO_QUBIT_GATES | EXTRA_TWO_QUBIT_GATES
61
63
 
62
64
  THREE_QUBIT_GATES: BasisGates = frozenset(("ccx", "cswap"))
63
65
  DEFAULT_BASIS_GATES: BasisGates = SINGLE_QUBIT_GATES | BASIC_TWO_QUBIT_GATES
64
- ALL_GATES: BasisGates = SINGLE_QUBIT_GATES | TWO_QUBIT_GATES | THREE_QUBIT_GATES
66
+ ALL_GATES: BasisGates = (
67
+ SINGLE_QUBIT_GATES | TWO_QUBIT_GATES | THREE_QUBIT_GATES | NON_UNITARY_GATES
68
+ )
65
69
 
66
70
  ROUTING_TWO_QUBIT_BASIS_GATES: BasisGates = frozenset(
67
71
  ("cx", "ecr", "rzx", "ryy", "rxx", "rzz", "cy", "cz", "cp", "swap")
@@ -132,13 +132,6 @@ class FermionMapping(IntEnum):
132
132
  FAST_BRAVYI_KITAEV = 3
133
133
 
134
134
 
135
- class FinanceFunctionType(IntEnum):
136
- VAR = 0
137
- SHORTFALL = 1
138
- X_SQUARE = 2
139
- EUROPEAN_CALL_OPTION = 3
140
-
141
-
142
135
  class LadderOperator(IntEnum):
143
136
  PLUS = 0
144
137
  MINUS = 1
@@ -171,7 +164,6 @@ class QSVMFeatureMapEntanglement(IntEnum):
171
164
  __all__ = [
172
165
  "Element",
173
166
  "FermionMapping",
174
- "FinanceFunctionType",
175
167
  "LadderOperator",
176
168
  "Optimizer",
177
169
  "Pauli",
@@ -37,8 +37,8 @@ class ModelNormalizer(Visitor):
37
37
 
38
38
  class ClearModelInternals(Transformer):
39
39
  def visit_Expression(self, expr: Expression) -> Expression:
40
- if expr._evaluated_expr is not None and not expr._evaluated_expr.is_constant():
41
- expr._evaluated_expr = None
40
+ expr._evaluated_expr = None
41
+ expr._try_to_immediate_evaluate()
42
42
  return expr
43
43
 
44
44
  def visit_ClassicalType(self, classical_type: ClassicalType) -> ClassicalType:
@@ -1,5 +1,10 @@
1
- def s(items: list) -> str:
2
- return "" if len(items) == 1 else "s"
1
+ from typing import Union
2
+
3
+
4
+ def s(items: Union[list, int]) -> str:
5
+ if isinstance(items, list):
6
+ items = len(items)
7
+ return "" if items == 1 else "s"
3
8
 
4
9
 
5
10
  def are(items: list) -> str:
@@ -1 +1 @@
1
- INTERFACE_VERSION = "11"
1
+ INTERFACE_VERSION = "12"
@@ -1,7 +1,13 @@
1
+ import functools
2
+ import operator
3
+ from collections.abc import Mapping
1
4
  from typing import TYPE_CHECKING, Literal
2
5
 
6
+ import pydantic
7
+
3
8
  from classiq.interface.ast_node import ASTNodeType, reset_lists
4
9
  from classiq.interface.generator.expressions.expression import Expression
10
+ from classiq.interface.model.handle_binding import ConcreteHandleBinding, HandleBinding
5
11
  from classiq.interface.model.quantum_statement import QuantumOperation
6
12
 
7
13
  if TYPE_CHECKING:
@@ -14,6 +20,9 @@ class ClassicalIf(QuantumOperation):
14
20
  condition: Expression
15
21
  then: "StatementBlock"
16
22
  else_: "StatementBlock"
23
+ _condition_wiring_inouts: dict[str, HandleBinding] = pydantic.PrivateAttr(
24
+ default_factory=dict
25
+ )
17
26
 
18
27
  def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
19
28
  return reset_lists(self, ["then", "else_"])
@@ -21,3 +30,42 @@ class ClassicalIf(QuantumOperation):
21
30
  @property
22
31
  def expressions(self) -> list[Expression]:
23
32
  return [self.condition]
33
+
34
+ @property
35
+ def wiring_inputs(self) -> Mapping[str, HandleBinding]:
36
+ return functools.reduce(
37
+ operator.ior,
38
+ (
39
+ op.wiring_inputs
40
+ for op in (*self.then, *self.else_)
41
+ if isinstance(op, QuantumOperation)
42
+ ),
43
+ dict(),
44
+ )
45
+
46
+ @property
47
+ def wiring_inouts(self) -> Mapping[str, ConcreteHandleBinding]:
48
+ return (
49
+ functools.reduce(
50
+ operator.ior,
51
+ (
52
+ op.wiring_inouts
53
+ for op in (*self.then, *self.else_)
54
+ if isinstance(op, QuantumOperation)
55
+ ),
56
+ dict(),
57
+ )
58
+ | self._condition_wiring_inouts
59
+ )
60
+
61
+ @property
62
+ def wiring_outputs(self) -> Mapping[str, HandleBinding]:
63
+ return functools.reduce(
64
+ operator.ior,
65
+ (
66
+ op.wiring_outputs
67
+ for op in (*self.then, *self.else_)
68
+ if isinstance(op, QuantumOperation)
69
+ ),
70
+ dict(),
71
+ )
@@ -33,6 +33,10 @@ class AnonClassicalParameterDeclaration(Parameter):
33
33
  }
34
34
  )
35
35
 
36
+ @property
37
+ def qmod_type_name(self) -> str:
38
+ return self.classical_type.qmod_type_name
39
+
36
40
 
37
41
  class ClassicalParameterDeclaration(AnonClassicalParameterDeclaration):
38
42
  name: str
@@ -156,7 +156,12 @@ class SubscriptHandleBinding(NestedHandleBinding):
156
156
  def _get_collapsed_index(self) -> Expression:
157
157
  if TYPE_CHECKING:
158
158
  assert isinstance(self.base_handle, SlicedHandleBinding)
159
- if self.index.is_evaluated() and self.base_handle.start.is_evaluated():
159
+ if (
160
+ self.index.is_evaluated()
161
+ and self.index.is_constant()
162
+ and self.base_handle.start.is_evaluated()
163
+ and self.base_handle.start.is_constant()
164
+ ):
160
165
  return Expression(
161
166
  expr=str(
162
167
  self.base_handle.start.to_int_value() + self.index.to_int_value()
@@ -170,7 +175,8 @@ class SubscriptHandleBinding(NestedHandleBinding):
170
175
  if (
171
176
  isinstance(other_handle, SlicedHandleBinding)
172
177
  and self.index.is_evaluated()
173
- and other_handle._is_evaluated()
178
+ and self.index.is_constant()
179
+ and other_handle.is_constant()
174
180
  ):
175
181
  return (
176
182
  other_handle.start.to_int_value()
@@ -186,7 +192,8 @@ class SubscriptHandleBinding(NestedHandleBinding):
186
192
  isinstance(prefix, SlicedHandleBinding)
187
193
  and self.base_handle == prefix.base_handle
188
194
  and self.index.is_evaluated()
189
- and prefix._is_evaluated()
195
+ and self.index.is_constant()
196
+ and prefix.is_constant()
190
197
  and prefix.start.to_int_value()
191
198
  <= self.index.to_int_value()
192
199
  < prefix.end.to_int_value()
@@ -243,19 +250,17 @@ class SlicedHandleBinding(NestedHandleBinding):
243
250
  )
244
251
 
245
252
  def _tail_overlaps(self, other_handle: "HandleBinding") -> bool:
246
- if not self._is_evaluated():
253
+ if not self.is_constant():
247
254
  return False
248
255
  start = self.start.to_int_value()
249
256
  end = self.end.to_int_value()
250
257
  if (
251
258
  isinstance(other_handle, SubscriptHandleBinding)
252
259
  and other_handle.index.is_evaluated()
260
+ and other_handle.index.is_constant()
253
261
  ):
254
262
  return start <= other_handle.index.to_int_value() < end
255
- if (
256
- isinstance(other_handle, SlicedHandleBinding)
257
- and other_handle._is_evaluated()
258
- ):
263
+ if isinstance(other_handle, SlicedHandleBinding) and other_handle.is_constant():
259
264
  other_start = other_handle.start.to_int_value()
260
265
  other_end = other_handle.end.to_int_value()
261
266
  return start <= other_start < end or other_start <= start < other_end
@@ -264,7 +269,12 @@ class SlicedHandleBinding(NestedHandleBinding):
264
269
  def _get_collapsed_start(self) -> Expression:
265
270
  if TYPE_CHECKING:
266
271
  assert isinstance(self.base_handle, SlicedHandleBinding)
267
- if self.start.is_evaluated() and self.base_handle.start.is_evaluated():
272
+ if (
273
+ self.start.is_evaluated()
274
+ and self.start.is_constant()
275
+ and self.base_handle.start.is_evaluated()
276
+ and self.base_handle.start.is_constant()
277
+ ):
268
278
  return Expression(
269
279
  expr=str(
270
280
  self.base_handle.start.to_int_value() + self.start.to_int_value()
@@ -275,7 +285,11 @@ class SlicedHandleBinding(NestedHandleBinding):
275
285
  def _get_collapsed_stop(self) -> Expression:
276
286
  if TYPE_CHECKING:
277
287
  assert isinstance(self.base_handle, SlicedHandleBinding)
278
- if self._is_evaluated() and self.base_handle.start.is_evaluated():
288
+ if (
289
+ self.is_constant()
290
+ and self.base_handle.start.is_evaluated()
291
+ and self.base_handle.start.is_constant()
292
+ ):
279
293
  return Expression(
280
294
  expr=str(
281
295
  self.end.to_int_value()
@@ -295,8 +309,8 @@ class SlicedHandleBinding(NestedHandleBinding):
295
309
  if (
296
310
  isinstance(prefix, SlicedHandleBinding)
297
311
  and self.base_handle == prefix.base_handle
298
- and self._is_evaluated()
299
- and prefix._is_evaluated()
312
+ and self.is_constant()
313
+ and prefix.is_constant()
300
314
  ):
301
315
  prefix_start = prefix.start.to_int_value()
302
316
  prefix_end = prefix.end.to_int_value()
@@ -313,14 +327,12 @@ class SlicedHandleBinding(NestedHandleBinding):
313
327
  )
314
328
  return super().replace_prefix(prefix, replacement)
315
329
 
316
- def _is_evaluated(self) -> bool:
317
- return self.start.is_evaluated() and self.end.is_evaluated()
318
-
319
330
  def is_constant(self) -> bool:
320
331
  return (
321
332
  super().is_constant()
322
- and self._is_evaluated()
333
+ and self.start.is_evaluated()
323
334
  and self.start.is_constant()
335
+ and self.end.is_evaluated()
324
336
  and self.end.is_constant()
325
337
  )
326
338
 
@@ -44,6 +44,18 @@ class AnonPortDeclaration(Parameter):
44
44
  raise ClassiqInternalError
45
45
  return PortDeclaration(**{**self.__dict__, "name": new_name})
46
46
 
47
+ @property
48
+ def qmod_type_name(self) -> str:
49
+ prefix = ""
50
+ suffix = ""
51
+ if self.type_modifier in (TypeModifier.Const, TypeModifier.Permutable):
52
+ prefix += f"{self.type_modifier.name}["
53
+ suffix += "]"
54
+ if self.direction != PortDeclarationDirection.Inout:
55
+ prefix += f"{self.direction.name}["
56
+ suffix += "]"
57
+ return f"{prefix}{self.quantum_type.qmod_type_name}{suffix}"
58
+
47
59
 
48
60
  class PortDeclaration(AnonPortDeclaration):
49
61
  name: str
@@ -190,6 +190,18 @@ class AnonQuantumOperandDeclaration(AnonQuantumFunctionDeclaration):
190
190
  def is_generative(self) -> bool:
191
191
  return self._is_generative
192
192
 
193
+ @property
194
+ def qmod_type_name(self) -> str:
195
+ if self.is_list:
196
+ type_name = "QCallableList"
197
+ else:
198
+ type_name = "QCallable"
199
+ if len(self.positional_arg_declarations) == 0:
200
+ params = ""
201
+ else:
202
+ params = f"[{', '.join(param.qmod_type_name for param in self.positional_arg_declarations)}]"
203
+ return f"{type_name}{params}"
204
+
193
205
 
194
206
  AnonQuantumFunctionDeclaration.model_rebuild()
195
207