classiq 0.83.0__py3-none-any.whl → 0.85.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 (103) hide show
  1. classiq/_internals/api_wrapper.py +27 -0
  2. classiq/applications/chemistry/chemistry_model_constructor.py +0 -2
  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_problem.py +20 -42
  11. classiq/{model_expansions/evaluators → evaluators}/arg_type_match.py +12 -4
  12. classiq/{model_expansions/evaluators → evaluators}/argument_types.py +1 -1
  13. classiq/evaluators/classical_expression.py +53 -0
  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 +238 -49
  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/debug_info/debug_info.py +0 -4
  24. classiq/interface/execution/primitives.py +29 -1
  25. classiq/interface/executor/estimate_cost.py +35 -0
  26. classiq/interface/executor/execution_result.py +13 -0
  27. classiq/interface/executor/result.py +116 -1
  28. classiq/interface/executor/user_budget.py +26 -33
  29. classiq/interface/generator/expressions/atomic_expression_functions.py +10 -1
  30. classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +0 -6
  31. classiq/interface/generator/functions/builtins/internal_operators.py +2 -0
  32. classiq/interface/generator/functions/classical_type.py +2 -35
  33. classiq/interface/generator/functions/concrete_types.py +20 -3
  34. classiq/interface/generator/functions/type_modifier.py +0 -19
  35. classiq/interface/generator/generated_circuit_data.py +5 -18
  36. classiq/interface/generator/types/compilation_metadata.py +0 -3
  37. classiq/interface/ide/operation_registry.py +45 -0
  38. classiq/interface/ide/visual_model.py +68 -3
  39. classiq/interface/model/bounds.py +12 -2
  40. classiq/interface/model/model.py +12 -7
  41. classiq/interface/model/port_declaration.py +2 -24
  42. classiq/interface/model/quantum_expressions/arithmetic_operation.py +7 -4
  43. classiq/interface/model/variable_declaration_statement.py +33 -6
  44. classiq/interface/pretty_print/__init__.py +0 -0
  45. classiq/{qmod/native → interface/pretty_print}/expression_to_qmod.py +18 -11
  46. classiq/interface/server/routes.py +4 -0
  47. classiq/model_expansions/atomic_expression_functions_defs.py +47 -6
  48. classiq/model_expansions/function_builder.py +4 -1
  49. classiq/model_expansions/interpreters/base_interpreter.py +3 -3
  50. classiq/model_expansions/interpreters/generative_interpreter.py +16 -1
  51. classiq/model_expansions/quantum_operations/allocate.py +1 -1
  52. classiq/model_expansions/quantum_operations/assignment_result_processor.py +64 -22
  53. classiq/model_expansions/quantum_operations/bind.py +2 -2
  54. classiq/model_expansions/quantum_operations/bounds.py +7 -1
  55. classiq/model_expansions/quantum_operations/call_emitter.py +26 -20
  56. classiq/model_expansions/quantum_operations/classical_var_emitter.py +16 -0
  57. classiq/model_expansions/quantum_operations/variable_decleration.py +31 -11
  58. classiq/model_expansions/scope.py +7 -0
  59. classiq/model_expansions/scope_initialization.py +3 -3
  60. classiq/model_expansions/transformers/model_renamer.py +6 -4
  61. classiq/model_expansions/transformers/type_modifier_inference.py +81 -43
  62. classiq/model_expansions/transformers/var_splitter.py +1 -1
  63. classiq/model_expansions/visitors/symbolic_param_inference.py +2 -3
  64. classiq/open_library/functions/__init__.py +3 -2
  65. classiq/open_library/functions/amplitude_amplification.py +10 -18
  66. classiq/open_library/functions/discrete_sine_cosine_transform.py +5 -5
  67. classiq/open_library/functions/grover.py +14 -6
  68. classiq/open_library/functions/modular_exponentiation.py +22 -20
  69. classiq/open_library/functions/qaoa_penalty.py +8 -1
  70. classiq/open_library/functions/state_preparation.py +18 -32
  71. classiq/qmod/__init__.py +2 -0
  72. classiq/qmod/builtins/enums.py +23 -0
  73. classiq/qmod/builtins/functions/__init__.py +2 -0
  74. classiq/qmod/builtins/functions/exponentiation.py +32 -4
  75. classiq/qmod/builtins/operations.py +65 -1
  76. classiq/qmod/builtins/structs.py +55 -3
  77. classiq/qmod/classical_variable.py +74 -0
  78. classiq/qmod/declaration_inferrer.py +3 -2
  79. classiq/qmod/native/pretty_printer.py +20 -20
  80. classiq/qmod/pretty_print/expression_to_python.py +2 -1
  81. classiq/qmod/pretty_print/pretty_printer.py +35 -21
  82. classiq/qmod/python_classical_type.py +12 -5
  83. classiq/qmod/qfunc.py +2 -19
  84. classiq/qmod/qmod_constant.py +2 -5
  85. classiq/qmod/qmod_parameter.py +2 -5
  86. classiq/qmod/qmod_variable.py +61 -23
  87. classiq/qmod/quantum_expandable.py +5 -3
  88. classiq/qmod/quantum_function.py +49 -4
  89. classiq/qmod/semantics/annotation/qstruct_annotator.py +1 -1
  90. classiq/qmod/semantics/validation/main_validation.py +1 -9
  91. classiq/qmod/symbolic_type.py +2 -1
  92. classiq/qmod/utilities.py +0 -2
  93. classiq/qmod/write_qmod.py +1 -1
  94. {classiq-0.83.0.dist-info → classiq-0.85.0.dist-info}/METADATA +4 -1
  95. {classiq-0.83.0.dist-info → classiq-0.85.0.dist-info}/RECORD +101 -90
  96. classiq/interface/model/quantum_variable_declaration.py +0 -7
  97. classiq/model_expansions/evaluators/classical_expression.py +0 -36
  98. /classiq/{model_expansions/evaluators → evaluators}/__init__.py +0 -0
  99. /classiq/{model_expansions/evaluators → evaluators}/control.py +0 -0
  100. /classiq/{model_expansions → evaluators}/expression_evaluator.py +0 -0
  101. /classiq/{model_expansions/evaluators → evaluators}/quantum_type_utils.py +0 -0
  102. /classiq/{model_expansions/evaluators → evaluators}/type_type_match.py +0 -0
  103. {classiq-0.83.0.dist-info → classiq-0.85.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,35 @@
1
+ from typing import Callable
2
+
3
+ import numpy as np
4
+
5
+ from classiq.interface.exceptions import ClassiqValueError
6
+ from classiq.interface.executor.result import ParsedCounts, ParsedState
7
+
8
+
9
+ def estimate_cost(
10
+ cost_func: Callable[[ParsedState], float],
11
+ parsed_counts: ParsedCounts,
12
+ quantile: float = 1.0,
13
+ ) -> float:
14
+ if quantile < 0 or quantile > 1:
15
+ raise ClassiqValueError("'quantile' must be between 0 and 1")
16
+ costs = np.fromiter((cost_func(sample.state) for sample in parsed_counts), float)
17
+ shots = np.fromiter((sample.shots for sample in parsed_counts), int)
18
+
19
+ if quantile == 1:
20
+ return float(np.average(costs, weights=shots))
21
+ return float(estimate_quantile_cost(costs, shots, quantile=quantile))
22
+
23
+
24
+ def estimate_quantile_cost(
25
+ costs: np.ndarray,
26
+ shots: np.ndarray,
27
+ quantile: float,
28
+ ) -> np.floating:
29
+ repeated_costs = np.repeat(costs, shots)
30
+ sort_idx = repeated_costs.argsort()
31
+ cutoff_idx = sort_idx[: int(quantile * len(repeated_costs))]
32
+ sorted_costs = repeated_costs[cutoff_idx]
33
+ if sorted_costs.size == 0:
34
+ sorted_costs = repeated_costs[sort_idx[0]]
35
+ return np.average(sorted_costs)
@@ -25,6 +25,7 @@ class SavedResultValueType(StrEnum):
25
25
  EstimationResult = "EstimationResult"
26
26
  EstimationResults = "EstimationResults"
27
27
  IQAEResult = "IQAEResult"
28
+ MinimizeResult = "MinimizeResult"
28
29
  Unstructured = "Unstructured"
29
30
 
30
31
 
@@ -88,6 +89,17 @@ class TaggedUnstructured(BaseModel):
88
89
  value: Any = None
89
90
 
90
91
 
92
+ class SingleMinimizeResult(BaseModel):
93
+ expectation_value: float
94
+ parameters: list[float]
95
+
96
+
97
+ class TaggedMinimizeResult(BaseModel):
98
+ value_type: Literal[SavedResultValueType.MinimizeResult]
99
+ name: str
100
+ value: list[SingleMinimizeResult]
101
+
102
+
91
103
  SavedResult = Annotated[
92
104
  Union[
93
105
  TaggedInteger,
@@ -100,6 +112,7 @@ SavedResult = Annotated[
100
112
  TaggedEstimationResults,
101
113
  TaggedIQAEResult,
102
114
  TaggedUnstructured,
115
+ TaggedMinimizeResult,
103
116
  ],
104
117
  Field(..., discriminator="value_type"),
105
118
  ]
@@ -10,11 +10,16 @@ from typing import (
10
10
  Union,
11
11
  )
12
12
 
13
+ import pandas as pd
13
14
  import pydantic
14
15
  from pydantic import BaseModel
15
16
  from typing_extensions import Self, TypeAlias
16
17
 
17
- from classiq.interface.exceptions import ClassiqError
18
+ from classiq.interface.exceptions import (
19
+ ClassiqError,
20
+ ClassiqInternalError,
21
+ ClassiqValueError,
22
+ )
18
23
  from classiq.interface.executor.quantum_code import OutputQubitsMap, Qubits
19
24
  from classiq.interface.generator.arith import number_utils
20
25
  from classiq.interface.generator.complex_type import Complex
@@ -22,6 +27,7 @@ from classiq.interface.generator.functions.classical_type import QmodPyObject
22
27
  from classiq.interface.helpers.custom_pydantic_types import PydanticNonNegIntTuple
23
28
  from classiq.interface.helpers.datastructures import get_sdk_compatible_python_object
24
29
  from classiq.interface.helpers.versioned_model import VersionedModel
30
+ from classiq.interface.model.quantum_type import RegisterQuantumTypeDict
25
31
 
26
32
  _ILLEGAL_QUBIT_ERROR_MSG: str = "Illegal qubit index requested"
27
33
  _REPEATED_QUBIT_ERROR_MSG: str = "Requested a qubit more than once"
@@ -36,6 +42,11 @@ ParsedStates: TypeAlias = Mapping[State, ParsedState]
36
42
  Counts: TypeAlias = dict[State, MeasuredShots]
37
43
  StateVector: TypeAlias = Optional[dict[str, Complex]]
38
44
 
45
+ BITSTRING = "bitstring"
46
+ PROBABILITY = "probability"
47
+ AMPLITUDE = "amplitude"
48
+ COUNT = "count"
49
+
39
50
  if TYPE_CHECKING:
40
51
  DotAccessParsedState = Mapping[Name, Any]
41
52
  else:
@@ -150,6 +161,36 @@ def prepare_parsed_state_vector(
150
161
  return sorted(parsed_state_vector, key=lambda k: abs(k.amplitude), reverse=True)
151
162
 
152
163
 
164
+ def _flatten_columns(df: pd.DataFrame, columns_to_flatten: list[str]) -> pd.DataFrame:
165
+ if len(df.columns) == 0:
166
+ return df
167
+
168
+ flattened_data = {}
169
+
170
+ for col in columns_to_flatten:
171
+ if col not in df.columns:
172
+ continue
173
+
174
+ flat_df = pd.json_normalize(df[col]).add_prefix(f"{col}.")
175
+ flat_df.index = df.index
176
+ flattened_data[col] = flat_df
177
+
178
+ new_df_columns = []
179
+ dfs_to_concat = []
180
+
181
+ for col_name in df.columns:
182
+ if col_name in flattened_data:
183
+ new_df_columns.extend(flattened_data[col_name].columns)
184
+ dfs_to_concat.append(flattened_data[col_name])
185
+ elif col_name not in columns_to_flatten:
186
+ new_df_columns.append(col_name)
187
+ dfs_to_concat.append(df[[col_name]])
188
+
189
+ final_df = pd.concat(dfs_to_concat, axis=1)
190
+ valid_final_columns = [c for c in new_df_columns if c in final_df.columns]
191
+ return final_df[valid_final_columns]
192
+
193
+
153
194
  class ExecutionDetails(BaseModel, QmodPyObject):
154
195
  vendor_format_result: dict[str, Any] = pydantic.Field(
155
196
  ..., description="Result in proprietary vendor format"
@@ -192,6 +233,8 @@ class ExecutionDetails(BaseModel, QmodPyObject):
192
233
  default=None, description="The total number of shots the circuit was executed"
193
234
  )
194
235
 
236
+ output_type_map: RegisterQuantumTypeDict = pydantic.Field(default_factory=dict)
237
+
195
238
  @pydantic.field_validator("counts", mode="after")
196
239
  @classmethod
197
240
  def _clean_spaces_from_counts_keys(cls, v: Counts) -> Counts:
@@ -302,6 +345,78 @@ class ExecutionDetails(BaseModel, QmodPyObject):
302
345
  )[::-1]
303
346
  return number_utils.binary_to_float_or_int(bin_rep=register_binary_string)
304
347
 
348
+ def _counts_df(self) -> pd.DataFrame:
349
+ data: dict[str, Any] = defaultdict(list)
350
+
351
+ for bitstring, count in self.counts.items():
352
+ data[BITSTRING].append(bitstring)
353
+ data[COUNT].append(count)
354
+ if self.probabilities:
355
+ data[PROBABILITY].append(self.probabilities[bitstring])
356
+ elif self.num_shots:
357
+ data[PROBABILITY].append(count / self.num_shots)
358
+
359
+ for name, value in self.parsed_states[bitstring].items():
360
+ data[name].append(value)
361
+
362
+ final_columns = [COUNT, PROBABILITY, BITSTRING]
363
+ columns = [
364
+ col for col in data.keys() if col not in final_columns
365
+ ] + final_columns
366
+
367
+ return pd.DataFrame(data, columns=columns)
368
+
369
+ def _state_vector_df(self) -> pd.DataFrame:
370
+ data: dict[str, Any] = defaultdict(list)
371
+
372
+ if not self.state_vector:
373
+ raise ClassiqInternalError("No state vector")
374
+ if not self.parsed_state_vector_states:
375
+ raise ClassiqInternalError("No parsed state vector states")
376
+
377
+ for bitstring, amplitude in self.state_vector.items():
378
+ data[BITSTRING].append(bitstring)
379
+ data[AMPLITUDE].append(amplitude)
380
+ data[PROBABILITY].append(abs(amplitude) ** 2)
381
+ for name, value in self.parsed_state_vector_states[bitstring].items():
382
+ data[name].append(value)
383
+
384
+ final_columns = [AMPLITUDE, PROBABILITY, BITSTRING]
385
+ columns = [
386
+ col for col in data.keys() if col not in final_columns
387
+ ] + final_columns
388
+
389
+ return pd.DataFrame(data, columns=columns)
390
+
391
+ @functools.cached_property
392
+ def dataframe(self) -> pd.DataFrame:
393
+ reserved_words = frozenset([BITSTRING, PROBABILITY, COUNT, AMPLITUDE])
394
+ _invalid_output_names = reserved_words.intersection(
395
+ self.output_qubits_map.keys()
396
+ )
397
+ if _invalid_output_names:
398
+ raise ClassiqValueError(f"Invalid output names: {_invalid_output_names}")
399
+
400
+ if self.state_vector:
401
+ df = self._state_vector_df()
402
+ else:
403
+ df = self._counts_df()
404
+
405
+ df.sort_values(
406
+ by=[AMPLITUDE if self.state_vector else COUNT, BITSTRING],
407
+ inplace=True,
408
+ ascending=[False, True],
409
+ ignore_index=True,
410
+ )
411
+
412
+ outputs_to_flatten = [
413
+ output
414
+ for output, type in self.output_type_map.items()
415
+ if type.quantum_types.kind == "struct_instance"
416
+ ]
417
+ df = _flatten_columns(df, outputs_to_flatten)
418
+ return df
419
+
305
420
 
306
421
  class MultipleExecutionDetails(VersionedModel):
307
422
  details: list[ExecutionDetails]
@@ -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")
@@ -20,10 +20,14 @@ CLASSIQ_EXPR_FUNCTIONS = {
20
20
  "get_field",
21
21
  }
22
22
 
23
+ MEASUREMENT_FUNCTIONS = {
24
+ "measure",
25
+ }
26
+
23
27
  SUPPORTED_CLASSIQ_BUILTIN_FUNCTIONS = {
24
28
  *CLASSIQ_BUILTIN_CLASSICAL_FUNCTIONS,
25
29
  *CLASSIQ_EXPR_FUNCTIONS,
26
- "mod_inverse",
30
+ *MEASUREMENT_FUNCTIONS,
27
31
  }
28
32
 
29
33
  SUPPORTED_CLASSIQ_SYMPY_WRAPPERS = {
@@ -34,6 +38,11 @@ SUPPORTED_CLASSIQ_SYMPY_WRAPPERS = {
34
38
  "LogicalXor",
35
39
  "RShift",
36
40
  "LShift",
41
+ "mod_inverse",
42
+ "min",
43
+ "Min",
44
+ "max",
45
+ "Max",
37
46
  }
38
47
 
39
48
  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)}]")
