classiq 0.104.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 (52) 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/combinatorial_optimization/combinatorial_problem.py +1 -1
  9. classiq/execution/all_hardware_devices.py +59 -1
  10. classiq/execution/functions/__init__.py +11 -1
  11. classiq/execution/functions/expectation_value.py +106 -0
  12. classiq/execution/functions/minimize.py +90 -0
  13. classiq/execution/functions/sample.py +8 -189
  14. classiq/execution/functions/state_vector.py +113 -0
  15. classiq/execution/functions/util/__init__.py +0 -0
  16. classiq/execution/functions/util/backend_preferences.py +188 -0
  17. classiq/interface/_version.py +1 -1
  18. classiq/interface/backend/backend_preferences.py +66 -0
  19. classiq/interface/backend/quantum_backend_providers.py +11 -0
  20. classiq/interface/exceptions.py +0 -4
  21. classiq/interface/generator/arith/binary_ops.py +24 -0
  22. classiq/interface/generator/arith/number_utils.py +15 -6
  23. classiq/interface/generator/compiler_keywords.py +1 -0
  24. classiq/interface/generator/function_param_list.py +4 -0
  25. classiq/interface/generator/function_params.py +1 -1
  26. classiq/interface/generator/functions/classical_type.py +15 -0
  27. classiq/interface/generator/functions/type_name.py +17 -4
  28. classiq/interface/generator/transpiler_basis_gates.py +1 -0
  29. classiq/interface/generator/types/compilation_metadata.py +15 -6
  30. classiq/interface/hardware.py +1 -0
  31. classiq/interface/interface_version.py +1 -1
  32. classiq/interface/model/model.py +19 -0
  33. classiq/interface/model/quantum_type.py +15 -0
  34. classiq/interface/qubits_mapping/__init__.py +4 -0
  35. classiq/interface/qubits_mapping/path_expr_range.py +69 -0
  36. classiq/interface/qubits_mapping/qubits_mapping.py +231 -0
  37. classiq/interface/qubits_mapping/slices.py +112 -0
  38. classiq/model_expansions/arithmetic.py +6 -0
  39. classiq/qmod/builtins/functions/__init__.py +12 -9
  40. classiq/qmod/builtins/functions/allocation.py +0 -36
  41. classiq/qmod/builtins/functions/arithmetic.py +52 -0
  42. classiq/qmod/builtins/functions/gray_code.py +23 -0
  43. classiq/qmod/builtins/functions/mcx_func.py +10 -0
  44. classiq/qmod/builtins/structs.py +22 -3
  45. classiq/qprog_to_cudaq.py +347 -0
  46. {classiq-0.104.0.dist-info → classiq-1.0.0.dist-info}/METADATA +4 -1
  47. {classiq-0.104.0.dist-info → classiq-1.0.0.dist-info}/RECORD +52 -39
  48. /classiq/execution/functions/{_logging.py → util/_logging.py} +0 -0
  49. /classiq/execution/functions/{constants.py → util/constants.py} +0 -0
  50. /classiq/execution/functions/{parse_provider_backend.py → util/parse_provider_backend.py} +0 -0
  51. {classiq-0.104.0.dist-info → classiq-1.0.0.dist-info}/WHEEL +0 -0
  52. {classiq-0.104.0.dist-info → classiq-1.0.0.dist-info}/licenses/LICENSE.txt +0 -0
