classiq 0.61.0__py3-none-any.whl → 0.63.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 (83) hide show
  1. classiq/__init__.py +3 -0
  2. classiq/_internals/api_wrapper.py +6 -26
  3. classiq/_internals/client.py +1 -9
  4. classiq/applications/chemistry/chemistry_model_constructor.py +1 -1
  5. classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +26 -8
  6. classiq/applications/combinatorial_helpers/optimization_model.py +13 -2
  7. classiq/applications/combinatorial_helpers/pyomo_utils.py +143 -13
  8. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
  9. classiq/applications/combinatorial_optimization/combinatorial_problem.py +58 -23
  10. classiq/applications/grover/grover_model_constructor.py +1 -1
  11. classiq/applications/libraries/qmci_library.py +2 -1
  12. classiq/execution/execution_session.py +66 -96
  13. classiq/execution/jobs.py +12 -10
  14. classiq/interface/_version.py +1 -1
  15. classiq/interface/backend/backend_preferences.py +26 -5
  16. classiq/interface/backend/pydantic_backend.py +1 -1
  17. classiq/interface/backend/quantum_backend_providers.py +3 -1
  18. classiq/interface/chemistry/operator.py +0 -204
  19. classiq/interface/execution/primitives.py +1 -0
  20. classiq/interface/generator/compiler_keywords.py +4 -0
  21. classiq/interface/generator/copy.py +47 -0
  22. classiq/interface/generator/function_param_list_without_self_reference.py +2 -0
  23. classiq/interface/generator/functions/type_name.py +6 -0
  24. classiq/interface/generator/generated_circuit_data.py +22 -7
  25. classiq/interface/generator/model/model.py +3 -0
  26. classiq/interface/generator/model/preferences/preferences.py +14 -1
  27. classiq/interface/generator/quantum_function_call.py +4 -2
  28. classiq/interface/generator/types/compilation_metadata.py +2 -1
  29. classiq/interface/model/handle_binding.py +50 -5
  30. classiq/interface/model/quantum_type.py +16 -0
  31. classiq/interface/server/routes.py +1 -3
  32. classiq/model_expansions/capturing/captured_vars.py +114 -28
  33. classiq/model_expansions/closure.py +25 -65
  34. classiq/model_expansions/function_builder.py +19 -9
  35. classiq/model_expansions/generative_functions.py +16 -2
  36. classiq/model_expansions/interpreter.py +110 -66
  37. classiq/model_expansions/model_tables.py +4 -0
  38. classiq/model_expansions/quantum_operations/call_emitter.py +83 -20
  39. classiq/model_expansions/quantum_operations/classicalif.py +1 -1
  40. classiq/model_expansions/quantum_operations/control.py +3 -10
  41. classiq/model_expansions/quantum_operations/emitter.py +3 -4
  42. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +1 -2
  43. classiq/model_expansions/quantum_operations/quantum_function_call.py +1 -1
  44. classiq/model_expansions/quantum_operations/repeat.py +4 -3
  45. classiq/model_expansions/quantum_operations/shallow_emitter.py +9 -3
  46. classiq/model_expansions/scope.py +9 -13
  47. classiq/model_expansions/scope_initialization.py +34 -25
  48. classiq/model_expansions/transformers/var_splitter.py +57 -7
  49. classiq/open_library/__init__.py +4 -0
  50. classiq/open_library/functions/__init__.py +130 -0
  51. classiq/{qmod/builtins → open_library}/functions/amplitude_estimation.py +2 -2
  52. classiq/{qmod/builtins → open_library}/functions/discrete_sine_cosine_transform.py +6 -4
  53. classiq/{qmod/builtins → open_library}/functions/grover.py +2 -2
  54. classiq/{qmod/builtins → open_library}/functions/linear_pauli_rotation.py +1 -1
  55. classiq/{qmod/builtins → open_library}/functions/modular_exponentiation.py +2 -2
  56. classiq/{qmod/builtins → open_library}/functions/qpe.py +2 -2
  57. classiq/{qmod/builtins → open_library}/functions/state_preparation.py +6 -149
  58. classiq/{qmod/builtins → open_library}/functions/swap_test.py +1 -1
  59. classiq/open_library/functions/utility_functions.py +81 -0
  60. classiq/{qmod/builtins → open_library}/functions/variational.py +1 -1
  61. classiq/qmod/builtins/functions/__init__.py +4 -130
  62. classiq/qmod/builtins/functions/allocation.py +150 -0
  63. classiq/qmod/builtins/functions/arithmetic.py +0 -34
  64. classiq/qmod/builtins/functions/operators.py +0 -6
  65. classiq/qmod/builtins/operations.py +19 -80
  66. classiq/qmod/create_model_function.py +8 -162
  67. classiq/qmod/generative.py +0 -16
  68. classiq/qmod/model_state_container.py +7 -0
  69. classiq/qmod/native/pretty_printer.py +10 -11
  70. classiq/qmod/pretty_print/pretty_printer.py +1 -1
  71. classiq/qmod/python_classical_type.py +1 -5
  72. classiq/qmod/qfunc.py +11 -12
  73. classiq/qmod/qmod_variable.py +1 -3
  74. classiq/qmod/quantum_expandable.py +23 -1
  75. classiq/qmod/quantum_function.py +69 -7
  76. {classiq-0.61.0.dist-info → classiq-0.63.0.dist-info}/METADATA +2 -1
  77. {classiq-0.61.0.dist-info → classiq-0.63.0.dist-info}/RECORD +82 -78
  78. classiq/qmod/builtins/functions/utility_functions.py +0 -43
  79. /classiq/{qmod/builtins → open_library}/functions/hea.py +0 -0
  80. /classiq/{qmod/builtins → open_library}/functions/qaoa_penalty.py +0 -0
  81. /classiq/{qmod/builtins → open_library}/functions/qft_functions.py +0 -0
  82. /classiq/{qmod/builtins → open_library}/functions/qsvt.py +0 -0
  83. {classiq-0.61.0.dist-info → classiq-0.63.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,150 @@
1
+ from typing import Literal
2
+
3
+ from classiq.qmod.qfunc import qfunc
4
+ from classiq.qmod.qmod_parameter import CArray, CInt, CReal
5
+ from classiq.qmod.qmod_variable import Input, Output, QArray, QBit
6
+
7
+
8
+ @qfunc(external=True)
9
+ def allocate(
10
+ num_qubits: CInt, out: Output[QArray[QBit, Literal["num_qubits"]]]
11
+ ) -> None:
12
+ """
13
+ [Qmod core-library function]
14
+
15
+ Allocates the specified number of qubits to a given quantum variable and initializes
16
+ them in the zero state:
17
+
18
+ $$
19
+ \\left|\\text{out}\\right\\rangle = \\left|0\\right\\rangle^{\\otimes \\text{num_qubits}}
20
+ $$
21
+
22
+ Args:
23
+ num_qubits: The number of qubits to allocate. Must be a positive integer.
24
+ out: The quantum variable that will receive the allocated qubits. Must be uninitialized before allocation.
25
+
26
+ Notes:
27
+ 1. If the output variable has been declared with a specific number of qubits, the number of qubits allocated must match the declared number.
28
+ 2. The synthesis engine automatically handles the allocation, either by drawing new qubits from the available pool or by reusing existing ones.
29
+ """
30
+ pass
31
+
32
+
33
+ @qfunc(external=True)
34
+ def free(in_: Input[QArray[QBit]]) -> None:
35
+ """
36
+ [Qmod core-library function]
37
+
38
+ Releases the qubits allocated to a quantum variable, allowing them to be reused.
39
+
40
+ Args:
41
+ in_: The quantum variable that will be freed. Must be initialized before.
42
+
43
+ Note:
44
+ This operation does not uncompute the qubits. It is the responsibility of the user to ensure that the qubits are at the zero state before freeing them.
45
+ """
46
+ pass
47
+
48
+
49
+ @qfunc(external=True)
50
+ def prepare_state(
51
+ probabilities: CArray[CReal],
52
+ bound: CReal,
53
+ out: Output[QArray[QBit, Literal["log(get_field(probabilities, 'len'), 2)"]]],
54
+ ) -> None:
55
+ """
56
+ [Qmod core-library function]
57
+
58
+ Initializes a quantum variable in a state corresponding to a given probability distribution:
59
+
60
+ $$
61
+ \\left|\\text{out}\\right\\rangle = \\sum_{i=0}^{\\text{len(probabilities)}-1} \\sqrt{\\text{probabilities}[i]} \\left|i\\right\\rangle
62
+ $$
63
+
64
+ with $i = 0, 1, 2, ..., \\text{len(amplitudes)}-1$ corresponding to computational basis states.
65
+
66
+ Args:
67
+ probabilities: The probability distribution to initialize the quantum variable. Must be a valid probability distribution, i.e., a list of non-negative real numbers that sum to 1. Must have a valid length (a power of 2).
68
+ bound: An error bound, expressed as the $L^{2}$ norm between the expected and actual distributions. A larger bound can reduce the circuit size at the expense of accuracy. Must be a positive real number.
69
+ out: The quantum variable that will receive the initialized state. Must be uninitialized.
70
+
71
+ Notes:
72
+ 1. If the output variable has been declared with a specific number of qubits, the number of qubits formed by the distribution must match the declared number.
73
+ 2. The synthesis engine automatically handles the allocation, either by drawing new qubits from the available pool or by reusing existing ones.
74
+ """
75
+ pass
76
+
77
+
78
+ @qfunc(external=True)
79
+ def prepare_amplitudes(
80
+ amplitudes: CArray[CReal],
81
+ bound: CReal,
82
+ out: Output[QArray[QBit, Literal["log(get_field(amplitudes, 'len'), 2)"]]],
83
+ ) -> None:
84
+ """
85
+ [Qmod core-library function]
86
+
87
+ Initializes a quantum variable in a state corresponding to the given amplitudes:
88
+
89
+ $$
90
+ \\left|\\text{out}\\right\\rangle = \\sum_{i=0}^{\\text{len(amplitudes)}-1} \\text{amplitudes}[i] \\left|i\\right\\rangle
91
+ $$
92
+
93
+ with $i = 0, 1, 2, ..., \\text{len(amplitudes)}-1$ corresponding to computational basis states.
94
+
95
+ Args:
96
+ amplitudes: The amplitudes to initialize the quantum variable. Must be a valid real quantum state vector, i.e., the sum of squares should be 1. Must have a valid length (a power of 2).
97
+ bound: An error bound, expressed as the $L^{2}$ norm between the expected and actual distributions. A larger bound can reduce the circuit size at the expense of accuracy. Must be a positive real number.
98
+ out: The quantum variable that will receive the initialized state. Must be uninitialized.
99
+
100
+ Notes:
101
+ 1. If the output variable has been declared with a specific number of qubits, the number of qubits formed by the distribution must match the declared number.
102
+ 2. The synthesis engine automatically handles the allocation, either by drawing new qubits from the available pool or by reusing existing ones.
103
+ """
104
+ pass
105
+
106
+
107
+ @qfunc(external=True)
108
+ def inplace_prepare_state(
109
+ probabilities: CArray[CReal],
110
+ bound: CReal,
111
+ target: QArray[QBit, Literal["log(get_field(probabilities, 'len'), 2)"]],
112
+ ) -> None:
113
+ """
114
+ [Qmod core-library function]
115
+
116
+ Transforms a given quantum variable in the state |0> to the state per the specified probability distribution
117
+ (similar to `prepare_state` but preformed on an initialized variable).
118
+
119
+ Args:
120
+ probabilities: The probability distribution corresponding to the quantum variable state. Must be a valid probability distribution, i.e., a list of non-negative real numbers that sum to 1. Must have a valid length (a power of 2).
121
+ bound: An error bound, expressed as the $L^{2}$ norm between the expected and actual distributions. A larger bound can reduce the circuit size at the expense of accuracy. Must be a positive real number.
122
+ target: The quantum variable to act upon.
123
+
124
+ This is useful as part of quantum building blocks like the Grover diffuser operator, $\\left|\\psi\\right\\rangle\\left\\langle\\psi\\right| \\left( 2\\left|0\\right\\rangle\\left\\langle0\\right| - \\mathcal{I} \\right)$, where the output state of the oracle is reflected about this state.
125
+
126
+ """
127
+ pass
128
+
129
+
130
+ @qfunc(external=True)
131
+ def inplace_prepare_amplitudes(
132
+ amplitudes: CArray[CReal],
133
+ bound: CReal,
134
+ target: QArray[QBit, Literal["log(get_field(amplitudes, 'len'), 2)"]],
135
+ ) -> None:
136
+ """
137
+ [Qmod core-library function]
138
+
139
+ Transforms a given quantum variable in the state |0> to the state per the specified amplitudes
140
+ (similar to `prepare_amplitudes` but preformed on an initialized variable).
141
+
142
+ Args:
143
+ amplitudes: The amplitudes to initialize the quantum variable. Must be a valid real quantum state vector, i.e., the sum of squares should be 1. Must have a valid length (a power of 2).
144
+ bound: An error bound, expressed as the $L^{2}$ norm between the expected and actual distributions. A larger bound can reduce the circuit size at the expense of accuracy. Must be a positive real number.
145
+ target: The quantum variable to act upon.
146
+
147
+ This is useful as part of quantum building blocks like the Grover diffuser operator, $\\left|\\psi\\right\\rangle\\left\\langle\\psi\\right| \\left( 2\\left|0\\right\\rangle\\left\\langle0\\right| - \\mathcal{I} \\right)$, where the output state of the oracle is reflected about this state.
148
+
149
+ """
150
+ pass
@@ -1,13 +1,8 @@
1
1
  from typing import Literal
2
2
 
3
- from classiq.qmod.builtins.functions.qft_functions import qft
4
- from classiq.qmod.builtins.functions.standard_gates import PHASE
5
- from classiq.qmod.builtins.operations import bind, repeat, within_apply
6
- from classiq.qmod.cparam import CInt
7
3
  from classiq.qmod.qfunc import qfunc
8
4
  from classiq.qmod.qmod_parameter import CArray, CReal
9
5
  from classiq.qmod.qmod_variable import Output, QArray, QBit, QNum
10
- from classiq.qmod.symbolic import pi
11
6
 
12
7
 
13
8
  @qfunc(external=True)
@@ -58,32 +53,3 @@ def integer_xor(left: QArray[QBit], right: QArray[QBit]) -> None:
58
53
  @qfunc(external=True)
59
54
  def real_xor_constant(left: CReal, right: QNum) -> None:
60
55
  pass
61
-
62
-
63
- @qfunc
64
- def modular_increment(a: CInt, x: QNum) -> None:
65
- """
66
- [Qmod Classiq-library function]
67
-
68
- Adds $a$ to $x$ modulo the range of $x$, assumed that $x$ is a non-negative integer and $a$ is an integer.
69
- Mathematically it is described as:
70
-
71
- $$
72
- x = (x+a)\\ \\mod \\ 2^{x.size}-1
73
- $$
74
-
75
- Args:
76
- a: A classical integer to be added to x.
77
- x: A quantum number that is assumed to be non-negative integer.
78
-
79
- """
80
- array_cast: QArray = QArray("array_cast")
81
- within_apply(
82
- lambda: ( # type:ignore[arg-type]
83
- bind(x, array_cast), # type:ignore[func-returns-value]
84
- qft(array_cast),
85
- ),
86
- lambda: repeat(
87
- x.size, lambda i: PHASE(a * 2 * pi * 2**i / (2**x.size), array_cast[i])
88
- ),
89
- )
@@ -1,4 +1,3 @@
1
- from classiq.qmod.cparam import CInt
2
1
  from classiq.qmod.qfunc import qfunc
3
2
  from classiq.qmod.quantum_callable import QCallable, QCallableList
4
3
 
@@ -15,8 +14,3 @@ def apply(
15
14
  operand: QCallable,
16
15
  ) -> None:
17
16
  pass
18
-
19
-
20
- @qfunc
21
- def switch(selector: CInt, cases: QCallableList) -> None:
22
- cases[selector]()
@@ -1,16 +1,12 @@
1
1
  import inspect
2
2
  import sys
3
- import warnings
4
3
  from collections.abc import Mapping
5
4
  from types import FrameType
6
5
  from typing import (
7
- TYPE_CHECKING,
8
6
  Any,
9
7
  Callable,
10
8
  Final,
11
- Optional,
12
9
  Union,
13
- overload,
14
10
  )
15
11
 
16
12
  from classiq.interface.exceptions import ClassiqValueError
@@ -44,7 +40,8 @@ from classiq.interface.model.repeat import Repeat
44
40
  from classiq.interface.model.statement_block import StatementBlock
45
41
  from classiq.interface.model.within_apply_operation import WithinApply
46
42
 
47
- from classiq.qmod.qmod_variable import Input, Output, QArray, QBit, QNum, QScalar, QVar
43
+ from classiq.qmod.generative import is_generative_mode
44
+ from classiq.qmod.qmod_variable import Input, Output, QArray, QBit, QScalar, QVar
48
45
  from classiq.qmod.quantum_callable import QCallable
49
46
  from classiq.qmod.quantum_expandable import prepare_arg
50
47
  from classiq.qmod.symbolic_expr import SymbolicExpr
@@ -89,9 +86,10 @@ def if_(
89
86
  else_=_operand_to_body(else_, "else") if else_ != _MISSING_VALUE else [], # type: ignore[arg-type]
90
87
  source_ref=source_ref,
91
88
  )
92
- if_stmt.set_generative_block("then", then)
93
- if callable(else_):
94
- if_stmt.set_generative_block("else", else_)
89
+ if is_generative_mode():
90
+ if_stmt.set_generative_block("then", then)
91
+ if callable(else_):
92
+ if_stmt.set_generative_block("else", else_)
95
93
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(if_stmt)
96
94
 
97
95
 
@@ -109,9 +107,10 @@ def control(
109
107
  else_block=_operand_to_body(else_block, "else_block") if else_block else None,
110
108
  source_ref=source_ref,
111
109
  )
112
- control_stmt.set_generative_block("body", stmt_block)
113
- if else_block is not None:
114
- control_stmt.set_generative_block("else_block", else_block)
110
+ if is_generative_mode():
111
+ control_stmt.set_generative_block("body", stmt_block)
112
+ if else_block is not None:
113
+ control_stmt.set_generative_block("else_block", else_block)
115
114
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(control_stmt)
116
115
 
117
116
 
@@ -161,22 +160,7 @@ def assign_amplitude(expression: SymbolicExpr, target_var: QScalar) -> None:
161
160
  )