@@ -6,6 +6,7 @@ CLASSICAL_IF_OPERATOR_NAME = "classical_if"
6
6
  POWER_OPERATOR_NAME = "power"
7
7
  UNCOMPUTE_OPERATOR_NAME = "uncompute"
8
8
  WITHIN_APPLY_NAME = "within_apply"
9
+ BLOCK_OPERATOR_NAME = "block"
9
10
 
10
11
  All_BUILTINS_OPERATORS = {
11
12
  CONTROL_OPERATOR_NAME,
@@ -14,4 +15,5 @@ All_BUILTINS_OPERATORS = {
14
15
  POWER_OPERATOR_NAME,
15
16
  UNCOMPUTE_OPERATOR_NAME,
16
17
  WITHIN_APPLY_NAME,
18
+ BLOCK_OPERATOR_NAME,
17
19
  }
@@ -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
 
@@ -56,3 +53,23 @@ QuantumBitvector.model_rebuild()
56
53
  TypeName.model_rebuild()
57
54
  QStructDeclaration.model_rebuild()
58
55
  RegisterQuantumType.model_rebuild()
56
+
57
+ ConcreteType = Annotated[
58
+ Union[
59
+ Integer,
60
+ Real,
61
+ Bool,
62
+ StructMetaType,
63
+ TypeName,
64
+ ClassicalArray,
65
+ ClassicalTuple,
66
+ VQEResult,
67
+ Histogram,
68
+ Estimation,
69
+ IQAERes,
70
+ QuantumBit,
71
+ QuantumBitvector,
72
+ QuantumNumeric,
73
+ ],
74
+ Field(discriminator="kind"),
75
+ ]
@@ -2,25 +2,6 @@ from classiq.interface.enum_utils import StrEnum
2
2
  from classiq.interface.exceptions import ClassiqInternalExpansionError
3
3
 
4
4
 
5
- class TypeQualifier(StrEnum):
6
- Const = "const"
7
- QFree = "qfree"
8
- Quantum = "quantum"
9
- Inferred = "inferred"
10
-
11
- def to_modifier(self) -> "TypeModifier":
12
- if self is TypeQualifier.Const:
13
- return TypeModifier.Const
14
- elif self is TypeQualifier.QFree:
15
- return TypeModifier.Permutable
16
- elif self is TypeQualifier.Quantum:
17
- return TypeModifier.Mutable
18
- elif self is TypeQualifier.Inferred:
19
- return TypeModifier.Inferred
20
- else:
21
- raise ClassiqInternalExpansionError(f"Unexpected type qualifier: {self}")
22
-
23
-
24
5
  class TypeModifier(StrEnum):
25
6
  Const = "const"
26
7
  Permutable = "permutable"
@@ -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
 
@@ -54,8 +52,6 @@ VISUALIZATION_HIDE_LIST = [
54
52
  "stmt_block",
55
53
  ]
56
54
 
57
- CONTROLLED_PREFIX = "c_"
58
-
59
55
 
60
56
  def last_name_in_call_hierarchy(name: str) -> str:
61
57
  return name.split(CLASSIQ_HIERARCHY_SEPARATOR)[-1]
@@ -85,12 +81,6 @@ class GeneratedRegister(pydantic.BaseModel):
85
81
  def is_captured(self) -> bool:
86
82
  return is_captured_var_name(self.name)
87
83
 
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
84
 
95
85
  class GeneratedFunction(pydantic.BaseModel):
96
86
  name: str
@@ -155,6 +145,7 @@ class StatementType(StrEnum):
155
145
  INPLACE_XOR = "inplace xor"
156
146
  INPLACE_ADD = "inplace add"
157
147
  REPEAT = "repeat"
148
+ BLOCK = "block"
158
149
 
159
150
 
160
151
  # Mapping between statement kind (or sub-kind) and statement type (visualization name)
@@ -175,6 +166,7 @@ STATEMENTS_NAME: dict[str, StatementType] = {
175
166
  ArithmeticOperationKind.InplaceXor.value: StatementType.INPLACE_XOR,
176
167
  ArithmeticOperationKind.InplaceAdd.value: StatementType.INPLACE_ADD,
177
168
  "Repeat": StatementType.REPEAT,
169
+ "Block": StatementType.BLOCK,
178
170
  }
179
171
 
180
172
 
@@ -215,8 +207,7 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
215
207
  ARITH_ENGINE_PREFIX
216
208
  )
217
209
  name_with_suffix = self.add_suffix_from_generated_name(generated_name, name)
218
- modified_name = self.modify_name_for_controlled_qfunc(name_with_suffix)
219
- return modified_name
210
+ return name_with_suffix
220
211
 
221
212
  statement_kind: str = back_ref.kind
222
213
  if isinstance(back_ref, ArithmeticOperation):
@@ -225,11 +216,6 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
225
216
  generated_name, STATEMENTS_NAME[statement_kind]
226
217
  )
227
218
 
228
- def modify_name_for_controlled_qfunc(self, generated_name: str) -> str:
229
- if self.control_variable is None:
230
- return generated_name
231
- return f"{CONTROLLED_PREFIX}{generated_name}"
232
-
233
219
  def add_suffix_from_generated_name(self, generated_name: str, name: str) -> str:
234
220
  if part_match := PART_SUFFIX_REGEX.match(generated_name):
235
221
  suffix = f" [{part_match.group(1)}]"
@@ -280,7 +266,8 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
280
266
  for register in self.registers
281
267
  for qubit in register.qubit_indexes_absolute
282
268
  if register.role is RegisterRole.INPUT
283
- and register.name == self.control_variable
269
+ and self.port_to_passed_variable_map.get(register.name, register.name)
270
+ == self.control_variable
284
271
  )
