classiq 0.67.0__py3-none-any.whl → 0.68.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 (56) hide show
  1. classiq/_internals/api_wrapper.py +5 -1
  2. classiq/_internals/async_utils.py +1 -1
  3. classiq/_internals/authentication/password_manager.py +1 -1
  4. classiq/_internals/client.py +1 -1
  5. classiq/execution/execution_session.py +7 -3
  6. classiq/executor.py +7 -2
  7. classiq/interface/_version.py +1 -1
  8. classiq/interface/ast_node.py +1 -1
  9. classiq/interface/chemistry/operator.py +1 -1
  10. classiq/interface/debug_info/debug_info.py +15 -0
  11. classiq/interface/generator/arith/arithmetic.py +96 -1
  12. classiq/interface/generator/arith/arithmetic_expression_parser.py +1 -1
  13. classiq/interface/generator/generated_circuit_data.py +4 -2
  14. classiq/interface/generator/quantum_program.py +18 -1
  15. classiq/interface/generator/types/enum_declaration.py +33 -2
  16. classiq/interface/model/classical_if.py +2 -2
  17. classiq/interface/model/control.py +2 -2
  18. classiq/interface/model/invert.py +2 -2
  19. classiq/interface/model/power.py +2 -2
  20. classiq/interface/model/quantum_function_call.py +4 -0
  21. classiq/interface/model/repeat.py +2 -2
  22. classiq/interface/model/statement_block.py +1 -1
  23. classiq/interface/model/within_apply_operation.py +2 -2
  24. classiq/model_expansions/interpreters/generative_interpreter.py +78 -18
  25. classiq/model_expansions/quantum_operations/allocate.py +3 -1
  26. classiq/model_expansions/quantum_operations/assignment_result_processor.py +52 -0
  27. classiq/model_expansions/quantum_operations/bind.py +2 -1
  28. classiq/model_expansions/quantum_operations/block_evaluator.py +76 -0
  29. classiq/model_expansions/quantum_operations/classicalif.py +5 -4
  30. classiq/model_expansions/quantum_operations/composite_emitter.py +27 -0
  31. classiq/model_expansions/quantum_operations/emitter.py +16 -2
  32. classiq/model_expansions/quantum_operations/expression_evaluator.py +33 -0
  33. classiq/model_expansions/quantum_operations/handle_evaluator.py +28 -0
  34. classiq/model_expansions/quantum_operations/quantum_function_call.py +3 -2
  35. classiq/model_expansions/quantum_operations/repeat.py +2 -1
  36. classiq/model_expansions/quantum_operations/variable_decleration.py +2 -1
  37. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +1 -1
  38. classiq/open_library/functions/__init__.py +1 -2
  39. classiq/open_library/functions/amplitude_amplification.py +8 -5
  40. classiq/open_library/functions/discrete_sine_cosine_transform.py +16 -13
  41. classiq/open_library/functions/grover.py +6 -10
  42. classiq/open_library/functions/modular_exponentiation.py +7 -13
  43. classiq/open_library/functions/state_preparation.py +16 -17
  44. classiq/open_library/functions/swap_test.py +1 -1
  45. classiq/open_library/functions/utility_functions.py +10 -2
  46. classiq/qmod/builtins/functions/chemistry.py +6 -38
  47. classiq/qmod/quantum_expandable.py +30 -6
  48. classiq/qmod/semantics/validation/types_validation.py +1 -1
  49. classiq/qmod/symbolic.py +2 -1
  50. classiq/qmod/utilities.py +2 -1
  51. classiq/qmod/write_qmod.py +10 -7
  52. classiq/synthesis.py +20 -7
  53. {classiq-0.67.0.dist-info → classiq-0.68.0.dist-info}/METADATA +1 -1
  54. {classiq-0.67.0.dist-info → classiq-0.68.0.dist-info}/RECORD +55 -51
  55. classiq/model_expansions/quantum_operations/shallow_emitter.py +0 -166
  56. {classiq-0.67.0.dist-info → classiq-0.68.0.dist-info}/WHEEL +0 -0
@@ -1,13 +1,11 @@
1
1
  from classiq.open_library.functions.qft_functions import qft
2
- from classiq.open_library.functions.utility_functions import (
3
- apply_to_all,
4
- modular_increment,
5
- )
2
+ from classiq.open_library.functions.utility_functions import apply_to_all
6
3
  from classiq.qmod.builtins.functions.standard_gates import PHASE, H, S, X, Z
