classiq 0.62.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 (75) 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 +5 -1
  7. classiq/applications/combinatorial_helpers/pyomo_utils.py +106 -27
  8. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
  9. classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -2
  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 +3 -9
  14. classiq/interface/_version.py +1 -1
  15. classiq/interface/backend/backend_preferences.py +8 -5
  16. classiq/interface/backend/pydantic_backend.py +1 -1
  17. classiq/interface/chemistry/operator.py +0 -204
  18. classiq/interface/execution/primitives.py +1 -0
  19. classiq/interface/generator/compiler_keywords.py +4 -0
  20. classiq/interface/generator/functions/type_name.py +6 -0
  21. classiq/interface/generator/generated_circuit_data.py +22 -7
  22. classiq/interface/generator/model/model.py +3 -0
  23. classiq/interface/generator/model/preferences/preferences.py +13 -0
  24. classiq/interface/generator/quantum_function_call.py +4 -2
  25. classiq/interface/model/handle_binding.py +50 -5
  26. classiq/interface/model/quantum_type.py +16 -0
  27. classiq/interface/server/routes.py +1 -3
  28. classiq/model_expansions/capturing/captured_vars.py +102 -19
  29. classiq/model_expansions/closure.py +19 -56
  30. classiq/model_expansions/function_builder.py +13 -8
  31. classiq/model_expansions/generative_functions.py +15 -1
  32. classiq/model_expansions/interpreter.py +94 -32
  33. classiq/model_expansions/model_tables.py +4 -0
  34. classiq/model_expansions/quantum_operations/call_emitter.py +61 -2
  35. classiq/model_expansions/quantum_operations/classicalif.py +1 -1
  36. classiq/model_expansions/quantum_operations/control.py +3 -10
  37. classiq/model_expansions/quantum_operations/emitter.py +1 -1
  38. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +1 -2
  39. classiq/model_expansions/quantum_operations/repeat.py +4 -3
  40. classiq/model_expansions/scope.py +7 -1
  41. classiq/model_expansions/scope_initialization.py +34 -25
  42. classiq/model_expansions/transformers/var_splitter.py +57 -7
  43. classiq/open_library/__init__.py +4 -0
  44. classiq/open_library/functions/__init__.py +130 -0
  45. classiq/{qmod/builtins → open_library}/functions/amplitude_estimation.py +2 -2
  46. classiq/{qmod/builtins → open_library}/functions/discrete_sine_cosine_transform.py +6 -4
  47. classiq/{qmod/builtins → open_library}/functions/grover.py +2 -2
  48. classiq/{qmod/builtins → open_library}/functions/linear_pauli_rotation.py +1 -1
  49. classiq/{qmod/builtins → open_library}/functions/modular_exponentiation.py +2 -2
  50. classiq/{qmod/builtins → open_library}/functions/qpe.py +2 -2
  51. classiq/{qmod/builtins → open_library}/functions/state_preparation.py +6 -149
  52. classiq/{qmod/builtins → open_library}/functions/swap_test.py +1 -1
  53. classiq/open_library/functions/utility_functions.py +81 -0
  54. classiq/{qmod/builtins → open_library}/functions/variational.py +1 -1
  55. classiq/qmod/builtins/functions/__init__.py +4 -130
  56. classiq/qmod/builtins/functions/allocation.py +150 -0
  57. classiq/qmod/builtins/functions/arithmetic.py +0 -34
  58. classiq/qmod/builtins/functions/operators.py +0 -6
  59. classiq/qmod/create_model_function.py +8 -162
  60. classiq/qmod/generative.py +0 -16
  61. classiq/qmod/model_state_container.py +7 -0
  62. classiq/qmod/native/pretty_printer.py +10 -11
  63. classiq/qmod/pretty_print/pretty_printer.py +1 -1
  64. classiq/qmod/qfunc.py +11 -12
  65. classiq/qmod/qmod_variable.py +1 -3
  66. classiq/qmod/quantum_expandable.py +21 -0
  67. classiq/qmod/quantum_function.py +65 -3
  68. {classiq-0.62.0.dist-info → classiq-0.63.0.dist-info}/METADATA +1 -1
  69. {classiq-0.62.0.dist-info → classiq-0.63.0.dist-info}/RECORD +74 -71
  70. classiq/qmod/builtins/functions/utility_functions.py +0 -43
  71. /classiq/{qmod/builtins → open_library}/functions/hea.py +0 -0
  72. /classiq/{qmod/builtins → open_library}/functions/qaoa_penalty.py +0 -0
  73. /classiq/{qmod/builtins → open_library}/functions/qft_functions.py +0 -0
  74. /classiq/{qmod/builtins → open_library}/functions/qsvt.py +0 -0
  75. {classiq-0.62.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,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"
@@ -412,7 +412,7 @@ class PythonPrettyPrinter(Visitor):
412
412
  def visit_PhaseOperation(self, op: PhaseOperation) -> str:
413
413
  self._imports["phase"] = 1
414
414
  theta = f", {self.visit(op.theta)}" if op.theta is not None else ""
415
- return f"{self._indent}phase({self.visit(op.expression)},{theta})\n"
415
+ return f"{self._indent}phase({self.visit(op.expression)}{theta})\n"
416
416
 
417
417
  def visit_ClassicalIf(self, op: ClassicalIf) -> str:
418
418
  self._imports["if_"] = 1
classiq/qmod/qfunc.py CHANGED
@@ -1,3 +1,5 @@
1
+ from collections.abc import Iterator
2
+ from contextlib import contextmanager
1
3
  from typing import Callable, Literal, Optional, Union, overload
2
4
 
3
5
  from classiq.interface.exceptions import ClassiqInternalError
@@ -10,17 +12,16 @@ from classiq.qmod.quantum_function import (
10
12
  QFunc,
11
13
  )
12
14
 
13
- GEN_QFUNCS: list[GenerativeQFunc] = []
14
- DEC_QFUNCS: list[QFunc] = []
15
+ _GENERATIVE_SWITCH = False
15
16
 
16
17
 
17
- def set_discovered_functions(
18
- dec_funcs: list[QFunc], gen_funcs: list[GenerativeQFunc]
19
- ) -> None:
20
- DEC_QFUNCS.clear()
21
- DEC_QFUNCS.extend(dec_funcs)
22
- GEN_QFUNCS.clear()
23
- GEN_QFUNCS.extend(gen_funcs)
18
+ @contextmanager
19
+ def set_global_generative_switch() -> Iterator[None]:
20
+ global _GENERATIVE_SWITCH
21
+ previous = _GENERATIVE_SWITCH
22
+ _GENERATIVE_SWITCH = True
23
+ yield
24
+ _GENERATIVE_SWITCH = previous
24
25
 
25
26
 
26
27
  @overload
@@ -56,9 +57,8 @@ def qfunc(
56
57
  ) -> Union[Callable[[Callable], QCallable], QCallable]:
57
58
  def wrapper(func: Callable) -> QCallable:
58
59
  qfunc: BaseQFunc
59
- if generative:
60
+ if generative or _GENERATIVE_SWITCH:
60
61
  qfunc = GenerativeQFunc(func)
61
- GEN_QFUNCS.append(qfunc)
62
62
  elif external:
63
63
  if synthesize_separately:
64
64
  raise ClassiqInternalError(
@@ -67,7 +67,6 @@ def qfunc(
67
67
  return ExternalQFunc(func)
68
68
  else:
69
69
  qfunc = QFunc(func)
70
- DEC_QFUNCS.append(qfunc)
71
70
  if synthesize_separately:
72
71
  qfunc.update_compilation_metadata(should_synthesize_separately=True)
73
72
  return qfunc
@@ -285,7 +285,7 @@ class QNum(Generic[_P], QScalar):
285
285
  def __init__(
286
286
  self,
287
287
  name: Union[str, HandleBinding],
288
- size: Union[int, CInt, Expression, None] = None,
288
+ size: Union[int, CInt, Expression, SymbolicExpr, None] = None,
289
289
  is_signed: Union[bool, Expression, SymbolicExpr, None] = None,
290
290
  fraction_digits: Union[int, CInt, Expression, None] = None,
291
291
  _expr_str: Optional[str] = None,
@@ -485,8 +485,6 @@ class QArray(ArrayBase[_P], QVar):
485
485
  if is_generative_mode():
486
486
  with generative_mode_context(False):
487
487
  return interpret_expression(str(self.len))
488
- if self._length is not None:
489
- return CParamScalar(f"{self._length}")
490
488
  return CParamScalar(f"get_field({self}, 'len')")
491
489
 
492
490
  @classmethod
@@ -101,6 +101,10 @@ class QExpandable(QCallable, QExpandableInterface, ABC):
101
101
  QExpandable.STACK[-1] if QExpandable.STACK else None
102
102
  )
103
103
 
104
+ def __call__(self, *args: Any, **kwargs: Any) -> None:
105
+ super().__call__(*args, **kwargs)
106
+ self.add_function_dependencies()
107
+
104
108
  def expand(self) -> None:
105
109
  if self not in QExpandable.STACK:
106
110
  with self, generative_mode_context(False):
@@ -161,6 +165,17 @@ class QExpandable(QCallable, QExpandableInterface, ABC):
161
165
  func_decl, None, source_ref_, *args, **kwargs
162
166
  )
163
167
 
168
+ def add_function_dependencies(self) -> None:
169
+ called_name = self.func_decl.name
170
+ if called_name is None:
171
+ return
172
+ for expandable in QExpandable.STACK:
173
+ caller_name = expandable.func_decl.name
174
+ if caller_name is not None:
175
+ caller_deps = self._qmodule.function_dependencies[caller_name]
176
+ if called_name not in caller_deps:
177
+ caller_deps.append(called_name)
178
+
164
179
 
165
180
  class QLambdaFunction(QExpandable):
166
181
  def __init__(
@@ -298,6 +313,12 @@ def prepare_arg(
298
313
  def prepare_arg(
299
314
  arg_decl: AnonPositionalArg, val: Any, func_name: Optional[str], param_name: str
300
315
  ) -> ArgValue:
316
+ from classiq.qmod.quantum_function import BaseQFunc, GenerativeQFunc
317
+
318
+ if isinstance(val, BaseQFunc):
319
+ val.add_function_dependencies()
320
+ if isinstance(val, GenerativeQFunc):
321
+ QMODULE.generative_functions[val.func_decl.name] = val
301
322
  if isinstance(val, QConstant):
302
323
  val.add_to_model()
303
324
  return Expression(expr=str(val.name))