classiq 0.62.0__py3-none-any.whl → 0.63.1__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.
- classiq/__init__.py +3 -0
- classiq/_internals/api_wrapper.py +6 -26
- classiq/_internals/client.py +1 -9
- classiq/applications/chemistry/chemistry_model_constructor.py +1 -1
- classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +26 -8
- classiq/applications/combinatorial_helpers/optimization_model.py +5 -1
- classiq/applications/combinatorial_helpers/pyomo_utils.py +106 -27
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
- classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -2
- classiq/applications/grover/grover_model_constructor.py +1 -1
- classiq/applications/libraries/qmci_library.py +2 -1
- classiq/execution/execution_session.py +66 -96
- classiq/execution/jobs.py +3 -9
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +8 -5
- classiq/interface/backend/pydantic_backend.py +1 -1
- classiq/interface/chemistry/operator.py +0 -204
- classiq/interface/execution/primitives.py +1 -0
- classiq/interface/generator/compiler_keywords.py +4 -0
- classiq/interface/generator/functions/type_name.py +6 -0
- classiq/interface/generator/generated_circuit_data.py +22 -7
- classiq/interface/generator/model/model.py +3 -0
- classiq/interface/generator/model/preferences/preferences.py +13 -0
- classiq/interface/generator/quantum_function_call.py +4 -2
- classiq/interface/model/handle_binding.py +50 -5
- classiq/interface/model/quantum_type.py +16 -0
- classiq/interface/server/routes.py +1 -3
- classiq/model_expansions/capturing/captured_vars.py +102 -19
- classiq/model_expansions/closure.py +19 -56
- classiq/model_expansions/function_builder.py +13 -8
- classiq/model_expansions/generative_functions.py +15 -1
- classiq/model_expansions/interpreter.py +94 -32
- classiq/model_expansions/model_tables.py +4 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +61 -2
- classiq/model_expansions/quantum_operations/classicalif.py +1 -1
- classiq/model_expansions/quantum_operations/control.py +3 -10
- classiq/model_expansions/quantum_operations/emitter.py +1 -1
- classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +1 -2
- classiq/model_expansions/quantum_operations/repeat.py +4 -3
- classiq/model_expansions/scope.py +7 -1
- classiq/model_expansions/scope_initialization.py +34 -25
- classiq/model_expansions/transformers/var_splitter.py +57 -7
- classiq/open_library/__init__.py +4 -0
- classiq/open_library/functions/__init__.py +130 -0
- classiq/{qmod/builtins → open_library}/functions/amplitude_estimation.py +2 -2
- classiq/{qmod/builtins → open_library}/functions/discrete_sine_cosine_transform.py +6 -4
- classiq/{qmod/builtins → open_library}/functions/grover.py +2 -2
- classiq/{qmod/builtins → open_library}/functions/linear_pauli_rotation.py +1 -1
- classiq/{qmod/builtins → open_library}/functions/modular_exponentiation.py +2 -2
- classiq/{qmod/builtins → open_library}/functions/qpe.py +2 -2
- classiq/{qmod/builtins → open_library}/functions/state_preparation.py +6 -149
- classiq/{qmod/builtins → open_library}/functions/swap_test.py +1 -1
- classiq/open_library/functions/utility_functions.py +81 -0
- classiq/{qmod/builtins → open_library}/functions/variational.py +1 -1
- classiq/qmod/builtins/functions/__init__.py +4 -130
- classiq/qmod/builtins/functions/allocation.py +150 -0
- classiq/qmod/builtins/functions/arithmetic.py +0 -34
- classiq/qmod/builtins/functions/operators.py +0 -6
- classiq/qmod/create_model_function.py +8 -162
- classiq/qmod/generative.py +0 -16
- classiq/qmod/model_state_container.py +7 -0
- classiq/qmod/native/pretty_printer.py +10 -11
- classiq/qmod/pretty_print/pretty_printer.py +1 -1
- classiq/qmod/qfunc.py +11 -12
- classiq/qmod/qmod_variable.py +1 -3
- classiq/qmod/quantum_expandable.py +21 -0
- classiq/qmod/quantum_function.py +65 -3
- {classiq-0.62.0.dist-info → classiq-0.63.1.dist-info}/METADATA +1 -1
- {classiq-0.62.0.dist-info → classiq-0.63.1.dist-info}/RECORD +74 -71
- classiq/qmod/builtins/functions/utility_functions.py +0 -43
- /classiq/{qmod/builtins → open_library}/functions/hea.py +0 -0
- /classiq/{qmod/builtins → open_library}/functions/qaoa_penalty.py +0 -0
- /classiq/{qmod/builtins → open_library}/functions/qft_functions.py +0 -0
- /classiq/{qmod/builtins → open_library}/functions/qsvt.py +0 -0
- {classiq-0.62.0.dist-info → classiq-0.63.1.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
|
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.
|
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
|
-
|
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
|
-
|
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
|
-
|
191
|
-
|
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
|
-
|
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
|
classiq/qmod/generative.py
CHANGED
@@ -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.
|
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
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
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)}
|
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
|
-
|
14
|
-
DEC_QFUNCS: list[QFunc] = []
|
15
|
+
_GENERATIVE_SWITCH = False
|
15
16
|
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
classiq/qmod/qmod_variable.py
CHANGED
@@ -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))
|