7
4
  from classiq.qmod.builtins.operations import (
8
5
  allocate,
9
6
  bind,
10
7
  control,
8
+ inplace_add,
11
9
  invert,
12
10
  repeat,
13
11
  within_apply,
@@ -30,8 +28,13 @@ def _qct_d_operator(x: QNum, q: QBit) -> None:
30
28
 
31
29
  @qfunc
32
30
  def _qct_pi_operator(x: QArray[QBit], q: QBit) -> None:
33
- control(q == 1, lambda: apply_to_all(X, x))
34
- control(q == 1, lambda: modular_increment(1, x))
31
+ control(
32
+ q == 1,
33
+ lambda: [ # type:ignore[arg-type]
34
+ apply_to_all(X, x),
35
+ inplace_add(1, x), # type:ignore[arg-type, func-returns-value]
36
+ ],
37
+ )
35
38
 
36
39
 
37
40
  def _t_operator(x: QArray) -> None:
@@ -63,7 +66,7 @@ def _d1_operator(x: QArray[QBit], q: QBit) -> None:
63
66
 
64
67
 
65
68
  def _pi2_operator(x: QArray[QBit], q: QBit) -> None:
66
- control(q == 1, lambda: modular_increment(1, x))
69
+ control(q == 1, lambda: inplace_add(1, x)) # type:ignore[arg-type]
67
70
 
68
71
 
69
72
  def _j_operator(q: QBit) -> None:
@@ -76,7 +79,7 @@ def _b_t_operator(q: QBit) -> None:
76
79
 
77
80
 
78
81
  def _d0dt_operator(x: QArray, q: QBit) -> None:
79
- x_num: QNum = QNum("x_num", x.len, False, 0)
82
+ x_num: QNum = QNum(size=x.len)
80
83
  bind(x, x_num)
81
84
  _b_t_operator(q)
82
85
  control(x_num == 0, lambda: _j_operator(q))
@@ -140,7 +143,7 @@ def qct_qst_type2(x: QArray[QBit], q: QBit) -> None:
140
143
  x: The LSB part of the qubit array to apply the transform to.
141
144
  q: The MSB of the qubit array to apply the transform to.
142
145
  """
143
- extended_state: QArray = QArray("extended_state")
146
+ extended_state: QArray = QArray()
144
147
  _vn_operator(x, q)
145
148
  bind([x, q], extended_state)
146
149
  qft(extended_state)
@@ -159,8 +162,8 @@ def qct_type2(x: QArray[QBit]) -> None:
159
162
  Args:
160
163
  x: The qubit array to apply the transform to.
161
164
  """
162
- q = QBit("q")
163
- within_apply(lambda: allocate(1, q), lambda: qct_qst_type2(x, q))
165
+ q = QBit()
166
+ within_apply(lambda: allocate(q), lambda: qct_qst_type2(x, q))
164
167
 
165
168
 
166
169
  @qfunc
@@ -174,8 +177,8 @@ def qst_type2(x: QArray[QBit]) -> None:
174
177
  Args:
175
178
  x: The qubit array to apply the transform to.
176
179
  """
177
- q = QBit("q")
180
+ q = QBit()
178
181
  within_apply(
179
- lambda: (allocate(1, q), X(q)), # type:ignore[arg-type]
182
+ lambda: (allocate(q), X(q)), # type:ignore[arg-type]
180
183
  lambda: qct_qst_type2(x, q),
181
184
  )
@@ -40,9 +40,9 @@ def phase_oracle(
40
40
  predicate: A predicate function that takes a QArray of QBits and sets a single QBit |1> if the predicate is true, and |0> otherwise.
41
41
  target: The target QArray of QBits to apply the phase oracle to.
42
42
  """
43
- aux = QBit("aux")
43
+ aux = QBit()
44
44
  within_apply(
45
- within=lambda: (allocate(1, aux), X(aux), H(aux)), # type:ignore[arg-type]
45
+ within=lambda: (allocate(aux), X(aux), H(aux)), # type:ignore[arg-type]
46
46
  apply=lambda: predicate(target, aux),
47
47
  )
48
48
 
@@ -64,8 +64,8 @@ def reflect_about_zero(packed_vars: QArray[QBit]) -> None:
64
64
  Args:
65
65
  packed_vars: The quantum state to reflect.
66
66
  """
67
- msbs: QNum = QNum("msbs", packed_vars.len - 1, False, 0)
68
- lsb = QBit("lsb")
67
+ msbs: QNum = QNum(size=packed_vars.len - 1)
68
+ lsb = QBit()
69
69
  bind(packed_vars, [msbs, lsb])
70
70
  within_apply(
71
71
  lambda: (X(lsb), H(lsb)), # type:ignore[arg-type]
@@ -126,7 +126,7 @@ def grover_operator(
126
126
  packed_vars: The state to which to apply the grover operator.
127
127
  """
128
128
  oracle(packed_vars)
129
- grover_diffuser(lambda qba: space_transform(qba), packed_vars)
129
+ grover_diffuser(space_transform, packed_vars)
130
130
  U(0, 0, 0, pi, packed_vars[0])
131
131
 
132
132
 
@@ -149,9 +149,5 @@ def grover_search(
149
149
  hadamard_transform(packed_vars)
150
150
  power(
151
151
  reps,
152
- lambda: grover_operator(
153
- lambda qba: oracle(qba),
154
- lambda qba: hadamard_transform(qba),
155
- packed_vars,
156
- ),
152
+ lambda: grover_operator(oracle, hadamard_transform, packed_vars),
157
153
  )
@@ -11,24 +11,18 @@ from classiq.qmod.builtins.operations import (
11
11
  )
12
12
  from classiq.qmod.cparam import CInt
13
13
  from classiq.qmod.qfunc import qfunc
14
- from classiq.qmod.qmod_variable import QArray, QBit, QNum
14
+ from classiq.qmod.qmod_variable import QArray, QBit
15
15
  from classiq.qmod.symbolic import min, mod_inverse
16
16
 
17
17
 
18
18
  @qfunc
19
19
  def _check_msb(ref: CInt, x: QArray[QBit], aux: QBit) -> None:
20
20
  within_apply(
21
- lambda: invert(lambda: qft_no_swap(x)), lambda: _ctrl_x(ref, x[0], aux)
21
+ lambda: invert(lambda: qft_no_swap(x)),
22
+ lambda: control(x[0] == ref, lambda: X(aux)),
22
23
  )
23
24
 
24
25
 
25
- @qfunc
26
- def _ctrl_x(
27
- ref: CInt, ctrl: QNum, aux: QBit
28
- ) -> None: # TODO: remove qfunc when expressions of QBit is supported
29
- control(ctrl == ref, lambda: X(aux))
30
-
31
-
32
26
  @qfunc
33
27
  def qft_space_add_const(value: CInt, phi_b: QArray[QBit]) -> None:
34
28
  """
@@ -72,12 +66,12 @@ def cc_modular_add(n: CInt, a: CInt, phi_b: QArray[QBit], c1: QBit, c2: QBit) ->
72
66
  c2: a control qubit.
73
67
 
74
68
  """
75
- ctrl: QArray[QBit] = QArray("ctrl")
76
- aux = QBit("aux")
69
+ ctrl: QArray[QBit] = QArray()
70
+ aux = QBit()
77
71
 
78
72
  within_apply(
79
73
  lambda: ( # type:ignore[arg-type]
80
- allocate(1, aux),
74
+ allocate(aux),
81
75
  bind([c1, c2], ctrl), # type:ignore[func-returns-value]
82
76
  ),
83
77
  lambda: ( # type:ignore[arg-type]
@@ -162,7 +156,7 @@ def inplace_c_modular_multiply(n: CInt, a: CInt, x: QArray[QBit], ctrl: QBit) ->
162
156
  x: The quantum factor.
163
157
  ctrl: The control bit.
164
158
  """
165
- b: QArray[QBit] = QArray("b")
159
+ b: QArray[QBit] = QArray()
166
160
 
167
161
  within_apply(
168
162
  lambda: allocate(x.len + 1, b),
@@ -3,12 +3,9 @@ from typing import Literal
3
3
 
4
4
  from classiq.interface.exceptions import ClassiqDeprecationWarning
5
5
 
6
- from classiq.open_library.functions.utility_functions import (
7
- hadamard_transform,
8
- modular_increment,
9
- )
6
+ from classiq.open_library.functions.utility_functions import hadamard_transform
10
7
  from classiq.qmod.builtins.functions.standard_gates import CX, IDENTITY, RY, H, X
11
- from classiq.qmod.builtins.operations import allocate, control, if_, repeat
8
+ from classiq.qmod.builtins.operations import allocate, control, if_, inplace_add, repeat
12
9
  from classiq.qmod.cparam import CBool, CInt
13
10
  from classiq.qmod.qfunc import qfunc
14
11
  from classiq.qmod.qmod_variable import Output, QArray, QBit, QNum
@@ -48,7 +45,7 @@ def allocate_num(
48
45
  allocate(num_qubits, out)
49
46
 
50
47
 
51
- def prepare_uniform_trimmed_state_apply_rotation(
48
+ def _prepare_uniform_trimmed_state_apply_rotation(
52
49
  size_lsb: CInt, lsbs_val: CInt, rotation_var: QBit
53
50
  ) -> None:
54
51
  # max hold for the case where the value is on the left side
@@ -68,7 +65,7 @@ def _prepare_uniform_trimmed_state_step(
68
65
  lsbs_val != 0, # stop condition
69
66
  lambda: control(
70
67
  ctrl_var == ctrl_val,
71
- lambda: prepare_uniform_trimmed_state_apply_rotation(
68
+ lambda: _prepare_uniform_trimmed_state_apply_rotation(
72
69
  size_lsb, lsbs_val, rotation_var
73
70
  ),
74
71
  ),
@@ -102,7 +99,7 @@ def prepare_uniform_trimmed_state(m: CInt, q: QArray[QBit]) -> None:
102
99
  if_(
103
100
  m < 2**q.len,
104
101
  # initial step without control
105
- lambda: prepare_uniform_trimmed_state_apply_rotation(
102
+ lambda: _prepare_uniform_trimmed_state_apply_rotation(
106
103
  q.len - 1, # type:ignore[arg-type]
107
104
  m,
108
105
  q[q.len - 1],
@@ -122,7 +119,7 @@ def prepare_uniform_trimmed_state(m: CInt, q: QArray[QBit]) -> None:
122
119
 
123
120
 
124
121
  @qfunc
125
- def prepare_uniform_interval_state(start: CInt, end: CInt, q: QArray[QBit]) -> None:
122
+ def prepare_uniform_interval_state(start: CInt, end: CInt, q: QNum) -> None:
126
123
  """
127
124
  [Qmod Classiq-library function]
128
125
 
@@ -144,7 +141,7 @@ def prepare_uniform_interval_state(start: CInt, end: CInt, q: QArray[QBit]) -> N
144
141
  2. The synthesis engine automatically handles the allocation, either by drawing new qubits from the available pool or by reusing existing ones.
145
142
  """
146
143
  prepare_uniform_trimmed_state(end - start, q)
147
- modular_increment(start, q)
144
+ inplace_add(start, q)
148
145
 
149
146
 
150
147
  @qfunc
@@ -199,7 +196,9 @@ def prepare_exponential_state(rate: CInt, q: QArray[QBit]) -> None:
199
196
 
200
197
 
201
198
  @qfunc
202
- def prepare_bell_state(state_num: CInt, q: Output[QArray[QBit, Literal[2]]]) -> None:
199
+ def prepare_bell_state(
200
+ state_num: CInt, qpair: Output[QArray[QBit, Literal[2]]]
201
+ ) -> None:
203
202
  """
204
203
  [Qmod Classiq-library function]
205
204
 
@@ -207,7 +206,7 @@ def prepare_bell_state(state_num: CInt, q: Output[QArray[QBit, Literal[2]]]) ->
207
206
 
208
207
  Args:
209
208
  state_num: The number of the Bell state to be prepared. Must be an integer between 0 and 3.
210
- q: The quantum variable that will receive the initialized state. Must be uninitialized.
209
+ qpair: The quantum variable that will receive the initialized state. Must be uninitialized.
211
210
 
212
211
  Bell States:
213
212
  The four Bell states are defined as follows (each state correlates to an integer between 0 and 3 as defined by the `state_num` argument):
@@ -241,11 +240,11 @@ def prepare_bell_state(state_num: CInt, q: Output[QArray[QBit, Literal[2]]]) ->
241
240
 
242
241
 
243
242
  """
244
- allocate(2, q)
245
- if_(logical_or(state_num == 1, state_num == 3), lambda: X(q[0]))
246
- if_(logical_or(state_num == 2, state_num == 3), lambda: X(q[1]))
247
- H(q[0])
248
- CX(q[0], q[1])
243
+ allocate(qpair)
244
+ if_(logical_or(state_num == 1, state_num == 3), lambda: X(qpair[0]))
245
+ if_(logical_or(state_num == 2, state_num == 3), lambda: X(qpair[1]))
246
+ H(qpair[0])
247
+ CX(qpair[0], qpair[1])
249
248
 
250
249
 
251
250
  @qfunc
@@ -21,7 +21,7 @@ def swap_test(state1: QArray[QBit], state2: QArray[QBit], test: Output[QBit]) ->
21
21
  state2: A quantum state to check its overlap with state1.
22
22
  test: A qubit for which the probability of measuring 0 is $0.5*(|\\langle state1 | state2 \\rangle |^2+1)$
23
23
  """
24
- allocate(1, test)
24
+ allocate(test)
25
25
  H(test)
26
26
  control(test, lambda: repeat(state1.len, lambda i: SWAP(state1[i], state2[i])))
27
27
  H(test)
@@ -1,5 +1,8 @@
1
+ import warnings
1
2
  from typing import Annotated
2
3
 
4
+ from classiq.interface.exceptions import ClassiqDeprecationWarning
5
+
3
6
  from classiq.open_library.functions.qft_functions import qft
4
7
  from classiq.qmod.builtins.functions.standard_gates import PHASE, H
5
8
  from classiq.qmod.builtins.operations import bind, repeat, within_apply
@@ -44,7 +47,7 @@ def hadamard_transform(target: QArray[QBit]) -> None:
44
47
  target: qubits to apply to Hadamard transform to.
45
48
 
46
49
  """
47
- apply_to_all(H, target)
50
+ repeat(target.len, lambda index: H(target[index]))
48
51
 
49
52
 
50
53
  @qfunc
@@ -69,7 +72,12 @@ def modular_increment(a: CInt, x: QNum) -> None:
69
72
  x: A quantum number that is assumed to be non-negative integer.
70
73
 
71
74
  """
72
- array_cast: QArray = QArray("array_cast")
75
+ warnings.warn(
76
+ "Function 'modular_increment' is deprecated. Use in-place-add statement in the form '<var> += <expression>' or 'inplace_add(<expression>, <var>)' instead.",
77
+ ClassiqDeprecationWarning,
78
+ stacklevel=1,
79
+ )
80
+ array_cast: QArray = QArray()
73
81
  within_apply(
74
82
  lambda: ( # type:ignore[arg-type]
75
83
  bind(x, array_cast), # type:ignore[func-returns-value]
@@ -1,5 +1,3 @@
1
- from typing import Literal
2
-
3
1
  from classiq.qmod.builtins.structs import (
4
2
  FockHamiltonianProblem,
5
3
  MoleculeProblem,
@@ -13,12 +11,7 @@ from classiq.qmod.qmod_variable import QArray, QBit
13
11
  def molecule_ucc(
14
12
  molecule_problem: MoleculeProblem,
15
13
  excitations: CArray[CInt],
16
- qbv: QArray[
17
- QBit,
18
- Literal[
19
- "get_field(get_field(molecule_problem_to_hamiltonian(molecule_problem)[0], 'pauli'), 'len')"
20
- ],
21
- ],
14
+ qbv: QArray[QBit],
22
15
  ) -> None:
23
16
  pass
24
17
 
@@ -27,12 +20,7 @@ def molecule_ucc(
27
20
  def molecule_hva(
28
21
  molecule_problem: MoleculeProblem,
29
22
  reps: CInt,
30
- qbv: QArray[
31
- QBit,
32
- Literal[
33
- "get_field(get_field(molecule_problem_to_hamiltonian(molecule_problem)[0], 'pauli'), 'len')"
34
- ],
35
- ],
23
+ qbv: QArray[QBit],
36
24
  ) -> None:
37
25
  pass
38
26
 
@@ -40,12 +28,7 @@ def molecule_hva(
40
28
  @qfunc(external=True)
41
29
  def molecule_hartree_fock(
42
30
  molecule_problem: MoleculeProblem,
43
- qbv: QArray[
44
- QBit,
45
- Literal[
46
- "get_field(get_field(molecule_problem_to_hamiltonian(molecule_problem)[0], 'pauli'), 'len')"
47
- ],
48
- ],
31
+ qbv: QArray[QBit],
49
32
  ) -> None:
50
33
  pass
51
34
 
@@ -54,12 +37,7 @@ def molecule_hartree_fock(
54
37
  def fock_hamiltonian_ucc(
55
38
  fock_hamiltonian_problem: FockHamiltonianProblem,
56
39
  excitations: CArray[CInt],
57
- qbv: QArray[
58
- QBit,
59
- Literal[
60
- "get_field(get_field(fock_hamiltonian_problem_to_hamiltonian(fock_hamiltonian_problem)[0], 'pauli'), 'len')"
61
- ],
62
- ],
40
+ qbv: QArray[QBit],
63
41
  ) -> None:
64
42
  pass
65
43
 
@@ -68,12 +46,7 @@ def fock_hamiltonian_ucc(
68
46
  def fock_hamiltonian_hva(
69
47
  fock_hamiltonian_problem: FockHamiltonianProblem,
70
48
  reps: CInt,
71
- qbv: QArray[
72
- QBit,
73
- Literal[
74
- "get_field(get_field(fock_hamiltonian_problem_to_hamiltonian(fock_hamiltonian_problem)[0], 'pauli'), 'len')"
75
- ],
76
- ],
49
+ qbv: QArray[QBit],
77
50
  ) -> None:
78
51
  pass
79
52
 
@@ -81,11 +54,6 @@ def fock_hamiltonian_hva(
81
54
  @qfunc(external=True)
82
55
  def fock_hamiltonian_hartree_fock(
83
56
  fock_hamiltonian_problem: FockHamiltonianProblem,
84
- qbv: QArray[
85
- QBit,
86
- Literal[
87
- "get_field(get_field(fock_hamiltonian_problem_to_hamiltonian(fock_hamiltonian_problem)[0], 'pauli'), 'len')"
88
- ],
89
- ],
57
+ qbv: QArray[QBit],
90
58
  ) -> None:
91
59
  pass
@@ -1,5 +1,6 @@
1
1
  import inspect
2
2
  from abc import ABC
3
+ from collections.abc import Generator, Iterable
3
4
  from dataclasses import is_dataclass
4
5
  from enum import Enum as PythonEnum
5
6
  from types import TracebackType
@@ -67,7 +68,10 @@ from classiq.qmod.qmod_variable import (
67
68
  from classiq.qmod.quantum_callable import QCallable, QExpandableInterface
68
69
  from classiq.qmod.symbolic_expr import SymbolicExpr
69
70
  from classiq.qmod.type_attribute_remover import decl_without_type_attributes
70
- from classiq.qmod.utilities import mangle_keyword, qmod_val_to_expr_str
71
+ from classiq.qmod.utilities import (
72
+ mangle_keyword,
73
+ qmod_val_to_expr_str,
74
+ )
71
75
 
72
76
  ArgType = Union[CParam, QVar, QCallable]
73
77
 
@@ -371,11 +375,14 @@ def prepare_arg(
371
375
  def _validate_classical_arg(
372
376
  arg: Any, arg_decl: AnonClassicalParameterDeclaration, func_name: Optional[str]
373
377
  ) -> None:
374
- is_native_or_compatible_type = not isinstance(
375
- arg, (*NativePythonClassicalTypes, CParam, SymbolicExpr, Basic, PythonEnum)
376
- ) and not is_dataclass(
377
- arg
378
- ) # type: ignore[unreachable]
378
+ is_native_or_compatible_type = (
379
+ not isinstance(
380
+ arg,
381
+ (*NativePythonClassicalTypes, CParam, SymbolicExpr, Basic, PythonEnum),
382
+ )
383
+ and not _is_legal_iterable(arg)
384
+ and not is_dataclass(arg) # type: ignore[unreachable]
385
+ )
379
386
  try:
380
387
  is_pydantic_classical_type = isinstance(
381
388
  arg, pydantic.BaseModel
@@ -481,3 +488,20 @@ def _create_quantum_function_call(
481
488
  return QuantumFunctionCall(
482
489
  function=function_ident, positional_args=prepared_args, source_ref=source_ref_
483
490
  )
491
+
492
+
493
+ _FORBIDDEN_ITERABLES = (set, dict, str, Generator)
494
+
495
+
496
+ def _is_legal_iterable(arg: Any) -> bool:
497
+ if not isinstance(arg, Iterable) or isinstance(arg, _FORBIDDEN_ITERABLES):
498
+ return False
499
+ return all(_is_legal_iterable_element(e) for e in arg)
500
+
501
+
502
+ def _is_legal_iterable_element(arg: Any) -> bool:
503
+ if isinstance(arg, _FORBIDDEN_ITERABLES):
504
+ return False
505
+ if isinstance(arg, Iterable):
506
+ return all(_is_legal_iterable_element(e) for e in arg)
507
+ return True
@@ -16,7 +16,7 @@ TYPE_EXISTS_ERROR_MESSAGE = "Type {!r} already exists"
16
16
 
17
17
 
18
18
  def check_duplicate_types(
19
- types: Sequence[Union[EnumDeclaration, StructDeclaration, QStructDeclaration]]
19
+ types: Sequence[Union[EnumDeclaration, StructDeclaration, QStructDeclaration]],
20
20
  ) -> None:
21
21
  known_types = {type_.name for type_ in BUILTIN_ENUM_DECLARATIONS.values()}
22
22
  known_types |= {type_.name for type_ in BUILTIN_STRUCT_DECLARATIONS.values()}
classiq/qmod/symbolic.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import sys
2
+ from collections.abc import Sequence
2
3
  from typing import (
3
4
  TYPE_CHECKING,
4
5
  Any,
@@ -296,7 +297,7 @@ def sum(arr: SymbolicTypes) -> CParamScalar:
296
297
 
297
298
 
298
299
  def subscript(
299
- amplitudes: Union[list[float], list[CReal], list[CParamScalar], CArray[CReal]],
300
+ amplitudes: Union[Sequence[Union[float, CReal, CParamScalar]], CArray[CReal]],
300
301
  index: QNum,
301
302
  ) -> CParamScalar:
302
303
  return CParamScalar(expr=f"{amplitudes}[{index}]")
classiq/qmod/utilities.py CHANGED
@@ -3,6 +3,7 @@ import inspect
3
3
  import itertools
4
4
  import keyword
5
5
  import sys
6
+ from collections.abc import Iterable
6
7
  from enum import Enum as PythonEnum
7
8
  from types import FrameType
8
9
  from typing import Any, ForwardRef, Literal, Optional, get_args, get_origin, overload
@@ -101,7 +102,7 @@ def qmod_val_to_expr_str(val: Any) -> str:
101
102
  )
102
103
  return f"struct_literal({val.struct_declaration.name}, {kwargs_str})"
103
104
 
104
- if isinstance(val, list):
105
+ if isinstance(val, Iterable):
105
106
  elements_str = ", ".join([qmod_val_to_expr_str(elem) for elem in val])
106
107
  return f"[{elements_str}]"
107
108
 
@@ -1,10 +1,11 @@
1
1
  import json
2
2
  from pathlib import Path
3
- from typing import Optional
3
+ from typing import Optional, Union
4
4
 
5
5
  from classiq.interface.model.model import Model, SerializedModel
6
6
 
7
7
  from classiq.qmod.native.pretty_printer import DSLPrettyPrinter
8
+ from classiq.qmod.quantum_function import GenerativeQFunc, QFunc
8
9
  from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
9
10
 
10
11
  _QMOD_SUFFIX = "qmod"
@@ -12,7 +13,7 @@ _SYNTHESIS_OPTIONS_SUFFIX = "synthesis_options.json"
12
13
 
13
14
 
14
15
  def write_qmod(
15
- serialized_model: SerializedModel,
16
+ model: Union[SerializedModel, QFunc, GenerativeQFunc],
16
17
  name: str,
17
18
  directory: Optional[Path] = None,
18
19
  decimal_precision: int = DEFAULT_DECIMAL_PRECISION,
@@ -22,7 +23,7 @@ def write_qmod(
22
23
  The native Qmod file may be uploaded to the Classiq IDE.
23
24
 
24
25
  Args:
25
- serialized_model: The serialized model to write as a native Qmod file and synthesis options file.
26
+ model: The entry point of the Qmod model - a qfunc named 'main' (or alternatively the output of 'create_model').
26
27
  name: The name to save the file by.
27
28
  directory: The directory to save the files in. If None, the current working directory is used.
28
29
  decimal_precision: The number of decimal places to use for numbers, set to 4 by default.
@@ -30,13 +31,15 @@ def write_qmod(
30
31
  Returns:
31
32
  None
32
33
  """
33
-
34
- model = Model.model_validate_json(serialized_model)
34
+ if isinstance(model, (QFunc, GenerativeQFunc)):
35
+ model_obj = model.create_model()
36
+ else:
37
+ model_obj = Model.model_validate_json(model)
35
38
  pretty_printed_model = DSLPrettyPrinter(decimal_precision=decimal_precision).visit(
36
- model
39
+ model_obj
37
40
  )
38
41
 
39
- synthesis_options = model.model_dump(
42
+ synthesis_options = model_obj.model_dump(
40
43
  include={"constraints", "preferences"}, exclude_none=True
41
44
  )
42
45
 
classiq/synthesis.py CHANGED
@@ -1,4 +1,4 @@
1
- from typing import Any, NewType, Optional
1
+ from typing import Any, NewType, Optional, Union
2
2
 
3
3
  import pydantic
4
4
 
@@ -12,6 +12,8 @@ from classiq.interface.model.model import Model, SerializedModel
12
12
  from classiq import QuantumProgram
13
13
  from classiq._internals import async_utils
14
14
  from classiq._internals.api_wrapper import ApiWrapper
15
+ from classiq.qmod.create_model_function import create_model
16
+ from classiq.qmod.quantum_function import GenerativeQFunc, QFunc
15
17
 
16
18
  SerializedQuantumProgram = NewType("SerializedQuantumProgram", str)
17
19
 
@@ -69,19 +71,30 @@ async def synthesize_async(
69
71
 
70
72
 
71
73
  def synthesize(
72
- serialized_model: SerializedModel, auto_show: bool = False
74
+ model: Union[SerializedModel, QFunc, GenerativeQFunc],
75
+ auto_show: bool = False,
76
+ constraints: Optional[Constraints] = None,
77
+ preferences: Optional[Preferences] = None,
73
78
  ) -> SerializedQuantumProgram:
74
79
  """
75
80
  Synthesize a model with the Classiq engine to receive a quantum program.
76
81
  [More details](https://docs.classiq.io/latest/reference-manual/synthesis/)
77
82
 
78
83
  Args:
79
- serialized_model: A model object serialized as a string.
80
- auto_show: boolean. whether to call `show` on the output
84
+ model: The entry point of the Qmod model - a qfunc named 'main' (or alternatively the output of 'create_model').
85
+ auto_show: Whether to 'show' the synthesized model (False by default).
86
+ constraints: Constraints for the synthesis of the model. See Constraints (Optional).
87
+ preferences: Preferences for the synthesis of the model. See Preferences (Optional).
81
88
 
82
89
  Returns:
83
90
  SerializedQuantumProgram: Quantum program serialized as a string. (See: QuantumProgram)
84
91
  """
92
+ if isinstance(model, (QFunc, GenerativeQFunc)):
93
+ serialized_model = create_model(
94
+ model, constraints=constraints, preferences=preferences
95
+ )
96
+ else:
97
+ serialized_model = model
85
98
  result = async_utils.run(synthesize_async(serialized_model))
86
99
  if auto_show:
87
100
  show(result)
@@ -91,7 +104,7 @@ def synthesize(
91
104
  def set_preferences(
92
105
  serialized_model: SerializedModel,
93
106
  preferences: Optional[Preferences] = None,
94
- **kwargs: Any
107
+ **kwargs: Any,
95
108
  ) -> SerializedModel:
96
109
  """
97
110
  Overrides the preferences of a (serialized) model and returns the updated model.
@@ -139,7 +152,7 @@ def update_preferences(
139
152
  def set_constraints(
140
153
  serialized_model: SerializedModel,
141
154
  constraints: Optional[Constraints] = None,
142
- **kwargs: Any
155
+ **kwargs: Any,
143
156
  ) -> SerializedModel:
144
157
  """
145
158
  Overrides the constraints of a (serialized) model and returns the updated model.
@@ -187,7 +200,7 @@ def update_constraints(
187
200
  def set_execution_preferences(
188
201
  serialized_model: SerializedModel,
189
202
  execution_preferences: Optional[ExecutionPreferences] = None,
190
- **kwargs: Any
203
+ **kwargs: Any,
191
204
  ) -> SerializedModel:
192
205
  """
193
206
  Overrides the execution preferences of a (serialized) model and returns the updated model.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: classiq
3
- Version: 0.67.0
3
+ Version: 0.68.0
4
4
  Summary: Classiq's Python SDK for quantum computing
5
5
  Home-page: https://classiq.io
6
6
  License: Proprietary