classiq 0.82.1__py3-none-any.whl → 0.84.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. classiq/_internals/api_wrapper.py +27 -0
  2. classiq/applications/chemistry/chemistry_model_constructor.py +2 -4
  3. classiq/applications/chemistry/hartree_fock.py +68 -0
  4. classiq/applications/chemistry/mapping.py +85 -0
  5. classiq/applications/chemistry/op_utils.py +79 -0
  6. classiq/applications/chemistry/problems.py +195 -0
  7. classiq/applications/chemistry/ucc.py +109 -0
  8. classiq/applications/chemistry/z2_symmetries.py +368 -0
  9. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +30 -1
  10. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +2 -2
  11. classiq/{model_expansions/evaluators → evaluators}/arg_type_match.py +12 -4
  12. classiq/{model_expansions/evaluators → evaluators}/argument_types.py +3 -3
  13. classiq/{model_expansions/evaluators → evaluators}/classical_expression.py +1 -1
  14. classiq/{model_expansions/evaluators → evaluators}/classical_type_inference.py +3 -4
  15. classiq/{model_expansions/evaluators → evaluators}/parameter_types.py +17 -15
  16. classiq/execution/__init__.py +12 -1
  17. classiq/execution/execution_session.py +189 -43
  18. classiq/execution/jobs.py +26 -1
  19. classiq/execution/qnn.py +2 -2
  20. classiq/execution/user_budgets.py +39 -0
  21. classiq/interface/_version.py +1 -1
  22. classiq/interface/constants.py +1 -0
  23. classiq/interface/execution/primitives.py +29 -1
  24. classiq/interface/executor/estimate_cost.py +35 -0
  25. classiq/interface/executor/execution_result.py +13 -0
  26. classiq/interface/executor/result.py +116 -1
  27. classiq/interface/executor/user_budget.py +26 -33
  28. classiq/interface/generator/application_apis/finance_declarations.py +3 -3
  29. classiq/interface/generator/expressions/atomic_expression_functions.py +11 -3
  30. classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +0 -6
  31. classiq/interface/generator/functions/classical_type.py +2 -35
  32. classiq/interface/generator/functions/concrete_types.py +0 -3
  33. classiq/interface/generator/functions/type_modifier.py +22 -0
  34. classiq/interface/generator/generated_circuit_data.py +5 -16
  35. classiq/interface/generator/model/model.py +8 -0
  36. classiq/interface/generator/quantum_program.py +0 -13
  37. classiq/interface/generator/types/compilation_metadata.py +0 -3
  38. classiq/interface/helpers/model_normalizer.py +2 -2
  39. classiq/interface/ide/visual_model.py +6 -2
  40. classiq/interface/model/model.py +12 -7
  41. classiq/interface/model/port_declaration.py +4 -2
  42. classiq/interface/pretty_print/__init__.py +0 -0
  43. classiq/{qmod/native → interface/pretty_print}/expression_to_qmod.py +18 -11
  44. classiq/interface/server/routes.py +4 -0
  45. classiq/model_expansions/atomic_expression_functions_defs.py +42 -5
  46. classiq/model_expansions/capturing/captured_vars.py +21 -8
  47. classiq/model_expansions/interpreters/base_interpreter.py +3 -3
  48. classiq/model_expansions/quantum_operations/allocate.py +1 -1
  49. classiq/model_expansions/quantum_operations/assignment_result_processor.py +1 -1
  50. classiq/model_expansions/quantum_operations/bind.py +2 -2
  51. classiq/model_expansions/quantum_operations/call_emitter.py +42 -36
  52. classiq/model_expansions/quantum_operations/variable_decleration.py +1 -1
  53. classiq/model_expansions/scope_initialization.py +3 -3
  54. classiq/model_expansions/transformers/model_renamer.py +16 -5
  55. classiq/model_expansions/transformers/{type_qualifier_inference.py → type_modifier_inference.py} +134 -100
  56. classiq/model_expansions/visitors/symbolic_param_inference.py +10 -7
  57. classiq/open_library/functions/__init__.py +3 -0
  58. classiq/open_library/functions/amplitude_amplification.py +10 -18
  59. classiq/open_library/functions/discrete_sine_cosine_transform.py +5 -5
  60. classiq/open_library/functions/grover.py +14 -6
  61. classiq/open_library/functions/modular_exponentiation.py +22 -20
  62. classiq/open_library/functions/state_preparation.py +18 -1
  63. classiq/qmod/__init__.py +2 -2
  64. classiq/qmod/builtins/enums.py +23 -0
  65. classiq/qmod/builtins/functions/__init__.py +2 -0
  66. classiq/qmod/builtins/functions/allocation.py +2 -2
  67. classiq/qmod/builtins/functions/arithmetic.py +16 -8
  68. classiq/qmod/builtins/functions/exponentiation.py +32 -4
  69. classiq/qmod/builtins/functions/standard_gates.py +7 -7
  70. classiq/qmod/builtins/structs.py +55 -3
  71. classiq/qmod/declaration_inferrer.py +8 -7
  72. classiq/qmod/native/pretty_printer.py +7 -11
  73. classiq/qmod/pretty_print/expression_to_python.py +2 -1
  74. classiq/qmod/pretty_print/pretty_printer.py +7 -12
  75. classiq/qmod/python_classical_type.py +12 -5
  76. classiq/qmod/qfunc.py +1 -1
  77. classiq/qmod/qmod_constant.py +2 -5
  78. classiq/qmod/qmod_parameter.py +2 -5
  79. classiq/qmod/qmod_variable.py +66 -25
  80. classiq/qmod/quantum_expandable.py +4 -2
  81. classiq/qmod/quantum_function.py +7 -2
  82. classiq/qmod/semantics/annotation/qstruct_annotator.py +1 -1
  83. classiq/qmod/semantics/validation/main_validation.py +1 -9
  84. classiq/qmod/semantics/validation/type_hints.py +9 -9
  85. classiq/qmod/utilities.py +0 -2
  86. classiq/qmod/write_qmod.py +1 -1
  87. classiq/synthesis.py +0 -2
  88. {classiq-0.82.1.dist-info → classiq-0.84.0.dist-info}/METADATA +4 -1
  89. {classiq-0.82.1.dist-info → classiq-0.84.0.dist-info}/RECORD +95 -86
  90. classiq/interface/generator/functions/type_qualifier.py +0 -22
  91. /classiq/{model_expansions/evaluators → evaluators}/__init__.py +0 -0
  92. /classiq/{model_expansions/evaluators → evaluators}/control.py +0 -0
  93. /classiq/{model_expansions → evaluators}/expression_evaluator.py +0 -0
  94. /classiq/{model_expansions/evaluators → evaluators}/quantum_type_utils.py +0 -0
  95. /classiq/{model_expansions/evaluators → evaluators}/type_type_match.py +0 -0
  96. {classiq-0.82.1.dist-info → classiq-0.84.0.dist-info}/WHEEL +0 -0
@@ -1,9 +1,9 @@
1
1
  import datetime
2
- from collections import defaultdict
3
2
  from typing import Optional
4
3
 
5
4
  import pydantic
6
5
  from pydantic import ConfigDict, Field
6
+ from tabulate import tabulate
7
7
 
8
8
  from classiq.interface.helpers.versioned_model import VersionedModel
9
9
 
@@ -15,6 +15,7 @@ class UserBudget(VersionedModel):
15
15
  available_budget: float
16
16
  used_budget: float
17
17
  last_allocation_date: datetime.datetime
18
+ budget_limit: Optional[float] = Field(default=None)
18
19
 
19
20
  model_config = ConfigDict(extra="ignore")
20
21
 
@@ -22,35 +23,27 @@ class UserBudget(VersionedModel):
22
23
  class UserBudgets(VersionedModel):
23
24
  budgets: list[UserBudget] = pydantic.Field(default=[])
24
25
 
25
- def print_budgets(self) -> None:
26
- def format_header() -> str:
27
- return f"| {'Provider':<20} | {'Available Budget':<18} | {'Used Budget':<18} | {'Currency':<8} |"
28
-
29
- def format_row(
30
- provider: str, available: float, used: float, currency: str
31
- ) -> str:
32
- return f"| {provider:<20} | {available:<18.3f} | {used:<18.3f} | {currency:<8} |"
33
-
34
- table_data: dict = defaultdict(
35
- lambda: {"used": 0.0, "available": 0.0, "currency": "USD"}
36
- )
37
-
38
- for budget in self.budgets:
39
- provider = budget.provider
40
- table_data[provider]["available"] += budget.available_budget
41
- table_data[provider]["used"] += budget.used_budget
42
- table_data[provider]["currency"] = budget.currency_code
43
-
44
- line = "=" * 77
45
- print(line) # noqa: T201
46
- print(format_header()) # noqa: T201
47
- print(line) # noqa: T201
48
-
49
- for provider, values in table_data.items():
50
- print( # noqa: T201
51
- format_row(
52
- provider, values["available"], values["used"], values["currency"]
53
- )
54
- )
55
-
56
- print(line) # noqa: T201
26
+ def __str__(self) -> str:
27
+ rows = [
28
+ [
29
+ budget.provider,
30
+ f"{budget.used_budget:.3f}",
31
+ f"{budget.available_budget:.3f}",
32
+ (
33
+ f"{budget.budget_limit:.3f}"
34
+ if budget.budget_limit is not None
35
+ else "NOT SET"
36
+ ),
37
+ budget.currency_code,
38
+ ]
39
+ for budget in self.budgets
40
+ ]
41
+
42
+ headers = [
43
+ "Provider",
44
+ "Used Budget",
45
+ "Remaining Budget",
46
+ "Budget Limit",
47
+ "Currency",
48
+ ]
49
+ return tabulate(rows, headers=headers, tablefmt="grid")
@@ -9,8 +9,8 @@ from classiq.interface.generator.functions.classical_type import Real
9
9
  from classiq.interface.generator.functions.port_declaration import (
10
10
  PortDeclarationDirection,
11
11
  )