285
272
 
286
273
  def propagate_absolute_qubits(self) -> "FunctionDebugInfoInterface":
@@ -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
12
9
 
13
10
  @property
14
11
  def occupation_number(self) -> NonNegativeInt:
@@ -0,0 +1,45 @@
1
+ from typing import Any
2
+
3
+ from classiq.interface.ide.visual_model import Operation
4
+
5
+
6
+ class OperationRegistry:
7
+ def __init__(self) -> None:
8
+ self._operation_hash_to_op_id: dict[int, int] = {}
9
+ self._id_to_operations: dict[int, Operation] = {}
10
+ self._unique_op_counter = 0
11
+ self._deduped_op_counter = 0
12
+
13
+ def build_operation(self, **kwargs: Any) -> Operation:
14
+ operation = Operation(**kwargs)
15
+ return self.add_operation(operation)
16
+
17
+ def add_operation(self, op: Operation) -> Operation:
18
+ """
19
+ Adds an operation to the global dictionaries for operations.
20
+ if operation already exist in the registry, it returns the existing operation.
21
+ """
22
+ op_hash = hash(op)
23
+ if op_hash not in self._operation_hash_to_op_id:
24
+ self._operation_hash_to_op_id[op_hash] = op.id
25
+ self._id_to_operations[op.id] = op
26
+ self._unique_op_counter += 1
27
+ else:
28
+ self._deduped_op_counter += 1
29
+ op = self._id_to_operations[self._operation_hash_to_op_id[op_hash]]
30
+ return op
31
+
32
+ def get_operation_mapping(self) -> dict[int, Operation]:
33
+ return self._id_to_operations
34
+
35
+ def get_operations(self, op_ids: list[int]) -> list[Operation]:
36
+ """
37
+ Returns a list of operations based on their IDs.
38
+ """
39
+ return [self._id_to_operations[op_id] for op_id in op_ids]
40
+
41
+ def get_unique_op_number(self) -> int:
42
+ return self._unique_op_counter
43
+
44
+ def get_deduped_op_number(self) -> int:
45
+ return self._deduped_op_counter