classiq 0.102.0__py3-none-any.whl → 0.104.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 (65) hide show
  1. classiq/applications/chemistry/op_utils.py +32 -0
  2. classiq/evaluators/qmod_annotated_expression.py +1 -1
  3. classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +1 -8
  4. classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +1 -1
  5. classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +2 -2
  6. classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +18 -29
  7. classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +1 -6
  8. classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +1 -7
  9. classiq/evaluators/qmod_type_inference/quantum_type_comparison.py +52 -0
  10. classiq/execution/execution_session.py +1 -1
  11. classiq/execution/functions/__init__.py +3 -0
  12. classiq/execution/functions/_logging.py +19 -0
  13. classiq/execution/functions/constants.py +9 -0
  14. classiq/execution/functions/parse_provider_backend.py +90 -0
  15. classiq/execution/functions/sample.py +257 -0
  16. classiq/interface/_version.py +1 -1
  17. classiq/interface/backend/backend_preferences.py +15 -0
  18. classiq/interface/backend/provider_config/providers/aqt.py +1 -1
  19. classiq/interface/backend/provider_config/providers/azure.py +1 -2
  20. classiq/interface/backend/provider_config/providers/ibm.py +1 -1
  21. classiq/interface/backend/quantum_backend_providers.py +3 -0
  22. classiq/interface/executor/result.py +9 -5
  23. classiq/interface/generator/arith/binary_ops.py +38 -2
  24. classiq/interface/generator/function_param_list.py +4 -2
  25. classiq/interface/generator/functions/builtins/internal_operators.py +5 -9
  26. classiq/interface/generator/functions/classical_type.py +45 -0
  27. classiq/interface/generator/functions/type_name.py +23 -0
  28. classiq/interface/generator/generated_circuit_data.py +0 -2
  29. classiq/interface/generator/types/compilation_metadata.py +9 -0
  30. classiq/interface/hardware.py +1 -0
  31. classiq/interface/helpers/model_normalizer.py +42 -6
  32. classiq/interface/interface_version.py +1 -1
  33. classiq/interface/model/invert.py +8 -0
  34. classiq/interface/model/model_visitor.py +4 -2
  35. classiq/interface/model/quantum_type.py +21 -0
  36. classiq/interface/model/statement_block.py +0 -4
  37. classiq/model_expansions/capturing/captured_vars.py +16 -12
  38. classiq/model_expansions/function_builder.py +9 -1
  39. classiq/model_expansions/interpreters/base_interpreter.py +9 -8
  40. classiq/model_expansions/interpreters/generative_interpreter.py +9 -24
  41. classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +1 -0
  42. classiq/model_expansions/quantum_operations/assignment_result_processor.py +132 -28
  43. classiq/model_expansions/quantum_operations/bind.py +4 -0
  44. classiq/model_expansions/quantum_operations/call_emitter.py +5 -35
  45. classiq/model_expansions/quantum_operations/emitter.py +1 -4
  46. classiq/model_expansions/quantum_operations/expression_evaluator.py +0 -3
  47. classiq/model_expansions/visitors/uncomputation_signature_inference.py +0 -9
  48. classiq/qmod/builtins/functions/__init__.py +9 -0
  49. classiq/qmod/builtins/functions/arithmetic.py +131 -0
  50. classiq/qmod/builtins/functions/exponentiation.py +32 -2
  51. classiq/qmod/builtins/operations.py +2 -38
  52. classiq/qmod/native/pretty_printer.py +1 -12
  53. classiq/qmod/pretty_print/pretty_printer.py +1 -17
  54. classiq/qmod/qmod_parameter.py +4 -0
  55. classiq/qmod/qmod_variable.py +38 -63
  56. classiq/qmod/quantum_function.py +43 -7
  57. classiq/qmod/semantics/validation/function_name_collisions_validation.py +7 -4
  58. classiq/qmod/semantics/validation/model_validation.py +7 -2
  59. classiq/qmod/symbolic_type.py +4 -2
  60. {classiq-0.102.0.dist-info → classiq-0.104.0.dist-info}/METADATA +1 -1
  61. {classiq-0.102.0.dist-info → classiq-0.104.0.dist-info}/RECORD +63 -59
  62. classiq/interface/generator/amplitude_loading.py +0 -103
  63. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +0 -77
  64. {classiq-0.102.0.dist-info → classiq-0.104.0.dist-info}/WHEEL +0 -0
  65. {classiq-0.102.0.dist-info → classiq-0.104.0.dist-info}/licenses/LICENSE.txt +0 -0
@@ -0,0 +1,257 @@
1
+ from collections.abc import Callable
2
+ from dataclasses import dataclass
3
+ from typing import TYPE_CHECKING, Any, Literal
4
+
5
+ if TYPE_CHECKING:
6
+ from pandas import DataFrame
7
+
8
+ from classiq.interface.backend.backend_preferences import (
9
+ AliceBobBackendPreferences,
10
+ AQTBackendPreferences,
11
+ AwsBackendPreferences,
12
+ AzureBackendPreferences,
13
+ AzureCredential,
14
+ BackendPreferencesTypes,
15
+ ClassiqBackendPreferences,
16
+ GCPBackendPreferences,
17
+ IBMBackendPreferences,
18
+ IntelBackendPreferences,
19
+ IonqBackendPreferences,
20
+ )
21
+ from classiq.interface.backend.provider_config.provider_config import ProviderConfig
22
+ from classiq.interface.backend.provider_config.providers.alice_bob import AliceBobConfig
23
+ from classiq.interface.backend.provider_config.providers.aqt import AQTConfig
24
+ from classiq.interface.backend.provider_config.providers.azure import AzureConfig
25
+ from classiq.interface.backend.provider_config.providers.braket import BraketConfig
26
+ from classiq.interface.backend.provider_config.providers.ibm import IBMConfig
27
+ from classiq.interface.backend.provider_config.providers.ionq import IonQConfig
28
+ from classiq.interface.backend.quantum_backend_providers import (
29
+ ClassiqNvidiaBackendNames,
30
+ ClassiqSimulatorBackendNames,
31
+ )
32
+ from classiq.interface.executor.execution_preferences import ExecutionPreferences
33
+ from classiq.interface.generator.model.preferences import create_random_seed
34
+ from classiq.interface.generator.model.preferences.preferences import (
35
+ TranspilationOption,
36
+ )
37
+ from classiq.interface.hardware import Provider
38
+
39
+ from classiq import (
40
+ ExecutionParams,
41
+ QuantumProgram,
42
+ )
43
+ from classiq.execution import ExecutionSession
44
+ from classiq.execution.functions._logging import _logger
45
+ from classiq.execution.functions.constants import Verbosity
46
+ from classiq.execution.functions.parse_provider_backend import (
47
+ _PROVIDER_TO_CANONICAL_NAME,
48
+ _parse_provider_backend,
49
+ )
50
+
51
+
52
+ @dataclass
53
+ class _ProviderConfigToBackendPrefSpec:
54
+ backend_preferences_class: type[BackendPreferencesTypes]
55
+ config_class: type[ProviderConfig] | None = None
56
+ # Maps the config dict (either passed in directly or dumped from config class) to a
57
+ # dict that we can load into the given BackendPreferences class. This is in case
58
+ # we need to rename fields or change structure.
59
+ config_dict_to_backend_preferences_dict: (
60
+ Callable[[dict[str, Any]], dict[str, Any]] | None
61
+ ) = None
62
+ # Maps from SDK names to names our backend recognizes, raising a useful error
63
+ # if the name is unrecognized.
64
+ backend_name_mapper: Callable[[str], str] | None = None
65
+
66
+
67
+ def _classiq_backend_name_mapper(backend_name: str) -> str:
68
+ backend_name = backend_name.lower()
69
+ if backend_name in [
70
+ ClassiqSimulatorBackendNames.SIMULATOR,
71
+ ClassiqSimulatorBackendNames.SIMULATOR_MATRIX_PRODUCT_STATE,
72
+ ClassiqSimulatorBackendNames.SIMULATOR_DENSITY_MATRIX,
73
+ ]:
74
+ return backend_name
75
+ if backend_name == "nvidia_simulator":
76
+ return ClassiqNvidiaBackendNames.SIMULATOR
77
+ if any(keyword in backend_name for keyword in ["gpu", "nvidia"]):
78
+ suggested_backend_name = "nvidia_simulator"
79
+ else:
80
+ suggested_backend_name = "simulator"
81
+ raise ValueError(
82
+ f"Unsupported backend name {backend_name}. Did you mean '{suggested_backend_name}'?"
83
+ )
84
+
85
+
86
+ def _ibm_backend_name_mapper(backend_name: str) -> str:
87
+ ibm_prefix: Literal["ibm_"] = "ibm_"
88
+ backend_name = backend_name.lower()
89
+ if backend_name.startswith(ibm_prefix):
90
+ backend_name_no_prefix = backend_name.removeprefix(ibm_prefix)
91
+ raise ValueError(
92
+ f"IBM backend names shouldn't start with ibm_. Try 'ibm/{backend_name_no_prefix}'."
93
+ )
94
+ return ibm_prefix + backend_name
95
+
96
+
97
+ def _azure_config_dict_to_backend_preferences_dict(
98
+ config_dict: dict[str, Any],
99
+ ) -> dict[str, Any]:
100
+ if "location" not in config_dict:
101
+ raise ValueError("Azure config must have 'location' property")
102
+ credentials = None
103
+ if all(
104
+ config_dict.get(key) is not None
105
+ for key in ["tenant_id", "client_id", "client_secret", "resource_id"]
106
+ ):
107
+ credentials = AzureCredential.model_validate(
108
+ {
109
+ "tenant_id": config_dict["tenant_id"],
110
+ "client_id": config_dict["client_id"],
111
+ "client_secret": config_dict["client_secret"],
112
+ "resource_id": config_dict["resource_id"],
113
+ }
114
+ )
115
+ return {
116
+ "location": config_dict["location"],
117
+ "credentials": credentials,
118
+ "ionq_error_mitigation_flag": config_dict.get("ionq_error_mitigation"),
119
+ }
120
+
121
+
122
+ def _braket_config_dict_to_backend_preferences_dict(
123
+ config_dict: dict[str, Any],
124
+ ) -> dict[str, Any]:
125
+ config_dict["aws_access_key_id"] = config_dict.pop("braket_access_key_id", None)
126
+ config_dict["aws_secret_access_key"] = config_dict.pop(
127
+ "braket_secret_access_key", None
128
+ )
129
+ return config_dict
130
+
131
+
132
+ _PROVIDER_CONFIG_TO_BACKEND_PREFERENCES_SPEC = {
133
+ Provider.CLASSIQ: _ProviderConfigToBackendPrefSpec(
134
+ backend_preferences_class=ClassiqBackendPreferences,
135
+ backend_name_mapper=_classiq_backend_name_mapper,
136
+ ),
137
+ Provider.GOOGLE: _ProviderConfigToBackendPrefSpec(
138
+ backend_preferences_class=GCPBackendPreferences
139
+ ),
140
+ Provider.INTEL: _ProviderConfigToBackendPrefSpec(
141
+ backend_preferences_class=IntelBackendPreferences
142
+ ),
143
+ Provider.IBM_QUANTUM: _ProviderConfigToBackendPrefSpec(
144
+ backend_preferences_class=IBMBackendPreferences,
145
+ config_class=IBMConfig,
146
+ backend_name_mapper=_ibm_backend_name_mapper,
147
+ ),
148
+ Provider.AMAZON_BRAKET: _ProviderConfigToBackendPrefSpec(
149
+ backend_preferences_class=AwsBackendPreferences,
150
+ config_dict_to_backend_preferences_dict=_braket_config_dict_to_backend_preferences_dict,
151
+ config_class=BraketConfig,
152
+ ),
153
+ Provider.IONQ: _ProviderConfigToBackendPrefSpec(
154
+ backend_preferences_class=IonqBackendPreferences,
155
+ config_class=IonQConfig,
156
+ ),
157
+ Provider.ALICE_AND_BOB: _ProviderConfigToBackendPrefSpec(
158
+ backend_preferences_class=AliceBobBackendPreferences,
159
+ config_class=AliceBobConfig,
160
+ ),
161
+ Provider.AQT: _ProviderConfigToBackendPrefSpec(
162
+ backend_preferences_class=AQTBackendPreferences,
163
+ config_class=AQTConfig,
164
+ ),
165
+ Provider.AZURE_QUANTUM: _ProviderConfigToBackendPrefSpec(
166
+ backend_preferences_class=AzureBackendPreferences,
167
+ config_dict_to_backend_preferences_dict=_azure_config_dict_to_backend_preferences_dict,
168
+ config_class=AzureConfig,
169
+ ),
170
+ }
171
+
172
+
173
+ def _get_backend_preferences_from_specifier(
174
+ backend_spec: str, config: dict[str, Any] | ProviderConfig
175
+ ) -> BackendPreferencesTypes:
176
+ provider, backend_name = _parse_provider_backend(backend_spec)
177
+
178
+ if provider not in _PROVIDER_CONFIG_TO_BACKEND_PREFERENCES_SPEC:
179
+ raise NotImplementedError(
180
+ f"Unsupported provider '{_PROVIDER_TO_CANONICAL_NAME.get(provider) or provider}'"
181
+ )
182
+
183
+ provider_spec = _PROVIDER_CONFIG_TO_BACKEND_PREFERENCES_SPEC[provider]
184
+ if isinstance(config, ProviderConfig):
185
+ if provider_spec.config_class is None:
186
+ raise ValueError(
187
+ f"This provider does not support any ProviderConfig classes. Received '{config.__class__.__name__}'"
188
+ )
189
+ if not isinstance(config, provider_spec.config_class):
190
+ raise ValueError(
191
+ f"{_PROVIDER_TO_CANONICAL_NAME[provider]} devices require {provider_spec.config_class.__name__}, got {config.__class__.__name__}"
192
+ )
193
+ config_dict = config.model_dump()
194
+ else:
195
+ config_dict = config
196
+ if provider_spec.backend_name_mapper is not None:
197
+ backend_name = provider_spec.backend_name_mapper(backend_name)
198
+
199
+ if provider_spec.config_dict_to_backend_preferences_dict is not None:
200
+ config_dict = provider_spec.config_dict_to_backend_preferences_dict(config_dict)
201
+
202
+ config_dict["backend_name"] = backend_name
203
+ return provider_spec.backend_preferences_class.model_validate(config_dict)
204
+
205
+
206
+ _DEFAULT_BACKEND_NAME = "simulator"
207
+
208
+
209
+ def _new_sample(
210
+ qprog: QuantumProgram,
211
+ backend: str | None = None,
212
+ *,
213
+ parameters: ExecutionParams | None = None,
214
+ config: dict[str, Any] | ProviderConfig | None = None,
215
+ num_shots: int | None = None,
216
+ random_seed: int | None = None,
217
+ transpilation_option: TranspilationOption = TranspilationOption.DECOMPOSE,
218
+ verbosity: Verbosity = Verbosity.INFO,
219
+ ) -> "DataFrame":
220
+ """
221
+ Sample a quantum program.
222
+
223
+ Args:
224
+ qprog: The quantum program
225
+ backend: The device (hardware or simulator) on which to run the quantum program. Specified as "provider/device_id"
226
+ parameters: The classical parameters for the quantum program
227
+ config: Provider-specific configuration, such as api keys
228
+ num_shots: The number of times to sample
229
+ random_seed: The random seed used for transpilation and simulation
230
+ transpilation_option: Advanced configuration for hardware-specific transpilation
231
+ verbosity: What level of information should be logged
232
+
233
+ Returns: A dataframe containing the histogram
234
+ """
235
+ if num_shots is not None and num_shots < 1:
236
+ raise ValueError(f"Argument num_shots must be greater than 0, got {num_shots}")
237
+ if config is None:
238
+ config = {}
239
+ if backend is None:
240
+ backend = _DEFAULT_BACKEND_NAME
241
+ backend_preferences = _get_backend_preferences_from_specifier(backend, config)
242
+ ep = ExecutionPreferences(
243
+ backend_preferences=backend_preferences,
244
+ num_shots=num_shots,
245
+ random_seed=create_random_seed() if random_seed is None else random_seed,
246
+ transpile_to_hardware=transpilation_option,
247
+ )
248
+ if verbosity != Verbosity.QUIET:
249
+ _logger.info(f"Submitting job to {backend}")
250
+ with ExecutionSession(qprog, execution_preferences=ep) as session:
251
+ job = session.submit_sample(parameters)
252
+ if verbosity != Verbosity.QUIET:
253
+ _logger.info(f"Job id: {job.id}")
254
+ result = job.get_sample_result()
255
+
256
+ df = result.dataframe
257
+ return df
@@ -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.102.0'
6
+ SEMVER_VERSION = '0.104.0'
7
7
  VERSION = str(Version(SEMVER_VERSION))
@@ -454,6 +454,20 @@ class CINECABackendPreferences(BackendPreferences):
454
454
  )
455
455
 
456
456
 
457
+ class SoftbankBackendPreferences(BackendPreferences):
458
+ """
459
+ Represents the backend preferences specific to Softbank.
460
+ """
461
+
462
+ backend_service_provider: ProviderTypeVendor.SOFTBANK = pydantic.Field(
463
+ default=ProviderVendor.SOFTBANK
464
+ )
465
+
466
+ priority: int | None = pydantic.Field(
467
+ default=None, description="Priority of the job"
468
+ )
469
+
470
+
457
471
  def is_exact_simulator(backend_preferences: BackendPreferences) -> bool:
458
472
  return backend_preferences.backend_name in EXACT_SIMULATORS
459
473
 
@@ -486,6 +500,7 @@ BackendPreferencesTypes = Union[
486
500
  IntelBackendPreferences,
487
501
  AQTBackendPreferences,
488
502
  CINECABackendPreferences,
503
+ SoftbankBackendPreferences,
489
504
  ]
490
505
 
491
506
  __all__ = [
@@ -12,5 +12,5 @@ class AQTConfig(ProviderConfig):
12
12
  workspace: The AQT workspace where the simulator/hardware is located.
13
13
  """
14
14
 
15
- api_key: str | None = pydantic.Field(default=None, description="AQT API key")
15
+ api_key: str = pydantic.Field(description="AQT API key")
16
16
  workspace: str = pydantic.Field(description="AQT workspace")
@@ -1,6 +1,5 @@
1
1
  import pydantic
2
2
 
3
- from classiq.interface.backend import pydantic_backend
4
3
  from classiq.interface.backend.provider_config.provider_config import ProviderConfig
5
4
 
6
5
 
@@ -26,7 +25,7 @@ class AzureConfig(ProviderConfig):
26
25
  client_secret: str | None = pydantic.Field(
27
26
  default=None, description="Azure Client Secret"
28
27
  )
29
- resource_id: pydantic_backend.PydanticAzureResourceIDType | None = pydantic.Field(
28
+ resource_id: str | None = pydantic.Field(
30
29
  default=None,
31
30
  description="Azure Resource ID (including Azure subscription ID, resource "
32
31
  "group and workspace), for personal resource",
@@ -10,7 +10,7 @@ class IBMConfig(ProviderConfig):
10
10
  Attributes:
11
11
  access_token (str | None): The IBM Cloud access token to be used with IBM Quantum hosted backends. Defaults to `None`.
12
12
  channel (str): Channel to use for IBM cloud backends. Defaults to `"ibm_cloud"`.
13
- instance_crn (str): The IBM Cloud instance CRN (Cloud Resource Name) for the IBM Quantum service.
13
+ instance_crn (str | None): The IBM Cloud instance CRN (Cloud Resource Name) for the IBM Quantum service.
14
14
  """
15
15
 
16
16
  access_token: str | None = pydantic.Field(
@@ -27,6 +27,7 @@ class ProviderVendor(StrEnum):
27
27
  INTEL = "Intel"
28
28
  AQT = "AQT"
29
29
  CINECA = "CINECA"
30
+ SOFTBANK = "Softbank"
30
31
 
31
32
 
32
33
  class ProviderTypeVendor:
@@ -41,6 +42,7 @@ class ProviderTypeVendor:
41
42
  INTEL = Literal[ProviderVendor.INTEL]
42
43
  AQT = Literal[ProviderVendor.AQT]
43
44
  CINECA = Literal[ProviderVendor.CINECA]
45
+ SOFTBANK = Literal[ProviderVendor.SOFTBANK]
44
46
 
45
47
 
46
48
  PROVIDER_NAME_MAPPER = {
@@ -54,6 +56,7 @@ PROVIDER_NAME_MAPPER = {
54
56
  ProviderVendor.INTEL: "INTEL",
55
57
  ProviderVendor.AQT: "AQT",
56
58
  ProviderVendor.CLASSIQ: "CLASSIQ",
59
+ ProviderVendor.SOFTBANK: "SOFTBANK",
57
60
  }
58
61
 
59
62
 
@@ -47,7 +47,7 @@ StateVector: TypeAlias = Optional[dict[str, Complex]]
47
47
  BITSTRING = "bitstring"
48
48
  PROBABILITY = "probability"
49
49
  AMPLITUDE = "amplitude"
50
- COUNT = "count"
50
+ COUNTS = "counts"
51
51
  MAGNITUDE = "magnitude"
52
52
  PHASE = "phase"
53
53
 
@@ -374,7 +374,9 @@ class ExecutionDetails(BaseModel, QmodPyObject):
374
374
 
375
375
  for bitstring, count in self.counts.items():
376
376
  data[BITSTRING].append(bitstring)
377
- data[COUNT].append(count)
377
+ # TODO CLS-4767: delete "count"
378
+ data["count"].append(count)
379
+ data[COUNTS].append(count)
378
380
  if self.probabilities:
379
381
  data[PROBABILITY].append(self.probabilities[bitstring])
380
382
  elif self.num_shots:
@@ -383,7 +385,8 @@ class ExecutionDetails(BaseModel, QmodPyObject):
383
385
  for name, value in self.parsed_states[bitstring].items():
384
386
  data[name].append(value)
385
387
 
386
- final_columns = [COUNT, PROBABILITY, BITSTRING]
388
+ # TODO CLS-4767: delete "count"
389
+ final_columns = [COUNTS, "count", PROBABILITY, BITSTRING]
387
390
  columns = [
388
391
  col for col in data.keys() if col not in final_columns
389
392
  ] + final_columns
@@ -419,8 +422,9 @@ class ExecutionDetails(BaseModel, QmodPyObject):
419
422
 
420
423
  @functools.cached_property
421
424
  def dataframe(self) -> "pd.DataFrame":
425
+ # TODO CLS-4767: remove "count"
422
426
  reserved_words = frozenset(
423
- [BITSTRING, PROBABILITY, COUNT, AMPLITUDE, PHASE, MAGNITUDE]
427
+ ["count", BITSTRING, PROBABILITY, COUNTS, AMPLITUDE, PHASE, MAGNITUDE]
424
428
  )
425
429
  _invalid_output_names = reserved_words.intersection(
426
430
  self.output_qubits_map.keys()
@@ -436,7 +440,7 @@ class ExecutionDetails(BaseModel, QmodPyObject):
436
440
  df = self._counts_df()
437
441
 
438
442
  df.sort_values(
439
- by=[AMPLITUDE if self.state_vector else COUNT, BITSTRING],
443
+ by=[AMPLITUDE if self.state_vector else COUNTS, BITSTRING],
440
444
  inplace=True,
441
445
  ascending=[False, True],
442
446
  ignore_index=True,
@@ -29,7 +29,10 @@ from classiq.interface.generator.arith.ast_node_rewrite import (
29
29
  )
30
30
  from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
31
31
  from classiq.interface.generator.arith.unary_ops import Negation
32
- from classiq.interface.generator.function_params import get_zero_input_name
32
+ from classiq.interface.generator.function_params import (
33
+ FunctionParams,
34
+ get_zero_input_name,
35
+ )
33
36
 
34
37
  from classiq.model_expansions.arithmetic import NumericAttributes
35
38
  from classiq.model_expansions.arithmetic_compute_result_attrs import (
@@ -533,10 +536,43 @@ class Multiplier(BinaryOpWithFloatInputs):
533
536
 
534
537
  def garbage_output_size(self) -> pydantic.NonNegativeInt:
535
538
  return max(
536
- 0, self.expected_fraction_places() - self.result_register.fraction_places
539
+ 0,
540
+ self.expected_fraction_places() - self.result_register.fraction_places - 1,
537
541
  )
538
542
 
539
543
 
544
+ class CanonicalMultiplier(FunctionParams):
545
+ left_size: pydantic.PositiveInt
546
+ extend_left: bool
547
+ right_size: pydantic.PositiveInt
548
+ extend_right: bool
549
+ result_size: pydantic.PositiveInt
550
+ trim_result_lsb: bool
551
+
552
+ def _create_ios(self) -> None:
553
+ self._inputs = {
554
+ "left": RegisterArithmeticInfo(size=self.left_size),
555
+ "right": RegisterArithmeticInfo(size=self.right_size),
556
+ "result": RegisterArithmeticInfo(size=self.result_size),
557
+ }
558
+ self._outputs = {**self._inputs}
559
+
560
+
561
+ class CanonicalConstantMultiplier(FunctionParams):
562
+ left: int
563
+ right_size: pydantic.PositiveInt
564
+ extend_right: bool
565
+ result_size: pydantic.PositiveInt
566
+ trim_result_lsb: bool
567
+
568
+ def _create_ios(self) -> None:
569
+ self._inputs = {
570
+ "right": RegisterArithmeticInfo(size=self.right_size),
571
+ "result": RegisterArithmeticInfo(size=self.result_size),
572
+ }
573
+ self._outputs = {**self._inputs}
574
+
575
+
540
576
  class Comparator(BinaryOpWithFloatInputs):
541
577
  output_size: Literal[1] = 1
542
578
 
@@ -1,12 +1,13 @@
1
1
  import itertools
2
2
 
3
- from classiq.interface.generator.amplitude_loading import AmplitudeLoading
4
3
  from classiq.interface.generator.arith.arithmetic import Arithmetic
5
4
  from classiq.interface.generator.arith.binary_ops import (
6
5
  Adder,
7
6
  BitwiseAnd,
8
7
  BitwiseOr,
9
8
  BitwiseXor,
9
+ CanonicalConstantMultiplier,
10
+ CanonicalMultiplier,
10
11
  Equal,
11
12
  GreaterEqual,
12
13
  GreaterThan,
@@ -79,6 +80,8 @@ function_param_library: FunctionParamLibrary = FunctionParamLibrary(
79
80
  HardwareEfficientAnsatz,
80
81
  UnitaryGate,
81
82
  Multiplier,
83
+ CanonicalMultiplier,
84
+ CanonicalConstantMultiplier,
82
85
  Power,
83
86
  Min,
84
87
  Max,
@@ -88,7 +91,6 @@ function_param_library: FunctionParamLibrary = FunctionParamLibrary(
88
91
  Identity,
89
92
  RandomizedBenchmarking,
90
93
  UGate,
91
- AmplitudeLoading,
92
94
  HadamardTransform,
93
95
  Copy,
94
96
  Reset,
@@ -1,7 +1,8 @@
1
1
  CTRL_VAR_PREFIX = "ctrl__"
2
2
  CONTROL_OPERATOR_NAME = "control"
3
3
  SKIP_CONTROL_OPERATOR_NAME = "skip_control"
4
- INVERT_OPERATOR_NAME = "invert"
4
+ COMPOUND_INVERT_OPERATOR_NAME = "compound_invert"
5
+ SINGLE_CALL_INVERT_OPERATOR_NAME = "single_call_invert"
5
6
  REPEAT_OPERATOR_NAME = "iteration"
6
7
  CLASSICAL_IF_OPERATOR_NAME = "classical_if"
7
8
  POWER_OPERATOR_NAME = "power"
@@ -9,12 +10,7 @@ UNCOMPUTE_OPERATOR_NAME = "uncompute"
9
10
  WITHIN_APPLY_NAME = "within_apply"
10
11
  BLOCK_OPERATOR_NAME = "block"
11
12
 
12
- All_BUILTINS_OPERATORS = {
13
- CONTROL_OPERATOR_NAME,
14
- INVERT_OPERATOR_NAME,
15
- REPEAT_OPERATOR_NAME,
16
- POWER_OPERATOR_NAME,
17
- UNCOMPUTE_OPERATOR_NAME,
18
- WITHIN_APPLY_NAME,
19
- BLOCK_OPERATOR_NAME,
13
+ INVERT_OPERATOR_NAMES = {
14
+ COMPOUND_INVERT_OPERATOR_NAME,
15
+ SINGLE_CALL_INVERT_OPERATOR_NAME,
20
16
  }
@@ -61,6 +61,10 @@ class ClassicalType(HashableASTNode):
61
61
  def raw_qmod_type_name(self) -> str:
62
62
  return self.qmod_type_name
63
63
 
64
+ @property
65
+ def python_type_name(self) -> str:
66
+ raise NotImplementedError
67
+
64
68
  @property
65
69
  def expressions(self) -> list[Expression]:
66
70
  return []
@@ -76,6 +80,9 @@ class ClassicalType(HashableASTNode):
76
80
  def without_symbolic_attributes(self) -> Self:
77
81
  return self
78
82
 
83
+ def get_compile_time_attributes(self, path_expr_prefix: str) -> dict[str, Any]:
84
+ return {}
85
+
79
86
 
80
87
  class Integer(ClassicalType):
81
88
  kind: Literal["int"]
@@ -89,6 +96,10 @@ class Integer(ClassicalType):
89
96
  def qmod_type_name(self) -> str:
90
97
  return "CInt"
91
98
 
99
+ @property
100
+ def python_type_name(self) -> str:
101
+ return "int"
102
+
92
103
 
93
104
  class Real(ClassicalType):
94
105
  kind: Literal["real"]
@@ -102,6 +113,10 @@ class Real(ClassicalType):
102
113
  def qmod_type_name(self) -> str:
103
114
  return "CReal"
104
115
 
116
+ @property
117
+ def python_type_name(self) -> str:
118
+ return "float"
119
+
105
120
 
106
121
  class Bool(ClassicalType):
107
122
  kind: Literal["bool"]
@@ -115,6 +130,10 @@ class Bool(ClassicalType):
115
130
  def qmod_type_name(self) -> str:
116
131
  return "CBool"
117
132
 
133
+ @property
134
+ def python_type_name(self) -> str:
135
+ return "bool"
136
+
118
137
 
119
138
  class StructMetaType(ClassicalType):
120
139
  kind: Literal["type_proxy"]
@@ -201,6 +220,10 @@ class ClassicalArray(ClassicalType):
201
220
  def raw_qmod_type_name(self) -> str:
202
221
  return "CArray"
203
222
 
223
+ @property
224
+ def python_type_name(self) -> str:
225
+ return f"list[{self.element_type.python_type_name}]"
226
+
204
227
  def without_symbolic_attributes(self) -> "ClassicalArray":
205
228
  length = (
206
229
  None
@@ -213,6 +236,14 @@ class ClassicalArray(ClassicalType):
213
236
  element_type=self.element_type.without_symbolic_attributes(), length=length
214
237
  )
215
238
 
239
+ def get_compile_time_attributes(self, path_expr_prefix: str) -> dict[str, Any]:
240
+ attrs: dict[str, Any] = {}
241
+ if self.has_constant_length:
242
+ attrs[f"{path_expr_prefix}.len"] = self.length_value
243
+ return attrs | self.element_type.get_compile_time_attributes(
244
+ f"{path_expr_prefix}[0]"
245
+ )
246
+
216
247
 
217
248
  class ClassicalTuple(ClassicalType):
218
249
  kind: Literal["tuple"]
@@ -283,6 +314,13 @@ class ClassicalTuple(ClassicalType):
283
314
  def raw_qmod_type_name(self) -> str:
284
315
  return "CArray"
285
316
 
317
+ @property
318
+ def python_type_name(self) -> str:
319
+ raw_type = self.get_raw_type(preserve_length=True)
320
+ if isinstance(raw_type, ClassicalTuple):
321
+ return "list"
322
+ return raw_type.python_type_name
323
+
286
324
  def without_symbolic_attributes(self) -> "ClassicalTuple":
287
325
  return ClassicalTuple(
288
326
  element_types=[
@@ -291,6 +329,13 @@ class ClassicalTuple(ClassicalType):
291
329
  ]
292
330
  )
293
331
 
332
+ def get_compile_time_attributes(self, path_expr_prefix: str) -> dict[str, Any]:
333
+ raw_type = self.get_raw_type(preserve_length=True)
334
+ attrs = {f"{path_expr_prefix}.len": len(self.element_types)}
335
+ if isinstance(raw_type, ClassicalTuple):
336
+ return attrs
337
+ return attrs | raw_type.get_compile_time_attributes(path_expr_prefix)
338
+
294
339
 
295
340
  class OpaqueHandle(ClassicalType):
296
341
  pass
@@ -60,6 +60,10 @@ class TypeName(ClassicalType, QuantumType):
60
60
  def qmod_type_name(self) -> str:
61
61
  return self.name
62
62
 
63
+ @property
64
+ def python_type_name(self) -> str:
65
+ return self.name
66
+
63
67
  @property
64
68
  def type_name(self) -> str:
65
69
  return self.name
@@ -202,6 +206,25 @@ class TypeName(ClassicalType, QuantumType):
202
206
  return type_name
203
207
  return self
204
208
 
209
+ def get_compile_time_attributes(self, path_expr_prefix: str) -> dict[str, Any]:
210
+ attrs: dict[str, Any] = {}
211
+ if self.has_fields:
212
+ for field_name, field_type in self.fields.items():
213
+ field_prefix = f"{path_expr_prefix}.{field_name}"
214
+ attrs[field_prefix] = field_type.get_compile_time_attributes(
215
+ field_prefix
216
+ )
217
+ elif self.has_classical_struct_decl:
218
+ for (
219
+ field_name,
220
+ classical_field_type,
221
+ ) in self.classical_struct_decl.variables.items():
222
+ field_prefix = f"{path_expr_prefix}.{field_name}"
223
+ attrs[field_prefix] = classical_field_type.get_compile_time_attributes(
224
+ field_prefix
225
+ )
226
+ return attrs
227
+
205
228
 
206
229
  class Enum(TypeName):
207
230
  pass
@@ -140,7 +140,6 @@ class StatementType(StrEnum):
140
140
  WITHIN = "within"
141
141
  APPLY = "apply"
142
142
  ASSIGN = "assign"
143
- ASSIGN_AMPLITUDE = "assign amplitude"
144
143
  PHASE = "phase"
145
144
  INPLACE_XOR = "inplace xor"
146
145
  INPLACE_ADD = "inplace add"
@@ -163,7 +162,6 @@ STATEMENTS_NAME: dict[str, StatementType] = {
163
162
  "Uncompute": StatementType.WITHIN,
164
163
  ArithmeticOperationKind.Assignment.value: StatementType.ASSIGN,
165
164
  "InplaceBinaryOperation": StatementType.ASSIGN,
166
- "AmplitudeLoadingOperation": StatementType.ASSIGN_AMPLITUDE,
167
165
  "PhaseOperation": StatementType.PHASE,
168
166
  ArithmeticOperationKind.InplaceXor.value: StatementType.INPLACE_XOR,
169
167
  ArithmeticOperationKind.InplaceAdd.value: StatementType.INPLACE_ADD,