classiq 0.39.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.
Files changed (100) hide show
  1. classiq/__init__.py +5 -2
  2. classiq/_internals/api_wrapper.py +3 -21
  3. classiq/applications/chemistry/chemistry_model_constructor.py +87 -101
  4. classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +7 -26
  5. classiq/applications/combinatorial_helpers/optimization_model.py +7 -6
  6. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +33 -55
  7. classiq/applications/combinatorial_optimization/__init__.py +4 -0
  8. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +29 -26
  9. classiq/applications/finance/finance_model_constructor.py +23 -26
  10. classiq/applications/grover/grover_model_constructor.py +37 -38
  11. classiq/applications/qsvm/qsvm.py +1 -2
  12. classiq/applications/qsvm/qsvm_model_constructor.py +15 -16
  13. classiq/execution/__init__.py +4 -0
  14. classiq/execution/execution_session.py +151 -0
  15. classiq/execution/qnn.py +80 -0
  16. classiq/executor.py +2 -109
  17. classiq/interface/_version.py +1 -1
  18. classiq/interface/analyzer/analysis_params.py +11 -0
  19. classiq/interface/applications/qsvm.py +0 -8
  20. classiq/interface/ast_node.py +12 -2
  21. classiq/interface/backend/backend_preferences.py +30 -6
  22. classiq/interface/backend/quantum_backend_providers.py +11 -11
  23. classiq/interface/executor/execution_preferences.py +7 -67
  24. classiq/interface/executor/execution_result.py +22 -1
  25. classiq/interface/generator/application_apis/chemistry_declarations.py +2 -4
  26. classiq/interface/generator/application_apis/finance_declarations.py +1 -1
  27. classiq/interface/generator/arith/binary_ops.py +88 -25
  28. classiq/interface/generator/arith/unary_ops.py +28 -19
  29. classiq/interface/generator/expressions/atomic_expression_functions.py +6 -2
  30. classiq/interface/generator/expressions/enums/__init__.py +10 -0
  31. classiq/interface/generator/expressions/enums/classical_enum.py +5 -1
  32. classiq/interface/generator/expressions/expression.py +9 -2
  33. classiq/interface/generator/expressions/qmod_qarray_proxy.py +89 -0
  34. classiq/interface/generator/expressions/qmod_qscalar_proxy.py +20 -0
  35. classiq/interface/generator/expressions/qmod_sized_proxy.py +22 -0
  36. classiq/interface/generator/expressions/sympy_supported_expressions.py +10 -1
  37. classiq/interface/generator/functions/builtins/core_library/atomic_quantum_functions.py +8 -6
  38. classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +10 -4
  39. classiq/interface/generator/functions/builtins/internal_operators.py +7 -62
  40. classiq/interface/generator/functions/builtins/open_lib_functions.py +1627 -271
  41. classiq/interface/generator/functions/classical_type.py +27 -17
  42. classiq/interface/generator/model/preferences/preferences.py +4 -2
  43. classiq/interface/generator/synthesis_metadata/synthesis_duration.py +0 -4
  44. classiq/interface/model/bind_operation.py +3 -1
  45. classiq/interface/model/call_synthesis_data.py +2 -13
  46. classiq/interface/model/classical_if.py +3 -1
  47. classiq/interface/model/classical_parameter_declaration.py +13 -0
  48. classiq/interface/model/control.py +6 -8
  49. classiq/interface/model/inplace_binary_operation.py +3 -1
  50. classiq/interface/model/invert.py +3 -1
  51. classiq/interface/model/port_declaration.py +8 -1
  52. classiq/interface/model/power.py +3 -1
  53. classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +4 -2
  54. classiq/interface/model/quantum_expressions/arithmetic_operation.py +3 -1
  55. classiq/interface/model/quantum_expressions/quantum_expression.py +11 -1
  56. classiq/interface/model/quantum_function_call.py +4 -10
  57. classiq/interface/model/quantum_function_declaration.py +26 -4
  58. classiq/interface/model/quantum_lambda_function.py +1 -20
  59. classiq/interface/model/quantum_statement.py +9 -2
  60. classiq/interface/model/quantum_type.py +6 -5
  61. classiq/interface/model/repeat.py +3 -1
  62. classiq/interface/model/resolvers/function_call_resolver.py +0 -5
  63. classiq/interface/model/statement_block.py +19 -16
  64. classiq/interface/model/validations/handles_validator.py +8 -2
  65. classiq/interface/model/variable_declaration_statement.py +3 -1
  66. classiq/interface/model/within_apply_operation.py +3 -1
  67. classiq/interface/server/routes.py +0 -5
  68. classiq/qmod/__init__.py +5 -2
  69. classiq/qmod/builtins/classical_execution_primitives.py +22 -2
  70. classiq/qmod/builtins/classical_functions.py +30 -35
  71. classiq/qmod/builtins/functions.py +263 -153
  72. classiq/qmod/builtins/operations.py +50 -26
  73. classiq/qmod/builtins/structs.py +50 -48
  74. classiq/qmod/declaration_inferrer.py +32 -27
  75. classiq/qmod/native/__init__.py +9 -0
  76. classiq/qmod/native/expression_to_qmod.py +8 -4
  77. classiq/qmod/native/pretty_printer.py +11 -18
  78. classiq/qmod/pretty_print/__init__.py +9 -0
  79. classiq/qmod/pretty_print/expression_to_python.py +221 -0
  80. classiq/qmod/pretty_print/pretty_printer.py +421 -0
  81. classiq/qmod/qmod_constant.py +7 -7
  82. classiq/qmod/qmod_parameter.py +57 -33
  83. classiq/qmod/qmod_struct.py +2 -2
  84. classiq/qmod/qmod_variable.py +40 -29
  85. classiq/qmod/quantum_callable.py +8 -4
  86. classiq/qmod/quantum_expandable.py +22 -15
  87. classiq/qmod/quantum_function.py +15 -4
  88. classiq/qmod/symbolic.py +73 -68
  89. classiq/qmod/symbolic_expr.py +1 -1
  90. classiq/qmod/symbolic_type.py +1 -4
  91. classiq/qmod/utilities.py +29 -0
  92. classiq/synthesis.py +15 -16
  93. {classiq-0.39.0.dist-info → classiq-0.41.0.dist-info}/METADATA +5 -4
  94. {classiq-0.39.0.dist-info → classiq-0.41.0.dist-info}/RECORD +95 -94
  95. classiq/interface/executor/error_mitigation.py +0 -6
  96. classiq/interface/generator/functions/builtins/core_library/chemistry_functions.py +0 -0
  97. classiq/interface/model/common_model_types.py +0 -23
  98. classiq/interface/model/quantum_expressions/control_state.py +0 -38
  99. classiq/interface/model/quantum_if_operation.py +0 -94
  100. {classiq-0.39.0.dist-info → classiq-0.41.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,221 @@
1
+ import ast
2
+ import re
3
+ from dataclasses import dataclass
4
+ from typing import Callable, Dict, List, Mapping, Type
5
+
6
+ import numpy as np
7
+
8
+ import classiq
9
+ from classiq.qmod.utilities import DEFAULT_DECIMAL_PRECISION
10
+
11
+ IDENTIFIER = re.compile(r"[a-zA-Z_]\w*")
12
+ BINARY_OPS: Mapping[Type[ast.operator], str] = {
13
+ ast.Add: "+",
14
+ ast.Sub: "-",
15
+ ast.Mult: "*",
16
+ ast.Div: "/",
17
+ ast.Mod: "%",
18
+ ast.Pow: "**",
19
+ ast.BitAnd: "&",
20
+ ast.BitOr: "|",
21
+ ast.BitXor: "^",
22
+ ast.LShift: "<<",
23
+ ast.RShift: ">>",
24
+ }
25
+ BOOL_OPS: Mapping[Type[ast.boolop], str] = {ast.And: "and", ast.Or: "or"}
26
+ UNARY_OPS: Mapping[Type[ast.unaryop], str] = {
27
+ ast.UAdd: "+",
28
+ ast.USub: "-",
29
+ ast.Invert: "~",
30
+ ast.Not: "not",
31
+ }
32
+ COMPARE_OPS: Mapping[Type[ast.cmpop], str] = {
33
+ ast.Eq: "==",
34
+ ast.NotEq: "!=",
35
+ ast.Lt: "<",
36
+ ast.LtE: "<=",
37
+ ast.Gt: ">",
38
+ ast.GtE: ">=",
39
+ }
40
+ LIST_FORMAT_CHAR_LIMIT = 20
41
+
42
+
43
+ @dataclass
44
+ class ASTToQMODCode(ast.NodeVisitor):
45
+ level: int
46
+ imports: Dict[str, int]
47
+ symbolic_imports: Dict[str, int]
48
+ decimal_precision: int
49
+ indent_seq: str = " "
50
+
51
+ @property
52
+ def indent(self) -> str:
53
+ return self.level * self.indent_seq
54
+
55
+ def _handle_imports(self, name: str, is_possibly_symbolic: bool = False) -> None:
56
+ if name in dir(classiq):
57
+ self.imports[name] = 1
58
+ if is_possibly_symbolic and name in dir(classiq.qmod.symbolic):
59
+ self.symbolic_imports[name] = 1
60
+
61
+ def visit(self, node: ast.AST) -> str:
62
+ res = super().visit(node)
63
+ if not isinstance(res, str):
64
+ raise AssertionError("Error parsing expression: unsupported AST node.")
65
+ return res
66
+
67
+ def visit_Module(self, node: ast.Module) -> str:
68
+ return self.indent.join(self.visit(child) for child in node.body)
69
+
70
+ def visit_Attribute(self, node: ast.Attribute) -> str:
71
+ if not isinstance(node.value, ast.Name) or not isinstance(node.attr, str):
72
+ raise AssertionError("Error parsing enum attribute access")
73
+ if not (IDENTIFIER.match(node.value.id) and IDENTIFIER.match(node.attr)):
74
+ raise AssertionError("Error parsing enum attribute access")
75
+ self._handle_imports(node.value.id)
76
+ return f"{node.value.id!s}.{node.attr!s}"
77
+
78
+ def visit_Name(self, node: ast.Name) -> str:
79
+ self._handle_imports(node.id, True)
80
+ return node.id
81
+
82
+ def visit_Num(self, node: ast.Num) -> str:
83
+ return str(np.round(node.n, self.decimal_precision))
84
+
85
+ def visit_Str(self, node: ast.Str) -> str:
86
+ return repr(node.s)
87
+
88
+ def visit_Constant(self, node: ast.Constant) -> str:
89
+ return repr(node.value)
90
+
91
+ def visit_BinOp(self, node: ast.BinOp) -> str:
92
+ return "({} {} {})".format(
93
+ self.visit(node.left),
94
+ BINARY_OPS[type(node.op)],
95
+ self.visit(node.right),
96
+ )
97
+
98
+ def visit_UnaryOp(self, node: ast.UnaryOp) -> str:
99
+ unary_op = UNARY_OPS[type(node.op)]
100
+ space = " " if unary_op == "not" else ""
101
+ return f"({unary_op}{space}{self.visit(node.operand)})"
102
+
103
+ def visit_BoolOp(self, node: ast.BoolOp) -> str:
104
+ return "({})".format(
105
+ (" " + BOOL_OPS[type(node.op)] + " ").join(
106
+ self.visit(value) for value in node.values
107
+ )
108
+ )
109
+
110
+ def visit_Compare(self, node: ast.Compare) -> str:
111
+ if len(node.ops) != 1 or len(node.comparators) != 1:
112
+ raise AssertionError("Error parsing comparison expression.")
113
+ return "({} {} {})".format(
114
+ self.visit(node.left),
115
+ COMPARE_OPS[type(node.ops[0])],
116
+ self.visit(node.comparators[0]),
117
+ )
118
+
119
+ def visit_List(self, node: ast.List) -> str:
120
+ elts = node.elts
121
+ elements = self.indent_items(lambda: [self.visit(element) for element in elts])
122
+ return f"[{elements}]"
123
+
124
+ def visit_Subscript(self, node: ast.Subscript) -> str:
125
+ return f"{self.visit(node.value)}[{_remove_redundant_parentheses(self.visit(node.slice))}]"
126
+
127
+ def visit_Slice(self, node: ast.Slice) -> str:
128
+ if node.lower is None or node.upper is None or node.step is not None:
129
+ raise AssertionError("Error parsing slice expression.")
130
+ return f"{self.visit(node.lower)}:{self.visit(node.upper)}"
131
+
132
+ def visit_Call(self, node: ast.Call) -> str:
133
+ func = self.visit(node.func)
134
+ self._handle_imports(func, True)
135
+ if func == "get_field":
136
+ if len(node.args) != 2:
137
+ raise AssertionError("Error parsing struct field access.")
138
+ field = str(self.visit(node.args[1])).replace("'", "")
139
+ if not IDENTIFIER.match(field):
140
+ raise AssertionError("Error parsing struct field access.")
141
+ return f"{self.visit(node.args[0])}.{field}"
142
+ elif func == "struct_literal":
143
+ if len(node.args) != 1 or not isinstance(node.args[0], ast.Name):
144
+ raise AssertionError("Error parsing struct literal.")
145
+ keywords = node.keywords
146
+ initializer_list = self.indent_items(
147
+ lambda: [
148
+ f"{keyword.arg} = {self._cleaned_ast_to_code(keyword.value)}"
149
+ for keyword in keywords
150
+ if keyword.arg is not None
151
+ ]
152
+ )
153
+ return f"{self.visit(node.args[0])}({initializer_list})"
154
+ else:
155
+ return "{}({})".format(
156
+ func, ", ".join(self._cleaned_ast_to_code(arg) for arg in node.args)
157
+ )
158
+
159
+ def visit_Expr(self, node: ast.Expr) -> str:
160
+ return self._cleaned_ast_to_code(node.value)
161
+
162
+ def generic_visit(self, node: ast.AST) -> None:
163
+ raise AssertionError("Cannot parse node of type: " + type(node).__name__)
164
+
165
+ def indent_items(self, items: Callable[[], List[str]]) -> str:
166
+ should_indent = (
167
+ len("".join([i.strip() for i in items()])) >= LIST_FORMAT_CHAR_LIMIT
168
+ )
169
+ if should_indent:
170
+ self.level += 1
171
+ left_ws = "\n" + self.indent
172
+ inner_ws = ",\n" + self.indent
173
+ else:
174
+ left_ws = ""
175
+ inner_ws = ", "
176
+ items_ = items()
177
+ if should_indent:
178
+ self.level -= 1
179
+ right_ws = "\n" + self.indent
180
+ else:
181
+ right_ws = ""
182
+ return f"{left_ws}{inner_ws.join(items_)}{right_ws}"
183
+
184
+ def _cleaned_ast_to_code(self, node: ast.AST) -> str:
185
+ return _remove_redundant_parentheses(self.visit(node))
186
+
187
+
188
+ def _remove_redundant_parentheses(expr: str) -> str:
189
+ if not (expr.startswith("(") and expr.endswith(")")):
190
+ return expr
191
+ parentheses_map: Dict[int, int] = dict()
192
+ stack: List[int] = []
193
+ for index, char in enumerate(expr):
194
+ if char == "(":
195
+ stack.append(index)
196
+ elif char == ")":
197
+ parentheses_map[stack.pop()] = index
198
+ index = 0
199
+ original_length = len(expr)
200
+ while (
201
+ index in parentheses_map
202
+ and parentheses_map[index] == original_length - index - 1
203
+ ):
204
+ expr = expr[1:-1]
205
+ index += 1
206
+ return expr
207
+
208
+
209
+ def transform_expression(
210
+ expr: str,
211
+ imports: Dict[str, int],
212
+ symbolic_imports: Dict[str, int],
213
+ level: int = 0,
214
+ decimal_precision: int = DEFAULT_DECIMAL_PRECISION,
215
+ ) -> str:
216
+ return ASTToQMODCode(
217
+ level=level,
218
+ decimal_precision=decimal_precision,
219
+ imports=imports,
220
+ symbolic_imports=symbolic_imports,
221
+ ).visit(ast.parse(expr))
@@ -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
@@ -6,13 +6,13 @@ from classiq.interface.generator.expressions.expression import Expression
6
6
  from classiq.interface.generator.functions.classical_type import (
7
7
  ClassicalArray,
8
8
  ClassicalList,
9
- QStructBase,
9
+ CStructBase,
10
10
  )
11
11
 
12
12
  from classiq.exceptions import ClassiqError
13
13
  from classiq.qmod.declaration_inferrer import python_type_to_qmod
14
14
  from classiq.qmod.model_state_container import ModelStateContainer
15
- from classiq.qmod.qmod_parameter import QParam, QParamList, QParamStruct
15
+ from classiq.qmod.qmod_parameter import CParam, CParamList, CParamStruct
16
16
 
17
17
 
18
18
  class QConstant:
@@ -61,22 +61,22 @@ class QConstant:
61
61
  value=Expression(expr=expr),
62
62
  )