@@ -0,0 +1,112 @@
1
+ from collections import deque
2
+ from collections.abc import Iterable, Reversible
3
+ from itertools import chain
4
+
5
+ from typing_extensions import override
6
+
7
+
8
+ class Slices(deque[tuple[int, int]]):
9
+ """A deque of slice objects that automatically merges adjacent slices.
10
+
11
+ Slices represent a collection of non-overlapping, potentially non-contiguous
12
+ slice ranges. When slices are appended or prepended, adjacent slices are
13
+ automatically merged to maintain a compact representation. Methods use virtual
14
+ notation, access the elements in the object as if all slices were explicitly
15
+ written out contiguously.
16
+
17
+ The class is primarily used for managing qubit allocations and mappings in
18
+ quantum circuit synthesis, where it tracks which physical qubit ranges
19
+ correspond to logical variable ranges.
20
+ """
21
+
22
+ @override
23
+ def append(self, physical_slice: tuple[int, int]) -> None:
24
+ if self and self[-1][1] == physical_slice[0]:
25
+ last_physical_slice = self.pop()
26
+ new_physical_slice = (last_physical_slice[0], physical_slice[1])
27
+ else:
28
+ new_physical_slice = physical_slice
29
+ super().append(new_physical_slice)
30
+
31
+ @override
32
+ def appendleft(self, physical_slice: tuple[int, int]) -> None:
33
+ if self and self[0][0] == physical_slice[1]:
34
+ first_physical_slice = self.popleft()
35
+ new_physical_slice = (physical_slice[0], first_physical_slice[1])
36
+ else:
37
+ new_physical_slice = physical_slice
38
+ super().appendleft(new_physical_slice)
39
+
40
+ def _multiple_appendleft(
41
+ self, physical_slices: Reversible[tuple[int, int]]
42
+ ) -> None:
43
+ for physical_slice in reversed(physical_slices):
44
+ self.appendleft(physical_slice)
45
+
46
+ @override
47
+ def extend(self, physical_slices: Iterable[tuple[int, int]]) -> None:
48
+ for physical_slice in physical_slices:
49
+ self.append(physical_slice)
50
+
51
+ def pop_prefix_virtual_slice(self, virtual_end: int) -> "Slices":
52
+ result = Slices()
53
+ current_virtual_end, result_physical_end, physical_slice_end = 0, 0, 0
54
+ while current_virtual_end < virtual_end:
55
+ physical_slice_start, physical_slice_end = self.popleft()
56
+ current_virtual_end += physical_slice_end - physical_slice_start
57
+ overlap_virtual_end = min(virtual_end, current_virtual_end)
58
+ result_physical_end = physical_slice_end + (
59
+ overlap_virtual_end - current_virtual_end
60
+ )
61
+ result.append((physical_slice_start, result_physical_end))
62
+ if result_physical_end != physical_slice_end:
63
+ self.appendleft((result_physical_end, physical_slice_end))
64
+ return result
65
+
66
+ def get_virtual_slice(self, virtual_start: int, virtual_end: int) -> "Slices":
67
+ result = Slices()
68
+ current_virtual_start = 0
69
+ for physical_slice in self:
70
+ physical_slice_start, physical_slice_end = physical_slice
71
+ current_virtual_end = current_virtual_start + (
72
+ physical_slice_end - physical_slice_start
73
+ )
74
+ overlap_virtual_start = max(virtual_start, current_virtual_start)
75
+ overlap_virtual_end = min(virtual_end, current_virtual_end)
76
+ if overlap_virtual_start < overlap_virtual_end:
77
+ new_physical_start = physical_slice_start + (
78
+ overlap_virtual_start - current_virtual_start
79
+ )
80
+ new_physical_end = physical_slice_end + (
81
+ overlap_virtual_end - current_virtual_end
82
+ )
83
+ result.append((new_physical_start, new_physical_end))
84
+ if current_virtual_end >= virtual_end:
85
+ break
86
+ current_virtual_start = current_virtual_end
87
+ return result
88
+
89
+ def update_virtual_slice(
90
+ self, virtual_start: int, virtual_end: int, new: "Slices"
91
+ ) -> None:
92
+ start = self.pop_prefix_virtual_slice(virtual_start)
93
+ self.pop_prefix_virtual_slice(virtual_end - virtual_start)
94
+ self._multiple_appendleft(new)
95
+ self._multiple_appendleft(start)
96
+
97
+ def mapping_virtual_slices(self, virtual_slices: "Slices") -> "Slices":
98
+ mappings = Slices()
99
+ for virtual_slice in virtual_slices:
100
+ virtual_start, virtual_end = virtual_slice
101
+ for mapped_slice in self.get_virtual_slice(virtual_start, virtual_end):
102
+ mappings.append(mapped_slice)
103
+ return mappings
104
+
105
+ def size(self) -> int:
106
+ return sum(_slice[1] - _slice[0] for _slice in self)
107
+
108
+ @property
109
+ def indices(self) -> tuple[int, ...]:
110
+ return tuple(
111
+ chain.from_iterable(range(_slice[0], _slice[1]) for _slice in self)
112
+ )
@@ -88,6 +88,12 @@ class NumericAttributes:
88
88
  return self.lb
