classiq 0.85.0__py3-none-any.whl → 0.86.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 (59) hide show
  1. classiq/applications/combinatorial_optimization/combinatorial_problem.py +4 -3
  2. classiq/evaluators/qmod_annotated_expression.py +207 -0
  3. classiq/evaluators/qmod_expression_visitors/__init__.py +0 -0
  4. classiq/evaluators/qmod_expression_visitors/qmod_expression_bwc.py +134 -0
  5. classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +232 -0
  6. classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +44 -0
  7. classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +308 -0
  8. classiq/evaluators/qmod_node_evaluators/__init__.py +0 -0
  9. classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +112 -0
  10. classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +132 -0
  11. classiq/evaluators/qmod_node_evaluators/bool_op_evaluation.py +70 -0
  12. classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +311 -0
  13. classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +107 -0
  14. classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +67 -0
  15. classiq/evaluators/qmod_node_evaluators/list_evaluation.py +107 -0
  16. classiq/evaluators/qmod_node_evaluators/measurement_evaluation.py +25 -0
  17. classiq/evaluators/qmod_node_evaluators/name_evaluation.py +50 -0
  18. classiq/evaluators/qmod_node_evaluators/struct_instantiation_evaluation.py +66 -0
  19. classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +225 -0
  20. classiq/evaluators/qmod_node_evaluators/unary_op_evaluation.py +58 -0
  21. classiq/evaluators/qmod_node_evaluators/utils.py +80 -0
  22. classiq/execution/execution_session.py +4 -0
  23. classiq/interface/_version.py +1 -1
  24. classiq/interface/analyzer/analysis_params.py +1 -1
  25. classiq/interface/analyzer/result.py +1 -1
  26. classiq/interface/executor/quantum_code.py +2 -2
  27. classiq/interface/generator/arith/arithmetic_expression_validator.py +5 -1
  28. classiq/interface/generator/arith/binary_ops.py +43 -51
  29. classiq/interface/generator/arith/number_utils.py +3 -2
  30. classiq/interface/generator/arith/register_user_input.py +15 -0
  31. classiq/interface/generator/arith/unary_ops.py +32 -28
  32. classiq/interface/generator/expressions/expression_types.py +2 -2
  33. classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
  34. classiq/interface/generator/functions/classical_function_declaration.py +0 -4
  35. classiq/interface/generator/functions/classical_type.py +0 -32
  36. classiq/interface/generator/generated_circuit_data.py +2 -0
  37. classiq/interface/generator/quantum_program.py +6 -1
  38. classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +29 -0
  39. classiq/interface/ide/operation_registry.py +2 -2
  40. classiq/interface/ide/visual_model.py +22 -1
  41. classiq/interface/model/quantum_type.py +67 -33
  42. classiq/model_expansions/arithmetic.py +115 -0
  43. classiq/model_expansions/arithmetic_compute_result_attrs.py +71 -0
  44. classiq/model_expansions/atomic_expression_functions_defs.py +5 -5
  45. classiq/model_expansions/generative_functions.py +15 -2
  46. classiq/model_expansions/interpreters/base_interpreter.py +7 -0
  47. classiq/model_expansions/interpreters/generative_interpreter.py +2 -0
  48. classiq/model_expansions/quantum_operations/call_emitter.py +5 -2
  49. classiq/model_expansions/scope_initialization.py +2 -0
  50. classiq/model_expansions/sympy_conversion/sympy_to_python.py +1 -1
  51. classiq/model_expansions/transformers/type_modifier_inference.py +5 -0
  52. classiq/model_expansions/visitors/boolean_expression_transformers.py +1 -1
  53. classiq/qmod/builtins/operations.py +1 -1
  54. classiq/qmod/declaration_inferrer.py +5 -3
  55. classiq/qmod/write_qmod.py +3 -1
  56. {classiq-0.85.0.dist-info → classiq-0.86.0.dist-info}/METADATA +1 -1
  57. {classiq-0.85.0.dist-info → classiq-0.86.0.dist-info}/RECORD +59 -37
  58. /classiq/{model_expansions/sympy_conversion/arithmetics.py → evaluators/qmod_expression_visitors/sympy_wrappers.py} +0 -0
  59. {classiq-0.85.0.dist-info → classiq-0.86.0.dist-info}/WHEEL +0 -0
