classiq 0.40.0__py3-none-any.whl → 0.41.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.
- classiq/__init__.py +4 -2
- classiq/_internals/api_wrapper.py +3 -21
- classiq/applications/chemistry/chemistry_model_constructor.py +86 -100
- classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +6 -24
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +33 -45
- classiq/applications/combinatorial_optimization/__init__.py +2 -0
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +29 -26
- classiq/applications/finance/finance_model_constructor.py +23 -26
- classiq/applications/grover/grover_model_constructor.py +37 -38
- classiq/applications/qsvm/qsvm.py +1 -2
- classiq/applications/qsvm/qsvm_model_constructor.py +15 -16
- classiq/execution/__init__.py +4 -0
- classiq/execution/execution_session.py +151 -0
- classiq/execution/qnn.py +80 -0
- classiq/executor.py +2 -109
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +11 -0
- classiq/interface/applications/qsvm.py +0 -8
- classiq/interface/ast_node.py +12 -2
- classiq/interface/backend/backend_preferences.py +25 -1
- classiq/interface/backend/quantum_backend_providers.py +4 -4
- classiq/interface/executor/execution_preferences.py +4 -59
- classiq/interface/executor/execution_result.py +22 -1
- classiq/interface/generator/arith/arithmetic_expression_validator.py +0 -2
- classiq/interface/generator/arith/binary_ops.py +88 -25
- classiq/interface/generator/arith/unary_ops.py +28 -19
- classiq/interface/generator/expressions/atomic_expression_functions.py +6 -2
- classiq/interface/generator/expressions/enums/__init__.py +10 -0
- classiq/interface/generator/expressions/enums/classical_enum.py +5 -1
- classiq/interface/generator/expressions/expression.py +9 -2
- classiq/interface/generator/expressions/qmod_qarray_proxy.py +7 -0
- classiq/interface/generator/expressions/qmod_qscalar_proxy.py +0 -1
- classiq/interface/generator/expressions/sympy_supported_expressions.py +10 -1
- classiq/interface/generator/functions/builtins/internal_operators.py +7 -62
- classiq/interface/generator/functions/builtins/open_lib_functions.py +810 -2
- classiq/interface/generator/functions/classical_type.py +1 -3
- classiq/interface/generator/synthesis_metadata/synthesis_duration.py +0 -4
- classiq/interface/model/bind_operation.py +3 -1
- classiq/interface/model/call_synthesis_data.py +2 -13
- classiq/interface/model/classical_if.py +3 -1
- classiq/interface/model/classical_parameter_declaration.py +13 -0
- classiq/interface/model/control.py +3 -101
- classiq/interface/model/inplace_binary_operation.py +3 -1
- classiq/interface/model/invert.py +3 -1
- classiq/interface/model/port_declaration.py +8 -1
- classiq/interface/model/power.py +3 -1
- classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +4 -2
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +3 -1
- classiq/interface/model/quantum_expressions/quantum_expression.py +11 -1
- classiq/interface/model/quantum_function_call.py +4 -10
- classiq/interface/model/quantum_function_declaration.py +26 -4
- classiq/interface/model/quantum_lambda_function.py +1 -20
- classiq/interface/model/quantum_statement.py +9 -2
- classiq/interface/model/repeat.py +3 -1
- classiq/interface/model/statement_block.py +19 -13
- classiq/interface/model/validations/handles_validator.py +8 -2
- classiq/interface/model/variable_declaration_statement.py +3 -1
- classiq/interface/model/within_apply_operation.py +3 -1
- classiq/interface/server/routes.py +0 -5
- classiq/qmod/__init__.py +1 -2
- classiq/qmod/builtins/classical_execution_primitives.py +22 -2
- classiq/qmod/builtins/functions.py +51 -1
- classiq/qmod/builtins/operations.py +6 -36
- classiq/qmod/declaration_inferrer.py +8 -15
- classiq/qmod/native/__init__.py +9 -0
- classiq/qmod/native/expression_to_qmod.py +12 -9
- classiq/qmod/native/pretty_printer.py +4 -4
- classiq/qmod/pretty_print/__init__.py +9 -0
- classiq/qmod/pretty_print/expression_to_python.py +221 -0
- classiq/qmod/pretty_print/pretty_printer.py +421 -0
- classiq/qmod/qmod_parameter.py +7 -4
- classiq/qmod/quantum_callable.py +2 -1
- classiq/qmod/quantum_expandable.py +4 -3
- classiq/qmod/quantum_function.py +4 -16
- classiq/qmod/symbolic.py +1 -6
- classiq/synthesis.py +15 -16
- {classiq-0.40.0.dist-info → classiq-0.41.0.dist-info}/METADATA +5 -4
- {classiq-0.40.0.dist-info → classiq-0.41.0.dist-info}/RECORD +79 -76
- classiq/interface/model/common_model_types.py +0 -23
- classiq/interface/model/quantum_expressions/control_state.py +0 -38
- {classiq-0.40.0.dist-info → classiq-0.41.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,421 @@
|
|
1
|
+
from typing import Dict, List, Optional, Tuple
|
2
|
+
|
3
|
+
import black
|
4
|
+
|
5
|
+
from classiq.interface.generator.constant import Constant
|
6
|
+
from classiq.interface.generator.expressions.expression import Expression
|
7
|
+
from classiq.interface.generator.functions.classical_type import (
|
8
|
+
ClassicalArray,
|
9
|
+
ConcreteClassicalType,
|
10
|
+
)
|
11
|
+
from classiq.interface.generator.functions.port_declaration import (
|
12
|
+
PortDeclarationDirection,
|
13
|
+
)
|
14
|
+
from classiq.interface.generator.visitor import NodeType, Visitor
|
15
|
+
from classiq.interface.model.bind_operation import BindOperation
|
16
|
+
from classiq.interface.model.classical_if import ClassicalIf
|
17
|
+
from classiq.interface.model.classical_parameter_declaration import (
|
18
|
+
ClassicalParameterDeclaration,
|
19
|
+
)
|
20
|
+
from classiq.interface.model.control import Control
|
21
|
+
from classiq.interface.model.handle_binding import (
|
22
|
+
HandleBinding,
|
23
|
+
SlicedHandleBinding,
|
24
|
+
SubscriptHandleBinding,
|
25
|
+
)
|
26
|
+
from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
|
27
|
+
from classiq.interface.model.invert import Invert
|
28
|
+
from classiq.interface.model.model import Model
|
29
|
+
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
30
|
+
from classiq.interface.model.port_declaration import PortDeclaration
|
31
|
+
from classiq.interface.model.power import Power
|
32
|
+
from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
|
33
|
+
AmplitudeLoadingOperation,
|
34
|
+
)
|
35
|
+
from classiq.interface.model.quantum_expressions.arithmetic_operation import (
|
36
|
+
ArithmeticOperation,
|
37
|
+
)
|
38
|
+
from classiq.interface.model.quantum_expressions.quantum_expression import (
|
39
|
+
QuantumAssignmentOperation,
|
40
|
+
)
|
41
|
+
from classiq.interface.model.quantum_function_call import (
|
42
|
+
OperandIdentifier,
|
43
|
+
QuantumFunctionCall,
|
44
|
+
)
|
45
|
+
from classiq.interface.model.quantum_function_declaration import (
|
46
|
+
PositionalArg,
|
47
|
+
QuantumFunctionDeclaration,
|
48
|
+
QuantumOperandDeclaration,
|
49
|
+
)
|
50
|
+
from classiq.interface.model.quantum_lambda_function import QuantumLambdaFunction
|
51
|
+
from classiq.interface.model.quantum_type import (
|
52
|
+
QuantumBit,
|
53
|
+
QuantumBitvector,
|
54
|
+
QuantumNumeric,
|
55
|
+
)
|
56
|
+
from classiq.interface.model.quantum_variable_declaration import (
|
57
|
+
QuantumVariableDeclaration,
|
58
|
+
)
|
59
|
+
from classiq.interface.model.repeat import Repeat
|
60
|
+
from classiq.interface.model.statement_block import StatementBlock
|
61
|
+
from classiq.interface.model.variable_declaration_statement import (
|
62
|
+
VariableDeclarationStatement,
|
63
|
+
)
|
64
|
+
from classiq.interface.model.within_apply_operation import WithinApply
|
65
|
+
|
66
|
+
import classiq
|
67
|
+
from classiq import Bool, ClassicalList, Integer, Pauli, Real, Struct, StructDeclaration
|
68
|
+
from classiq.qmod.pretty_print.expression_to_python import transform_expression
|
69
|
+
from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
|
70
|
+
|
71
|
+
|
72
|
+
class VariableDeclarationAssignment(Visitor):
|
73
|
+
def __init__(self, pretty_printer: "PythonPrettyPrinter") -> None:
|
74
|
+
self.pretty_printer = pretty_printer
|
75
|
+
|
76
|
+
def visit(self, node: NodeType) -> Tuple[str, Optional[List[str]]]:
|
77
|
+
res = super().visit(node)
|
78
|
+
if not isinstance(res, tuple):
|
79
|
+
raise AssertionError(f"Pretty printing for {type(node)} is not supported ")
|
80
|
+
return res # type: ignore[return-value]
|
81
|
+
|
82
|
+
def visit_QuantumBit(self, qtype: QuantumBit) -> Tuple[str, Optional[List[str]]]:
|
83
|
+
return "QBit", None
|
84
|
+
|
85
|
+
def visit_QuantumBitvector(
|
86
|
+
self, qtype: QuantumBitvector
|
87
|
+
) -> Tuple[str, Optional[List[str]]]:
|
88
|
+
if qtype.length is not None:
|
89
|
+
return "QArray", ["QBit", self.pretty_printer.visit(qtype.length)]
|
90
|
+
return "QArray", ["QBit"]
|
91
|
+
|
92
|
+
def visit_QuantumNumeric(
|
93
|
+
self, qtype: QuantumNumeric
|
94
|
+
) -> Tuple[str, Optional[List[str]]]:
|
95
|
+
params = []
|
96
|
+
if qtype.size is not None:
|
97
|
+
assert qtype.is_signed is not None
|
98
|
+
assert qtype.fraction_digits is not None
|
99
|
+
|
100
|
+
params = [
|
101
|
+
self.pretty_printer.visit(param)
|
102
|
+
for param in [qtype.size, qtype.is_signed, qtype.fraction_digits]
|
103
|
+
]
|
104
|
+
|
105
|
+
return "QNum", params
|
106
|
+
|
107
|
+
|
108
|
+
class PythonPrettyPrinter(Visitor):
|
109
|
+
def __init__(self, decimal_precision: int = DEFAULT_DECIMAL_PRECISION) -> None:
|
110
|
+
self._level = 0
|
111
|
+
self._decimal_precision = decimal_precision
|
112
|
+
self._imports = {"qfunc": 1}
|
113
|
+
self._symbolic_imports: Dict[str, int] = dict()
|
114
|
+
|
115
|
+
def visit(self, node: NodeType) -> str:
|
116
|
+
res = super().visit(node)
|
117
|
+
if not isinstance(res, str):
|
118
|
+
raise AssertionError(f"Pretty printing for {type(node)} is not supported ")
|
119
|
+
return res
|
120
|
+
|
121
|
+
def visit_Model(self, model: Model) -> str:
|
122
|
+
struct_decls = [self.visit(decl) for decl in model.types]
|
123
|
+
func_defs = [self.visit(func) for func in model.functions]
|
124
|
+
constants = [self.visit(const) for const in model.constants]
|
125
|
+
classical_code = self.format_classical_code(model.classical_execution_code)
|
126
|
+
|
127
|
+
code = f"{self.format_imports()}\n\n{self.join_code_parts(*constants, *struct_decls, *func_defs, classical_code)}"
|
128
|
+
return black.format_str(code, mode=black.FileMode())
|
129
|
+
|
130
|
+
def format_classical_code(self, code: str) -> str:
|
131
|
+
if not code:
|
132
|
+
return ""
|
133
|
+
self._imports["cfunc"] = 1
|
134
|
+
self.check_execution_primitives(code)
|
135
|
+
formatted_code = code.replace("\n", "\n" + self._indent + " ")
|
136
|
+
return f"{self._indent}@cfunc\n{self._indent}def cmain() -> None:\n{self._indent} {formatted_code}"
|
137
|
+
|
138
|
+
def check_execution_primitives(self, code: str) -> None:
|
139
|
+
for primitive in dir(classiq.qmod.builtins.classical_execution_primitives):
|
140
|
+
if primitive + "(" in code:
|
141
|
+
self._imports[primitive] = 1
|
142
|
+
|
143
|
+
def format_imports(self) -> str:
|
144
|
+
imports = f"from classiq import {', '.join(self._imports.keys())}\n"
|
145
|
+
symbolic_imports = (
|
146
|
+
f"from classiq.qmod.symbolic import {', '.join(self._symbolic_imports.keys())}\n"
|
147
|
+
if self._symbolic_imports
|
148
|
+
else ""
|
149
|
+
)
|
150
|
+
return imports + symbolic_imports
|
151
|
+
|
152
|
+
def join_code_parts(self, *code_parts: str) -> str:
|
153
|
+
return "\n".join(code_parts)
|
154
|
+
|
155
|
+
def visit_Constant(self, constant: Constant) -> str:
|
156
|
+
self._imports["QConstant"] = 1
|
157
|
+
constant_name = self.visit(constant.name)
|
158
|
+
return f'{self._indent}{constant_name} = QConstant("{constant_name}", {self.visit(constant.const_type)}, {self.visit(constant.value)})\n'
|
159
|
+
|
160
|
+
def _visit_arg_decls(self, func_def: QuantumFunctionDeclaration) -> str:
|
161
|
+
return ", ".join(
|
162
|
+
self.visit(arg_decl) for arg_decl in func_def.get_positional_arg_decls()
|
163
|
+
)
|
164
|
+
|
165
|
+
def visit_QuantumFunctionDeclaration(
|
166
|
+
self, func_decl: QuantumFunctionDeclaration
|
167
|
+
) -> str:
|
168
|
+
return (
|
169
|
+
f"@qfunc\ndef {func_decl.name}({self._visit_arg_decls(func_decl)}) -> None:"
|
170
|
+
)
|
171
|
+
|
172
|
+
def visit_StructDeclaration(self, struct_decl: StructDeclaration) -> str:
|
173
|
+
self._imports["struct"] = 1
|
174
|
+
return f"@struct\nclass {struct_decl.name}:\n{self._visit_variables(struct_decl.variables)}\n"
|
175
|
+
|
176
|
+
def _visit_variables(self, variables: Dict[str, ConcreteClassicalType]) -> str:
|
177
|
+
self._level += 1
|
178
|
+
variables_str = "".join(
|
179
|
+
f"{self._indent}{self.visit(field_name)}: {self.visit(var_decl)};\n"
|
180
|
+
for field_name, var_decl in variables.items()
|
181
|
+
)
|
182
|
+
self._level -= 1
|
183
|
+
return variables_str
|
184
|
+
|
185
|
+
def visit_QuantumVariableDeclaration(
|
186
|
+
self, var_decl: QuantumVariableDeclaration
|
187
|
+
) -> str:
|
188
|
+
return f"{var_decl.name}: {self.visit(var_decl.quantum_type)}"
|
189
|
+
|
190
|
+
def visit_PortDeclaration(self, port_decl: PortDeclaration) -> str:
|
191
|
+
var = self.visit_QuantumVariableDeclaration(port_decl)
|
192
|
+
var_name, var_type = var.split(": ")
|
193
|
+
for direction in PortDeclarationDirection:
|
194
|
+
if port_decl.direction == PortDeclarationDirection.Inout:
|
195
|
+
return var
|
196
|
+
if port_decl.direction == direction:
|
197
|
+
direction_identifier = direction.name
|
198
|
+
self._imports[direction_identifier] = 1
|
199
|
+
return f"{var_name}: {direction_identifier}[{var_type}]"
|
200
|
+
raise RuntimeError("Should not reach here")
|
201
|
+
|
202
|
+
def visit_QuantumBit(self, qtype: QuantumBit) -> str:
|
203
|
+
self._imports["QBit"] = 1
|
204
|
+
return "QBit"
|
205
|
+
|
206
|
+
def visit_QuantumBitvector(self, qtype: QuantumBitvector) -> str:
|
207
|
+
self._imports.update({"QArray": 1, "QBit": 1})
|
208
|
+
if qtype.length is not None:
|
209
|
+
return f"QArray[QBit, {self.visit(qtype.length)}]"
|
210
|
+
return "QArray[QBit]"
|
211
|
+
|
212
|
+
def visit_QuantumNumeric(self, qtype: QuantumNumeric) -> str:
|
213
|
+
params = ""
|
214
|
+
self._imports["QNum"] = 1
|
215
|
+
if qtype.size is not None:
|
216
|
+
assert qtype.is_signed is not None
|
217
|
+
assert qtype.fraction_digits is not None
|
218
|
+
|
219
|
+
params = "[{}]".format(
|
220
|
+
", ".join(
|
221
|
+
self.visit(param)
|
222
|
+
for param in [qtype.size, qtype.is_signed, qtype.fraction_digits]
|
223
|
+
)
|
224
|
+
)
|
225
|
+
|
226
|
+
return f"QNum{params}"
|
227
|
+
|
228
|
+
def visit_ClassicalParameterDeclaration(
|
229
|
+
self, cparam: ClassicalParameterDeclaration
|
230
|
+
) -> str:
|
231
|
+
return f"{cparam.name}: {self.visit(cparam.classical_type)}"
|
232
|
+
|
233
|
+
def visit_Integer(self, ctint: Integer) -> str:
|
234
|
+
self._imports["CInt"] = 1
|
235
|
+
return "CInt"
|
236
|
+
|
237
|
+
def visit_Real(self, ctint: Real) -> str:
|
238
|
+
self._imports["CReal"] = 1
|
239
|
+
return "CReal"
|
240
|
+
|
241
|
+
def visit_Bool(self, ctbool: Bool) -> str:
|
242
|
+
self._imports["CBool"] = 1
|
243
|
+
return "CBool"
|
244
|
+
|
245
|
+
def visit_Pauli(self, ctbool: Pauli) -> str:
|
246
|
+
self._imports["Pauli"] = 1
|
247
|
+
return "Pauli"
|
248
|
+
|
249
|
+
def visit_ClassicalList(self, ctlist: ClassicalList) -> str:
|
250
|
+
self._imports["CArray"] = 1
|
251
|
+
return f"CArray[{self.visit(ctlist.element_type)}]"
|
252
|
+
|
253
|
+
def visit_ClassicalArray(self, ctarray: ClassicalArray) -> str:
|
254
|
+
self._imports["CArray"] = 1
|
255
|
+
return f"CArray[{self.visit(ctarray.element_type)}, {ctarray.size}]"
|
256
|
+
|
257
|
+
def visit_Struct(self, struct: Struct) -> str:
|
258
|
+
if struct.name in dir(classiq.qmod.builtins.structs):
|
259
|
+
self._imports[struct.name] = 1
|
260
|
+
return struct.name
|
261
|
+
|
262
|
+
def visit_VariableDeclarationStatement(
|
263
|
+
self, local_decl: VariableDeclarationStatement
|
264
|
+
) -> str:
|
265
|
+
type_name, params = VariableDeclarationAssignment(self).visit(
|
266
|
+
local_decl.quantum_type
|
267
|
+
)
|
268
|
+
self._imports[type_name] = 1
|
269
|
+
params = [f'"{local_decl.name}"'] + (params or [])
|
270
|
+
param_args = ", ".join(params)
|
271
|
+
return f"{self._indent}{self.visit_QuantumVariableDeclaration(local_decl)} = {type_name}({param_args})\n"
|
272
|
+
|
273
|
+
def _visit_operand_arg_decl(self, arg_decl: PositionalArg) -> str:
|
274
|
+
if isinstance(arg_decl, QuantumVariableDeclaration):
|
275
|
+
return self.visit(arg_decl.quantum_type)
|
276
|
+
if isinstance(arg_decl, ClassicalParameterDeclaration):
|
277
|
+
return self.visit(arg_decl.classical_type)
|
278
|
+
if isinstance(arg_decl, QuantumOperandDeclaration):
|
279
|
+
return self.visit_QuantumOperandDeclaration(arg_decl)
|
280
|
+
|
281
|
+
def visit_QuantumOperandDeclaration(
|
282
|
+
self, op_decl: QuantumOperandDeclaration
|
283
|
+
) -> str:
|
284
|
+
qcallable_identifier = "QCallable"
|
285
|
+
if op_decl.is_list:
|
286
|
+
qcallable_identifier = "QCallableList"
|
287
|
+
self._imports[qcallable_identifier] = 1
|
288
|
+
args = ", ".join(
|
289
|
+
self._visit_operand_arg_decl(arg_decl)
|
290
|
+
for arg_decl in op_decl.get_positional_arg_decls()
|
291
|
+
)
|
292
|
+
|
293
|
+
return f"{op_decl.name}: {qcallable_identifier}" + (f"[{args}]" if args else "")
|
294
|
+
|
295
|
+
def visit_NativeFunctionDefinition(self, func_def: NativeFunctionDefinition) -> str:
|
296
|
+
self._level += 1
|
297
|
+
body = "".join(self.visit(statement) for statement in func_def.body)
|
298
|
+
self._level -= 1
|
299
|
+
return f"{self.visit_QuantumFunctionDeclaration(func_def)} \n{body}\n"
|
300
|
+
|
301
|
+
def visit_QuantumFunctionCall(self, func_call: QuantumFunctionCall) -> str:
|
302
|
+
if len(func_call.get_positional_args()) <= 2:
|
303
|
+
args = ", ".join(self.visit(arg) for arg in func_call.get_positional_args())
|
304
|
+
else:
|
305
|
+
args = ", ".join(
|
306
|
+
f"{self.visit(arg_decl.name)}={self.visit(arg)}"
|
307
|
+
for arg_decl, arg in zip(
|
308
|
+
func_call.func_decl.get_positional_arg_decls(),
|
309
|
+
func_call.get_positional_args(),
|
310
|
+
)
|
311
|
+
)
|
312
|
+
if func_call.func_name in dir(classiq.qmod.builtins.functions):
|
313
|
+
self._imports[func_call.func_name] = 1
|
314
|
+
return f"{self._indent}{func_call.func_name}{f'[{self.visit(func_call.function.index)}]' if isinstance(func_call.function, OperandIdentifier) else ''}({args})\n"
|
315
|
+
|
316
|
+
def visit_Control(self, op: Control) -> str:
|
317
|
+
self._imports["control"] = 1
|
318
|
+
return f"{self._indent}control({self.visit(op.expression)}, {self._visit_body(op.body)})\n"
|
319
|
+
|
320
|
+
def visit_ClassicalIf(self, op: ClassicalIf) -> str:
|
321
|
+
self._imports["if_"] = 1
|
322
|
+
return f"{self._indent}if_(condition={self.visit(op.condition)}, then={self._visit_body(op.then)}, else_={self._visit_body(op.else_)})\n"
|
323
|
+
|
324
|
+
def visit_WithinApply(self, op: WithinApply) -> str:
|
325
|
+
self._imports["within_apply"] = 1
|
326
|
+
return f"{self._indent}within_apply({self._visit_body(op.compute)}, {self._visit_body(op.action)})\n"
|
327
|
+
|
328
|
+
def visit_Repeat(self, repeat: Repeat) -> str:
|
329
|
+
self._imports["repeat"] = 1
|
330
|
+
return f"{self._indent}repeat({self.visit(repeat.count)}, {self._visit_body(repeat.body, [repeat.iter_var])})\n"
|
331
|
+
|
332
|
+
def visit_Power(self, power: Power) -> str:
|
333
|
+
self._imports["power"] = 1
|
334
|
+
return f"{self._indent}power({self.visit(power.power)}, {self._visit_body(power.body)})\n"
|
335
|
+
|
336
|
+
def visit_Invert(self, invert: Invert) -> str:
|
337
|
+
self._imports["invert"] = 1
|
338
|
+
return f"{self._indent}invert({self._visit_body(invert.body)})\n"
|
339
|
+
|
340
|
+
def _visit_body(
|
341
|
+
self, body: StatementBlock, operand_arguments: Optional[List[str]] = None
|
342
|
+
) -> str:
|
343
|
+
argument_string = (
|
344
|
+
(" " + ", ".join(operand_arguments))
|
345
|
+
if operand_arguments is not None
|
346
|
+
else ""
|
347
|
+
)
|
348
|
+
code = f"lambda{argument_string}: {'[' if len(body) > 1 else ''}\n"
|
349
|
+
self._level += 1
|
350
|
+
for i, statement in enumerate(body):
|
351
|
+
if isinstance(
|
352
|
+
statement, (QuantumAssignmentOperation, VariableDeclarationStatement)
|
353
|
+
):
|
354
|
+
raise AssertionError(
|
355
|
+
"pretty printing quantum assignment operations or variable declaration statements in quantum lambda function is unsupported."
|
356
|
+
)
|
357
|
+
code += self.visit(statement)
|
358
|
+
if i < len(body) - 1:
|
359
|
+
code += ","
|
360
|
+
self._level -= 1
|
361
|
+
return f"{code}{']' if len(body) > 1 else ''}"
|
362
|
+
|
363
|
+
def visit_InplaceBinaryOperation(self, op: InplaceBinaryOperation) -> str:
|
364
|
+
self._imports[op.operation.value] = 1
|
365
|
+
return (
|
366
|
+
f"{self._indent}{op.operation.value}({op.value.name}, {op.target.name})\n"
|
367
|
+
)
|
368
|
+
|
369
|
+
def visit_Expression(self, expr: Expression) -> str:
|
370
|
+
return transform_expression(
|
371
|
+
expr.expr,
|
372
|
+
level=self._level,
|
373
|
+
decimal_precision=self._decimal_precision,
|
374
|
+
imports=self._imports,
|
375
|
+
symbolic_imports=self._symbolic_imports,
|
376
|
+
)
|
377
|
+
|
378
|
+
def visit_QuantumLambdaFunction(self, qlambda: QuantumLambdaFunction) -> str:
|
379
|
+
assert qlambda.func_decl is not None
|
380
|
+
return self._visit_body(
|
381
|
+
qlambda.body,
|
382
|
+
[
|
383
|
+
self.visit(arg_decl.name)
|
384
|
+
for arg_decl in qlambda.func_decl.get_positional_arg_decls()
|
385
|
+
],
|
386
|
+
)
|
387
|
+
|
388
|
+
def visit_HandleBinding(self, var_ref: HandleBinding) -> str:
|
389
|
+
return var_ref.name
|
390
|
+
|
391
|
+
def visit_SlicedHandleBinding(self, var_ref: SlicedHandleBinding) -> str:
|
392
|
+
return f"{var_ref.name}[{self.visit(var_ref.start)}:{self.visit(var_ref.end)}]"
|
393
|
+
|
394
|
+
def visit_SubscriptHandleBinding(self, var_ref: SubscriptHandleBinding) -> str:
|
395
|
+
return f"{var_ref.name}[{self.visit(var_ref.index)}]"
|
396
|
+
|
397
|
+
def visit_ArithmeticOperation(self, arith_op: ArithmeticOperation) -> str:
|
398
|
+
op = "^=" if arith_op.inplace_result else "|="
|
399
|
+
return f"{self._indent}{self.visit(arith_op.result_var)} {op} {self.visit(arith_op.expression)}\n"
|
400
|
+
|
401
|
+
def visit_AmplitudeLoadingOperation(
|
402
|
+
self, amplitude_loading_op: AmplitudeLoadingOperation
|
403
|
+
) -> str:
|
404
|
+
return f"{self._indent}{self.visit(amplitude_loading_op.result_var)} *= {self.visit(amplitude_loading_op.expression)}\n"
|
405
|
+
|
406
|
+
def _print_bind_handles(self, handles: List[HandleBinding]) -> str:
|
407
|
+
if len(handles) == 1:
|
408
|
+
return self.visit(handles[0])
|
409
|
+
|
410
|
+
return "[" + ", ".join(self.visit(handle) for handle in handles) + "]"
|
411
|
+
|
412
|
+
def visit_BindOperation(self, bind_op: BindOperation) -> str:
|
413
|
+
self._imports["bind"] = 1
|
414
|
+
return f"{self._indent}bind({self._print_bind_handles(bind_op.in_handles)}, {self._print_bind_handles(bind_op.out_handles)})\n"
|
415
|
+
|
416
|
+
def visit_list(self, node: list) -> str:
|
417
|
+
return "[" + ", ".join(self.visit(elem) for elem in node) + "]"
|
418
|
+
|
419
|
+
@property
|
420
|
+
def _indent(self) -> str:
|
421
|
+
return " " * self._level
|
classiq/qmod/qmod_parameter.py
CHANGED
@@ -35,10 +35,6 @@ class CParam(SymbolicSuperclass):
|
|
35
35
|
pass
|
36
36
|
|
37
37
|
|
38
|
-
class QParam(CParam, Generic[_T]):
|
39
|
-
pass
|
40
|
-
|
41
|
-
|
42
38
|
class CInt(CParam):
|
43
39
|
pass
|
44
40
|
|
@@ -85,6 +81,13 @@ class CParamList(CParam):
|
|
85
81
|
self._list_type = list_type
|
86
82
|
|
87
83
|
def __getitem__(self, key: Any) -> CParam:
|
84
|
+
if isinstance(key, slice):
|
85
|
+
start = key.start if key.start is not None else ""
|
86
|
+
stop = key.stop if key.stop is not None else ""
|
87
|
+
if key.step is not None:
|
88
|
+
key = f"{start}:{key.step}:{stop}"
|
89
|
+
else:
|
90
|
+
key = f"{start}:{stop}"
|
88
91
|
return create_param(
|
89
92
|
f"({self})[{key}]",
|
90
93
|
self._list_type.element_type,
|
classiq/qmod/quantum_callable.py
CHANGED
@@ -41,10 +41,11 @@ class QExpandableInterface(ABC):
|
|
41
41
|
|
42
42
|
class QCallable(Generic[P], ABC):
|
43
43
|
CURRENT_EXPANDABLE: ClassVar[Optional[QExpandableInterface]] = None
|
44
|
+
FRAME_DEPTH = 1
|
44
45
|
|
45
46
|
def __call__(self, *args: Any, **kwargs: Any) -> None:
|
46
47
|
assert QCallable.CURRENT_EXPANDABLE is not None
|
47
|
-
source_ref = get_source_ref(sys._getframe(
|
48
|
+
source_ref = get_source_ref(sys._getframe(self.FRAME_DEPTH))
|
48
49
|
QCallable.CURRENT_EXPANDABLE.append_statement_to_body(
|
49
50
|
self.create_quantum_function_call(source_ref, *args, **kwargs)
|
50
51
|
)
|
@@ -81,8 +81,9 @@ class QExpandable(QCallable, QExpandableInterface, ABC):
|
|
81
81
|
)
|
82
82
|
|
83
83
|
def expand(self) -> None:
|
84
|
-
|
85
|
-
self
|
84
|
+
if self not in QExpandable.STACK:
|
85
|
+
with self:
|
86
|
+
self._py_callable(*self._get_positional_args())
|
86
87
|
|
87
88
|
def infer_rename_params(self) -> Dict[str, str]:
|
88
89
|
return {}
|
@@ -175,7 +176,7 @@ class QTerminalCallable(QCallable):
|
|
175
176
|
def len(self) -> CParamScalar:
|
176
177
|
if not self.is_list:
|
177
178
|
raise ClassiqValueError("Cannot get length of a non-list operand")
|
178
|
-
return CParamScalar(f"{self.func_decl.name}
|
179
|
+
return CParamScalar(f"get_field({self.func_decl.name}, 'len')")
|
179
180
|
|
180
181
|
@property
|
181
182
|
def func_decl(self) -> QuantumFunctionDeclaration:
|
classiq/qmod/quantum_function.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
import ast
|
2
2
|
import functools
|
3
|
-
import warnings
|
4
3
|
from inspect import isclass
|
5
4
|
from typing import Any, Callable, Dict, List, Optional, Tuple, get_origin
|
6
5
|
|
@@ -18,7 +17,7 @@ from classiq.exceptions import ClassiqError
|
|
18
17
|
from classiq.qmod.classical_function import CFunc
|
19
18
|
from classiq.qmod.declaration_inferrer import infer_func_decl
|
20
19
|
from classiq.qmod.qmod_constant import QConstant
|
21
|
-
from classiq.qmod.qmod_parameter import CArray, CParam
|
20
|
+
from classiq.qmod.qmod_parameter import CArray, CParam
|
22
21
|
from classiq.qmod.qmod_variable import QVar
|
23
22
|
from classiq.qmod.quantum_callable import QCallable, QCallableList
|
24
23
|
from classiq.qmod.quantum_expandable import QExpandable, QTerminalCallable
|
@@ -47,20 +46,10 @@ def create_model(
|
|
47
46
|
|
48
47
|
|
49
48
|
class QFunc(QExpandable):
|
49
|
+
FRAME_DEPTH = 2
|
50
|
+
|
50
51
|
def __init__(self, py_callable: Callable) -> None:
|
51
52
|
_validate_no_gen_params(py_callable.__annotations__)
|
52
|
-
if any(
|
53
|
-
get_origin(annotation) == QParam
|
54
|
-
for annotation in py_callable.__annotations__.values()
|
55
|
-
):
|
56
|
-
warnings.warn(
|
57
|
-
"QParam is deprecated and will be removed in a future release. Use the "
|
58
|
-
"new classical parameter types instead:\n * QParam[int] -> CInt\n * QParam[float] "
|
59
|
-
"-> CReal\n * QParam[bool] -> CBool\n * QParam[list[int]] -> "
|
60
|
-
"CArray[CInt]",
|
61
|
-
category=DeprecationWarning,
|
62
|
-
stacklevel=4,
|
63
|
-
)
|
64
53
|
super().__init__(py_callable)
|
65
54
|
functools.update_wrapper(self, py_callable)
|
66
55
|
|
@@ -109,7 +98,7 @@ class QFunc(QExpandable):
|
|
109
98
|
return
|
110
99
|
self.expand()
|
111
100
|
self._qmodule.native_defs[self.func_decl.name] = NativeFunctionDefinition(
|
112
|
-
**self.func_decl.
|
101
|
+
**{**self.func_decl.dict(), **{"body": self.body}}
|
113
102
|
)
|
114
103
|
|
115
104
|
def _add_constants_from_classical_code(
|
@@ -174,7 +163,6 @@ def _validate_no_gen_params(annotations: Dict[str, Any]) -> None:
|
|
174
163
|
for name, annotation in annotations.items()
|
175
164
|
if not (
|
176
165
|
name == "return"
|
177
|
-
or get_origin(annotation) is QParam
|
178
166
|
or isclass(annotation)
|
179
167
|
and issubclass(annotation, CParam)
|
180
168
|
or isclass(annotation)
|
classiq/qmod/symbolic.py
CHANGED
@@ -7,15 +7,13 @@ from typing import (
|
|
7
7
|
Tuple,
|
8
8
|
Type,
|
9
9
|
TypeVar,
|
10
|
-
get_args,
|
11
|
-
get_origin,
|
12
10
|
overload,
|
13
11
|
)
|
14
12
|
|
15
13
|
from classiq.exceptions import ClassiqValueError
|
16
14
|
from classiq.qmod import model_state_container
|
17
15
|
from classiq.qmod.declaration_inferrer import python_type_to_qmod
|
18
|
-
from classiq.qmod.qmod_parameter import CParam, CParamScalar,
|
16
|
+
from classiq.qmod.qmod_parameter import CParam, CParamScalar, create_param
|
19
17
|
from classiq.qmod.symbolic_expr import SymbolicExpr
|
20
18
|
from classiq.qmod.symbolic_type import SymbolicTypes
|
21
19
|
|
@@ -47,9 +45,6 @@ def symbolic_function(*args: Any, return_type: Optional[Type[T]] = None) -> CPar
|
|
47
45
|
if return_type is None:
|
48
46
|
return CParamScalar(expr)
|
49
47
|
|
50
|
-
if get_origin(return_type) is QParam:
|
51
|
-
return_type = get_args(return_type)[0]
|
52
|
-
|
53
48
|
if TYPE_CHECKING:
|
54
49
|
assert return_type is not None
|
55
50
|
|
classiq/synthesis.py
CHANGED
@@ -5,8 +5,7 @@ import pydantic
|
|
5
5
|
from classiq.interface.executor.execution_preferences import ExecutionPreferences
|
6
6
|
from classiq.interface.generator.model.constraints import Constraints
|
7
7
|
from classiq.interface.generator.model.preferences.preferences import Preferences
|
8
|
-
from classiq.interface.model.
|
9
|
-
from classiq.interface.model.model import SerializedModel
|
8
|
+
from classiq.interface.model.model import Model, SerializedModel
|
10
9
|
|
11
10
|
from classiq._internals.api_wrapper import ApiWrapper
|
12
11
|
from classiq._internals.async_utils import syncify_function
|
@@ -15,9 +14,9 @@ SerializedQuantumProgram = NewType("SerializedQuantumProgram", str)
|
|
15
14
|
|
16
15
|
|
17
16
|
async def synthesize_async(
|
18
|
-
serialized_model:
|
17
|
+
serialized_model: SerializedModel,
|
19
18
|
) -> SerializedQuantumProgram:
|
20
|
-
model
|
19
|
+
model = pydantic.parse_raw_as(Model, serialized_model)
|
21
20
|
quantum_program = await ApiWrapper.call_generation_task(model)
|
22
21
|
return SerializedQuantumProgram(quantum_program.json(indent=2))
|
23
22
|
|
@@ -26,27 +25,27 @@ synthesize = syncify_function(synthesize_async)
|
|
26
25
|
|
27
26
|
|
28
27
|
def set_preferences(
|
29
|
-
serialized_model:
|
30
|
-
) ->
|
31
|
-
model
|
28
|
+
serialized_model: SerializedModel, preferences: Preferences
|
29
|
+
) -> SerializedModel:
|
30
|
+
model = pydantic.parse_raw_as(Model, serialized_model)
|
32
31
|
model.preferences = preferences
|
33
|
-
return model.get_model()
|
32
|
+
return model.get_model()
|
34
33
|
|
35
34
|
|
36
35
|
def set_constraints(
|
37
|
-
serialized_model:
|
38
|
-
) ->
|
39
|
-
model
|
36
|
+
serialized_model: SerializedModel, constraints: Constraints
|
37
|
+
) -> SerializedModel:
|
38
|
+
model = pydantic.parse_raw_as(Model, serialized_model)
|
40
39
|
model.constraints = constraints
|
41
|
-
return model.get_model()
|
40
|
+
return model.get_model()
|
42
41
|
|
43
42
|
|
44
43
|
def set_execution_preferences(
|
45
|
-
serialized_model:
|
46
|
-
) ->
|
47
|
-
model
|
44
|
+
serialized_model: SerializedModel, execution_preferences: ExecutionPreferences
|
45
|
+
) -> SerializedModel:
|
46
|
+
model = pydantic.parse_raw_as(Model, serialized_model)
|
48
47
|
model.execution_preferences = execution_preferences
|
49
|
-
return model.get_model()
|
48
|
+
return model.get_model()
|
50
49
|
|
51
50
|
|
52
51
|
__all__ = [
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: classiq
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.41.0
|
4
4
|
Summary: Classiq's Python SDK for quantum computing
|
5
5
|
Home-page: https://classiq.io
|
6
6
|
License: Proprietary
|
@@ -31,6 +31,7 @@ Provides-Extra: analyzer-sdk
|
|
31
31
|
Provides-Extra: qml
|
32
32
|
Requires-Dist: ConfigArgParse (>=1.5.3,<2.0.0)
|
33
33
|
Requires-Dist: Pyomo (>=6.5,<6.6)
|
34
|
+
Requires-Dist: black (>=24.0,<25.0)
|
34
35
|
Requires-Dist: httpx (>=0.23.0,<1)
|
35
36
|
Requires-Dist: ipywidgets (>=7.7.1,<8.0.0) ; extra == "analyzer-sdk"
|
36
37
|
Requires-Dist: keyring (>=23.5.0,<24.0.0)
|
@@ -38,11 +39,11 @@ Requires-Dist: matplotlib (>=3.4.3,<4.0.0)
|
|
38
39
|
Requires-Dist: networkx (>=2.5.1,<3.0.0)
|
39
40
|
Requires-Dist: numexpr (>=2.7.3,<3.0.0)
|
40
41
|
Requires-Dist: numpy (>=1.20.1,<2.0.0)
|
41
|
-
Requires-Dist: packaging (>=
|
42
|
-
Requires-Dist: pandas (>=1.4.0,<
|
42
|
+
Requires-Dist: packaging (>=22.0,<23.0)
|
43
|
+
Requires-Dist: pandas (>=1.4.0,<3.0.0)
|
43
44
|
Requires-Dist: plotly (>=5.7.0,<6.0.0)
|
44
45
|
Requires-Dist: pydantic (>=1.9.1,<2.0.0)
|
45
|
-
Requires-Dist: scipy (
|
46
|
+
Requires-Dist: scipy (>=1.10.1,<2.0.0)
|
46
47
|
Requires-Dist: sympy (>=1.9.0,<1.11.0)
|
47
48
|
Requires-Dist: tabulate (>=0.8.9,<1)
|
48
49
|
Requires-Dist: torch (>=2.0,<3.0) ; extra == "qml"
|