89
89
  return None
90
90
 
91
+ def fits_in(self, other: "NumericAttributes") -> bool:
92
+ return (
93
+ self.integer_digits <= other.integer_digits
94
+ and self.fraction_digits <= other.fraction_digits
95
+ )
96
+
91
97
  @classmethod
92
98
  def from_bounds(
93
99
  cls,
@@ -2,7 +2,8 @@ from .allocation import *
2
2
  from .arithmetic import *
3
3
  from .benchmarking import *
4
4
  from .exponentiation import *
5
- from .mcx_func import mcx
5
+ from .gray_code import *
6
+ from .mcx_func import *
6
7
  from .mid_circuit_measurement import *
7
8
  from .operators import *
8
9
  from .standard_gates import *
@@ -38,12 +39,12 @@ CORE_LIB_DECLS = [
38
39
  SWAP,
39
40
  IDENTITY,
40
41
  prepare_state,
41
- prepare_state_approx,
42
42
  prepare_amplitudes,
43
- prepare_amplitudes_approx,
44
43
  unitary,
45
44
  add,
46
45
  add_inplace_right,
46
+ canonical_add,
47
+ canonical_add_constant,
47
48
  modular_add,
48
49
  integer_xor,
49
50
  modular_add_constant,
@@ -58,9 +59,7 @@ CORE_LIB_DECLS = [
58
59
  drop,
59
60
  randomized_benchmarking,
60
61
  inplace_prepare_state,
61
- inplace_prepare_state_approx,
62
62
  inplace_prepare_amplitudes,
63
- inplace_prepare_amplitudes_approx,
64
63
  single_pauli_exponent,
65
64
  commuting_paulis_exponent,
66
65
  suzuki_trotter,
@@ -74,7 +73,10 @@ CORE_LIB_DECLS = [
74
73
  exponentiation_with_depth_constraint,
75
74
  RESET,
76
75
  mcx,
76
+ mcx_gray_code,
77
+ mcx_hybrid_rec,
77
78
  SX,
79
+ select_rotation,
78
80
  )
79
81
  ]
80
82
 
@@ -94,6 +96,8 @@ __all__ = [ # noqa: RUF022
94
96
  "I",
95
97
  "IDENTITY",
96
98
  "mcx",
99
+ "mcx_gray_code",
100
+ "mcx_hybrid_rec",
97
101
  "PHASE",
98
102
  "R",
99
103
  "RX",
@@ -114,6 +118,8 @@ __all__ = [ # noqa: RUF022
114
118
  "SX",
115
119
  "add",
116
120
  "add_inplace_right",
121
+ "canonical_add",
122
+ "canonical_add_constant",
117
123
  "apply",
118
124
  "exponentiation_with_depth_constraint",
119
125
  "multiply",
@@ -123,9 +129,7 @@ __all__ = [ # noqa: RUF022
123
129
  "free",
124
130
  "drop",
125
131
  "inplace_prepare_amplitudes",
126
- "inplace_prepare_amplitudes_approx",
127
132
  "inplace_prepare_state",
128
- "inplace_prepare_state_approx",
129
133
  "integer_xor",
130
134
  "modular_add",
131
135
  "modular_add_constant",
@@ -133,9 +137,7 @@ __all__ = [ # noqa: RUF022
133
137
  "parametric_suzuki_trotter",
134
138
  "permute",
135
139
  "prepare_amplitudes",
136
- "prepare_amplitudes_approx",
137
140
  "prepare_state",
138
- "prepare_state_approx",
139
141
  "qdrift",
140
142
  "randomized_benchmarking",
141
143
  "real_xor_constant",
@@ -144,6 +146,7 @@ __all__ = [ # noqa: RUF022
144
146
  "suzuki_trotter",
145
147
  "unitary",
146
148
  "RESET",
149
+ "select_rotation",
147
150
  ]
148
151
  BUILTIN_FUNCTION_DECLARATIONS = {
149
152
  func_decl.name: func_decl for func_decl in STD_QMOD_OPERATORS + CORE_LIB_DECLS
@@ -144,39 +144,3 @@ def inplace_prepare_amplitudes(
144
144
 
145
145
  """
146
146
  pass
147
-
148
-
149
- @qfunc(external=True)
150
- def inplace_prepare_amplitudes_approx(
151
- amplitudes: CArray[CReal],
152
- bound: CReal,
153
- target: QArray[QBit, Literal["log(amplitudes.len, 2)"]],
154
- ) -> None:
155
- pass
156
-
157
-
158
- @qfunc(external=True)
159
- def prepare_amplitudes_approx(
160
- amplitudes: CArray[CReal],
161
- bound: CReal,
162
- out: Output[QArray[QBit, Literal["log(amplitudes.len, 2)"]]],
163
- ) -> None:
164
- pass
165
-
166
-
167
- @qfunc(external=True)
168
- def inplace_prepare_state_approx(
169
- probabilities: CArray[CReal],
170
- bound: CReal,
171
- target: QArray[QBit, Literal["log(probabilities.len, 2)"]],
172
- ) -> None:
173
- pass
174
-
175
-
176
- @qfunc(external=True)
177
- def prepare_state_approx(
178
- probabilities: CArray[CReal],
179
- bound: CReal,
180
- out: Output[QArray[QBit, Literal["log(probabilities.len, 2)"]]],
181
- ) -> None:
182
- pass
@@ -66,6 +66,58 @@ def add_inplace_right(
66
66
  pass
67
67
 
68
68
 
69
+ @qperm(external=True)
70
+ def canonical_add(
71
+ left: Const[QArray],
72
+ extend_left: CBool,
73
+ right: QArray,
74
+ ) -> None:
75
+ """
76
+ [Qmod core-library function]
77
+
78
+ Adds two quantum variables representing integers (signed or unsigned), storing the
79
+ result in the second variable (in-place):
80
+
81
+ $$
82
+ \\left|\\text{left}\\right\\rangle \\left|\\text{right}\\right\\rangle
83
+ \\mapsto
84
+ \\left|\\text{left}\\right\\rangle \\left|\\left(\\text{right} +
85
+ \\text{left}\\right) \\bmod 2^{\\text{right.size}} \\right\\rangle
86
+ $$
87
+
88
+ Args:
89
+ left: The out-of-place argument for the addition.
90
+ extend_left: Whether to sign-extend the left argument.
91
+ right: The in-place argument for the addition, holds the final result.
92
+ """
93
+ pass
94
+
95
+
96
+ @qperm(external=True)
97
+ def canonical_add_constant(
98
+ left: CInt,
99
+ right: QArray,
100
+ ) -> None:
101
+ """
102
+ [Qmod core-library function]
103
+
104
+ Adds an integer constant to a quantum variable representing an integer (signed or
105
+ unsigned):
106
+
107
+ $$
108
+ \\left|\\text{right}\\right\\rangle
109
+ \\mapsto
110
+ \\left|\\left(\\text{right} +
111
+ \\text{left}\\right) \\bmod 2^{\\text{right.size}} \\right\\rangle
112
+ $$
113
+
114
+ Args:
115
+ left: The constant argument for the addition.
116
+ right: The quantum argument for the addition, holds the final result.
117
+ """
118
+ pass
119
+
120
+
69
121
  @qperm(external=True)
70
122
  def modular_add(left: Const[QArray[QBit]], right: QArray[QBit]) -> None:
71
123
  pass
@@ -0,0 +1,23 @@
1
+ from classiq.qmod.builtins.enums import Pauli
2
+ from classiq.qmod.qfunc import qfunc
3
+ from classiq.qmod.qmod_parameter import CArray, CReal
4
+ from classiq.qmod.qmod_variable import QBit, QNum
5
+
6
+
7
+ @qfunc(external=True)
8
+ def select_rotation(
9
+ basis: Pauli, angles: CArray[CReal], selector: QNum, target: QBit
10
+ ) -> None:
11
+ """
12
+ [Qmod core-library function]
13
+
14
+ Applies a set of controlled single-qubit rotations to a target qubit, using
15
+ a specified rotation axis and a list of rotation angles.
16
+
17
+ Args:
18
+ basis: The Pauli operator defining the rotation axis.
19
+ angles: The list of rotation angles in radians. The list must have length 2**selector.size.
20
+ selector: The qubits that act as the selection register.
21
+ target: The qubit on which the selected rotation is applied.
22
+ """
23
+ pass
@@ -5,3 +5,13 @@ from classiq.qmod.qmod_variable import Const, QArray, QBit
5
5
  @qperm(external=True)
6
6
  def mcx(ctrl: Const[QArray[QBit]], target: QBit) -> None:
7
7
  pass
8
+
9
+
10
+ @qperm(external=True)
11
+ def mcx_gray_code(ctrl: Const[QArray[QBit]], target: QBit) -> None:
12
+ pass
13
+
14
+
15
+ @qperm(external=True)
16
+ def mcx_hybrid_rec(ctrl: Const[QArray[QBit]], target: QBit, aux: QArray[QBit]) -> None:
17
+ pass
@@ -1,5 +1,5 @@
1
1
  from dataclasses import dataclass, fields, is_dataclass
2
- from typing import Union
2
+ from typing import Any, Union
3
3
 
4
4
  from classiq.interface.exceptions import ClassiqValueError
5
5
  from classiq.interface.generator.types.struct_declaration import StructDeclaration
@@ -117,13 +117,21 @@ class SparsePauliOp:
117
117
  def __rmul__(self, obj: Union[float, "SparsePauliOp"]) -> "SparsePauliOp":
118
118
  return self.__mul__(obj)
119
119
 
120
- def __add__(self, other: "SparsePauliOp") -> "SparsePauliOp":
120
+ def __add__(self, other: Any) -> "SparsePauliOp":
121
+ if not isinstance(other, SparsePauliOp):
122
+ raise ClassiqValueError(
123
+ f"Cannot add an object of type {type(other).__name__} to SparsePauliOp"
124
+ )
121
125
  return SparsePauliOp(
122
126
  terms=LenList(self.terms + other.terms),
123
127
  num_qubits=max(self.num_qubits, other.num_qubits),
124
128
  )
125
129
 
126
- def __sub__(self, other: "SparsePauliOp") -> "SparsePauliOp":
130
+ def __sub__(self, other: Any) -> "SparsePauliOp":
131
+ if not isinstance(other, SparsePauliOp):
132
+ raise ClassiqValueError(
133
+ f"Cannot substruct an object of type {type(other).__name__} to SparsePauliOp"
134
+ )
127
135
  return self + -1.0 * other
128
136
 
129
137
  def __str__(self) -> str:
@@ -136,6 +144,17 @@ class SparsePauliOp:
136
144
  for term in self.terms
137
145
  )
138
146
 
147
+ def __truediv__(self, other: Any) -> "SparsePauliOp":
148
+ if not isinstance(other, (int, float)):
149
+ raise ClassiqValueError(
150
+ f"Cannot divide SparsePauliOp by object of type {type(other).__name__}"
151
+ )
152
+ if other == 0:
153
+ raise ClassiqValueError("Cannot divide by zero")
154
+ return SparsePauliOp(terms=self.terms, num_qubits=self.num_qubits) * float(
155
+ 1 / other
156
+ )
157
+
139
158
 
140
159
  @dataclass
141
160
  class CombinatorialOptimizationSolution: