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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. classiq/_internals/api_wrapper.py +27 -0
  2. classiq/applications/chemistry/chemistry_model_constructor.py +2 -4
  3. classiq/applications/chemistry/hartree_fock.py +68 -0
  4. classiq/applications/chemistry/mapping.py +85 -0
  5. classiq/applications/chemistry/op_utils.py +79 -0
  6. classiq/applications/chemistry/problems.py +195 -0
  7. classiq/applications/chemistry/ucc.py +109 -0
  8. classiq/applications/chemistry/z2_symmetries.py +368 -0
  9. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +30 -1
  10. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +2 -2
  11. classiq/{model_expansions/evaluators → evaluators}/arg_type_match.py +12 -4
  12. classiq/{model_expansions/evaluators → evaluators}/argument_types.py +3 -3
  13. classiq/{model_expansions/evaluators → evaluators}/classical_expression.py +1 -1
  14. classiq/{model_expansions/evaluators → evaluators}/classical_type_inference.py +3 -4
  15. classiq/{model_expansions/evaluators → evaluators}/parameter_types.py +17 -15
  16. classiq/execution/__init__.py +12 -1
  17. classiq/execution/execution_session.py +189 -43
  18. classiq/execution/jobs.py +26 -1
  19. classiq/execution/qnn.py +2 -2
  20. classiq/execution/user_budgets.py +39 -0
  21. classiq/interface/_version.py +1 -1
  22. classiq/interface/constants.py +1 -0
  23. classiq/interface/execution/primitives.py +29 -1
  24. classiq/interface/executor/estimate_cost.py +35 -0
  25. classiq/interface/executor/execution_result.py +13 -0
  26. classiq/interface/executor/result.py +116 -1
  27. classiq/interface/executor/user_budget.py +26 -33
  28. classiq/interface/generator/application_apis/finance_declarations.py +3 -3
  29. classiq/interface/generator/expressions/atomic_expression_functions.py +11 -3
  30. classiq/interface/generator/expressions/proxies/classical/any_classical_value.py +0 -6
  31. classiq/interface/generator/functions/classical_type.py +2 -35
  32. classiq/interface/generator/functions/concrete_types.py +0 -3
  33. classiq/interface/generator/functions/type_modifier.py +22 -0
  34. classiq/interface/generator/generated_circuit_data.py +5 -16
  35. classiq/interface/generator/model/model.py +8 -0
  36. classiq/interface/generator/quantum_program.py +0 -13
  37. classiq/interface/generator/types/compilation_metadata.py +0 -3
  38. classiq/interface/helpers/model_normalizer.py +2 -2
  39. classiq/interface/ide/visual_model.py +6 -2
  40. classiq/interface/model/model.py +12 -7
  41. classiq/interface/model/port_declaration.py +4 -2
  42. classiq/interface/pretty_print/__init__.py +0 -0
  43. classiq/{qmod/native → interface/pretty_print}/expression_to_qmod.py +18 -11
  44. classiq/interface/server/routes.py +4 -0
  45. classiq/model_expansions/atomic_expression_functions_defs.py +42 -5
  46. classiq/model_expansions/capturing/captured_vars.py +21 -8
  47. classiq/model_expansions/interpreters/base_interpreter.py +3 -3
  48. classiq/model_expansions/quantum_operations/allocate.py +1 -1
  49. classiq/model_expansions/quantum_operations/assignment_result_processor.py +1 -1
  50. classiq/model_expansions/quantum_operations/bind.py +2 -2
  51. classiq/model_expansions/quantum_operations/call_emitter.py +42 -36
  52. classiq/model_expansions/quantum_operations/variable_decleration.py +1 -1
  53. classiq/model_expansions/scope_initialization.py +3 -3
  54. classiq/model_expansions/transformers/model_renamer.py +16 -5
  55. classiq/model_expansions/transformers/{type_qualifier_inference.py → type_modifier_inference.py} +134 -100
  56. classiq/model_expansions/visitors/symbolic_param_inference.py +10 -7
  57. classiq/open_library/functions/__init__.py +3 -0
  58. classiq/open_library/functions/amplitude_amplification.py +10 -18
  59. classiq/open_library/functions/discrete_sine_cosine_transform.py +5 -5
  60. classiq/open_library/functions/grover.py +14 -6
  61. classiq/open_library/functions/modular_exponentiation.py +22 -20
  62. classiq/open_library/functions/state_preparation.py +18 -1
  63. classiq/qmod/__init__.py +2 -2
  64. classiq/qmod/builtins/enums.py +23 -0
  65. classiq/qmod/builtins/functions/__init__.py +2 -0
  66. classiq/qmod/builtins/functions/allocation.py +2 -2
  67. classiq/qmod/builtins/functions/arithmetic.py +16 -8
  68. classiq/qmod/builtins/functions/exponentiation.py +32 -4
  69. classiq/qmod/builtins/functions/standard_gates.py +7 -7
  70. classiq/qmod/builtins/structs.py +55 -3
  71. classiq/qmod/declaration_inferrer.py +8 -7
  72. classiq/qmod/native/pretty_printer.py +7 -11
  73. classiq/qmod/pretty_print/expression_to_python.py +2 -1
  74. classiq/qmod/pretty_print/pretty_printer.py +7 -12
  75. classiq/qmod/python_classical_type.py +12 -5
  76. classiq/qmod/qfunc.py +1 -1
  77. classiq/qmod/qmod_constant.py +2 -5
  78. classiq/qmod/qmod_parameter.py +2 -5
  79. classiq/qmod/qmod_variable.py +66 -25
  80. classiq/qmod/quantum_expandable.py +4 -2
  81. classiq/qmod/quantum_function.py +7 -2
  82. classiq/qmod/semantics/annotation/qstruct_annotator.py +1 -1
  83. classiq/qmod/semantics/validation/main_validation.py +1 -9
  84. classiq/qmod/semantics/validation/type_hints.py +9 -9
  85. classiq/qmod/utilities.py +0 -2
  86. classiq/qmod/write_qmod.py +1 -1
  87. classiq/synthesis.py +0 -2
  88. {classiq-0.82.1.dist-info → classiq-0.84.0.dist-info}/METADATA +4 -1
  89. {classiq-0.82.1.dist-info → classiq-0.84.0.dist-info}/RECORD +95 -86
  90. classiq/interface/generator/functions/type_qualifier.py +0 -22
  91. /classiq/{model_expansions/evaluators → evaluators}/__init__.py +0 -0
  92. /classiq/{model_expansions/evaluators → evaluators}/control.py +0 -0
  93. /classiq/{model_expansions → evaluators}/expression_evaluator.py +0 -0
  94. /classiq/{model_expansions/evaluators → evaluators}/quantum_type_utils.py +0 -0
  95. /classiq/{model_expansions/evaluators → evaluators}/type_type_match.py +0 -0
  96. {classiq-0.82.1.dist-info → classiq-0.84.0.dist-info}/WHEEL +0 -0
@@ -1,5 +1,6 @@
1
1
  from classiq.open_library.functions.qft_functions import qft, qft_no_swap
2
2
  from classiq.qmod.builtins.classical_functions import qft_const_adder_phase
3
+ from classiq.qmod.builtins.functions.allocation import free
3
4
  from classiq.qmod.builtins.functions.standard_gates import PHASE, SWAP, X
4
5
  from classiq.qmod.builtins.operations import (
5
6
  allocate,
@@ -11,7 +12,7 @@ from classiq.qmod.builtins.operations import (
11
12
  )
12
13
  from classiq.qmod.cparam import CInt
13
14
  from classiq.qmod.qfunc import qfunc
14
- from classiq.qmod.qmod_variable import QArray, QBit
15
+ from classiq.qmod.qmod_variable import Const, Permutable, QArray, QBit
15
16
  from classiq.qmod.symbolic import min, mod_inverse
16
17
 
17
18
 
@@ -50,8 +51,10 @@ def qft_space_add_const(value: CInt, phi_b: QArray[QBit]) -> None:
50
51
  )
51
52
 
52
53
 
53
- @qfunc
54
- def cc_modular_add(n: CInt, a: CInt, phi_b: QArray[QBit], c1: QBit, c2: QBit) -> None:
54
+ @qfunc(unchecked=["phi_b"])
55
+ def cc_modular_add(
56
+ n: CInt, a: CInt, phi_b: Permutable[QArray[QBit]], c1: Const[QBit], c2: Const[QBit]
57
+ ) -> None:
55
58
  """
56
59
  [Qmod Classiq-library function]
57
60
 
@@ -69,11 +72,9 @@ def cc_modular_add(n: CInt, a: CInt, phi_b: QArray[QBit], c1: QBit, c2: QBit) ->
69
72
  ctrl: QArray[QBit] = QArray()
70
73
  aux = QBit()
71
74
 
75
+ allocate(aux)
72
76
  within_apply(
73
- lambda: (
74
- allocate(aux),
75
- bind([c1, c2], ctrl),
76
- ),
77
+ lambda: bind([c1, c2], ctrl),
77
78
  lambda: (
78
79
  control(ctrl, lambda: qft_space_add_const(a, phi_b)),
79
80
  invert(lambda: qft_space_add_const(n, phi_b)),
@@ -87,11 +88,16 @@ def cc_modular_add(n: CInt, a: CInt, phi_b: QArray[QBit], c1: QBit, c2: QBit) ->
87
88
  ),
88
89
  ),
89
90
  )
91
+ free(aux)
90
92
 
91
93
 
92
- @qfunc
94
+ @qfunc(unchecked=["b"])
93
95
  def c_modular_multiply(
94
- n: CInt, a: CInt, b: QArray[QBit], x: QArray[QBit], ctrl: QBit
96
+ n: CInt,
97
+ a: CInt,
98
+ b: Permutable[QArray[QBit]],
99
+ x: Const[QArray[QBit]],
100
+ ctrl: Const[QBit],
95
101
  ) -> None:
96
102
  """
97
103
  [Qmod Classiq-library function]