63
63
 
64
- def __getattr__(self, name: str) -> QParam:
64
+ def __getattr__(self, name: str) -> CParam:
65
65
  self.add_to_model()
66
66
 
67
67
  py_type = type(self._value)
68
68
  if (
69
69
  QConstant.CURRENT_QMODULE is None
70
70
  or not inspect.isclass(py_type)
71
- or not issubclass(py_type, QStructBase)
71
+ or not issubclass(py_type, CStructBase)
72
72
  ):
73
73
  return self.__getattribute__(name)
74
74
 
75
- return QParamStruct.get_field(
75
+ return CParamStruct.get_field(
76
76
  QConstant.CURRENT_QMODULE, self.name, py_type.__name__, name
77
77
  )
78
78
 
79
- def __getitem__(self, item: Any) -> QParam:
79
+ def __getitem__(self, item: Any) -> CParam:
80
80
  self.add_to_model()
81
81
 
82
82
  assert QConstant.CURRENT_QMODULE is not None
@@ -90,7 +90,7 @@ class QConstant:
90
90
  if not isinstance(qmod_type, (ClassicalList, ClassicalArray)):
91
91
  raise ClassiqError("Invalid subscript to non-list constant")
92
92
 
93
- return QParamList(
93
+ return CParamList(
94
94
  self.name,
95
95
  qmod_type,
96
96
  QConstant.CURRENT_QMODULE,