@@ -46,11 +46,11 @@ class CombinatorialProblem:
46
46
  self._es: ExecutionSession | None = None
47
47
  self._optimized_params: list[float] | None = None
48
48
  self.params_trace_: list[np.ndarray] = []
49
- self.cost_trace_: list = []
49
+ self._cost_trace: list[float] = []
50
50
 
51
51
  @property
52
- def cost_trace(self) -> list:
53
- return self.cost_trace_
52
+ def cost_trace(self) -> list[float]:
53
+ return self._cost_trace
54
54
 
55
55
  @property
56
56
  def params_trace(self) -> list[np.ndarray]:
@@ -119,6 +119,7 @@ class CombinatorialProblem:
119
119
  )
120
120
  _optimized_params = cast(list[float], result[-1][1]["params"])
121
121
  self._optimized_params = _optimized_params
122
+ self._cost_trace = [cost for cost, _ in result]
122
123
  self._es = _es
123
124
  return _optimized_params
124
125
 
@@ -0,0 +1,207 @@
1
+ import ast
2
+ from collections.abc import Sequence
3
+ from dataclasses import dataclass
4
+ from typing import Any, Union, cast
5
+
6
+ from classiq.interface.model.handle_binding import HandleBinding
7
+
8
+ from classiq.evaluators.qmod_node_evaluators.utils import QmodType, is_classical_type
9
+
10
+ QmodExprNodeId = int
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class QuantumSubscriptAnnotation:
15
+ value: QmodExprNodeId
16
+ index: QmodExprNodeId
17
+
18
+
19
+ @dataclass(frozen=True)
20
+ class QuantumTypeAttributeAnnotation:
21
+ value: QmodExprNodeId
22
+ attr: str
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class ConcatenationAnnotation:
27
+ elements: list[QmodExprNodeId]
28
+
29
+
30
+ class _ExprInliner(ast.NodeTransformer):
31
+ def __init__(self, expr_val: "QmodAnnotatedExpression") -> None:
32
+ self._expr_val = expr_val
33
+
34
+ def visit(self, node: ast.AST) -> Any:
35
+ if self._expr_val.has_value(node):
36
+ return ast.Name(id=str(self._expr_val.get_value(node)))
37
+ if self._expr_val.has_var(node):
38
+ return ast.Name(id=str(self._expr_val.get_var(node)))
39
+ return super().visit(node)
40
+
41
+
42
+ class QmodAnnotatedExpression:
43
+ def __init__(self, expr_ast: ast.AST) -> None:
44
+ self.root = expr_ast
45
+ self._node_mapping: dict[QmodExprNodeId, ast.AST] = {}
46
+ self._values: dict[QmodExprNodeId, Any] = {}
47
+ self._types: dict[QmodExprNodeId, QmodType] = {}
48
+ self._classical_vars: dict[QmodExprNodeId, HandleBinding] = {}
49
+ self._quantum_vars: dict[QmodExprNodeId, HandleBinding] = {}
50
+ self._quantum_subscripts: dict[QmodExprNodeId, QuantumSubscriptAnnotation] = {}
51
+ self._quantum_type_attrs: dict[
52
+ QmodExprNodeId, QuantumTypeAttributeAnnotation
53
+ ] = {}
54
+ self._concatenations: dict[QmodExprNodeId, ConcatenationAnnotation] = {}
55
+
56
+ def to_qmod_expr(self) -> str:
57
+ return ast.unparse(_ExprInliner(self).visit(self.root))
58
+
59
+ def has_node(self, node_id: QmodExprNodeId) -> bool:
60
+ return node_id in self._node_mapping
61
+
62
+ def get_node(self, node_id: QmodExprNodeId) -> ast.AST:
63
+ return self._node_mapping[node_id]
64
+
65
+ def set_value(self, node: Union[ast.AST, QmodExprNodeId], value: Any) -> None:
66
+ if isinstance(node, ast.AST):
67
+ node = id(node)
68
+ self._values[node] = value
69
+
70
+ def get_value(self, node: Union[ast.AST, QmodExprNodeId]) -> Any:
71
+ if isinstance(node, ast.AST):
72
+ node = id(node)
73
+ return self._values[node]
74
+
75
+ def has_value(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
76
+ if isinstance(node, ast.AST):
77
+ node = id(node)
78
+ return node in self._values
79
+
80
+ def set_type(
81
+ self, node: Union[ast.AST, QmodExprNodeId], qmod_type: QmodType
82
+ ) -> None:
83
+ if isinstance(node, ast.AST):
84
+ node_id = id(node)
85
+ self._node_mapping[node_id] = node
86
+ node = id(node)
87
+ self._types[node] = qmod_type
88
+
89
+ def get_type(self, node: Union[ast.AST, QmodExprNodeId]) -> QmodType:
90
+ if isinstance(node, ast.AST):
91
+ node = id(node)
92
+ return self._types[node]
93
+
94
+ def set_var(self, node: Union[ast.AST, QmodExprNodeId], var: HandleBinding) -> None:
95
+ var = var.collapse()
96
+ if isinstance(node, ast.AST):
97
+ node = id(node)
98
+ if is_classical_type(self.get_type(node)):
99
+ self._classical_vars[node] = var
100
+ else:
101
+ self._quantum_vars[node] = var
102
+
103
+ def get_var(self, node: Union[ast.AST, QmodExprNodeId]) -> HandleBinding:
104
+ if isinstance(node, ast.AST):
105
+ node = id(node)
106
+ return (self._classical_vars | self._quantum_vars)[node]
107
+
108
+ def has_var(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
109
+ return self.has_classical_var(node) or self.has_quantum_var(node)
110
+
111
+ def has_classical_var(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
112
+ if isinstance(node, ast.AST):
113
+ node = id(node)
114
+ return node in self._classical_vars
115
+
116
+ def has_quantum_var(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
117
+ if isinstance(node, ast.AST):
118
+ node = id(node)
119
+ return node in self._quantum_vars
120
+
121
+ def remove_var(self, node: Union[ast.AST, QmodExprNodeId]) -> None:
122
+ if isinstance(node, ast.AST):
123
+ node = id(node)
124
+ if node in self._classical_vars:
125
+ self._classical_vars.pop(node)
126
+ else:
127
+ self._quantum_vars.pop(node)
128
+
129
+ def set_quantum_subscript(
130
+ self,
131
+ node: Union[ast.AST, QmodExprNodeId],
132
+ value: Union[ast.AST, QmodExprNodeId],
133
+ index: Union[ast.AST, QmodExprNodeId],
134
+ ) -> None:
135
+ if isinstance(node, ast.AST):
136
+ node = id(node)
137
+ if isinstance(value, ast.AST):
138
+ value = id(value)
139
+ if isinstance(index, ast.AST):
140
+ index = id(index)
141
+ self._quantum_subscripts[node] = QuantumSubscriptAnnotation(
142
+ value=value, index=index
143
+ )
144
+
145
+ def has_quantum_subscript(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
146
+ if isinstance(node, ast.AST):
147
+ node = id(node)
148
+ return node in self._quantum_subscripts
149
+
150
+ def get_quantum_subscripts(
151
+ self,
152
+ ) -> dict[QmodExprNodeId, QuantumSubscriptAnnotation]:
153
+ return self._quantum_subscripts
154
+
155
+ def set_quantum_type_attr(
156
+ self,
157
+ node: Union[ast.AST, QmodExprNodeId],
158
+ value: Union[ast.AST, QmodExprNodeId],
159
+ attr: str,
160
+ ) -> None:
161
+ if isinstance(node, ast.AST):
162
+ node = id(node)
163
+ if isinstance(value, ast.AST):
164
+ value = id(value)
165
+ self._quantum_type_attrs[node] = QuantumTypeAttributeAnnotation(
166
+ value=value, attr=attr
167
+ )
168
+
169
+ def has_quantum_type_attribute(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
170
+ if isinstance(node, ast.AST):
171
+ node = id(node)
172
+ return node in self._quantum_type_attrs
173
+
174
+ def get_quantum_type_attributes(
175
+ self,
176
+ ) -> dict[QmodExprNodeId, QuantumTypeAttributeAnnotation]:
177
+ return self._quantum_type_attrs
178
+
179
+ def set_concatenation(
180
+ self,
181
+ node: Union[ast.AST, QmodExprNodeId],
182
+ elements: Sequence[Union[ast.AST, QmodExprNodeId]],
183
+ ) -> None:
184
+ if isinstance(node, ast.AST):
185
+ node = id(node)
186
+ elements = cast(
187
+ list[QmodExprNodeId],
188
+ [
189
+ id(element) if isinstance(element, ast.AST) else element
190
+ for element in elements
191
+ ],
192
+ )
193
+ self._concatenations[node] = ConcatenationAnnotation(elements=elements)
194
+
195
+ def has_concatenation(self, node: Union[ast.AST, QmodExprNodeId]) -> bool:
196
+ if isinstance(node, ast.AST):
197
+ node = id(node)
198
+ return node in self._concatenations
199
+
200
+ def get_concatenations(self) -> dict[QmodExprNodeId, ConcatenationAnnotation]:
201
+ return self._concatenations
202
+
203
+ def get_classical_vars(self) -> dict[QmodExprNodeId, HandleBinding]:
204
+ return self._classical_vars
205
+
206
+ def get_quantum_vars(self) -> dict[QmodExprNodeId, HandleBinding]:
207
+ return self._quantum_vars
@@ -0,0 +1,134 @@
1
+ import ast
2
+ from typing import Any, cast
3
+
4
+ import sympy
5
+
6
+ MAX_PIECEWISE_LOOPS = 1000
7
+
8
+
9
+ # FIXME: Remove with deprecation (CLS-3214)
10
+ class QmodExpressionBwc(ast.NodeTransformer):
11
+ def visit_Call(self, node: ast.Call) -> Any:
12
+ node = cast(ast.Call, self.generic_visit(node))
13
+ if not isinstance(node.func, ast.Name):
14
+ return node
15
+ func = node.func.id
16
+ args = node.args
17
+ kwargs = node.keywords
18
+ num_args = len(args)
19
+ num_kwargs = len(kwargs)
20
+
21
+ if func == "BitwiseNot":
22
+ if num_args != 1 or num_kwargs != 0:
23
+ return node
24
+ return ast.UnaryOp(op=ast.Invert(), operand=args[0])
25
+ if func == "LShift":
26
+ if num_args != 2 or num_kwargs != 0:
27
+ return node
28
+ return ast.BinOp(left=args[0], op=ast.LShift(), right=args[1])
29
+ if func == "RShift":
30
+ if num_args != 2 or num_kwargs != 0:
31
+ return node
32
+ return ast.BinOp(left=args[0], op=ast.RShift(), right=args[1])
33
+ if func == "BitwiseOr":
34
+ if num_args != 2 or num_kwargs != 0:
35
+ return node
36
+ return ast.BinOp(left=args[0], op=ast.BitOr(), right=args[1])
37
+ if func == "BitwiseXor":
38
+ if num_args != 2 or num_kwargs != 0:
39
+ return node
40
+ return ast.BinOp(left=args[0], op=ast.BitXor(), right=args[1])
41
+ if func == "BitwiseAnd":
42
+ if num_args != 2 or num_kwargs != 0:
43
+ return node
44
+ return ast.BinOp(left=args[0], op=ast.BitAnd(), right=args[1])
45
+
46
+ if func == "LogicalXor":
47
+ if num_args != 2 or num_kwargs != 0:
48
+ return node
49
+ return ast.BinOp(left=args[0], op=ast.BitXor(), right=args[1])
50
+
51
+ if func == "Eq":
52
+ if num_args != 2 or num_kwargs != 0:
53
+ return node
54
+ return ast.Compare(left=args[0], ops=[ast.Eq()], comparators=[args[1]])
55
+ if func == "Ne":
56
+ if num_args != 2 or num_kwargs != 0:
57
+ return node
58
+ return ast.Compare(left=args[0], ops=[ast.NotEq()], comparators=[args[1]])
59
+ if func == "Lt":
60
+ if num_args != 2 or num_kwargs != 0:
61
+ return node
62
+ return ast.Compare(left=args[0], ops=[ast.Lt()], comparators=[args[1]])
63
+ if func == "Le":
64
+ if num_args != 2 or num_kwargs != 0:
65
+ return node
66
+ return ast.Compare(left=args[0], ops=[ast.LtE()], comparators=[args[1]])
67
+ if func == "Gt":
68
+ if num_args != 2 or num_kwargs != 0:
69
+ return node
70
+ return ast.Compare(left=args[0], ops=[ast.Gt()], comparators=[args[1]])
71
+ if func == "Ge":
72
+ if num_args != 2 or num_kwargs != 0:
73
+ return node
74
+ return ast.Compare(left=args[0], ops=[ast.GtE()], comparators=[args[1]])
75
+
76
+ if func == "struct_literal":
77
+ if num_args != 1:
78
+ return node
79
+ return ast.Call(func=node.args[0], args=[], keywords=node.keywords)
80
+
81
+ if func == "do_subscript":
82
+ if num_args != 2 or num_kwargs != 0:
83
+ return node
84
+ return ast.Subscript(value=args[0], slice=args[1])
85
+
86
+ if func == "get_field":
87
+ if num_args != 2 or num_kwargs != 0:
88
+ return node
89
+ attr = args[1]
90
+ if not isinstance(attr, ast.Constant):
91
+ return node
92
+ return ast.Attribute(value=args[0], attr=attr.value)
93
+
94
+ if func == "Piecewise":
95
+ if num_args == 0:
96
+ return node
97
+ first_piece = args[0]
98
+ if not isinstance(first_piece, ast.Tuple) or len(first_piece.elts) != 2:
99
+ return node
100
+ first_cond = first_piece.elts[1]
101
+ if isinstance(first_cond, ast.BinOp):
102
+ first_cond = first_cond.right
103
+ if not isinstance(first_cond, ast.Compare) or len(first_cond.ops) != 1:
104
+ return node
105
+ index_var_node = first_cond.left
106
+ if not isinstance(index_var_node, ast.Name):
107
+ return node
108
+ index_var = index_var_node.id
109
+ last_cond = args[-1]
110
+ if not isinstance(last_cond, ast.Tuple) or len(last_cond.elts) != 2:
111
+ return node
112
+ last_value = last_cond.elts[0]
113
+ if not isinstance(last_value, ast.Constant) and (
114
+ not isinstance(last_value, ast.UnaryOp)
115
+ or not isinstance(last_value.operand, ast.Constant)
116
+ ):
117
+ return node
118
+ dummy_var_name = f"{index_var}_not_it"
119
+ last_cond.elts[0] = ast.Name(id=dummy_var_name)
120
+ items: list = []
121
+ idx = 0
122
+ for idx in range(MAX_PIECEWISE_LOOPS):
123
+ item = sympy.sympify(ast.unparse(node), locals={index_var: idx})
124
+ if str(item) == dummy_var_name:
125
+ items.append(last_value)
126
+ break
127
+ items.append(ast.parse(str(item), mode="eval").body)
128
+ if idx == MAX_PIECEWISE_LOOPS:
129
+ return node
130
+ return ast.Subscript(
131
+ value=ast.List(elts=items), slice=ast.Name(id=index_var)
132
+ )
133
+
134
+ return node
@@ -0,0 +1,232 @@
1
+ import ast
2
+ from collections.abc import Mapping, Sequence
3
+ from enum import IntEnum
4
+ from typing import Any, Callable, Optional
5
+
6
+ from classiq.interface.exceptions import (
7
+ ClassiqExpansionError,
8
+ ClassiqInternalExpansionError,
9
+ )
10
+ from classiq.interface.generator.arith.machine_precision import (
11
+ DEFAULT_MACHINE_PRECISION,
12
+ )
13
+ from classiq.interface.generator.functions.classical_function_declaration import (
14
+ ClassicalFunctionDeclaration,
15
+ )
16
+ from classiq.interface.generator.types.enum_declaration import EnumDeclaration
17
+ from classiq.interface.generator.types.struct_declaration import StructDeclaration
18
+ from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
19
+
20
+ from classiq.evaluators.qmod_annotated_expression import QmodAnnotatedExpression
21
+ from classiq.evaluators.qmod_node_evaluators.attribute_evaluation import eval_attribute
22
+ from classiq.evaluators.qmod_node_evaluators.binary_op_evaluation import eval_binary_op
23
+ from classiq.evaluators.qmod_node_evaluators.bool_op_evaluation import eval_bool_op
24
+ from classiq.evaluators.qmod_node_evaluators.classical_function_evaluation import (
25
+ eval_function,
26
+ eval_symbolic_function,
27
+ try_eval_builtin_function,
28
+ try_eval_sympy_function,
29
+ )
30
+ from classiq.evaluators.qmod_node_evaluators.compare_evaluation import eval_compare
31
+ from classiq.evaluators.qmod_node_evaluators.constant_evaluation import (
32
+ eval_constant,
33
+ eval_enum_member,
34
+ try_eval_sympy_constant,
35
+ )
36
+ from classiq.evaluators.qmod_node_evaluators.list_evaluation import eval_list
37
+ from classiq.evaluators.qmod_node_evaluators.measurement_evaluation import (
38
+ eval_measurement,
39
+ )
40
+ from classiq.evaluators.qmod_node_evaluators.name_evaluation import eval_name
41
+ from classiq.evaluators.qmod_node_evaluators.struct_instantiation_evaluation import (
42
+ eval_struct_instantiation,
43
+ )
44
+ from classiq.evaluators.qmod_node_evaluators.subscript_evaluation import (
45
+ eval_quantum_subscript,
46
+ eval_subscript,
47
+ )
48
+ from classiq.evaluators.qmod_node_evaluators.unary_op_evaluation import eval_unary_op
49
+ from classiq.evaluators.qmod_node_evaluators.utils import is_classical_type
50
+
51
+ _SUPPORTED_NODES = (
52
+ ast.BoolOp,
53
+ ast.BinOp,
54
+ ast.UnaryOp,
55
+ ast.Compare,
56
+ ast.Call,
57
+ ast.Constant,
58
+ ast.Attribute,
59
+ ast.Subscript,
60
+ ast.Name,
61
+ ast.List,
62
+ ast.cmpop,
63
+ ast.operator,
64
+ ast.expr_context,
65
+ ast.keyword,
66
+ ast.unaryop,
67
+ ast.boolop,
68
+ ast.Slice,
69
+ )
70
+
71
+
72
+ class QmodExpressionEvaluator(ast.NodeVisitor):
73
+ def __init__(
74
+ self,
75
+ expr_val: QmodAnnotatedExpression,
76
+ *,
77
+ machine_precision: int = DEFAULT_MACHINE_PRECISION,
78
+ classical_struct_declarations: Optional[Sequence[StructDeclaration]] = None,
79
+ enum_declarations: Optional[Sequence[EnumDeclaration]] = None,
80
+ classical_function_declarations: Optional[
81
+ Sequence[ClassicalFunctionDeclaration]
82
+ ] = None,
83
+ classical_function_callables: Optional[Mapping[str, Callable]] = None,
84
+ scope: Optional[dict[str, Any]] = None,
85
+ ) -> None:
86
+ self._expr_val = expr_val
87
+ self._machine_precision = machine_precision
88
+ self._classical_struct_decls = nameables_to_dict(
89
+ classical_struct_declarations or []
90
+ )
91
+ self._enum_declarations = {decl.name: decl for decl in enum_declarations or []}
92
+ self._enums: dict[str, type[IntEnum]] = {}
93
+ self._classical_function_declarations = nameables_to_dict(
94
+ classical_function_declarations or []
95
+ )
96
+ self._classical_function_callables = classical_function_callables or {}
97
+ self._scope = scope or {}
98
+
99
+ def visit(self, node: ast.AST) -> None:
100
+ if not isinstance(node, _SUPPORTED_NODES):
101
+ raise ClassiqInternalExpansionError(
102
+ f"Syntax error: {type(node).__name__!r} is not supported"
103
+ )
104
+ super().visit(node)
105
+
106
+ def visit_BoolOp(self, node: ast.BoolOp) -> None:
107
+ super().generic_visit(node)
108
+ eval_bool_op(self._expr_val, node)
109
+
110
+ def visit_BinOp(self, node: ast.BinOp) -> None:
111
+ super().generic_visit(node)
112
+ eval_binary_op(self._expr_val, node)
113
+
114
+ def visit_UnaryOp(self, node: ast.UnaryOp) -> None:
115
+ super().generic_visit(node)
116
+ eval_unary_op(self._expr_val, node)
117
+
118
+ def visit_Compare(self, node: ast.Compare) -> None:
119
+ super().generic_visit(node)
120
+ eval_compare(self._expr_val, node)
121
+
122
+ def visit_Call(self, node: ast.Call) -> None:
123
+ for arg in node.args:
124
+ self.visit(arg)
125
+ for kwarg in node.keywords:
126
+ self.visit(kwarg)
127
+
128
+ func = node.func
129
+ if not isinstance(func, ast.Name):
130
+ raise ClassiqExpansionError(
131
+ f"Function {ast.unparse(node.func)!r} is not supported"
132
+ )
133
+ func_name = func.id
134
+
135
+ if func_name == "measure":
136
+ eval_measurement(self._expr_val, node)
137
+ return
138
+
139
+ if func_name in self._classical_struct_decls:
140
+ eval_struct_instantiation(
141
+ self._expr_val, node, self._classical_struct_decls[func_name]
142
+ )
143
+ return
144
+
145
+ if func_name in self._classical_function_callables:
146
+ if func_name not in self._classical_function_declarations:
147
+ raise ClassiqInternalExpansionError
148
+ eval_function(
149
+ self._expr_val,
150
+ node,
151
+ self._classical_function_declarations[func_name],
152
+ self._classical_function_callables[func_name],
153
+ )
154
+ return
155
+
156
+ if func_name in self._classical_function_declarations:
157
+ eval_symbolic_function(
158
+ self._expr_val, node, self._classical_function_declarations[func_name]
159
+ )
160
+ return
161
+
162
+ if try_eval_builtin_function(self._expr_val, node, func_name):
163
+ return
164
+
165
+ if try_eval_sympy_function(self._expr_val, node, func_name):
166
+ return
167
+
168
+ raise ClassiqExpansionError(f"{func.id!r} is undefined")
169
+
170
+ def visit_Constant(self, node: ast.Constant) -> None:
171
+ eval_constant(self._expr_val, node)
172
+
173
+ def visit_Attribute(self, node: ast.Attribute) -> None:
174
+ if (
175
+ isinstance(node.value, ast.Name)
176
+ and (enum_name := node.value.id) in self._enum_declarations
177
+ ):
178
+ if enum_name not in self._enums:
179
+ self._enums[enum_name] = self._enum_declarations[
180
+ enum_name
181
+ ].create_enum()
182
+ eval_enum_member(self._expr_val, node, self._enums[enum_name])
183
+ return
184
+ super().generic_visit(node)
185
+ eval_attribute(self._expr_val, node)
186
+
187
+ def visit_Subscript(self, node: ast.Subscript) -> None:
188
+ super().generic_visit(node)
189
+ if not isinstance(node.slice, ast.Slice) and not is_classical_type(
190
+ self._expr_val.get_type(node.slice)
191
+ ):
192
+ eval_quantum_subscript(self._expr_val, node, self._machine_precision)
193
+ return
194
+ eval_subscript(self._expr_val, node)
195
+
196
+ def visit_Name(self, node: ast.Name) -> None:
197
+ if try_eval_sympy_constant(self._expr_val, node):
198
+ return
199
+
200
+ if node.id not in self._scope:
201
+ raise ClassiqExpansionError(f"Variable {node.id!r} is undefined")
202
+ eval_name(self._expr_val, node, self._scope[node.id])
203
+
204
+ def visit_List(self, node: ast.List) -> None:
205
+ super().generic_visit(node)
206
+ eval_list(self._expr_val, node)
207
+
208
+
209
+ def evaluate_qmod_expression(
210
+ expr: str,
211
+ *,
212
+ machine_precision: int = DEFAULT_MACHINE_PRECISION,
213
+ classical_struct_declarations: Optional[Sequence[StructDeclaration]] = None,
214
+ enum_declarations: Optional[Sequence[EnumDeclaration]] = None,
215
+ classical_function_declarations: Optional[
216
+ Sequence[ClassicalFunctionDeclaration]
217
+ ] = None,
218
+ classical_function_callables: Optional[Mapping[str, Callable]] = None,
219
+ scope: Optional[dict[str, Any]] = None,
220
+ ) -> QmodAnnotatedExpression:
221
+ expr_ast = ast.parse(expr, mode="eval").body
222
+ expr_value = QmodAnnotatedExpression(expr_ast)
223
+ QmodExpressionEvaluator(
224
+ expr_value,
225
+ machine_precision=machine_precision,
226
+ classical_struct_declarations=classical_struct_declarations,
227
+ enum_declarations=enum_declarations,
228
+ classical_function_declarations=classical_function_declarations,
229
+ classical_function_callables=classical_function_callables,
230
+ scope=scope,
231
+ ).visit(expr_value.root)
232
+ return expr_value
@@ -0,0 +1,44 @@
1
+ from typing import Optional
2
+
3
+ from classiq.interface.model.handle_binding import HandleBinding, NestedHandleBinding
4
+
5
+ from classiq.evaluators.qmod_annotated_expression import (
6
+ QmodAnnotatedExpression,
7
+ QmodExprNodeId,
8
+ )
9
+
10
+
11
+ def rename_handles_in_expression(
12
+ expr_val: QmodAnnotatedExpression,
13
+ renaming: dict[HandleBinding, HandleBinding],
14
+ ) -> str:
15
+ if len(renaming) == 0:
16
+ return expr_val.to_qmod_expr()
17
+ all_vars = expr_val.get_classical_vars() | expr_val.get_quantum_vars()
18
+ for node_id, var in all_vars.items():
19
+ renamed_var = _rename_var(renaming, var)
20
+ if renamed_var is not None:
21
+ expr_val.set_var(node_id, renamed_var)
22
+ return expr_val.to_qmod_expr()
23
+
24
+
25
+ def _rename_var(
26
+ renaming: dict[HandleBinding, HandleBinding], var: HandleBinding
27
+ ) -> Optional[HandleBinding]:
28
+ if (renamed_var := renaming.get(var)) is not None:
29
+ return renamed_var
30
+ if not isinstance(var, NestedHandleBinding):
31
+ return None
32
+ renamed_inner = _rename_var(renaming, var.base_handle)
33
+ if renamed_inner is None:
34
+ return None
35
+ return var.model_copy(update=dict(base_handle=renamed_inner))
36
+
37
+
38
+ def rename_nodes_in_expression(
39
+ expr_val: QmodAnnotatedExpression,
40
+ renaming: dict[QmodExprNodeId, HandleBinding],
41
+ ) -> str:
42
+ for node_id, renamed_var in renaming.items():
43
+ expr_val.set_var(node_id, renamed_var)
44
+ return expr_val.to_qmod_expr()