@@ -118,7 +124,7 @@ def c_modular_multiply(
118
124
 
119
125
 
120
126
  @qfunc
121
- def multiswap(x: QArray[QBit], y: QArray[QBit]) -> None:
127
+ def multiswap(x: Permutable[QArray[QBit]], y: Permutable[QArray[QBit]]) -> None:
122
128
  """
123
129
  [Qmod Classiq-library function]
124
130
 
@@ -150,16 +156,12 @@ def inplace_c_modular_multiply(n: CInt, a: CInt, x: QArray[QBit], ctrl: QBit) ->
150
156
  x: The quantum factor.
151
157
  ctrl: The control bit.
152
158
  """
153
- b: QArray[QBit] = QArray()
154
-
155
- within_apply(
156
- lambda: allocate(x.len + 1, b),
157
- lambda: (
158
- c_modular_multiply(n, a, b, x, ctrl),
159
- control(ctrl, lambda: multiswap(x, b)),
160
- invert(lambda: c_modular_multiply(n, mod_inverse(a, n), b, x, ctrl)),
161
- ),
162
- )
159
+ b: QArray[QBit] = QArray(length=x.len + 1)
160
+ allocate(b)
161
+ c_modular_multiply(n, a, b, x, ctrl)
162
+ control(ctrl, lambda: multiswap(x, b))
163
+ invert(lambda: c_modular_multiply(n, mod_inverse(a, n), b, x, ctrl))
164
+ free(b)
163
165
 
164
166
 
165
167
  @qfunc
@@ -458,7 +458,7 @@ def prepare_dicke_state_unary_input(max_k: int, qvar: QArray[QBit]) -> None:
458
458
  """
459
459
  if qvar.len > max(1, max_k):
460
460
  _dicke_split_cycle_shift(max_k, qvar)
461
- prepare_dicke_state_unary_input(qmin(max_k, qvar.len - 2), qvar[1 : qvar.len])
461
+ prepare_dicke_state_unary_input(min(max_k, qvar.len - 2), qvar[1 : qvar.len])
462
462
 
463
463
 
464
464
  @qfunc
@@ -483,3 +483,20 @@ def prepare_dicke_state(k: int, qvar: QArray[QBit]) -> None:
483
483
  if k > 0:
484
484
  apply_to_all(X, qvar[0:k])
485
485
  prepare_dicke_state_unary_input(k, qvar)
486
+
487
+
488
+ @qfunc
489
+ def prepare_basis_state(state: list[bool], arr: Output[QArray]) -> None:
490
+ """
491
+ [Qmod Classiq-library function]
492
+
493
+ Initializes a quantum array in the specified basis state.
494
+
495
+ Args:
496
+ values: The desired basis state, given as a list of boolean values for each qubit.
497
+ arr: The quantum array to prepare.
498
+ """
499
+ allocate(len(state), arr)
500
+ for idx, value in enumerate(state):
501
+ if value:
502
+ X(arr[idx])
classiq/qmod/__init__.py CHANGED
@@ -6,7 +6,7 @@ from .expression_query import get_expression_numeric_attributes
6
6
  from .qfunc import qfunc
7
7
  from .qmod_constant import QConstant
8
8
  from .qmod_parameter import Array, CArray, CBool, CInt, CReal
9
- from .qmod_variable import Const, Input, Output, QArray, QBit, QFree, QNum, QStruct
9
+ from .qmod_variable import Const, Input, Output, Permutable, QArray, QBit, QNum, QStruct
10
10
  from .quantum_callable import QCallable, QCallableList
11
11
  from .write_qmod import write_qmod
12
12
 
@@ -19,7 +19,7 @@ __all__ = [
19
19
  "Input",
20
20
  "Output",
21
21
  "Const",
22
- "QFree",
22
+ "Permutable",
23
23
  "QArray",
24
24
  "QBit",
25
25
  "QNum",
@@ -1,7 +1,11 @@
1
1
  from enum import IntEnum
2
+ from typing import TYPE_CHECKING
2
3
 
3
4
  from classiq.interface.generator.types.enum_declaration import EnumDeclaration
4
5
 
6
+ if TYPE_CHECKING:
7
+ from classiq.qmod.builtins.structs import SparsePauliOp
8
+
5
9
 
6
10
  class Element(IntEnum):
7
11
  H = 0
@@ -176,6 +180,25 @@ class Pauli(IntEnum):
176
180
  """Z (int): Pauli-Z matrix, represented by the integer 3. \n
177
181
  $Z = \\begin{bmatrix} 1 & 0 \\\\ 0 & -1 \\end{bmatrix}$"""
178
182
 
183
+ def __call__(self, index: int) -> "SparsePauliOp":
184
+ from classiq.qmod.builtins.structs import (
185
+ IndexedPauli,
186
+ SparsePauliOp,
187
+ SparsePauliTerm,
188
+ )
189
+
190
+ return SparsePauliOp(
191
+ terms=[ # type:ignore[arg-type]
192
+ SparsePauliTerm(
193
+ paulis=[ # type:ignore[arg-type]
194
+ IndexedPauli(pauli=self, index=index) # type:ignore[arg-type]
195
+ ],
196
+ coefficient=1.0, # type:ignore[arg-type]
197
+ )
198
+ ],
199
+ num_qubits=index + 1, # type:ignore[arg-type]
200
+ )
201
+
179
202
 
180
203
  class QSVMFeatureMapEntanglement(IntEnum):
181
204
  FULL = 0
@@ -71,6 +71,7 @@ CORE_LIB_DECLS = [
71
71
  single_pauli_exponent,
72
72
  commuting_paulis_exponent,
73
73
  suzuki_trotter,
74
+ multi_suzuki_trotter,
74
75
  parametric_suzuki_trotter,
75
76
  sparse_suzuki_trotter,
76
77
  qdrift,
@@ -132,6 +133,7 @@ __all__ = [ # noqa: RUF022
132
133
  "molecule_hartree_fock",
133
134
  "molecule_hva",
134
135
  "molecule_ucc",
136
+ "multi_suzuki_trotter",
135
137
  "parametric_suzuki_trotter",
136
138
  "pauli_feature_map",
137
139
  "permute",
@@ -2,11 +2,11 @@ from typing import Literal
2
2
 
3
3
  from classiq.qmod.qfunc import qfunc
4
4
  from classiq.qmod.qmod_parameter import CArray, CReal
5
- from classiq.qmod.qmod_variable import Input, Output, QArray, QBit, QFree
5
+ from classiq.qmod.qmod_variable import Input, Output, Permutable, QArray, QBit
6
6
 
7
7
 
8
8
  @qfunc(external=True)
9
- def free(in_: QFree[Input[QArray[QBit]]]) -> None:
9
+ def free(in_: Permutable[Input[QArray[QBit]]]) -> None:
10
10
  """
11
11
  [Qmod core-library function]
12
12
 
@@ -2,7 +2,15 @@ from typing import Literal
2
2
 
3
3
  from classiq.qmod.qfunc import qfunc
4
4
  from classiq.qmod.qmod_parameter import CArray, CBool, CReal
5
- from classiq.qmod.qmod_variable import Const, Input, Output, QArray, QBit, QFree, QNum
5
+ from classiq.qmod.qmod_variable import (
6
+ Const,
7
+ Input,
8
+ Output,
9
+ Permutable,
10
+ QArray,
11
+ QBit,
12
+ QNum,
13
+ )
6
14
 
7
15
 
8
16
  @qfunc(external=True)
@@ -26,7 +34,7 @@ def unitary(
26
34
  def add(
27
35
  left: Const[QNum],
28
36
  right: Const[QNum],
29
- result: QFree[
37
+ result: Permutable[
30
38
  Output[
31
39
  QNum[
32
40
  Literal["result_size"],
@@ -45,8 +53,8 @@ def add(
45
53
  @qfunc(external=True)
46
54
  def add_inplace_right(
47
55
  left: Const[QNum],
48
- right: QFree[Input[QNum]],
49
- result: QFree[
56
+ right: Permutable[Input[QNum]],
57
+ result: Permutable[
50
58
  Output[
51
59
  QNum[
52
60
  Literal["result_size"],
@@ -63,20 +71,20 @@ def add_inplace_right(
63
71
 
64
72
 
65
73
  @qfunc(external=True)
66
- def modular_add(left: Const[QArray[QBit]], right: QFree[QArray[QBit]]) -> None:
74
+ def modular_add(left: Const[QArray[QBit]], right: Permutable[QArray[QBit]]) -> None:
67
75
  pass
68
76
 
69
77
 
70
78
  @qfunc(external=True)
71
- def modular_add_constant(left: CReal, right: QFree[QNum]) -> None:
79
+ def modular_add_constant(left: CReal, right: Permutable[QNum]) -> None:
72
80
  pass
73
81
 
74
82
 
75
83
  @qfunc(external=True)
76
- def integer_xor(left: Const[QArray[QBit]], right: QFree[QArray[QBit]]) -> None:
84
+ def integer_xor(left: Const[QArray[QBit]], right: Permutable[QArray[QBit]]) -> None:
77
85
  pass
78
86
 
79
87
 
80
88
  @qfunc(external=True)
81
- def real_xor_constant(left: CReal, right: QFree[QNum]) -> None:
89
+ def real_xor_constant(left: CReal, right: Permutable[QNum]) -> None:
82
90
  pass
@@ -55,13 +55,11 @@ def commuting_paulis_exponent(
55
55
 
56
56
  @qfunc(external=True)
57
57
  def suzuki_trotter(
58
- pauli_operator: CArray[PauliTerm],
58
+ pauli_operator: SparsePauliOp, # FIXME: Rename to hamiltonian (CLS-2912)
59
59
  evolution_coefficient: CReal,
60
60
  order: CInt,
61
61
  repetitions: CInt,
62
- qbv: QArray[
63
- QBit, Literal["get_field(get_field(pauli_operator[0], 'pauli'), 'len')"]
64
- ],
62
+ qbv: QArray[QBit], # FIXME: Add length expr (CLS-2912)
65
63
  ) -> None:
66
64
  """
67
65
  [Qmod core-library function]
@@ -82,6 +80,36 @@ def suzuki_trotter(
82
80
  pass
83
81
 
84
82
 
83
+ @qfunc(external=True)
84
+ def multi_suzuki_trotter(
85
+ hamiltonians: CArray[SparsePauliOp],
86
+ evolution_coefficients: CArray[CReal],
87
+ order: CInt,
88
+ repetitions: CInt,
89
+ qbv: QArray,
90
+ ) -> None:
91
+ """
92
+ [Qmod core-library function]
93
+
94
+ Applies the Suzuki-Trotter decomposition jointly to a sum of Hamiltonians
95
+ (represented as Pauli operators), each with its separate evolution coefficient,
96
+ approximating $\\exp{-iH_1t_1+H_2t_2+\\dots}$ with a specified order and number of
97
+ repetitions.
98
+
99
+ The Suzuki-Trotter decomposition is a method for approximating the exponential of a sum of operators by a product of exponentials of each operator.
100
+ The Suzuki-Trotter decomposition of a given order nullifies the error of the Taylor series expansion of the product of exponentials up to that order.
101
+ The error of a Suzuki-Trotter decomposition decreases as the order and number of repetitions increase.
102
+
103
+ Args:
104
+ hamiltonians: The hamitonians to be exponentiated, in sparse representation.
105
+ evolution_coefficients: The hamiltonian coefficients (can be link-time).
106
+ order: The order of the Suzuki-Trotter decomposition.
107
+ repetitions: The number of repetitions of the Suzuki-Trotter decomposition.
108
+ qbv: The target quantum variable of the exponentiation.
109
+ """
110
+ pass
111
+
112
+
85
113
  @qfunc(external=True)
86
114
  def parametric_suzuki_trotter(
87
115
  paulis: CArray[CArray[Pauli]],
@@ -2,7 +2,7 @@ from typing import Literal
2
2
 
3
3
  from classiq.qmod.qfunc import qfunc
4
4
  from classiq.qmod.qmod_parameter import CReal
5
- from classiq.qmod.qmod_variable import Const, QArray, QBit, QFree
5
+ from classiq.qmod.qmod_variable import Const, Permutable, QArray, QBit
6
6
 
7
7
 
8
8
  @qfunc(external=True)
@@ -25,7 +25,7 @@ def H(target: QBit) -> None:
25
25
 
26
26
 
27
27
  @qfunc(external=True)
28
- def X(target: QFree[QBit]) -> None:
28
+ def X(target: Permutable[QBit]) -> None:
29
29
  """
30
30
  [Qmod core-library function]
31
31
 
@@ -44,7 +44,7 @@ def X(target: QFree[QBit]) -> None:
44
44
 
45
45
 
46
46
  @qfunc(external=True)
47
- def Y(target: QFree[QBit]) -> None:
47
+ def Y(target: Permutable[QBit]) -> None:
48
48
  """
49
49
  [Qmod core-library function]
50
50
 
@@ -370,7 +370,7 @@ def CH(ctrl: Const[QBit], target: QBit) -> None:
370
370
 
371
371
 
372
372
  @qfunc(external=True)
373
- def CX(ctrl: Const[QBit], target: QFree[QBit]) -> None:
373
+ def CX(ctrl: Const[QBit], target: Permutable[QBit]) -> None:
374
374
  """
375
375
  [Qmod core-library function]
376
376
 
@@ -395,7 +395,7 @@ def CX(ctrl: Const[QBit], target: QFree[QBit]) -> None:
395
395
 
396
396
 
397
397
  @qfunc(external=True)
398
- def CY(ctrl: Const[QBit], target: QFree[QBit]) -> None:
398
+ def CY(ctrl: Const[QBit], target: Permutable[QBit]) -> None:
399
399
  """
400
400
  [Qmod core-library function]
401
401
 
@@ -549,7 +549,7 @@ def CPHASE(theta: CReal, ctrl: Const[QBit], target: Const[QBit]) -> None:
549
549
 
550
550
 
551
551
  @qfunc(external=True)
552
- def SWAP(qbit0: QFree[QBit], qbit1: QFree[QBit]) -> None:
552
+ def SWAP(qbit0: Permutable[QBit], qbit1: Permutable[QBit]) -> None:
553
553
  """
554
554
  [Qmod core-library function]
555
555
 
@@ -623,7 +623,7 @@ def U(theta: CReal, phi: CReal, lam: CReal, gam: CReal, target: QBit) -> None:
623
623
 
624
624
 
625
625
  @qfunc(external=True)
626
- def CCX(ctrl: Const[QArray[QBit, Literal[2]]], target: QFree[QBit]) -> None:
626
+ def CCX(ctrl: Const[QArray[QBit, Literal[2]]], target: Permutable[QBit]) -> None:
627
627
  """
628
628
  [Qmod core-library function]
629
629
 
@@ -1,6 +1,9 @@
1
1
  from dataclasses import dataclass, fields, is_dataclass
2
+ from typing import Union
2
3
 
4
+ from classiq.interface.exceptions import ClassiqValueError
3
5
  from classiq.interface.generator.types.struct_declaration import StructDeclaration
6
+ from classiq.interface.helpers.text_utils import are, readable_list, s
4
7
 
5
8
  from classiq.qmod.builtins.enums import LadderOperator, Pauli
6
9
  from classiq.qmod.cparam import CArray, CBool, CInt, CReal
@@ -56,13 +59,62 @@ class SparsePauliOp:
56
59
  Represents a collection of sparse Pauli operators.
57
60
 
58
61
  Attributes:
59
- paulis (CArray[SparsePauliTerm]): The list of chosen sparse Pauli operators in the term, corresponds to a product of them. (See: SparsePauliTerm)
62
+ terms (CArray[SparsePauliTerm]): The list of chosen sparse Pauli terms, corresponds to a product of them. (See: SparsePauliTerm)
60
63
  num_qubits (CInt): The number of qubits in the Hamiltonian.
61
64
  """
62
65
 
63
- paulis: CArray[SparsePauliTerm]
66
+ terms: CArray[SparsePauliTerm]
64
67
  num_qubits: CInt
65
68
 
69
+ def __mul__(self, obj: Union[float, "SparsePauliOp"]) -> "SparsePauliOp":
70
+ if isinstance(obj, (int, float, complex)):
71
+ return SparsePauliOp(
72
+ terms=[ # type:ignore[arg-type]
73
+ SparsePauliTerm(
74
+ paulis=term.paulis,
75
+ coefficient=obj * term.coefficient,
76
+ )
77
+ for term in self.terms # type:ignore[attr-defined]
78
+ ],
79
+ num_qubits=self.num_qubits,
80
+ )
81
+ if len(self.terms) != 1 or len(obj.terms) != 1: # type:ignore[arg-type]
82
+ raise ClassiqValueError("Cannot attach a pauli to multiple pauli terms")
83
+ existing_indices = {
84
+ indexed_pauli.index for indexed_pauli in self.terms[0].paulis
85
+ }
86
+ added_indices = {indexed_pauli.index for indexed_pauli in obj.terms[0].paulis}
87
+ overlapping_indices = sorted(existing_indices.intersection(added_indices))
88
+ if len(overlapping_indices):
89
+ raise ClassiqValueError(
90
+ f"Pauli{s(overlapping_indices)} at "
91
+ f"{'indices' if len(overlapping_indices) > 1 else 'index'} "
92
+ f"{readable_list(overlapping_indices)} {are(overlapping_indices)} "
93
+ f"already assigned"
94
+ )
95
+ return SparsePauliOp(
96
+ terms=[ # type:ignore[arg-type]
97
+ SparsePauliTerm(
98
+ paulis=self.terms[0].paulis + obj.terms[0].paulis,
99
+ coefficient=self.terms[0].coefficient * obj.terms[0].coefficient,
100
+ )
101
+ ],
102
+ num_qubits=max(
103
+ self.num_qubits, obj.num_qubits # type:ignore[call-overload]
104
+ ),
105
+ )
106
+
107
+ def __rmul__(self, obj: Union[float, "SparsePauliOp"]) -> "SparsePauliOp":
108
+ return self.__mul__(obj)
109
+
110
+ def __add__(self, other: "SparsePauliOp") -> "SparsePauliOp":
111
+ return SparsePauliOp(
112
+ terms=self.terms + other.terms, # type:ignore[arg-type]
113
+ num_qubits=max(
114
+ self.num_qubits, other.num_qubits # type:ignore[call-overload]
115
+ ),
116
+ )
117
+
66
118
 
67
119
  @dataclass
68
120
  class Position:
@@ -167,7 +219,7 @@ BUILTIN_STRUCT_DECLARATIONS = {
167
219
  struct_decl.__name__: StructDeclaration(
168
220
  name=struct_decl.__name__,
169
221
  variables={
170
- field.name: PythonClassicalType().convert(field.type)
222
+ field.name: PythonClassicalType().convert(field.type, nested=True)
171
223
  for field in fields(struct_decl)
172
224
  },
173
225
  )
@@ -19,8 +19,8 @@ from classiq.interface.generator.functions.concrete_types import ConcreteClassic
19
19
  from classiq.interface.generator.functions.port_declaration import (
20
20
  PortDeclarationDirection,
21
21
  )
22
+ from classiq.interface.generator.functions.type_modifier import TypeModifier
22
23
  from classiq.interface.generator.functions.type_name import TypeName
23
- from classiq.interface.generator.functions.type_qualifier import TypeQualifier
24
24
  from classiq.interface.generator.types.enum_declaration import declaration_from_enum
25
25
  from classiq.interface.generator.types.struct_declaration import StructDeclaration
26
26
  from classiq.interface.model.classical_parameter_declaration import (
@@ -75,14 +75,15 @@ class _PythonClassicalType(PythonClassicalType):
75
75
  all_decls = BUILTIN_STRUCT_DECLARATIONS | self.qmodule.type_decls
76
76
  if py_type.__name__ in all_decls:
77
77
  classical_type.set_classical_struct_decl(
78
- all_decls[py_type.__name__].model_copy()
78
+ all_decls[py_type.__name__].model_copy(deep=True)
79
79
  )
80
80
  return classical_type
81
81
 
82
82
  struct_decl = StructDeclaration(
83
83
  name=py_type.__name__,
84
84
  variables={
85
- f.name: self.convert(f.type) for f in dataclasses.fields(py_type)
85
+ f.name: self.convert(f.type, nested=True)
86
+ for f in dataclasses.fields(py_type)
86
87
  },
87
88
  )
88
89
  check_duplicate_types([struct_decl, *self.qmodule.user_types()])
@@ -100,12 +101,12 @@ def python_type_to_qmod(
100
101
 
101
102
 
102
103
  def _extract_port_decl(name: Optional[str], py_type: Any) -> AnonPortDeclaration:
103
- quantum_type, direction, qualifier = get_port_from_type_hint(py_type)
104
+ quantum_type, direction, modifier = get_port_from_type_hint(py_type)
104
105
  param = AnonPortDeclaration(
105
106
  name=None,
106
107
  direction=direction,
107
108
  quantum_type=quantum_type,
108
- type_qualifier=qualifier,
109
+ type_modifier=modifier,
109
110
  )
110
111
  if name is not None:
111
112
  param = param.rename(name)
@@ -163,7 +164,7 @@ def _unpacked_annotated(arg_0: Any, args: Any) -> _AnnotatedAlias:
163
164
 
164
165
  def _get_param_name(py_type_args: Any) -> Optional[str]:
165
166
  if isinstance(py_type_args[-1], str) and not isinstance(
166
- py_type_args[-1], (PortDeclarationDirection, TypeQualifier)
167
+ py_type_args[-1], (PortDeclarationDirection, TypeModifier)
167
168
  ):
168
169
  return py_type_args[-1]
169
170
  elif py_type_args[-1] is Literal:
@@ -176,7 +177,7 @@ def _validate_annotations(py_type_args: Any, py_type: Any) -> None:
176
177
  for arg in py_type_args[1:-1]:
177
178
  if (
178
179
  isinstance(arg, str)
179
- and not isinstance(arg, (PortDeclarationDirection, TypeQualifier))
180
+ and not isinstance(arg, (PortDeclarationDirection, TypeModifier))
180
181
  ) or arg is Literal:
181
182
  raise ClassiqValueError(
182
183
  f"Operand parameter declaration must be of the form <param-type> or "
@@ -1,13 +1,13 @@
1
1
  from collections.abc import Mapping
2
2
  from typing import Optional, Union
3
3
 
4
+ from classiq.interface.constants import DEFAULT_DECIMAL_PRECISION
4
5
  from classiq.interface.exceptions import ClassiqInternalError
5
6
  from classiq.interface.generator.constant import Constant
6
7
  from classiq.interface.generator.expressions.expression import Expression
7
8
  from classiq.interface.generator.functions.classical_type import (
8
9
  Bool,
9
10
  ClassicalArray,
10
- ClassicalList,
11
11
  ClassicalTuple,
12
12
  Integer,
13
13
  Real,
@@ -19,8 +19,8 @@ from classiq.interface.generator.functions.concrete_types import (
19
19
  from classiq.interface.generator.functions.port_declaration import (
20
20
  PortDeclarationDirection,
21
21
  )
22
+ from classiq.interface.generator.functions.type_modifier import TypeModifier
22
23
  from classiq.interface.generator.functions.type_name import TypeName
23
- from classiq.interface.generator.functions.type_qualifier import TypeQualifier
24
24
  from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
25
25
  from classiq.interface.generator.types.enum_declaration import EnumDeclaration
26
26
  from classiq.interface.generator.types.qstruct_declaration import QStructDeclaration
@@ -85,11 +85,10 @@ from classiq.interface.model.variable_declaration_statement import (
85
85
  VariableDeclarationStatement,
86
86
  )
87
87
  from classiq.interface.model.within_apply_operation import WithinApply
88
+ from classiq.interface.pretty_print.expression_to_qmod import transform_expression
88
89
 
89
90
  from classiq.open_library.functions import OPEN_LIBRARY_FUNCTIONS
90
- from classiq.qmod.native.expression_to_qmod import transform_expression
91
91
  from classiq.qmod.semantics.annotation.call_annotation import resolve_function_calls
92
- from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
93
92
 
94
93
 
95
94
  class DSLPrettyPrinter(ModelVisitor):
@@ -196,9 +195,9 @@ class DSLPrettyPrinter(ModelVisitor):
196
195
  return f"{var_decl.name}: {self.visit(var_decl.quantum_type)}"
197
196
 
198
197
  def visit_AnonPortDeclaration(self, port_decl: AnonPortDeclaration) -> str:
199
- qualifier_str = (
200
- f"{port_decl.type_qualifier} "
201
- if port_decl.type_qualifier in [TypeQualifier.Const, TypeQualifier.QFree]
198
+ modifier_str = (
199
+ f"{port_decl.type_modifier} "
200
+ if port_decl.type_modifier in [TypeModifier.Const, TypeModifier.Permutable]
202
201
  else ""
203
202
  )
204
203
  dir_str = (
@@ -208,7 +207,7 @@ class DSLPrettyPrinter(ModelVisitor):
208
207
  )
209
208
  param_name = f"{port_decl.name}: " if port_decl.name is not None else ""
210
209
  return (
211
- f"{qualifier_str}{dir_str}{param_name}{self.visit(port_decl.quantum_type)}"
210
+ f"{modifier_str}{dir_str}{param_name}{self.visit(port_decl.quantum_type)}"
212
211
  )
213
212
 
214
213
  def visit_QuantumBit(self, qtype: QuantumBit) -> str:
@@ -248,9 +247,6 @@ class DSLPrettyPrinter(ModelVisitor):
248
247
  def visit_Bool(self, ctbool: Bool) -> str:
249
248
  return "bool"
250
249
 
251
- def visit_ClassicalList(self, ctlist: ClassicalList) -> str:
252
- return f"{self.visit(ctlist.element_type)}[]"
253
-
254
250
  def visit_ClassicalArray(self, ctarray: ClassicalArray) -> str:
255
251
  element_type = self.visit(ctarray.element_type)
256
252
  if ctarray.length is not None:
@@ -6,8 +6,9 @@ from typing import Callable
6
6
 
7
7
  import numpy as np
8
8
 
9
+ from classiq.interface.constants import DEFAULT_DECIMAL_PRECISION
10
+
9
11
  import classiq
10
- from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
11
12
 
12
13
  IDENTIFIER = re.compile(r"[a-zA-Z_]\w*")
13
14
  BINARY_OPS: Mapping[type[ast.operator], str] = {
@@ -3,13 +3,13 @@ from typing import Optional, Union, cast
3
3
 
4
4
  import black
5
5
 
6
+ from classiq.interface.constants import DEFAULT_DECIMAL_PRECISION
6
7
  from classiq.interface.exceptions import ClassiqInternalError
7
8
  from classiq.interface.generator.constant import Constant
8
9
  from classiq.interface.generator.expressions.expression import Expression
9
10
  from classiq.interface.generator.functions.classical_type import (
10
11
  Bool,
11
12
  ClassicalArray,
12
- ClassicalList,
13
13
  ClassicalTuple,
14
14
  Integer,
15
15
  Real,
@@ -21,8 +21,8 @@ from classiq.interface.generator.functions.concrete_types import (
21
21
  from classiq.interface.generator.functions.port_declaration import (
22
22
  PortDeclarationDirection,
23
23
  )
24
+ from classiq.interface.generator.functions.type_modifier import TypeModifier
24
25
  from classiq.interface.generator.functions.type_name import TypeName
25
- from classiq.interface.generator.functions.type_qualifier import TypeQualifier
26
26
  from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
27
27
  from classiq.interface.generator.types.enum_declaration import EnumDeclaration
28
28
  from classiq.interface.generator.types.qstruct_declaration import QStructDeclaration
@@ -88,7 +88,6 @@ from classiq.interface.model.within_apply_operation import WithinApply
88
88
  import classiq
89
89
  from classiq.qmod.builtins.functions import BUILTIN_FUNCTION_DECLARATIONS
90
90
  from classiq.qmod.pretty_print.expression_to_python import transform_expression
91
- from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
92
91
 
93
92
 
94
93
  class VariableDeclarationAssignment(Visitor):
@@ -210,8 +209,8 @@ class PythonPrettyPrinter(ModelVisitor):
210
209
  if len(unchecked) == 0:
211
210
  return no_unchecked_decorator
212
211
 
213
- unchecked_qualifiers = (f'"{qualifier}"' for qualifier in unchecked)
214
- unchecked_list = f"[{', '.join(unchecked_qualifiers)}]"
212
+ unchecked_modifiers = (f'"{modifier}"' for modifier in unchecked)
213
+ unchecked_list = f"[{', '.join(unchecked_modifiers)}]"
215
214
  return no_unchecked_decorator + f" (unchecked={unchecked_list})"
216
215
 
217
216
  def visit_QuantumFunctionDeclaration(
@@ -266,9 +265,9 @@ class PythonPrettyPrinter(ModelVisitor):
266
265
  if port_decl.direction is not PortDeclarationDirection.Inout:
267
266
  self._imports[port_decl.direction.name] = 1
268
267
  var_type = f"{port_decl.direction.name}[{var_type}]"
269
- if port_decl.type_qualifier in [TypeQualifier.Const, TypeQualifier.QFree]:
270
- self._imports[port_decl.type_qualifier.name] = 1
271
- var_type = f"{port_decl.type_qualifier.name}[{var_type}]"
268
+ if port_decl.type_modifier in [TypeModifier.Const, TypeModifier.Permutable]:
269
+ self._imports[port_decl.type_modifier.name] = 1
270
+ var_type = f"{port_decl.type_modifier.name}[{var_type}]"
272
271
  return var_type
273
272
 
274
273
  def visit_QuantumBit(self, qtype: QuantumBit) -> str:
@@ -338,10 +337,6 @@ class PythonPrettyPrinter(ModelVisitor):
338
337
  self._imports["CBool"] = 1
339
338
  return "CBool"
340
339
 
341
- def visit_ClassicalList(self, ctlist: ClassicalList) -> str:
342
- self._imports["CArray"] = 1
343
- return f"CArray[{self.visit(ctlist.element_type)}]"
344
-
345
340
  def visit_ClassicalArray(self, ctarray: ClassicalArray) -> str:
346
341
  self._imports["CArray"] = 1
347
342
  element_type = self.visit(ctarray.element_type)