162
161
 
163
162
 
164
- @overload
165
163
  def inplace_add(expression: SymbolicExpr, target_var: QScalar) -> None:
166
- pass
167
-
168
-
169
- @overload
170
- def inplace_add(*, value: QNum, target: QNum) -> None:
171
- pass
172
-
173
-
174
- def inplace_add(
175
- expression: Optional[SymbolicExpr] = None,
176
- target_var: Optional[QScalar] = None,
177
- value: Optional[QNum] = None,
178
- target: Optional[QNum] = None,
179
- ) -> None:
180
164
  """
181
165
  Add an arithmetic expression to a quantum variable.
182
166
 
@@ -187,23 +171,6 @@ def inplace_add(
187
171
  target_var: A scalar quantum variable
188
172
  """
189
173
  assert QCallable.CURRENT_EXPANDABLE is not None
190
- if value is not None or target is not None:
191
- warnings.warn(
192
- "Parameters 'value' and 'target of function 'inplace_add' have "
193
- "been renamed to 'expression' and 'target_var' respectively. Parameters "
194
- "'value' and 'target' will no longer be supported starting on 02/12/24 at "
195
- "the earliest.\nHint: Change `inplace_add(value=..., target=...)` to "
196
- "`inplace_add(expression=..., target_var=...)` or `inplace_add(..., ...)`.",
197
- category=DeprecationWarning,
198
- stacklevel=2,
199
- )
200
- if value is not None:
201
- expression = value
202
- if target is not None:
203
- target_var = target
204
- if TYPE_CHECKING:
205
- assert expression is not None
206
- assert target_var is not None
207
174
  source_ref = get_source_ref(sys._getframe(1))
208
175
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
209
176
  ArithmeticOperation(
@@ -215,22 +182,7 @@ def inplace_add(
215
182
  )
216
183
 
217
184
 
218
- @overload
219
185
  def inplace_xor(expression: SymbolicExpr, target_var: QScalar) -> None:
220
- pass
221
-
222
-
223
- @overload
224
- def inplace_xor(*, value: QNum, target: QNum) -> None:
225
- pass
226
-
227
-
228
- def inplace_xor(
229
- expression: Optional[SymbolicExpr] = None,
230
- target_var: Optional[QScalar] = None,
231
- value: Optional[QNum] = None,
232
- target: Optional[QNum] = None,
233
- ) -> None:
234
186
  """
235
187
  Bitwise-XOR a quantum variable with an arithmetic expression.
236
188
 
@@ -241,23 +193,6 @@ def inplace_xor(
241
193
  target_var: A scalar quantum variable
242
194
  """
243
195
  assert QCallable.CURRENT_EXPANDABLE is not None
244
- if value is not None or target is not None:
245
- warnings.warn(
246
- "Parameters 'value' and 'target of function 'inplace_xor' have "
247
- "been renamed to 'expression' and 'target_var' respectively. Parameters "
248
- "'value' and 'target' will no longer be supported starting on 02/12/24 at "
249
- "the earliest.\nHint: Change `inplace_xor(value=..., target=...)` to "
250
- "`inplace_xor(expression=..., target_var=...)` or `inplace_xor(..., ...)`.",
251
- category=DeprecationWarning,
252
- stacklevel=2,
253
- )
254
- if value is not None:
255
- expression = value
256
- if target is not None:
257
- target_var = target
258
- if TYPE_CHECKING:
259
- assert expression is not None
260
- assert target_var is not None
261
196
  source_ref = get_source_ref(sys._getframe(1))
262
197
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
263
198
  ArithmeticOperation(
@@ -282,8 +217,9 @@ def within_apply(
282
217
  action=_operand_to_body(apply, "apply"),
283
218
  source_ref=source_ref,
284
219
  )
285
- within_apply_stmt.set_generative_block("within", within)
286
- within_apply_stmt.set_generative_block("apply", apply)
220
+ if is_generative_mode():
221
+ within_apply_stmt.set_generative_block("within", within)
222
+ within_apply_stmt.set_generative_block("apply", apply)
287
223
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(within_apply_stmt)
288
224
 
289
225
 
@@ -313,7 +249,8 @@ def repeat(count: Union[SymbolicExpr, int], iteration: Callable[[int], None]) ->
313
249
  body=iteration_operand.body,
314
250
  source_ref=source_ref,
315
251
  )
316
- repeat_stmt.set_generative_block("body", iteration)
252
+ if is_generative_mode():
253
+ repeat_stmt.set_generative_block("body", iteration)
317
254
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(repeat_stmt)
318
255
 
319
256
 
@@ -329,7 +266,8 @@ def power(
329
266
  body=_operand_to_body(stmt_block, "stmt_block"),
330
267
  source_ref=source_ref,
331
268
  )
332
- power_stmt.set_generative_block("body", stmt_block)
269
+ if is_generative_mode():
270
+ power_stmt.set_generative_block("body", stmt_block)
333
271
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(power_stmt)
334
272
 
335
273
 
@@ -340,7 +278,8 @@ def invert(stmt_block: Union[QCallable, Callable[[], None]]) -> None:
340
278
  invert_stmt = Invert(
341
279
  body=_operand_to_body(stmt_block, "stmt_block"), source_ref=source_ref
342
280
  )
343
- invert_stmt.set_generative_block("body", stmt_block)
281
+ if is_generative_mode():
282
+ invert_stmt.set_generative_block("body", stmt_block)
344
283
  QCallable.CURRENT_EXPANDABLE.append_statement_to_body(invert_stmt)
345
284
 
346
285
 
@@ -1,30 +1,15 @@
1
- from typing import TYPE_CHECKING, Optional, Union, cast
1
+ from typing import Optional, Union
2
2
 
3
3
  from classiq.interface.exceptions import ClassiqError
4
4
  from classiq.interface.executor.execution_preferences import ExecutionPreferences
5
5
  from classiq.interface.generator.model.constraints import Constraints
6
6
  from classiq.interface.generator.model.preferences.preferences import Preferences
7
- from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
8
- from classiq.interface.model.model import MAIN_FUNCTION_NAME, Model, SerializedModel
9
- from classiq.interface.model.native_function_definition import NativeFunctionDefinition
10
- from classiq.interface.model.quantum_function_call import QuantumFunctionCall
7
+ from classiq.interface.model.model import MAIN_FUNCTION_NAME, SerializedModel
11
8
 
12
- from classiq.model_expansions.interpreter import Interpreter
13
- from classiq.qmod.builtins.functions import BUILTIN_FUNCTION_DECLARATIONS
14
9
  from classiq.qmod.classical_function import CFunc
15
- from classiq.qmod.generative import (
16
- is_generative_expansion_enabled,
17
- set_frontend_interpreter,
18
- )
19
- from classiq.qmod.model_state_container import QMODULE
20
- from classiq.qmod.qfunc import DEC_QFUNCS, GEN_QFUNCS
21
- from classiq.qmod.quantum_expandable import _prepare_args
22
10
  from classiq.qmod.quantum_function import GenerativeQFunc, QFunc
23
- from classiq.qmod.semantics.static_semantics_visitor import resolve_function_calls
24
11
  from classiq.qmod.write_qmod import write_qmod
25
12
 
26
- GEN_MAIN_NAME = "_gen_main"
27
-
28
13
 
29
14
  def create_model(
30
15
  entry_point: Union[QFunc, GenerativeQFunc],
@@ -57,154 +42,15 @@ def create_model(
57
42
  f"The entry point function must be named 'main', got '{entry_point.func_decl.name}'"
58
43
  )
59
44
 
60
- user_gen_functions = {
61
- gen_func._py_callable.__name__ for gen_func in GEN_QFUNCS
62
- } - set(BUILTIN_FUNCTION_DECLARATIONS.keys())
63
-
64
- if len(user_gen_functions) > 0 and is_generative_expansion_enabled():
65
- model = _expand_generative_model(
66
- (
67
- entry_point
68
- if isinstance(entry_point, QFunc)
69
- else QFunc(entry_point._py_callable)
70
- ),
71
- constraints,
72
- execution_preferences,
73
- preferences,
74
- )
75
- else:
76
- if TYPE_CHECKING:
77
- assert isinstance(entry_point, QFunc)
78
- model = entry_point.create_model(
79
- constraints,
80
- execution_preferences,
81
- preferences,
82
- classical_execution_function,
83
- )
84
- result = model.get_model()
85
-
86
- if out_file is not None:
87
- write_qmod(result, out_file)
88
-
89
- return result
90
-
91
-
92
- def _expand_generative_model(
93
- gen_main: QFunc,
94
- constraints: Optional[Constraints] = None,
95
- execution_preferences: Optional[ExecutionPreferences] = None,
96
- preferences: Optional[Preferences] = None,
97
- ) -> Model:
98
- @QFunc
99
- def _dummy() -> None:
100
- pass
101
-
102
- model = _dummy.create_model(
45
+ model = entry_point.create_model(
103
46
  constraints,
104
47
  execution_preferences,
105
48
  preferences,
49
+ classical_execution_function,
106
50
  )
107
- gen_expand_model = _get_generative_functions(gen_main, preferences)
108
- model.functions = gen_expand_model.functions
109
- model.functions_compilation_metadata = (
110
- gen_expand_model.functions_compilation_metadata
111
- )
112
- model.types = list(QMODULE.type_decls.values())
113
- model.enums = list(QMODULE.enum_decls.values())
114
- model.qstructs = list(QMODULE.qstruct_decls.values())
115
- return model
116
-
117
-
118
- def _get_generative_functions(
119
- gen_main: QFunc,
120
- preferences: Optional[Preferences],
121
- ) -> Model:
122
- # The Interpreter accepts a model and a list of generative functions.
123
- # Since the main function is generative, it can only be expanded using the
124
- # Interpreter.
125
- # To solve this deadlock, we create a wrapper model
126
- # `qfunc main(...) { _gen_main(...); }` and rename `main` to `_gen_main` before
127
- # passing them to the Interpreter.
128
- gen_model = _get_wrapper_main(gen_main, preferences)
129
- gen_functions = _get_all_model_functions_as_generative_functions()
130
- return _interpret_generative_model(gen_model, gen_functions)
131
-
132
-
133
- def _get_wrapper_main(
134
- gen_main: QFunc,
135
- preferences: Optional[Preferences],
136
- ) -> Model:
137
- extra_args = {}
138
- if preferences is not None:
139
- extra_args["preferences"] = preferences
140
- functions_compilation_metadata = {
141
- qfunc._py_callable.__name__: qfunc.compilation_metadata
142
- for qfunc in DEC_QFUNCS + GEN_QFUNCS
143
- if qfunc.compilation_metadata is not None
144
- }
145
- return Model(
146
- functions=[
147
- NativeFunctionDefinition(
148
- name=MAIN_FUNCTION_NAME,
149
- positional_arg_declarations=gen_main.func_decl.positional_arg_declarations,
150
- body=[
151
- QuantumFunctionCall(
152
- function=GEN_MAIN_NAME,
153
- positional_args=_prepare_args(
154
- gen_main.func_decl,
155
- gen_main._get_positional_args(),
156
- {},
157
- ),
158
- ),
159
- ],
160
- ),
161
- ],
162
- functions_compilation_metadata=functions_compilation_metadata,
163
- **extra_args,
164
- )
165
-
166
-
167
- def _get_all_model_functions_as_generative_functions() -> list[GenerativeQFunc]:
168
-
169
- gen_functions = list(GEN_QFUNCS) + [
170
- GenerativeQFunc(
171
- dec_func._py_callable, dec_func.func_decl, dec_func.compilation_metadata
172
- )
173
- for dec_func in DEC_QFUNCS
174
- ]
175
- return [
176
- (
177
- gen_func
178
- if gen_func.func_decl.name != MAIN_FUNCTION_NAME
179
- else GenerativeQFunc(
180
- gen_func._py_callable,
181
- gen_func.func_decl.model_copy(update={"name": GEN_MAIN_NAME}),
182
- gen_func.compilation_metadata,
183
- )
184
- )
185
- for gen_func in gen_functions
186
- if gen_func.func_decl.name not in BUILTIN_FUNCTION_DECLARATIONS
187
- ]
188
-
51
+ result = model.get_model()
189
52
 
190
- def _interpret_generative_model(
191
- gen_model: Model, gen_functions: list[GenerativeQFunc]
192
- ) -> Model:
193
- resolve_function_calls(
194
- gen_model,
195
- {gen_func.func_decl.name: gen_func.func_decl for gen_func in gen_functions},
196
- )
197
- interpreter = Interpreter(gen_model, gen_functions, is_frontend=True)
198
- set_frontend_interpreter(interpreter)
199
- expand_model = interpreter.expand()
200
- functions_dict = nameables_to_dict(expand_model.functions)
53
+ if out_file is not None:
54
+ write_qmod(result, out_file)
201
55
 
202
- # Inline _gen_main call in main
203
- expanded_gen_main_name = cast(
204
- QuantumFunctionCall, functions_dict[MAIN_FUNCTION_NAME].body[0]
205
- ).func_name
206
- functions_dict[MAIN_FUNCTION_NAME] = functions_dict[
207
- expanded_gen_main_name
208
- ].model_copy(update={"name": MAIN_FUNCTION_NAME})
209
- functions_dict.pop(expanded_gen_main_name)
210
- return expand_model.model_copy(update={"functions": list(functions_dict.values())})
56
+ return result
@@ -8,7 +8,6 @@ from classiq.interface.generator.expressions.expression import Expression
8
8
  if TYPE_CHECKING:
9
9
  from classiq.model_expansions.interpreter import Interpreter
10
10
 
11
- _GENERATIVE_ENABLED_SWITCH: bool = True
12
11
  _GENERATIVE_MODE: bool = False
13
12
  _FRONTEND_INTERPRETER: Optional["Interpreter"] = None
14
13
 
@@ -28,21 +27,6 @@ def generative_mode_context(generative: bool) -> Iterator[None]:
28
27
  _GENERATIVE_MODE = previous
29
28
 
30
29
 
31
- @contextmanager
32
- def enable_generative_expansion(enabled: bool) -> Iterator[None]:
33
- global _GENERATIVE_ENABLED_SWITCH
34
- previous = _GENERATIVE_ENABLED_SWITCH
35
- _GENERATIVE_ENABLED_SWITCH = enabled
36
- try:
37
- yield
38
- finally:
39
- _GENERATIVE_ENABLED_SWITCH = previous
40
-
41
-
42
- def is_generative_expansion_enabled() -> bool:
43
- return _GENERATIVE_ENABLED_SWITCH
44
-
45
-
46
30
  def set_frontend_interpreter(interpreter: "Interpreter") -> None:
47
31
  global _FRONTEND_INTERPRETER
48
32
  _FRONTEND_INTERPRETER = interpreter
@@ -1,3 +1,5 @@
1
+ from typing import TYPE_CHECKING
2
+
1
3
  from classiq.interface.generator.constant import Constant
2
4
  from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
3
5
  from classiq.interface.generator.types.enum_declaration import EnumDeclaration
@@ -7,6 +9,9 @@ from classiq.interface.model.native_function_definition import (
7
9
  NativeFunctionDefinition,
8
10
  )
9
11
 
12
+ if TYPE_CHECKING:
13
+ from classiq.qmod.quantum_function import GenerativeQFunc
14
+
10
15
 
11
16
  class ModelStateContainer:
12
17
  enum_decls: dict[str, EnumDeclaration]
@@ -15,6 +20,8 @@ class ModelStateContainer:
15
20
  native_defs: dict[str, NativeFunctionDefinition]
16
21
  constants: dict[str, Constant]
17
22
  functions_compilation_metadata: dict[str, CompilationMetadata]
23
+ generative_functions: dict[str, "GenerativeQFunc"]
24
+ function_dependencies: dict[str, list[str]]
18
25
 
19
26
 
20
27
  QMODULE = ModelStateContainer()
@@ -78,7 +78,7 @@ from classiq.interface.model.variable_declaration_statement import (
78
78
  )
79
79
  from classiq.interface.model.within_apply_operation import WithinApply
80
80
 
81
- from classiq.qmod.builtins.functions import OPEN_LIBRARY_FUNCTIONS
81
+ from classiq.open_library.functions import OPEN_LIBRARY_FUNCTIONS
82
82
  from classiq.qmod.native.expression_to_qmod import transform_expression
83
83
  from classiq.qmod.semantics.static_semantics_visitor import resolve_function_calls
84
84
  from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
@@ -114,16 +114,15 @@ class DSLPrettyPrinter(Visitor):
114
114
  else []
115
115
  )
116
116
 
117
- return "\n".join(
118
- [
119
- *constants,
120
- *enum_decls,
121
- *struct_decls,
122
- *qstruct_decls,
123
- *func_defs,
124
- *classical_code,
125
- ]
126
- )
117
+ items = [
118
+ *constants,
119
+ *enum_decls,
120
+ *struct_decls,
121
+ *qstruct_decls,
122
+ *func_defs,
123
+ *classical_code,
124
+ ]
125
+ return "\n".join([item for item in items if item != ""])
127
126
 
128
127
  def visit_Constant(self, constant: Constant) -> str:
129
128
  return f"{self._indent}{self.visit(constant.name)}: {self.visit(constant.const_type)} = {self.visit(constant.value)};\n"