classiq 0.87.0__py3-none-any.whl → 0.89.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 (81) hide show
  1. classiq/_internals/config.py +1 -1
  2. classiq/applications/__init__.py +1 -2
  3. classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +1 -1
  4. classiq/applications/hamiltonian/pauli_decomposition.py +1 -1
  5. classiq/evaluators/qmod_annotated_expression.py +37 -15
  6. classiq/evaluators/qmod_expression_visitors/qmod_expression_bwc.py +0 -5
  7. classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +20 -13
  8. classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +15 -8
  9. classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +39 -16
  10. classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +67 -6
  11. classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +18 -8
  12. classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +8 -0
  13. classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +4 -1
  14. classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +1 -1
  15. classiq/evaluators/qmod_node_evaluators/struct_instantiation_evaluation.py +1 -1
  16. classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +3 -3
  17. classiq/evaluators/qmod_node_evaluators/utils.py +1 -1
  18. classiq/evaluators/qmod_type_inference/classical_type_inference.py +4 -4
  19. classiq/evaluators/qmod_type_inference/quantum_type_inference.py +38 -0
  20. classiq/evaluators/type_type_match.py +1 -1
  21. classiq/execution/execution_session.py +17 -2
  22. classiq/interface/_version.py +1 -1
  23. classiq/interface/analyzer/analysis_params.py +1 -1
  24. classiq/interface/backend/backend_preferences.py +1 -1
  25. classiq/interface/execution/primitives.py +1 -0
  26. classiq/interface/generator/application_apis/__init__.py +0 -1
  27. classiq/interface/generator/arith/register_user_input.py +1 -1
  28. classiq/interface/generator/expressions/atomic_expression_functions.py +0 -2
  29. classiq/interface/generator/function_param_list.py +0 -4
  30. classiq/interface/generator/functions/classical_type.py +32 -3
  31. classiq/interface/generator/functions/function_declaration.py +0 -4
  32. classiq/interface/generator/functions/type_name.py +31 -0
  33. classiq/interface/generator/hardware_efficient_ansatz.py +1 -1
  34. classiq/interface/generator/quantum_function_call.py +3 -3
  35. classiq/interface/generator/transpiler_basis_gates.py +5 -1
  36. classiq/interface/generator/types/builtin_enum_declarations.py +0 -8
  37. classiq/interface/generator/user_defined_function_params.py +0 -3
  38. classiq/interface/ide/ide_data.py +1 -1
  39. classiq/interface/ide/visual_model.py +2 -2
  40. classiq/interface/model/block.py +5 -1
  41. classiq/interface/model/classical_if.py +16 -8
  42. classiq/interface/model/classical_parameter_declaration.py +4 -0
  43. classiq/interface/model/handle_binding.py +1 -1
  44. classiq/interface/model/port_declaration.py +12 -0
  45. classiq/interface/model/quantum_function_declaration.py +12 -0
  46. classiq/interface/model/quantum_lambda_function.py +1 -1
  47. classiq/interface/model/quantum_statement.py +2 -4
  48. classiq/interface/model/quantum_type.py +102 -3
  49. classiq/interface/pretty_print/expression_to_qmod.py +3 -17
  50. classiq/model_expansions/quantum_operations/allocate.py +1 -1
  51. classiq/model_expansions/quantum_operations/handle_evaluator.py +2 -8
  52. classiq/open_library/functions/__init__.py +3 -0
  53. classiq/open_library/functions/lcu.py +2 -2
  54. classiq/open_library/functions/state_preparation.py +182 -5
  55. classiq/qmod/builtins/__init__.py +0 -3
  56. classiq/qmod/builtins/classical_functions.py +0 -28
  57. classiq/qmod/builtins/enums.py +24 -18
  58. classiq/qmod/builtins/functions/__init__.py +0 -5
  59. classiq/qmod/builtins/operations.py +142 -0
  60. classiq/qmod/builtins/structs.py +0 -11
  61. classiq/qmod/native/pretty_printer.py +1 -1
  62. classiq/qmod/pretty_print/expression_to_python.py +3 -6
  63. classiq/qmod/pretty_print/pretty_printer.py +4 -1
  64. classiq/qmod/qmod_variable.py +141 -3
  65. classiq/qmod/semantics/annotation/call_annotation.py +4 -2
  66. classiq/qmod/semantics/annotation/qstruct_annotator.py +20 -5
  67. {classiq-0.87.0.dist-info → classiq-0.89.0.dist-info}/METADATA +4 -4
  68. {classiq-0.87.0.dist-info → classiq-0.89.0.dist-info}/RECORD +69 -81
  69. classiq/applications/finance/__init__.py +0 -15
  70. classiq/interface/finance/__init__.py +0 -0
  71. classiq/interface/finance/finance_modelling_params.py +0 -11
  72. classiq/interface/finance/function_input.py +0 -102
  73. classiq/interface/finance/gaussian_model_input.py +0 -50
  74. classiq/interface/finance/log_normal_model_input.py +0 -40
  75. classiq/interface/finance/model_input.py +0 -22
  76. classiq/interface/generator/application_apis/finance_declarations.py +0 -108
  77. classiq/interface/generator/expressions/enums/__init__.py +0 -0
  78. classiq/interface/generator/expressions/enums/finance_functions.py +0 -12
  79. classiq/interface/generator/finance.py +0 -107
  80. classiq/qmod/builtins/functions/finance.py +0 -34
  81. {classiq-0.87.0.dist-info → classiq-0.89.0.dist-info}/WHEEL +0 -0
@@ -33,7 +33,10 @@ def eval_enum_member(
33
33
  }
34
34
  attr = node.attr
35
35
  if attr not in enum_members:
36
- raise ClassiqExpansionError(f"Enum {enum_name} has no member named {attr!r}")
36
+ raise ClassiqExpansionError(
37
+ f"Enum {enum_name} has no member {attr!r}. Available members: "
38
+ f"{', '.join(enum_members.keys())}"
39
+ )
37
40
 
38
41
  expr_val.set_type(node, Enum(name=enum_name))
39
42
  expr_val.set_value(node, enum_members[attr])
@@ -29,7 +29,7 @@ def get_numeric_attrs(
29
29
 
30
30
  if TYPE_CHECKING:
31
31
  assert isinstance(qmod_type, QuantumScalar)
32
- if not qmod_type.is_evaluated:
32
+ if not qmod_type.is_constant:
33
33
  return None
34
34
  return NumericAttributes.from_quantum_scalar(qmod_type, machine_precision)
35
35
 
@@ -20,7 +20,7 @@ from classiq.evaluators.qmod_node_evaluators.compare_evaluation import (
20
20
  def eval_struct_instantiation(
21
21
  expr_val: QmodAnnotatedExpression, node: ast.Call, decl: StructDeclaration
22
22
  ) -> None:
23
- if len(node.args) > 0 or any(kwarg.arg is None for kwarg in node.keywords):
23
+ if len(node.args) != 1 or any(kwarg.arg is None for kwarg in node.keywords):
24
24
  raise ClassiqExpansionError(
25
25
  "Classical structs must be instantiated using keyword arguments"
26
26
  )
@@ -69,7 +69,7 @@ def _eval_slice(expr_val: QmodAnnotatedExpression, node: ast.Subscript) -> None:
69
69
  subject_type = expr_val.get_type(subject)
70
70
  slice_type: QmodType
71
71
  if isinstance(subject_type, ClassicalArray):
72
- if subject_type.has_length and (
72
+ if subject_type.has_constant_length and (
73
73
  (start_val is not None and start_val >= subject_type.length_value)
74
74
  or (stop_val is not None and stop_val > subject_type.length_value)
75
75
  ):
@@ -93,7 +93,7 @@ def _eval_slice(expr_val: QmodAnnotatedExpression, node: ast.Subscript) -> None:
93
93
  slice_type = subject_type.get_raw_type()
94
94
  elif isinstance(subject_type, QuantumBitvector):
95
95
  if start_val is not None and stop_val is not None:
96
- if subject_type.has_length and (
96
+ if subject_type.has_constant_length and (
97
97
  start_val >= subject_type.length_value
98
98
  or stop_val > subject_type.length_value
99
99
  ):
@@ -150,7 +150,7 @@ def _eval_subscript(expr_val: QmodAnnotatedExpression, node: ast.Subscript) -> N
150
150
  if isinstance(subject_type, (ClassicalArray, QuantumBitvector)):
151
151
  if (
152
152
  sub_val is not None
153
- and subject_type.has_length
153
+ and subject_type.has_constant_length
154
154
  and sub_val >= subject_type.length_value
155
155
  ):
156
156
  raise ClassiqExpansionError("Array index out of range")
@@ -85,7 +85,7 @@ def array_len(
85
85
  ) -> Optional[int]:
86
86
  if isinstance(classical_type, ClassicalTuple):
87
87
  return len(classical_type.element_types)
88
- if classical_type.has_length:
88
+ if classical_type.has_constant_length:
89
89
  return classical_type.length_value
90
90
  return None
91
91
 
@@ -73,9 +73,9 @@ def _inject_classical_array_attributes(
73
73
  if isinstance(to_type, ClassicalArray):
74
74
  if isinstance(from_type, ClassicalArray):
75
75
  length: Optional[Expression]
76
- if from_type.has_length:
76
+ if from_type.has_constant_length:
77
77
  if (
78
- to_type.has_length
78
+ to_type.has_constant_length
79
79
  and from_type.length_value != to_type.length_value
80
80
  ):
81
81
  return None
@@ -90,7 +90,7 @@ def _inject_classical_array_attributes(
90
90
  return ClassicalArray(element_type=element_type, length=length)
91
91
  if isinstance(from_type, ClassicalTuple):
92
92
  if (
93
- to_type.has_length
93
+ to_type.has_constant_length
94
94
  and len(from_type.element_types) != to_type.length_value
95
95
  ):
96
96
  return None
@@ -103,7 +103,7 @@ def _inject_classical_array_attributes(
103
103
  return ClassicalTuple(element_types=element_types)
104
104
  return None
105
105
  if isinstance(from_type, ClassicalArray):
106
- if from_type.has_length and from_type.length_value != len(
106
+ if from_type.has_constant_length and from_type.length_value != len(
107
107
  to_type.element_types
108
108
  ):
109
109
  return None
@@ -290,3 +290,41 @@ def inject_quantum_type_attributes_inplace(
290
290
  to_type.set_fields(updated_type.fields)
291
291
  return True
292
292
  raise ClassiqInternalExpansionError
293
+
294
+
295
+ def validate_quantum_type_attributes(quantum_type: QuantumType) -> None:
296
+ if isinstance(quantum_type, TypeName):
297
+ if len(quantum_type.fields) == 0:
298
+ raise ClassiqExpansionError(
299
+ f"QStruct {quantum_type.name} must have at least one field"
300
+ )
301
+ for field_type in quantum_type.fields.values():
302
+ validate_quantum_type_attributes(field_type)
303
+ return
304
+ if isinstance(quantum_type, QuantumBitvector):
305
+ validate_quantum_type_attributes(quantum_type.element_type)
306
+ if quantum_type.has_constant_length and quantum_type.length_value < 1:
307
+ raise ClassiqExpansionError(
308
+ f"QArray length must be positive, got {quantum_type.length_value}"
309
+ )
310
+ return
311
+ if isinstance(quantum_type, QuantumNumeric):
312
+ if quantum_type.has_size_in_bits and quantum_type.size_in_bits < 1:
313
+ raise ClassiqExpansionError(
314
+ f"QNum size must be positive, got {quantum_type.size_in_bits}"
315
+ )
316
+ if quantum_type.has_fraction_digits:
317
+ if quantum_type.fraction_digits_value < 0:
318
+ raise ClassiqExpansionError(
319
+ f"QNum fraction digits must be positive, got "
320
+ f"{quantum_type.fraction_digits_value}"
321
+ )
322
+ if (
323
+ quantum_type.has_size_in_bits
324
+ and quantum_type.fraction_digits_value > quantum_type.size_in_bits
325
+ ):
326
+ raise ClassiqExpansionError(
327
+ f"QNum size ({quantum_type.size_in_bits}) must be greater or "
328
+ f"equals than the fraction digits "
329
+ f"({quantum_type.fraction_digits_value})"
330
+ )
@@ -21,7 +21,7 @@ def check_signature_match(
21
21
  ) -> None:
22
22
  if len(decl_params) != len(op_params):
23
23
  raise ClassiqExpansionError(
24
- f"{location_str} should have {len(decl_params)} parameters, "
24
+ f"{location_str.capitalize()} should have {len(decl_params)} parameters, "
25
25
  f"not {len(op_params)}"
26
26
  )
27
27
  for idx, (decl_param, op_param) in enumerate(zip(decl_params, op_params)):
@@ -340,6 +340,7 @@ class ExecutionSession:
340
340
  initial_params: ExecutionParams,
341
341
  max_iteration: int,
342
342
  quantile: float = 1.0,
343
+ tolerance: Optional[float] = None,
343
344
  ) -> list[tuple[float, ExecutionParams]]:
344
345
  """
345
346
  Minimizes the given cost function using the quantum program.
@@ -355,6 +356,7 @@ class ExecutionSession:
355
356
  - The value is either a float or a list of floats.
356
357
  max_iteration: The maximum number of iterations for the minimization.
357
358
  quantile: The quantile to use for cost estimation.
359
+ tolerance: The tolerance for the minimization.
358
360
  Returns:
359
361
  A list of tuples, each containing the estimated cost and the corresponding parameters for that iteration.
360
362
  `cost` is a float, and `parameters` is a dictionary matching the execution parameter format.
@@ -365,6 +367,7 @@ class ExecutionSession:
365
367
  initial_params=initial_params,
366
368
  max_iteration=max_iteration,
367
369
  quantile=quantile,
370
+ tolerance=tolerance,
368
371
  _check_deprecation=False,
369
372
  )
370
373
  result = job.get_minimization_result(_http_client=self._async_client)
@@ -379,6 +382,7 @@ class ExecutionSession:
379
382
  initial_params: ExecutionParams,
380
383
  max_iteration: int,
381
384
  quantile: float = 1.0,
385
+ tolerance: Optional[float] = None,
382
386
  *,
383
387
  _check_deprecation: bool = True,
384
388
  ) -> ExecutionJob:
@@ -400,6 +404,7 @@ class ExecutionSession:
400
404
  - The value is either a float or a list of floats.
401
405
  max_iteration: The maximum number of iterations for the minimization.
402
406
  quantile: The quantile to use for cost estimation.
407
+ tolerance: The tolerance for the minimization.
403
408
 
404
409
  Returns:
405
410
  The execution job.
@@ -424,6 +429,7 @@ class ExecutionSession:
424
429
  initial_params=_initial_params,
425
430
  max_iteration=max_iteration,
426
431
  quantile=quantile,
432
+ tolerance=tolerance,
427
433
  )
428
434
  else:
429
435
  _cost_function = self._hamiltonian_to_pauli_operator(cost_function)
@@ -432,6 +438,7 @@ class ExecutionSession:
432
438
  initial_params=_initial_params,
433
439
  max_iteration=max_iteration,
434
440
  quantile=quantile,
441
+ tolerance=tolerance,
435
442
  )
436
443
 
437
444
  execution_primitives_input = PrimitivesInput(minimize=minimize)
@@ -545,10 +552,18 @@ class ExecutionSession:
545
552
  name: create_qvar_from_quantum_type(reg.quantum_types, name)
546
553
  for name, reg in circuit_output_types.items()
547
554
  }
548
- for name in inspect.signature(qmod_expression_creator).parameters.keys():
555
+ creator_parameters = inspect.signature(
556
+ qmod_expression_creator
557
+ ).parameters.keys()
558
+ for name in creator_parameters:
549
559
  if name not in symbolic_output:
550
560
  raise ClassiqValueError(
551
- f"The provided QVar: {name} does not match the model outputs: {tuple(circuit_output_types.keys())}. "
561
+ f"Expected cost function with parameters {tuple(symbolic_output.keys())} corresponding to the quantum program outputs, but found '{name}'. "
562
+ )
563
+ for name in symbolic_output:
564
+ if name not in creator_parameters:
565
+ raise ClassiqValueError(
566
+ f"Expected cost function with parameter '{name}' corresponding to the quantum program outputs. "
552
567
  )
553
568
 
554
569
  qmod_expression = qmod_expression_creator(**symbolic_output)
@@ -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.87.0'
6
+ SEMVER_VERSION = '0.89.0'
7
7
  VERSION = str(Version(SEMVER_VERSION))
@@ -80,7 +80,7 @@ class AnalysisHardwareListParams(AnalysisParams, HardwareListParams):
80
80
 
81
81
 
82
82
  class HardwareParams(pydantic.BaseModel):
83
- device: PydanticNonEmptyString = pydantic.Field(default=None, description="Devices")
83
+ device: PydanticNonEmptyString = pydantic.Field(default=None, description="Devices") # type: ignore[assignment]
84
84
  provider: AnalyzerProviderVendor
85
85
 
86
86
 
@@ -274,7 +274,7 @@ class AzureCredential(BaseSettings):
274
274
 
275
275
  def __init__(self, **data: Any) -> None:
276
276
  initial_data = {
277
- field: data[field] for field in data if field in self.model_fields
277
+ field: data[field] for field in data if field in self.__class__.model_fields
278
278
  }
279
279
  super().__init__(**data)
280
280
  for field, value in initial_data.items():
@@ -17,6 +17,7 @@ class MinimizeCostInput(BaseModel, json_encoders=CUSTOM_ENCODERS):
17
17
  initial_params: Arguments
18
18
  max_iteration: int
19
19
  quantile: float
20
+ tolerance: Optional[float]
20
21
 
21
22
 
22
23
  class MinimizeClassicalCostInput(MinimizeCostInput):
@@ -6,7 +6,6 @@ from .arithmetic_declarations import * # noqa: F403
6
6
  from .chemistry_declarations import * # noqa: F403
7
7
  from .combinatorial_optimization_declarations import * # noqa: F403
8
8
  from .entangler_declarations import * # noqa: F403
9
- from .finance_declarations import * # noqa: F403
10
9
  from .qsvm_declarations import * # noqa: F403
11
10
 
12
11
  populate_builtin_declarations(vars().values())
@@ -19,7 +19,7 @@ class RegisterArithmeticInfo(HashablePydanticBaseModel):
19
19
  is_signed: bool = pydantic.Field(default=False)
20
20
  fraction_places: pydantic.NonNegativeInt = pydantic.Field(default=0)
21
21
  bypass_bounds_validation: bool = pydantic.Field(default=False)
22
- bounds: PydanticFloatTuple = pydantic.Field(
22
+ bounds: PydanticFloatTuple = pydantic.Field( # type: ignore[assignment]
23
23
  default=None,
24
24
  validate_default=True,
25
25
  )
@@ -4,8 +4,6 @@ CLASSIQ_BUILTIN_CLASSICAL_FUNCTIONS = {
4
4
  "hypercube_entangler_graph",
5
5
  "grid_entangler_graph",
6
6
  "qft_const_adder_phase",
7
- "log_normal_finance_post_process",
8
- "gaussian_finance_post_process",
9
7
  "molecule_problem_to_hamiltonian",
10
8
  "fock_hamiltonian_problem_to_hamiltonian",
11
9
  "molecule_ground_state_solution_post_process",
@@ -33,7 +33,6 @@ from classiq.interface.generator.entangler_params import (
33
33
  HypercubeEntangler,
34
34
  TwoDimensionalEntangler,
35
35
  )
36
- from classiq.interface.generator.finance import Finance, FinanceModels, FinancePayoff
37
36
  from classiq.interface.generator.function_param_library import FunctionParamLibrary
38
37
  from classiq.interface.generator.hadamard_transform import HadamardTransform
39
38
  from classiq.interface.generator.hamiltonian_evolution.exponentiation import (
@@ -106,9 +105,6 @@ function_param_library: FunctionParamLibrary = FunctionParamLibrary(
106
105
  CyclicShift,
107
106
  Modulo,
108
107
  TwoDimensionalEntangler,
109
- Finance,
110
- FinanceModels,
111
- FinancePayoff,
112
108
  HypercubeEntangler,
113
109
  GridEntangler,
114
110
  Mcx,
@@ -2,7 +2,7 @@ from itertools import chain
2
2
  from typing import TYPE_CHECKING, Any, Literal, Optional
3
3
 
4
4
  import pydantic
5
- from pydantic import ConfigDict, PrivateAttr
5
+ from pydantic import PrivateAttr
6
6
  from typing_extensions import Self
7
7
 
8
8
  from classiq.interface.ast_node import HashableASTNode
@@ -34,8 +34,6 @@ if TYPE_CHECKING:
34
34
  class ClassicalType(HashableASTNode):
35
35
  _is_generative: bool = PrivateAttr(default=False)
36
36
 
37
- model_config = ConfigDict(extra="forbid")
38
-
39
37
  def __str__(self) -> str:
40
38
  return str(type(self).__name__)
41
39
 
@@ -78,6 +76,9 @@ class ClassicalType(HashableASTNode):
78
76
  def get_raw_type(self) -> "ConcreteClassicalType":
79
77
  return self # type:ignore[return-value]
80
78
 
79
+ def without_symbolic_attributes(self) -> Self:
80
+ return self
81
+
81
82
 
82
83
  class Integer(ClassicalType):
83
84
  kind: Literal["int"]
@@ -144,6 +145,14 @@ class ClassicalArray(ClassicalType):
144
145
  def has_length(self) -> bool:
145
146
  return self.length is not None and self.length.is_evaluated()
146
147
 
148
+ @property
149
+ def has_constant_length(self) -> bool:
150
+ return (
151
+ self.length is not None
152
+ and self.length.is_evaluated()
153
+ and self.length.is_constant()
154
+ )
155
+
147
156
  @property
148
157
  def length_value(self) -> int:
149
158
  if not self.has_length:
@@ -195,6 +204,18 @@ class ClassicalArray(ClassicalType):
195
204
  def raw_qmod_type_name(self) -> str:
196
205
  return "CArray"
197
206
 
207
+ def without_symbolic_attributes(self) -> "ClassicalArray":
208
+ length = (
209
+ None
210
+ if self.length is None
211
+ or not self.length.is_evaluated()
212
+ or not self.length.is_constant()
213
+ else self.length
214
+ )
215
+ return ClassicalArray(
216
+ element_type=self.element_type.without_symbolic_attributes(), length=length
217
+ )
218
+
198
219
 
199
220
  class ClassicalTuple(ClassicalType):
200
221
  kind: Literal["tuple"]
@@ -265,6 +286,14 @@ class ClassicalTuple(ClassicalType):
265
286
  def raw_qmod_type_name(self) -> str:
266
287
  return "CArray"
267
288
 
289
+ def without_symbolic_attributes(self) -> "ClassicalTuple":
290
+ return ClassicalTuple(
291
+ element_types=[
292
+ element_type.without_symbolic_attributes()
293
+ for element_type in self.element_types
294
+ ]
295
+ )
296
+
268
297
 
269
298
  class OpaqueHandle(ClassicalType):
270
299
  pass
@@ -1,8 +1,6 @@
1
1
  import abc
2
2
  from collections.abc import Sequence
3
3
 
4
- from pydantic import ConfigDict
5
-
6
4
  from classiq.interface.model.classical_parameter_declaration import (
7
5
  AnonClassicalParameterDeclaration,
8
6
  )
@@ -19,7 +17,5 @@ class FunctionDeclaration(Parameter, abc.ABC):
19
17
  def param_decls(self) -> Sequence["AnonClassicalParameterDeclaration"]:
20
18
  pass
21
19
 
22
- model_config = ConfigDict(extra="forbid")
23
-
24
20
 
25
21
  FunctionDeclaration.model_rebuild()
@@ -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
@@ -183,6 +189,31 @@ class TypeName(ClassicalType, QuantumType):
183
189
  field_type.minimal_size_in_bits for field_type in self.fields.values()
184
190
  )
185
191
 
192
+ def without_symbolic_attributes(self) -> "TypeName":
193
+ if self.has_fields:
194
+ type_name = TypeName(name=self.name)
195
+ type_name.set_fields(
196
+ {
197
+ field_name: field_type.without_symbolic_attributes()
198
+ for field_name, field_type in self.fields.items()
199
+ }
200
+ )
201
+ return type_name
202
+ if self.has_classical_struct_decl:
203
+ type_name = TypeName(name=self.name)
204
+ type_name.set_classical_struct_decl(
205
+ self.classical_struct_decl.model_copy(
206
+ update=dict(
207
+ variables={
208
+ field_name: field_type.without_symbolic_attributes()
209
+ for field_name, field_type in self.classical_struct_decl.variables.items()
210
+ }
211
+ )
212
+ )
213
+ )
214
+ return type_name
215
+ return self
216
+
186
217
 
187
218
  class Enum(TypeName):
188
219
  pass
@@ -42,7 +42,7 @@ class HardwareEfficientAnsatz(function_params.FunctionParams):
42
42
  "If none specified - use connectivity map from the model hardware settings. "
43
43
  "If none specified as well, all qubit pairs will be connected.",
44
44
  )
45
- num_qubits: pydantic.PositiveInt = pydantic.Field(
45
+ num_qubits: pydantic.PositiveInt = pydantic.Field( # type: ignore[assignment]
46
46
  default=None,
47
47
  description="Number of qubits in the ansatz.",
48
48
  validate_default=True,
@@ -130,7 +130,7 @@ class SynthesisQuantumFunctionCall(BaseModel):
130
130
  default=True,
131
131
  description="False value indicates this call shouldn't be controlled even if the flow is controlled.",
132
132
  )
133
- inputs: IOType = pydantic.Field(
133
+ inputs: IOType = pydantic.Field( # type: ignore[assignment]
134
134
  default_factory=dict,
135
135
  description="A mapping from the input name to the wire it connects to",
136
136
  )
@@ -138,14 +138,14 @@ class SynthesisQuantumFunctionCall(BaseModel):
138
138
  default_factory=dict,
139
139
  description="A mapping from in/out name to the wires that connect to it",
140
140
  )
141
- outputs: IOType = pydantic.Field(
141
+ outputs: IOType = pydantic.Field( # type: ignore[assignment]
142
142
  default_factory=dict,
143
143
  description="A mapping from the output name to the wire it connects to",
144
144
  )
145
145
  power: PydanticPowerType = pydantic.Field(
146
146
  default=1, description="Number of successive calls to the operation"
147
147
  )
148
- name: PydanticNonEmptyString = pydantic.Field(
148
+ name: PydanticNonEmptyString = pydantic.Field( # type: ignore[assignment]
149
149
  default=None,
150
150
  validate_default=True,
151
151
  description="The name of the function instance. "
@@ -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",
@@ -1,7 +1,6 @@
1
1
  from collections.abc import Mapping
2
2
 
3
3
  import pydantic
4
- from pydantic import ConfigDict
5
4
 
6
5
  from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
7
6
  from classiq.interface.generator.function_params import ArithmeticIODict, FunctionParams
@@ -12,8 +11,6 @@ class CustomFunction(FunctionParams):
12
11
  A user-defined custom function parameters object.
13
12
  """
14
13
 
15
- model_config = ConfigDict(frozen=True, extra="forbid")
16
-
17
14
  _name: str = pydantic.PrivateAttr(default="")
18
15
 
19
16
  input_decls: ArithmeticIODict = pydantic.Field(
@@ -59,7 +59,7 @@ class IDEDataOperation(pydantic.BaseModel):
59
59
  _qubits: list = pydantic.PrivateAttr() # list[Qubit]
60
60
 
61
61
  displayArgs: str = ""
62
- targets: Union[list[IDEQubitDef], list[IDEClassicalBitDef]] = pydantic.Field(
62
+ targets: Union[list[IDEQubitDef], list[IDEClassicalBitDef]] = pydantic.Field( # type: ignore[assignment]
63
63
  default_factory=list
64
64
  )
65
65
  controls: list[IDEQubitDef] = list()
@@ -207,9 +207,9 @@ class Operation(pydantic.BaseModel):
207
207
 
208
208
 
209
209
  class ProgramVisualModel(VersionedModel):
210
- main_operation: Operation = pydantic.Field(default=None)
210
+ main_operation: Operation = pydantic.Field(default=None) # type: ignore[assignment]
211
211
  id_to_operations: dict[int, Operation] = pydantic.Field(default_factory=dict)
212
- main_operation_id: int = pydantic.Field(default=None)
212
+ main_operation_id: int = pydantic.Field(default=None) # type: ignore[assignment]
213
213
  program_data: ProgramData
214
214
 
215
215
  @property
@@ -1,4 +1,6 @@
1
- from typing import TYPE_CHECKING, Literal
1
+ from typing import TYPE_CHECKING, Literal, Optional
2
+
3
+ import pydantic
2
4
 
3
5
  from classiq.interface.model.quantum_statement import QuantumOperation
4
6
 
@@ -10,3 +12,5 @@ class Block(QuantumOperation):
10
12
  kind: Literal["Block"]
11
13
 
12
14
  statements: "StatementBlock"
15
+
16
+ label: Optional[str] = pydantic.Field(default=None)
@@ -3,6 +3,8 @@ import operator
3
3
  from collections.abc import Mapping
4
4
  from typing import TYPE_CHECKING, Literal
5
5
 
6
+ import pydantic
7
+
6
8
  from classiq.interface.ast_node import ASTNodeType, reset_lists
7
9
  from classiq.interface.generator.expressions.expression import Expression
8
10
  from classiq.interface.model.handle_binding import ConcreteHandleBinding, HandleBinding
@@ -18,6 +20,9 @@ class ClassicalIf(QuantumOperation):
18
20
  condition: Expression
19
21
  then: "StatementBlock"
20
22
  else_: "StatementBlock"
23
+ _condition_wiring_inouts: dict[str, HandleBinding] = pydantic.PrivateAttr(
24
+ default_factory=dict
25
+ )
21
26
 
22
27
  def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
23
28
  return reset_lists(self, ["then", "else_"])
@@ -40,14 +45,17 @@ class ClassicalIf(QuantumOperation):
40
45
 
41
46
  @property
42
47
  def wiring_inouts(self) -> Mapping[str, ConcreteHandleBinding]:
43
- return functools.reduce(
44
- operator.ior,
45
- (
46
- op.wiring_inouts
47
- for op in (*self.then, *self.else_)
48
- if isinstance(op, QuantumOperation)
49
- ),
50
- dict(),
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
51
59
  )
52
60
 
53
61
  @property
@@ -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
@@ -19,7 +19,7 @@ def _get_expr_id(expr: Expression) -> str:
19
19
 
20
20
 
21
21
  class HandleBinding(ASTNode):
22
- name: str = Field(default=None)
22
+ name: str = Field(default=None) # type: ignore[assignment]
23
23
  model_config = ConfigDict(frozen=True, extra="forbid")
24
24
 
25
25
  def __str__(self) -> str: