classiq 0.102.0__py3-none-any.whl → 1.0.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 (95) hide show
  1. classiq/__init__.py +2 -0
  2. classiq/_internals/authentication/auth0.py +29 -0
  3. classiq/_internals/authentication/auth_flow_factory.py +43 -0
  4. classiq/_internals/authentication/machine_credentials_flow.py +26 -0
  5. classiq/_internals/authentication/password_manager.py +84 -0
  6. classiq/_internals/authentication/token_manager.py +24 -8
  7. classiq/analyzer/show_interactive_hack.py +0 -8
  8. classiq/applications/chemistry/op_utils.py +32 -0
  9. classiq/applications/combinatorial_optimization/combinatorial_problem.py +1 -1
  10. classiq/evaluators/qmod_annotated_expression.py +1 -1
  11. classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +1 -8
  12. classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +1 -1
  13. classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +2 -2
  14. classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +18 -29
  15. classiq/evaluators/qmod_node_evaluators/min_max_evaluation.py +1 -6
  16. classiq/evaluators/qmod_node_evaluators/numeric_attrs_utils.py +1 -7
  17. classiq/evaluators/qmod_type_inference/quantum_type_comparison.py +52 -0
  18. classiq/execution/all_hardware_devices.py +59 -1
  19. classiq/execution/execution_session.py +1 -1
  20. classiq/execution/functions/__init__.py +13 -0
  21. classiq/execution/functions/expectation_value.py +106 -0
  22. classiq/execution/functions/minimize.py +90 -0
  23. classiq/execution/functions/sample.py +76 -0
  24. classiq/execution/functions/state_vector.py +113 -0
  25. classiq/execution/functions/util/__init__.py +0 -0
  26. classiq/execution/functions/util/_logging.py +19 -0
  27. classiq/execution/functions/util/backend_preferences.py +188 -0
  28. classiq/execution/functions/util/constants.py +9 -0
  29. classiq/execution/functions/util/parse_provider_backend.py +90 -0
  30. classiq/interface/_version.py +1 -1
  31. classiq/interface/backend/backend_preferences.py +81 -0
  32. classiq/interface/backend/provider_config/providers/aqt.py +1 -1
  33. classiq/interface/backend/provider_config/providers/azure.py +1 -2
  34. classiq/interface/backend/provider_config/providers/ibm.py +1 -1
  35. classiq/interface/backend/quantum_backend_providers.py +14 -0
  36. classiq/interface/exceptions.py +0 -4
  37. classiq/interface/executor/result.py +9 -5
  38. classiq/interface/generator/arith/binary_ops.py +62 -2
  39. classiq/interface/generator/arith/number_utils.py +15 -6
  40. classiq/interface/generator/compiler_keywords.py +1 -0
  41. classiq/interface/generator/function_param_list.py +8 -2
  42. classiq/interface/generator/function_params.py +1 -1
  43. classiq/interface/generator/functions/builtins/internal_operators.py +5 -9
  44. classiq/interface/generator/functions/classical_type.py +60 -0
  45. classiq/interface/generator/functions/type_name.py +36 -0
  46. classiq/interface/generator/generated_circuit_data.py +0 -2
  47. classiq/interface/generator/transpiler_basis_gates.py +1 -0
  48. classiq/interface/generator/types/compilation_metadata.py +18 -0
  49. classiq/interface/hardware.py +2 -0
  50. classiq/interface/helpers/model_normalizer.py +42 -6
  51. classiq/interface/interface_version.py +1 -1
  52. classiq/interface/model/invert.py +8 -0
  53. classiq/interface/model/model.py +19 -0
  54. classiq/interface/model/model_visitor.py +4 -2
  55. classiq/interface/model/quantum_type.py +36 -0
  56. classiq/interface/model/statement_block.py +0 -4
  57. classiq/interface/qubits_mapping/__init__.py +4 -0
  58. classiq/interface/qubits_mapping/path_expr_range.py +69 -0
  59. classiq/interface/qubits_mapping/qubits_mapping.py +231 -0
  60. classiq/interface/qubits_mapping/slices.py +112 -0
  61. classiq/model_expansions/arithmetic.py +6 -0
  62. classiq/model_expansions/capturing/captured_vars.py +16 -12
  63. classiq/model_expansions/function_builder.py +9 -1
  64. classiq/model_expansions/interpreters/base_interpreter.py +9 -8
  65. classiq/model_expansions/interpreters/generative_interpreter.py +9 -24
  66. classiq/model_expansions/quantum_operations/arithmetic/explicit_boolean_expressions.py +1 -0
  67. classiq/model_expansions/quantum_operations/assignment_result_processor.py +132 -28
  68. classiq/model_expansions/quantum_operations/bind.py +4 -0
  69. classiq/model_expansions/quantum_operations/call_emitter.py +5 -35
  70. classiq/model_expansions/quantum_operations/emitter.py +1 -4
  71. classiq/model_expansions/quantum_operations/expression_evaluator.py +0 -3
  72. classiq/model_expansions/visitors/uncomputation_signature_inference.py +0 -9
  73. classiq/qmod/builtins/functions/__init__.py +21 -9
  74. classiq/qmod/builtins/functions/allocation.py +0 -36
  75. classiq/qmod/builtins/functions/arithmetic.py +183 -0
  76. classiq/qmod/builtins/functions/exponentiation.py +32 -2
  77. classiq/qmod/builtins/functions/gray_code.py +23 -0
  78. classiq/qmod/builtins/functions/mcx_func.py +10 -0
  79. classiq/qmod/builtins/operations.py +2 -38
  80. classiq/qmod/builtins/structs.py +22 -3
  81. classiq/qmod/native/pretty_printer.py +1 -12
  82. classiq/qmod/pretty_print/pretty_printer.py +1 -17
  83. classiq/qmod/qmod_parameter.py +4 -0
  84. classiq/qmod/qmod_variable.py +38 -63
  85. classiq/qmod/quantum_function.py +43 -7
  86. classiq/qmod/semantics/validation/function_name_collisions_validation.py +7 -4
  87. classiq/qmod/semantics/validation/model_validation.py +7 -2
  88. classiq/qmod/symbolic_type.py +4 -2
  89. classiq/qprog_to_cudaq.py +347 -0
  90. {classiq-0.102.0.dist-info → classiq-1.0.0.dist-info}/METADATA +4 -1
  91. {classiq-0.102.0.dist-info → classiq-1.0.0.dist-info}/RECORD +93 -76
  92. classiq/interface/generator/amplitude_loading.py +0 -103
  93. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +0 -77
  94. {classiq-0.102.0.dist-info → classiq-1.0.0.dist-info}/WHEEL +0 -0
  95. {classiq-0.102.0.dist-info → classiq-1.0.0.dist-info}/licenses/LICENSE.txt +0 -0
@@ -0,0 +1,188 @@
1
+ from collections.abc import Callable
2
+ from dataclasses import dataclass
3
+ from typing import Any, Literal
4
+
5
+ from classiq.interface.backend.backend_preferences import (
6
+ AliceBobBackendPreferences,
7
+ AQTBackendPreferences,
8
+ AwsBackendPreferences,
9
+ AzureBackendPreferences,
10
+ AzureCredential,
11
+ BackendPreferencesTypes,
12
+ ClassiqBackendPreferences,
13
+ GCPBackendPreferences,
14
+ IBMBackendPreferences,
15
+ IntelBackendPreferences,
16
+ IonqBackendPreferences,
17
+ )
18
+ from classiq.interface.backend.provider_config.provider_config import ProviderConfig
19
+ from classiq.interface.backend.provider_config.providers.alice_bob import AliceBobConfig
20
+ from classiq.interface.backend.provider_config.providers.aqt import AQTConfig
21
+ from classiq.interface.backend.provider_config.providers.azure import AzureConfig
22
+ from classiq.interface.backend.provider_config.providers.braket import BraketConfig
23
+ from classiq.interface.backend.provider_config.providers.ibm import IBMConfig
24
+ from classiq.interface.backend.provider_config.providers.ionq import IonQConfig
25
+ from classiq.interface.backend.quantum_backend_providers import (
26
+ ClassiqNvidiaBackendNames,
27
+ ClassiqSimulatorBackendNames,
28
+ )
29
+ from classiq.interface.hardware import Provider
30
+
31
+ from classiq.execution.functions.util.parse_provider_backend import (
32
+ _PROVIDER_TO_CANONICAL_NAME,
33
+ _parse_provider_backend,
34
+ )
35
+
36
+
37
+ @dataclass
38
+ class _ProviderConfigToBackendPrefSpec:
39
+ backend_preferences_class: type[BackendPreferencesTypes]
40
+ config_class: type[ProviderConfig] | None = None
41
+ # Maps the config dict (either passed in directly or dumped from config class) to a
42
+ # dict that we can load into the given BackendPreferences class. This is in case
43
+ # we need to rename fields or change structure.
44
+ config_dict_to_backend_preferences_dict: (
45
+ Callable[[dict[str, Any]], dict[str, Any]] | None
46
+ ) = None
47
+ # Maps from SDK names to names our backend recognizes, raising a useful error
48
+ # if the name is unrecognized.
49
+ backend_name_mapper: Callable[[str], str] | None = None
50
+
51
+
52
+ def _classiq_backend_name_mapper(backend_name: str) -> str:
53
+ backend_name = backend_name.lower()
54
+ if backend_name in [
55
+ ClassiqSimulatorBackendNames.SIMULATOR,
56
+ ClassiqSimulatorBackendNames.SIMULATOR_MATRIX_PRODUCT_STATE,
57
+ ClassiqSimulatorBackendNames.SIMULATOR_DENSITY_MATRIX,
58
+ ]:
59
+ return backend_name
60
+ if backend_name == "nvidia_simulator":
61
+ return ClassiqNvidiaBackendNames.SIMULATOR
62
+ if any(keyword in backend_name for keyword in ["gpu", "nvidia"]):
63
+ suggested_backend_name = "nvidia_simulator"
64
+ else:
65
+ suggested_backend_name = "simulator"
66
+ raise ValueError(
67
+ f"Unsupported backend name {backend_name}. Did you mean '{suggested_backend_name}'?"
68
+ )
69
+
70
+
71
+ def _ibm_backend_name_mapper(backend_name: str) -> str:
72
+ ibm_prefix: Literal["ibm_"] = "ibm_"
73
+ backend_name = backend_name.lower()
74
+ if backend_name.startswith(ibm_prefix):
75
+ backend_name_no_prefix = backend_name.removeprefix(ibm_prefix)
76
+ raise ValueError(
77
+ f"IBM backend names shouldn't start with ibm_. Try 'ibm/{backend_name_no_prefix}'."
78
+ )
79
+ return ibm_prefix + backend_name
80
+
81
+
82
+ def _azure_config_dict_to_backend_preferences_dict(
83
+ config_dict: dict[str, Any],
84
+ ) -> dict[str, Any]:
85
+ if "location" not in config_dict:
86
+ raise ValueError("Azure config must have 'location' property")
87
+ credentials = None
88
+ if all(
89
+ config_dict.get(key) is not None
90
+ for key in ["tenant_id", "client_id", "client_secret", "resource_id"]
91
+ ):
92
+ credentials = AzureCredential.model_validate(
93
+ {
94
+ "tenant_id": config_dict["tenant_id"],
95
+ "client_id": config_dict["client_id"],
96
+ "client_secret": config_dict["client_secret"],
97
+ "resource_id": config_dict["resource_id"],
98
+ }
99
+ )
100
+ return {
101
+ "location": config_dict["location"],
102
+ "credentials": credentials,
103
+ "ionq_error_mitigation_flag": config_dict.get("ionq_error_mitigation"),
104
+ }
105
+
106
+
107
+ def _braket_config_dict_to_backend_preferences_dict(
108
+ config_dict: dict[str, Any],
109
+ ) -> dict[str, Any]:
110
+ config_dict["aws_access_key_id"] = config_dict.pop("braket_access_key_id", None)
111
+ config_dict["aws_secret_access_key"] = config_dict.pop(
112
+ "braket_secret_access_key", None
113
+ )
114
+ return config_dict
115
+
116
+
117
+ _PROVIDER_CONFIG_TO_BACKEND_PREFERENCES_SPEC = {
118
+ Provider.CLASSIQ: _ProviderConfigToBackendPrefSpec(
119
+ backend_preferences_class=ClassiqBackendPreferences,
120
+ backend_name_mapper=_classiq_backend_name_mapper,
121
+ ),
122
+ Provider.GOOGLE: _ProviderConfigToBackendPrefSpec(
123
+ backend_preferences_class=GCPBackendPreferences
124
+ ),
125
+ Provider.INTEL: _ProviderConfigToBackendPrefSpec(
126
+ backend_preferences_class=IntelBackendPreferences
127
+ ),
128
+ Provider.IBM_QUANTUM: _ProviderConfigToBackendPrefSpec(
129
+ backend_preferences_class=IBMBackendPreferences,
130
+ config_class=IBMConfig,
131
+ backend_name_mapper=_ibm_backend_name_mapper,
132
+ ),
133
+ Provider.AMAZON_BRAKET: _ProviderConfigToBackendPrefSpec(
134
+ backend_preferences_class=AwsBackendPreferences,
135
+ config_dict_to_backend_preferences_dict=_braket_config_dict_to_backend_preferences_dict,
136
+ config_class=BraketConfig,
137
+ ),
138
+ Provider.IONQ: _ProviderConfigToBackendPrefSpec(
139
+ backend_preferences_class=IonqBackendPreferences,
140
+ config_class=IonQConfig,
141
+ ),
142
+ Provider.ALICE_AND_BOB: _ProviderConfigToBackendPrefSpec(
143
+ backend_preferences_class=AliceBobBackendPreferences,
144
+ config_class=AliceBobConfig,
145
+ ),
146
+ Provider.AQT: _ProviderConfigToBackendPrefSpec(
147
+ backend_preferences_class=AQTBackendPreferences,
148
+ config_class=AQTConfig,
149
+ ),
150
+ Provider.AZURE_QUANTUM: _ProviderConfigToBackendPrefSpec(
151
+ backend_preferences_class=AzureBackendPreferences,
152
+ config_dict_to_backend_preferences_dict=_azure_config_dict_to_backend_preferences_dict,
153
+ config_class=AzureConfig,
154
+ ),
155
+ }
156
+
157
+
158
+ def _get_backend_preferences_from_specifier(
159
+ backend_spec: str, config: dict[str, Any] | ProviderConfig
160
+ ) -> BackendPreferencesTypes:
161
+ provider, backend_name = _parse_provider_backend(backend_spec)
162
+
163
+ if provider not in _PROVIDER_CONFIG_TO_BACKEND_PREFERENCES_SPEC:
164
+ raise NotImplementedError(
165
+ f"Unsupported provider '{_PROVIDER_TO_CANONICAL_NAME.get(provider) or provider}'"
166
+ )
167
+
168
+ provider_spec = _PROVIDER_CONFIG_TO_BACKEND_PREFERENCES_SPEC[provider]
169
+ if isinstance(config, ProviderConfig):
170
+ if provider_spec.config_class is None:
171
+ raise ValueError(
172
+ f"This provider does not support any ProviderConfig classes. Received '{config.__class__.__name__}'"
173
+ )
174
+ if not isinstance(config, provider_spec.config_class):
175
+ raise ValueError(
176
+ f"{_PROVIDER_TO_CANONICAL_NAME[provider]} devices require {provider_spec.config_class.__name__}, got {config.__class__.__name__}"
177
+ )
178
+ config_dict = config.model_dump()
179
+ else:
180
+ config_dict = config
181
+ if provider_spec.backend_name_mapper is not None:
182
+ backend_name = provider_spec.backend_name_mapper(backend_name)
183
+
184
+ if provider_spec.config_dict_to_backend_preferences_dict is not None:
185
+ config_dict = provider_spec.config_dict_to_backend_preferences_dict(config_dict)
186
+
187
+ config_dict["backend_name"] = backend_name
188
+ return provider_spec.backend_preferences_class.model_validate(config_dict)
@@ -0,0 +1,9 @@
1
+ from enum import auto, unique
2
+
3
+ from classiq.interface.enum_utils import StrEnum
4
+
5
+
6
+ @unique
7
+ class Verbosity(StrEnum):
8
+ QUIET = auto()
9
+ INFO = auto()
@@ -0,0 +1,90 @@
1
+ from classiq.interface.hardware import Provider
2
+
3
+ _PROVIDER_BACKEND_SEPARATOR = "/"
4
+
5
+ _PROVIDER_TO_CANONICAL_NAME: dict[Provider, str] = {
6
+ Provider.IBM_QUANTUM: "ibm",
7
+ Provider.AZURE_QUANTUM: "azure",
8
+ Provider.AMAZON_BRAKET: "braket",
9
+ Provider.IONQ: "ionq",
10
+ Provider.CLASSIQ: "classiq",
11
+ Provider.GOOGLE: "google",
12
+ Provider.ALICE_AND_BOB: "alice&bob",
13
+ Provider.OQC: "oqc",
14
+ Provider.INTEL: "intel",
15
+ Provider.AQT: "aqt",
16
+ Provider.CINECA: "cineca",
17
+ Provider.SOFTBANK: "softbank",
18
+ }
19
+
20
+ _CANONICAL_NAMES_TO_PROVIDER: dict[str, Provider] = {
21
+ name: provider for provider, name in _PROVIDER_TO_CANONICAL_NAME.items()
22
+ }
23
+
24
+
25
+ def _error_suggested_provider(provider_name: str) -> Provider | None:
26
+ """
27
+ In the case that receive an incorrect provider name, return a possible suggestion.
28
+ """
29
+ provider_name = provider_name.strip().lower()
30
+ for canonical_name in _CANONICAL_NAMES_TO_PROVIDER:
31
+ if canonical_name in provider_name:
32
+ return _CANONICAL_NAMES_TO_PROVIDER[canonical_name]
33
+ # Special cases
34
+ if "gcp" in provider_name:
35
+ return Provider.GOOGLE
36
+ if "microsoft" in provider_name:
37
+ return Provider.AZURE_QUANTUM
38
+ if "amazon" in provider_name or "aws" in provider_name:
39
+ return Provider.AMAZON_BRAKET
40
+ if "oxford" in provider_name:
41
+ return Provider.OQC
42
+ if "alice" in provider_name or "bob" in provider_name:
43
+ return Provider.ALICE_AND_BOB
44
+ if "alpine" in provider_name:
45
+ return Provider.AQT
46
+
47
+ return None
48
+
49
+
50
+ def _parse_provider_backend(spec: str) -> tuple[Provider, str]:
51
+ """
52
+ Parse a backend specification into (provider, backend). Provider is case-insensitive.
53
+ Backend must NOT contain the separator. If provider is not specified, it defaults to
54
+ Classiq.
55
+ """
56
+ if not spec.strip():
57
+ raise ValueError("Backend specification must be a non-empty string")
58
+
59
+ spec = spec.strip()
60
+
61
+ if _PROVIDER_BACKEND_SEPARATOR not in spec:
62
+ return Provider.CLASSIQ, spec
63
+
64
+ provider_raw, backend = spec.split(_PROVIDER_BACKEND_SEPARATOR, 1)
65
+
66
+ if _PROVIDER_BACKEND_SEPARATOR in backend:
67
+ raise ValueError(
68
+ f"Backend name must not contain '{_PROVIDER_BACKEND_SEPARATOR}': '{backend}'"
69
+ )
70
+
71
+ provider_key = provider_raw.strip().lower()
72
+ backend = backend.strip()
73
+
74
+ if not provider_key:
75
+ raise ValueError("Provider name cannot be empty")
76
+ if not backend:
77
+ raise ValueError("Backend name cannot be empty")
78
+
79
+ try:
80
+ provider = _CANONICAL_NAMES_TO_PROVIDER[provider_key]
81
+ except KeyError:
82
+ error_message = f"Unrecognized provider '{provider_raw}'."
83
+ suggested_provider = _error_suggested_provider(provider_key)
84
+ if suggested_provider is not None:
85
+ error_message += (
86
+ f" Did you mean '{_PROVIDER_TO_CANONICAL_NAME[suggested_provider]}'?"
87
+ )
88
+ raise ValueError(error_message) from None
89
+
90
+ return provider, backend
@@ -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 = '1.0.0'
7
7
  VERSION = str(Version(SEMVER_VERSION))
@@ -454,6 +454,84 @@ 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
+
471
+ class C12BackendPreferences(BackendPreferences):
472
+ """
473
+ Represents the backend preferences specific to C12.
474
+
475
+ Attributes:
476
+ backend_name: Name of the requested backend or target.
477
+ result_format: Result format of the job; one of "counts",
478
+ "state_vector", or "density_matrix". Defaults to "counts".
479
+ inilabel: Initial state specified using a binary label format
480
+ (e.g. "00", "01"). Mutually exclusive with inistatevector.
481
+ inistatevector: Initial state vector as a comma-separated list of
482
+ complex values (e.g. "1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j").
483
+ Mutually exclusive with inilabel.
484
+ ininoisy: Whether to use noisy initialization of the circuit.
485
+ """
486
+
487
+ backend_service_provider: ProviderTypeVendor.C12 = pydantic.Field(
488
+ default=ProviderVendor.C12
489
+ )
490
+
491
+ result_format: str = pydantic.Field(
492
+ default="counts", description="Result format of the job"
493
+ )
494
+
495
+ inilabel: str | None = pydantic.Field(
496
+ default=None, description="Initial label of the initial state."
497
+ )
498
+
499
+ inistatevector: str | None = pydantic.Field(
500
+ default=None, description="Initial state vector of the job."
501
+ )
502
+
503
+ ininoisy: bool | None = pydantic.Field(
504
+ default=None, description="Whether the initial state is noisy."
505
+ )
506
+
507
+ @pydantic.model_validator(mode="after")
508
+ def _validate_initial_label_statevector(
509
+ self: C12BackendPreferences,
510
+ ) -> C12BackendPreferences:
511
+ if self.inilabel is not None and self.inistatevector is not None:
512
+ raise ValueError(
513
+ f"Cannot specify both inilabel and inistatevector.\n"
514
+ f"inilabel={self.inilabel}, inistatevector={self.inistatevector}"
515
+ )
516
+ return self
517
+
518
+ @pydantic.field_validator("result_format")
519
+ @classmethod
520
+ def _validate_result_format(cls, result_format: str) -> str:
521
+ if result_format not in ["density_matrix", "state_vector", "counts"]:
522
+ raise ValueError(f"Invalid result format: {result_format}")
523
+ return result_format
524
+
525
+ @property
526
+ def parameters(self) -> dict:
527
+ data = self.model_dump(exclude_none=True)
528
+ for field in ["inilabel", "inistatevector", "ininoisy"]:
529
+ if field in data and data[field] is None:
530
+ data.pop(field)
531
+
532
+ return data
533
+
534
+
457
535
  def is_exact_simulator(backend_preferences: BackendPreferences) -> bool:
458
536
  return backend_preferences.backend_name in EXACT_SIMULATORS
459
537
 
@@ -486,6 +564,8 @@ BackendPreferencesTypes = Union[
486
564
  IntelBackendPreferences,
487
565
  AQTBackendPreferences,
488
566
  CINECABackendPreferences,
567
+ SoftbankBackendPreferences,
568
+ C12BackendPreferences,
489
569
  ]
490
570
 
491
571
  __all__ = [
@@ -497,6 +577,7 @@ __all__ = [
497
577
  "AzureBackendPreferences",
498
578
  "AzureCredential",
499
579
  "AzureQuantumBackendNames",
580
+ "C12BackendPreferences",
500
581
  "ClassiqBackendPreferences",
501
582
  "ClassiqNvidiaBackendNames",
502
583
  "ClassiqSimulatorBackendNames",
@@ -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,8 @@ class ProviderVendor(StrEnum):
27
27
  INTEL = "Intel"
28
28
  AQT = "AQT"
29
29
  CINECA = "CINECA"
30
+ SOFTBANK = "Softbank"
31
+ C12 = "C12"
30
32
 
31
33
 
32
34
  class ProviderTypeVendor:
@@ -41,6 +43,8 @@ class ProviderTypeVendor:
41
43
  INTEL = Literal[ProviderVendor.INTEL]
42
44
  AQT = Literal[ProviderVendor.AQT]
43
45
  CINECA = Literal[ProviderVendor.CINECA]
46
+ SOFTBANK = Literal[ProviderVendor.SOFTBANK]
47
+ C12 = Literal[ProviderVendor.C12]
44
48
 
45
49
 
46
50
  PROVIDER_NAME_MAPPER = {
@@ -54,6 +58,8 @@ PROVIDER_NAME_MAPPER = {
54
58
  ProviderVendor.INTEL: "INTEL",
55
59
  ProviderVendor.AQT: "AQT",
56
60
  ProviderVendor.CLASSIQ: "CLASSIQ",
61
+ ProviderVendor.SOFTBANK: "SOFTBANK",
62
+ ProviderVendor.C12: "C12",
57
63
  }
58
64
 
59
65
 
@@ -245,6 +251,11 @@ class OQCBackendNames(StrEnum):
245
251
  LUCY = "Lucy"
246
252
 
247
253
 
254
+ class C12BackendNames(StrEnum):
255
+ SIMULATOR = "c12sim-iswap"
256
+ SQUARED = "squared-iswap"
257
+
258
+
248
259
  EXACT_SIMULATORS = {
249
260
  IonqBackendNames.SIMULATOR,
250
261
  AzureQuantumBackendNames.IONQ_SIMULATOR,
@@ -255,6 +266,7 @@ EXACT_SIMULATORS = {
255
266
  *ClassiqSimulatorBackendNames,
256
267
  *IntelBackendNames,
257
268
  *ClassiqNvidiaBackendNames,
269
+ *C12BackendNames,
258
270
  }
259
271
 
260
272
  AllIBMQBackendNames = IBMQHardwareNames
@@ -268,6 +280,7 @@ AllBackendsNameByVendor = Union[
268
280
  ClassiqNvidiaBackendNames,
269
281
  AliceBobBackendNames,
270
282
  OQCBackendNames,
283
+ C12BackendNames,
271
284
  ]
272
285
 
273
286
  AllBackendsNameEnums = [
@@ -279,4 +292,5 @@ AllBackendsNameEnums = [
279
292
  IntelBackendNames,
280
293
  ClassiqNvidiaBackendNames,
281
294
  OQCBackendNames,
295
+ C12BackendNames,
282
296
  ]
@@ -39,10 +39,6 @@ class ClassiqAnalyzerError(ClassiqError):
39
39
  pass
40
40
 
41
41
 
42
- class ClassiqAnalyzerVisualizationError(ClassiqError):
43
- pass
44
-
45
-
46
42
  class ClassiqAPIError(ClassiqError):
47
43
  def __init__(self, message: str, status_code: int | None = None) -> None:
48
44
  self.status_code = status_code
@@ -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 (
@@ -335,6 +338,30 @@ class Adder(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
335
338
  return isinstance(arg, float) and arg == 0
336
339
 
337
340
 
341
+ class CanonicalAdder(FunctionParams):
342
+ left_size: pydantic.PositiveInt
343
+ extend_left: bool
344
+ right_size: pydantic.PositiveInt
345
+
346
+ def _create_ios(self) -> None:
347
+ self._inputs = {
348
+ DEFAULT_LEFT_ARG_NAME: RegisterArithmeticInfo(size=self.left_size),
349
+ DEFAULT_RIGHT_ARG_NAME: RegisterArithmeticInfo(size=self.right_size),
350
+ }
351
+ self._outputs = {**self._inputs}
352
+
353
+
354
+ class CanonicalConstantAdder(FunctionParams):
355
+ left: int
356
+ right_size: pydantic.PositiveInt
357
+
358
+ def _create_ios(self) -> None:
359
+ self._inputs = {
360
+ "right_arg": RegisterArithmeticInfo(size=self.right_size),
361
+ }
362
+ self._outputs = {**self._inputs}
363
+
364
+
338
365
  class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
339
366
  output_name = "difference"
340
367
 
@@ -533,10 +560,43 @@ class Multiplier(BinaryOpWithFloatInputs):
533
560
 
534
561
  def garbage_output_size(self) -> pydantic.NonNegativeInt:
535
562
  return max(
536
- 0, self.expected_fraction_places() - self.result_register.fraction_places
563
+ 0,
564
+ self.expected_fraction_places() - self.result_register.fraction_places - 1,
537
565
  )
538
566
 
539
567
 
568
+ class CanonicalMultiplier(FunctionParams):
569
+ left_size: pydantic.PositiveInt
570
+ extend_left: bool
571
+ right_size: pydantic.PositiveInt
572
+ extend_right: bool
573
+ result_size: pydantic.PositiveInt
574
+ trim_result_lsb: bool
575
+
576
+ def _create_ios(self) -> None:
577
+ self._inputs = {
578
+ "left": RegisterArithmeticInfo(size=self.left_size),
579
+ "right": RegisterArithmeticInfo(size=self.right_size),
580
+ "result": RegisterArithmeticInfo(size=self.result_size),
581
+ }
582
+ self._outputs = {**self._inputs}
583
+
584
+
585
+ class CanonicalConstantMultiplier(FunctionParams):
586
+ left: int
587
+ right_size: pydantic.PositiveInt
588
+ extend_right: bool
589
+ result_size: pydantic.PositiveInt
590
+ trim_result_lsb: bool
591
+
592
+ def _create_ios(self) -> None:
593
+ self._inputs = {
594
+ "right": RegisterArithmeticInfo(size=self.right_size),
595
+ "result": RegisterArithmeticInfo(size=self.result_size),
596
+ }
597
+ self._outputs = {**self._inputs}
598
+
599
+
540
600
  class Comparator(BinaryOpWithFloatInputs):
541
601
  output_size: Literal[1] = 1
542
602
 
@@ -3,13 +3,22 @@ from typing import Final
3
3
  MAXIMAL_MACHINE_PRECISION: Final[int] = 20
4
4
 
5
5
 
6
- def signed_int_to_unsigned(number: int) -> int:
6
+ def signed_int_to_unsigned(number: int, reg_size: int | None = None) -> int:
7
7
  """Return the integer value of a signed int if it would we read as un-signed in binary representation"""
8
- if number >= 0:
9
- return number
10
-
11
- not_power2 = abs(number) & (abs(number) - 1) != 0
12
- return number + 2 ** (number.bit_length() + 1 * not_power2)
8
+ signed: bool = False
9
+ if number < 0:
10
+ signed = True
11
+ not_power2 = abs(number) & (abs(number) - 1) != 0
12
+ number = number + 2 ** (number.bit_length() + 1 * not_power2)
13
+
14
+ if reg_size is not None:
15
+ bits = bin(number)[2:][::-1]
16
+ bits = bits[:reg_size]
17
+ if signed and len(bits) < reg_size:
18
+ bits += "1" * (reg_size - len(bits))
19
+ number = int(bits[::-1], 2)
20
+
21
+ return number
13
22
 
14
23
 
15
24
  def binary_to_int(bin_rep: str, is_signed: bool = False) -> int:
@@ -2,6 +2,7 @@ EXPANDED_KEYWORD = "expanded__"
2
2
  CAPTURE_SUFFIX = "_captured__"
3
3
  LAMBDA_KEYWORD = "lambda__"
4
4
  INPLACE_ARITH_AUX_VAR_PREFIX = "_tmp"
5
+ INPLACE_CONTROL_AUX_VAR_PREFIX = "_ctmp"
5
6
  THEN_KEYWORD = "then"
6
7
  ELSE_KEYWORD = "else"
7
8