12
+ from classiq.interface.generator.functions.type_modifier import TypeModifier
12
13
  from classiq.interface.generator.functions.type_name import Struct
13
- from classiq.interface.generator.functions.type_qualifier import TypeQualifier
14
14
  from classiq.interface.model.classical_parameter_declaration import (
15
15
  ClassicalParameterDeclaration,
16
16
  )
@@ -56,13 +56,13 @@ def _generate_finance_function(
56
56
  )
57
57
  ),
58
58
  direction=PortDeclarationDirection.Inout,
59
- type_qualifier=TypeQualifier.Quantum,
59
+ type_modifier=TypeModifier.Mutable,
60
60
  ),
61
61
  PortDeclaration(
62
62
  name=OBJECTIVE_PORT_NAME,
63
63
  quantum_type=QuantumBit(),
64
64
  direction=PortDeclarationDirection.Inout,
65
- type_qualifier=TypeQualifier.Quantum,
65
+ type_modifier=TypeModifier.Mutable,
66
66
  ),
67
67
  ],
68
68
  )
@@ -11,15 +11,18 @@ CLASSIQ_BUILTIN_CLASSICAL_FUNCTIONS = {
11
11
  "molecule_ground_state_solution_post_process",
12
12
  }
13
13
 
14
- SUPPORTED_CLASSIQ_BUILTIN_FUNCTIONS = {
15
- *CLASSIQ_BUILTIN_CLASSICAL_FUNCTIONS,
14
+ CLASSIQ_EXPR_FUNCTIONS = {
16
15
  "do_div",
17
16
  "do_slice",
18
17
  "do_subscript",
19
18
  "get_type",
20
19
  "struct_literal",
21
20
  "get_field",
22
- "mod_inverse",
21
+ }
22
+
23
+ SUPPORTED_CLASSIQ_BUILTIN_FUNCTIONS = {
24
+ *CLASSIQ_BUILTIN_CLASSICAL_FUNCTIONS,
25
+ *CLASSIQ_EXPR_FUNCTIONS,
23
26
  }
24
27
 
25
28
  SUPPORTED_CLASSIQ_SYMPY_WRAPPERS = {
@@ -30,6 +33,11 @@ SUPPORTED_CLASSIQ_SYMPY_WRAPPERS = {
30
33
  "LogicalXor",
31
34
  "RShift",
32
35
  "LShift",
36
+ "mod_inverse",
37
+ "min",
38
+ "Min",
39
+ "max",
40
+ "Max",
33
41
  }
34
42
 
35
43
  SUPPORTED_ATOMIC_EXPRESSION_FUNCTIONS = {
@@ -21,12 +21,6 @@ def subscript_to_str(index: Any) -> str:
21
21
 
22
22
 
23
23
  class AnyClassicalValue(sympy.Symbol):
24
-
25
- is_commutative = None
26
- is_infinite = None
27
- is_finite = None
28
- is_extended_real = None
29
-
30
24
  def __getitem__(self, item: Any) -> "AnyClassicalValue":
31
25
  if isinstance(item, slice):
32
26
  return AnyClassicalValue(f"{self}[{subscript_to_str(item)}]")
@@ -98,39 +98,6 @@ class Bool(ClassicalType):
98
98
  return values_with_discriminator(values, "kind", "bool")
99
99
 
100
100
 
101
- class ClassicalList(ClassicalType):
102
- kind: Literal["list"]
103
- element_type: "ConcreteClassicalType"
104
-
105
- @pydantic.model_validator(mode="before")
106
- @classmethod
107
- def _set_kind(cls, values: Any) -> dict[str, Any]:
108
- return values_with_discriminator(values, "kind", "list")
109
-
110
- def get_classical_proxy(self, handle: HandleBinding) -> ClassicalProxy:
111
- return ClassicalArrayProxy(
112
- handle, self.element_type, AnyClassicalValue(f"get_field({handle}, 'len')")
113
- )
114
-
115
- @property
116
- def expressions(self) -> list[Expression]:
117
- return self.element_type.expressions
118
-
119
- @property
120
- def is_purely_declarative(self) -> bool:
121
- return super().is_purely_declarative and self.element_type.is_purely_declarative
122
-
123
- @property
124
- def is_purely_generative(self) -> bool:
125
- return super().is_purely_generative and self.element_type.is_purely_generative
126
-
127
- def get_raw_type(self) -> "ConcreteClassicalType":
128
- raw_type = ClassicalArray(element_type=self.element_type.get_raw_type())
129
- if self._is_generative:
130
- raw_type.set_generative()
131
- return raw_type
132
-
133
-
134
101
  class StructMetaType(ClassicalType):
135
102
  kind: Literal["type_proxy"]
136
103
 
@@ -146,8 +113,8 @@ class StructMetaType(ClassicalType):
146
113
  class ClassicalArray(ClassicalType):
147
114
  kind: Literal["array"]
148
115
  element_type: "ConcreteClassicalType"
149
- size: Optional[int] = None
150
- length: Optional[Expression] = pydantic.Field(exclude=True, default=None)
116
+ size: Optional[int] = pydantic.Field(exclude=True, default=None)
117
+ length: Optional[Expression] = None
151
118
 
152
119
  @pydantic.model_validator(mode="before")
153
120
  @classmethod
@@ -5,7 +5,6 @@ from pydantic import Field
5
5
  from classiq.interface.generator.functions.classical_type import (
6
6
  Bool,
7
7
  ClassicalArray,
8
- ClassicalList,
9
8
  ClassicalTuple,
10
9
  Estimation,
11
10
  Histogram,
@@ -29,7 +28,6 @@ ConcreteClassicalType = Annotated[
29
28
  Integer,
30
29
  Real,
31
30
  Bool,
32
- ClassicalList,
33
31
  StructMetaType,
34
32
  TypeName,
35
33
  ClassicalArray,
@@ -41,7 +39,6 @@ ConcreteClassicalType = Annotated[
41
39
  ],
42
40
  Field(discriminator="kind"),
43
41
  ]
44
- ClassicalList.model_rebuild()
45
42
  ClassicalArray.model_rebuild()
46
43
  ClassicalTuple.model_rebuild()
47
44
 
@@ -0,0 +1,22 @@
1
+ from classiq.interface.enum_utils import StrEnum
2
+ from classiq.interface.exceptions import ClassiqInternalExpansionError
3
+
4
+
5
+ class TypeModifier(StrEnum):
6
+ Const = "const"
7
+ Permutable = "permutable"
8
+ Mutable = "mutable"
9
+ Inferred = "inferred"
10
+
11
+ @staticmethod
12
+ def and_(first: "TypeModifier", second: "TypeModifier") -> "TypeModifier":
13
+ if second is TypeModifier.Inferred:
14
+ raise ClassiqInternalExpansionError
15
+ if first is TypeModifier.Mutable or second is TypeModifier.Mutable:
16
+ return TypeModifier.Mutable
17
+ elif first is TypeModifier.Permutable or second is TypeModifier.Permutable:
18
+ return TypeModifier.Permutable
19
+ else:
20
+ if first is not TypeModifier.Const and second is not TypeModifier.Const:
21
+ raise ClassiqInternalExpansionError("Unexpected type modifiers")
22
+ return TypeModifier.Const
@@ -28,8 +28,6 @@ from classiq.interface.model.statement_block import (
28
28
  )
29
29
 
30
30
  from classiq.model_expansions.capturing.mangling_utils import (
31
- demangle_capture_name,
32
- demangle_name,
33
31
  is_captured_var_name,
34
32
  )
35
33
 
@@ -85,12 +83,6 @@ class GeneratedRegister(pydantic.BaseModel):
85
83
  def is_captured(self) -> bool:
86
84
  return is_captured_var_name(self.name)
87
85
 
88
- @staticmethod
89
- def demangle_name(name: str) -> str:
90
- if is_captured_var_name(name):
91
- name = demangle_capture_name(name)
92
- return demangle_name(name)
93
-
94
86
 
95
87
  class GeneratedFunction(pydantic.BaseModel):
96
88
  name: str
@@ -202,8 +194,8 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
202
194
  @property
203
195
  def name(self) -> str:
204
196
  generated_name = self.generated_function.name if self.generated_function else ""
205
- # Temp fix for currently "supported" statements (same as for level_ property)
206
- if generated_name in {StatementType.CONTROL, StatementType.POWER}:
197
+ # Temp fix for remaining old "supported" statements - power
198
+ if generated_name == StatementType.POWER:
207
199
  return generated_name
208
200
 
209
201
  back_ref = self.first_back_ref
@@ -243,8 +235,8 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
243
235
 
244
236
  @property
245
237
  def level(self) -> OperationLevel:
246
- # Temp fix for currently "supported" statements
247
- if self.name in {StatementType.CONTROL, StatementType.POWER}:
238
+ # Temp fix for remaining old "supported" statements - power
239
+ if self.name == StatementType.POWER:
248
240
  return OperationLevel.QMOD_STATEMENT
249
241
 
250
242
  if self.first_back_ref is None:
@@ -275,15 +267,12 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
275
267
 
276
268
  @property
277
269
  def control_qubits(self) -> tuple[int, ...]:
278
- control_states_names = {
279
- control_state.name for control_state in self.control_states
280
- }
281
270
  return tuple(
282
271
  qubit
283
272
  for register in self.registers
284
273
  for qubit in register.qubit_indexes_absolute
285
274
  if register.role is RegisterRole.INPUT
286
- and register.name in control_states_names
275
+ and register.name == self.control_variable
287
276
  )
288
277
 
289
278
  def propagate_absolute_qubits(self) -> "FunctionDebugInfoInterface":
@@ -11,6 +11,9 @@ from classiq.interface.generator.types.qstruct_declaration import QStructDeclara
11
11
  from classiq.interface.generator.types.struct_declaration import StructDeclaration
12
12
  from classiq.interface.helpers.validation_helpers import is_list_unique
13
13
  from classiq.interface.helpers.versioned_model import VersionedModel
14
+ from classiq.interface.model.classical_parameter_declaration import (
15
+ ClassicalParameterDeclaration,
16
+ )
14
17
  from classiq.interface.model.quantum_type import RegisterQuantumTypeDict
15
18
 
16
19
  TYPE_LIBRARY_DUPLICATED_TYPE_NAMES = (
@@ -71,3 +74,8 @@ class ExecutionModel(ClassiqBaseModel):
71
74
  register_filter_bitstrings: dict[str, list[str]] = pydantic.Field(
72
75
  default_factory=dict,
73
76
  )
77
+
78
+ circuit_execution_params: dict[str, ClassicalParameterDeclaration] = pydantic.Field(
79
+ default_factory=dict,
80
+ description="Mapping between a execution parameter name and its declaration",
81
+ )
@@ -166,19 +166,6 @@ class QuantumProgram(VersionedModel, CircuitCodeInterface):
166
166
  with open(filename, "w") as file:
167
167
  file.write(self.model_dump_json(indent=4))
168
168
 
169
- @classmethod
170
- def from_qprog(cls, qprog: "QuantumProgram") -> "QuantumProgram":
171
- """
172
- Creates a `QuantumProgram` instance from a raw quantum program string.
173
-
174
- Args:
175
- qprog: The raw quantum program in string format.
176
-
177
- Returns:
178
- QuantumProgram: The `QuantumProgram` instance.
179
- """
180
- return qprog
181
-
182
169
  @property
183
170
  def _can_use_transpiled_code(self) -> bool:
184
171
  return self.data.execution_data is None
@@ -6,9 +6,6 @@ class CompilationMetadata(BaseModel):
6
6
  occurrences_number: NonNegativeInt = Field(default=1)
7
7
  _occupation_number: NonNegativeInt = PrivateAttr(default=0)
8
8
  unchecked: list[str] = Field(default_factory=list)
9
- atomic_qualifiers: list[str] = Field(
10
- default_factory=list, exclude=True
11
- ) # TODO remove after deprecation https://classiq.atlassian.net/browse/CLS-2671 atomic_qualifiers: list[str] = Field(default_factory=list)
12
9
 
13
10
  @property
14
11
  def occupation_number(self) -> NonNegativeInt:
@@ -5,7 +5,7 @@ from classiq.interface.ast_node import ASTNode
5
5
  from classiq.interface.debug_info.debug_info import DebugInfoCollection
6
6
  from classiq.interface.generator.expressions.expression import Expression
7
7
  from classiq.interface.generator.functions.classical_type import ClassicalType
8
- from classiq.interface.generator.functions.type_qualifier import TypeQualifier
8
+ from classiq.interface.generator.functions.type_modifier import TypeModifier
9
9
  from classiq.interface.generator.visitor import Transformer, Visitor
10
10
  from classiq.interface.model.model import Model
11
11
  from classiq.interface.model.port_declaration import AnonPortDeclaration
@@ -32,7 +32,7 @@ class ModelNormalizer(Visitor):
32
32
  expr.expr = ast.unparse(ExprNormalizer().visit(ast.parse(expr.expr)))
33
33
 
34
34
  def visit_AnonPortDeclaration(self, decl: AnonPortDeclaration) -> None:
35
- decl.type_qualifier = TypeQualifier.Quantum
35
+ decl.type_modifier = TypeModifier.Mutable
36
36
 
37
37
 
38
38
  class ClearModelInternals(Transformer):
@@ -110,7 +110,9 @@ class Operation(pydantic.BaseModel):
110
110
  name: str
111
111
  qasm_name: str = pydantic.Field(default="")
112
112
  details: str = pydantic.Field(default="")
113
- children: list["Operation"]
113
+ children: list["Operation"] = pydantic.Field(default_factory=list)
114
+ # children_ids is optional in order to support backwards compatibility.
115
+ children_ids: list[int] = pydantic.Field(default_factory=list)
114
116
  operation_data: Optional[OperationData] = None
115
117
  operation_links: OperationLinks
116
118
  control_qubits: tuple[int, ...] = pydantic.Field(default_factory=tuple)
@@ -129,5 +131,7 @@ class Operation(pydantic.BaseModel):
129
131
 
130
132
 
131
133
  class ProgramVisualModel(VersionedModel):
132
- main_operation: Operation
134
+ main_operation: Operation = pydantic.Field(default=None)
135
+ id_to_operations: dict[int, Operation] = pydantic.Field(default_factory=dict)
136
+ main_operation_id: int = pydantic.Field(default=None)
133
137
  program_data: ProgramData
@@ -185,11 +185,16 @@ class Model(VersionedModel, ASTNode):
185
185
  return constants
186
186
 
187
187
  def dump_no_metadata(self) -> dict[str, Any]:
188
- return self.model_dump(
189
- exclude={
190
- "constraints",
191
- "execution_preferences",
192
- "preferences",
193
- "functions_compilation_metadata",
194
- },
188
+ compilation_metadata_with_unchecked = {
189
+ name: CompilationMetadata(unchecked=comp_metadata.unchecked)
190
+ for name, comp_metadata in self.functions_compilation_metadata.items()
191
+ if len(comp_metadata.unchecked) > 0
192
+ }
193
+ model = self.model_copy(
194
+ update={
195
+ "functions_compilation_metadata": compilation_metadata_with_unchecked,
196
+ }
197
+ )
198
+ return model.model_dump(
199
+ exclude={"constraints", "execution_preferences", "preferences"},
195
200
  )
@@ -8,7 +8,9 @@ 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
+ from classiq.interface.generator.functions.type_modifier import (
12
+ TypeModifier,
13
+ )
12
14
  from classiq.interface.helpers.pydantic_model_helpers import values_with_discriminator
13
15
  from classiq.interface.model.parameter import Parameter
14
16
 
@@ -16,8 +18,8 @@ from classiq.interface.model.parameter import Parameter
16
18
  class AnonPortDeclaration(Parameter):
17
19
  quantum_type: ConcreteQuantumType
18
20
  direction: PortDeclarationDirection
19
- type_qualifier: TypeQualifier
20
21
  kind: Literal["PortDeclaration"]
22
+ type_modifier: TypeModifier
21
23
 
22
24
  @pydantic.model_validator(mode="before")
23
25
  @classmethod
File without changes
@@ -6,7 +6,7 @@ from typing import Callable, Optional
6
6
 
7
7
  import numpy as np
8
8
 
9
- from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
9
+ from classiq.interface.constants import DEFAULT_DECIMAL_PRECISION
10
10
 
11
11
  IDENTIFIER = re.compile(r"[a-zA-Z_]\w*")
12
12
  BINARY_OPS: Mapping[type[ast.operator], str] = {
@@ -40,6 +40,10 @@ COMPARE_OPS: Mapping[type[ast.cmpop], str] = {
40
40
  LIST_FORMAT_CHAR_LIMIT = 20
41
41
 
42
42
 
43
+ class PrettyPrinterError(AssertionError):
44
+ pass
45
+
46
+
43
47
  @dataclass
44
48
  class ASTToQMODCode:
45
49
  level: int
@@ -59,10 +63,13 @@ class ASTToQMODCode:
59
63
  elif isinstance(node, ast.Attribute):
60
64
  # Enum attribute access
61
65
  if not isinstance(node.value, ast.Name) or not isinstance(node.attr, str):
62
- raise AssertionError("Error parsing enum attribute access")
66
+ raise PrettyPrinterError("Error parsing enum attribute access")
63
67
  if not (IDENTIFIER.match(node.value.id) and IDENTIFIER.match(node.attr)):
64
- raise AssertionError("Error parsing enum attribute access")
65
- return f"{node.value.id!s}::{node.attr!s}"
68
+ raise PrettyPrinterError("Error parsing enum attribute access")
69
+ # FIXME: identify enum member accesses by type name (CLS-2858)
70
+ if len(node.value.id) > 0 and node.value.id[0].isupper():
71
+ return f"{node.value.id!s}::{node.attr!s}"
72
+ return f"{node.value.id!s}.{node.attr!s}"
66
73
  elif isinstance(node, ast.Name):
67
74
  return node.id
68
75
  elif isinstance(node, ast.Num):
@@ -91,7 +98,7 @@ class ASTToQMODCode:
91
98
  )
92
99
  elif isinstance(node, ast.Compare):
93
100
  if len(node.ops) != 1 or len(node.comparators) != 1:
94
- raise AssertionError("Error parsing comparison expression.")
101
+ raise PrettyPrinterError("Error parsing comparison expression.")
95
102
  return "({} {} {})".format(
96
103
  self.ast_to_code(node.left),
97
104
  COMPARE_OPS[type(node.ops[0])],
@@ -108,20 +115,20 @@ class ASTToQMODCode:
108
115
  elif isinstance(node, ast.Slice):
109
116
  # A QMOD expression does not support slice step
110
117
  if node.lower is None or node.upper is None or node.step is not None:
111
- raise AssertionError("Error parsing slice expression.")
118
+ raise PrettyPrinterError("Error parsing slice expression.")
112
119
  return f"{self.ast_to_code(node.lower)}:{self.ast_to_code(node.upper)}"
113
120
  elif isinstance(node, ast.Call):
114
121
  func = self.ast_to_code(node.func)
115
122
  if func == "get_field":
116
123
  if len(node.args) != 2:
117
- raise AssertionError("Error parsing struct field access.")
124
+ raise PrettyPrinterError("Error parsing struct field access.")
118
125
  field = str(self.ast_to_code(node.args[1])).replace("'", "")
119
126
  if not IDENTIFIER.match(field):
120
- raise AssertionError("Error parsing struct field access.")
127
+ raise PrettyPrinterError("Error parsing struct field access.")
121
128
  return f"{self.ast_to_code(node.args[0])}.{field}"
122
129
  elif func == "struct_literal":
123
130
  if len(node.args) != 1 or not isinstance(node.args[0], ast.Name):
124
- raise AssertionError("Error parsing struct literal.")
131
+ raise PrettyPrinterError("Error parsing struct literal.")
125
132
  keywords = node.keywords
126
133
  initializer_list = self.indent_items(
127
134
  lambda: [
@@ -133,7 +140,7 @@ class ASTToQMODCode:
133
140
  return f"{self.ast_to_code(node.args[0])} {{{initializer_list}}}"
134
141
  elif func == "do_subscript":
135
142
  if len(node.args) != 2:
136
- raise AssertionError("Error parsing array access.")
143
+ raise PrettyPrinterError("Error parsing array access.")
137
144
  return f"{self.ast_to_code(node.args[0])}[{self.ast_to_code(node.args[1])}]"
138
145
  else:
139
146
  return "{}({})".format(
@@ -142,7 +149,7 @@ class ASTToQMODCode:
142
149
  elif isinstance(node, ast.Expr):
143
150
  return self._cleaned_ast_to_code(node.value)
144
151
  else:
145
- raise AssertionError("Error parsing expression: unsupported AST node.")
152
+ raise PrettyPrinterError("Error parsing expression: unsupported AST node.")
146
153
 
147
154
  def indent_items(self, items: Callable[[], list[str]]) -> str:
148
155
  should_indent = (
@@ -91,3 +91,7 @@ IQCC_LIST_AUTH_TARGETS_FULL_PATH = IQCC_PREFIX + IQCC_LIST_AUTH_TARGETS_SUFFIX
91
91
 
92
92
  USER_BUDGETS_SUFFIX = "/all"
93
93
  USER_BUDGETS_FULL_PATH = USER_BUDGETS_PREFIX + USER_BUDGETS_SUFFIX
94
+ USER_BUDGET_SET_LIMIT_SUFFIX = "/set_limit"
95
+ USER_BUDGET_SET_LIMIT_FULL_PATH = USER_BUDGETS_PREFIX + USER_BUDGET_SET_LIMIT_SUFFIX
96
+ USER_BUDGET_CLEAR_LIMIT_SUFFIX = "/clear_limit"
97
+ USER_BUDGET_CLEAR_LIMIT_FULL_PATH = USER_BUDGETS_PREFIX + USER_BUDGET_CLEAR_LIMIT_SUFFIX
@@ -39,7 +39,6 @@ from classiq.interface.generator.functions.classical_function_declaration import
39
39
  from classiq.interface.generator.functions.classical_type import (
40
40
  Bool,
41
41
  ClassicalArray,
42
- ClassicalList,
43
42
  ClassicalTuple,
44
43
  ClassicalType,
45
44
  OpaqueHandle,
@@ -91,7 +90,7 @@ def qmod_val_to_python(val: ExpressionValue, qmod_type: ClassicalType) -> Any:
91
90
  if isinstance(val, (Enum, int)):
92
91
  return val
93
92
 
94
- elif isinstance(qmod_type, (ClassicalArray, ClassicalList)):
93
+ elif isinstance(qmod_type, ClassicalArray):
95
94
  if isinstance(val, list):
96
95
  return [qmod_val_to_python(elem, qmod_type.element_type) for elem in val]
97
96
 
@@ -145,9 +144,9 @@ def python_val_to_qmod(val: Any, qmod_type: ClassicalType) -> ExpressionValue:
145
144
  field_name: python_val_to_qmod(val[field_name], field_type)
146
145
  for field_name, field_type in struct_decl.variables.items()
147
146
  }
148
- return QmodStructInstance(struct_decl.model_copy(), qmod_dict)
147
+ return QmodStructInstance(struct_decl.model_copy(deep=True), qmod_dict)
149
148
 
150
- if isinstance(qmod_type, (ClassicalList, ClassicalArray)):
149
+ if isinstance(qmod_type, ClassicalArray):
151
150
  if not isinstance(val, list):
152
151
  raise ClassiqInternalExpansionError("Bad value for list")
153
152
  return [python_val_to_qmod(elem, qmod_type.element_type) for elem in val]
@@ -182,7 +181,7 @@ def python_call_wrapper(func: Callable, *args: ExpressionValue) -> Any:
182
181
 
183
182
  def struct_literal(struct_type_symbol: Symbol, **kwargs: Any) -> QmodStructInstance:
184
183
  return QmodStructInstance(
185
- QMODULE.type_decls[struct_type_symbol.name].model_copy(),
184
+ QMODULE.type_decls[struct_type_symbol.name].model_copy(deep=True),
186
185
  {field: sympy_to_python(field_value) for field, field_value in kwargs.items()},
187
186
  )
188
187
 
@@ -330,6 +329,40 @@ def mod_inverse(a: Any, b: Any) -> Any:
330
329
  return sympy.mod_inverse(a, b)
331
330
 
332
331
 
332
+ def min_wrapper(*vals: Any) -> Any:
333
+ try:
334
+ return sympy.Min(*vals)
335
+ except ValueError:
336
+ return AnyClassicalValue(f"Min({', '.join(map(str, vals))})")
337
+
338
+
339
+ min_wrapper.__name__ = "min"
340
+
341
+
342
+ def Min_wrapper(*vals: Any) -> Any: # noqa: N802
343
+ return min_wrapper(*vals)
344
+
345
+
346
+ Min_wrapper.__name__ = "Min"
347
+
348
+
349
+ def max_wrapper(*vals: Any) -> Any:
350
+ try:
351
+ return sympy.Max(*vals)
352
+ except ValueError:
353
+ return AnyClassicalValue(f"Max({', '.join(map(str, vals))})")
354
+
355
+
356
+ max_wrapper.__name__ = "max"
357
+
358
+
359
+ def Max_wrapper(*vals: Any) -> Any: # noqa: N802
360
+ return max_wrapper(*vals)
361
+
362
+
363
+ Max_wrapper.__name__ = "Max"
364
+
365
+
333
366
  CORE_LIB_FUNCTIONS_LIST: list[Callable] = [
334
367
  print,
335
368
  do_sum,
@@ -347,6 +380,10 @@ CORE_LIB_FUNCTIONS_LIST: list[Callable] = [
347
380
  RShift,
348
381
  LShift,
349
382
  mod_inverse,
383
+ min_wrapper,
384
+ Min_wrapper,
385
+ max_wrapper,
386
+ Max_wrapper,
350
387
  ]
351
388
 
352
389