classiq 0.56.1__py3-none-any.whl → 0.57.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 (31) hide show
  1. classiq/analyzer/show_interactive_hack.py +16 -4
  2. classiq/executor.py +1 -1
  3. classiq/interface/_version.py +1 -1
  4. classiq/interface/executor/execution_request.py +5 -5
  5. classiq/interface/generator/arith/arithmetic_expression_validator.py +28 -9
  6. classiq/interface/generator/types/builtin_enum_declarations.py +1 -0
  7. classiq/model_expansions/closure.py +24 -6
  8. classiq/model_expansions/quantum_operations/call_emitter.py +207 -0
  9. classiq/model_expansions/quantum_operations/classicalif.py +2 -2
  10. classiq/model_expansions/quantum_operations/control.py +7 -5
  11. classiq/model_expansions/quantum_operations/emitter.py +1 -186
  12. classiq/model_expansions/quantum_operations/expression_operation.py +26 -189
  13. classiq/model_expansions/quantum_operations/inplace_binary_operation.py +2 -2
  14. classiq/model_expansions/quantum_operations/invert.py +2 -2
  15. classiq/model_expansions/quantum_operations/phase.py +3 -1
  16. classiq/model_expansions/quantum_operations/power.py +2 -2
  17. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +7 -9
  18. classiq/model_expansions/quantum_operations/quantum_function_call.py +2 -2
  19. classiq/model_expansions/quantum_operations/repeat.py +2 -2
  20. classiq/model_expansions/transformers/__init__.py +0 -0
  21. classiq/model_expansions/transformers/var_splitter.py +237 -0
  22. classiq/qmod/builtins/classical_functions.py +1 -0
  23. classiq/qmod/builtins/functions/state_preparation.py +1 -1
  24. classiq/qmod/create_model_function.py +25 -20
  25. classiq/qmod/pretty_print/pretty_printer.py +53 -28
  26. classiq/qmod/qfunc.py +18 -16
  27. classiq/qmod/quantum_function.py +30 -24
  28. classiq/synthesis.py +3 -1
  29. {classiq-0.56.1.dist-info → classiq-0.57.0.dist-info}/METADATA +1 -1
  30. {classiq-0.56.1.dist-info → classiq-0.57.0.dist-info}/RECORD +31 -28
  31. {classiq-0.56.1.dist-info → classiq-0.57.0.dist-info}/WHEEL +0 -0
@@ -10,7 +10,7 @@ from classiq._internals.async_utils import syncify_function
10
10
  from classiq.analyzer.url_utils import circuit_page_uri, client_ide_base_url
11
11
 
12
12
 
13
- async def handle_remote_app(circuit: QuantumProgram, dispaly_url: bool = False) -> None:
13
+ async def handle_remote_app(circuit: QuantumProgram, display_url: bool = True) -> None:
14
14
  if circuit.outputs.get(QuantumFormat.QASM) is None:
15
15
  raise ClassiqAnalyzerVisualizationError(
16
16
  "Missing QASM transpilation: visualization is only supported "
@@ -23,14 +23,26 @@ async def handle_remote_app(circuit: QuantumProgram, dispaly_url: bool = False)
23
23
  circuit_page_uri(circuit_id=circuit_dataid.id, circuit_version=circuit.version),
24
24
  )
25
25
 
26
- if dispaly_url:
26
+ if display_url:
27
27
  print(f"Opening: {app_url}") # noqa: T201
28
28
 
29
29
  webbrowser.open_new_tab(app_url)
30
30
 
31
31
 
32
- async def _show_interactive(self: QuantumProgram, dispaly_url: bool = False) -> None:
33
- await handle_remote_app(self, dispaly_url)
32
+ async def _show_interactive(self: QuantumProgram, display_url: bool = True) -> None:
33
+ """
34
+ Displays the interactive representation of the quantum program in the Classiq IDE.
35
+
36
+ Args:
37
+ self:
38
+ The serialized quantum program to be displayed.
39
+ display_url:
40
+ Whether to print the url
41
+
42
+ Links:
43
+ [Visualization tool](https://docs.classiq.io/latest/reference-manual/analyzer/quantum-program-visualization-tool/)
44
+ """
45
+ await handle_remote_app(self, display_url)
34
46
 
35
47
 
36
48
  QuantumProgram.show = syncify_function(_show_interactive) # type: ignore[attr-defined]
classiq/executor.py CHANGED
@@ -46,7 +46,7 @@ def execute(quantum_program: SerializedQuantumProgram) -> ExecutionJob:
46
46
  Returns:
47
47
  ExecutionJob: The result of the execution.
48
48
 
49
- For examples please see [Execution Documentation](https://docs.classiq.io/latest/user-guide/executor/)
49
+ For examples please see [Execution Documentation](https://docs.classiq.io/latest/user-guide/execution/)
50
50
  """
51
51
  return async_utils.run(execute_async(quantum_program))
52
52
 
@@ -3,5 +3,5 @@ from packaging.version import Version
3
3
  # This file was generated automatically
4
4
  # Please don't track in version control (DONTTRACK)
5
5
 
6
- SEMVER_VERSION = '0.56.1'
6
+ SEMVER_VERSION = '0.57.0'
7
7
  VERSION = str(Version(SEMVER_VERSION))
@@ -46,16 +46,16 @@ class QuantumProgramExecutionRequest(ExecutionRequest):
46
46
  class ExecutionJobDetails(VersionedModel):
47
47
  id: str
48
48
 
49
- name: Optional[str]
49
+ name: Optional[str] = Field(default=None)
50
50
  start_time: datetime
51
- end_time: Optional[datetime]
51
+ end_time: Optional[datetime] = Field(default=None)
52
52
 
53
- provider: Optional[str]
54
- backend_name: Optional[str]
53
+ provider: Optional[str] = Field(default=None)
54
+ backend_name: Optional[str] = Field(default=None)
55
55
 
56
56
  status: JobStatus
57
57
 
58
- num_shots: Optional[int]
58
+ num_shots: Optional[int] = Field(default=None)
59
59
  program_id: Optional[str] = Field(default=None)
60
60
 
61
61
  error: Optional[str] = Field(default=None)
@@ -16,9 +16,6 @@ DEFAULT_SUPPORTED_FUNC_NAMES: set[str] = {"CLShift", "CRShift", "min", "max"}
16
16
  DEFAULT_EXPRESSION_TYPE = "arithmetic"
17
17
  IDENITIFIER_REGEX = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*")
18
18
 
19
- _REPEATED_VARIABLES_ERROR_MESSAGE: str = (
20
- "Repeated variables in the beginning of an arithmetic expression are not allowed."
21
- )
22
19
  ValidKeyValuePairs: TypeAlias = dict[str, set[str]]
23
20
 
24
21
  SupportedNodesTypes = Union[
@@ -107,13 +104,25 @@ class ExpressionValidator(ast.NodeVisitor):
107
104
  raise ClassiqArithmeticError("Must call `validate` before getting ast_obj")
108
105
  return self._ast_obj
109
106
 
110
- @staticmethod
111
- def _check_repeated_variables(variables: tuple[Any, Any]) -> None:
107
+ def _check_repeated_variables(
108
+ self, variables: tuple[Any, Any], expr: ast.AST, error_suffix: str
109
+ ) -> None:
110
+ if (
111
+ isinstance(expr, ast.BinOp)
112
+ and isinstance(expr.op, ast.Pow)
113
+ and ast.Pow not in self.supported_nodes
114
+ ):
115
+ raise ClassiqValueError(
116
+ "Raising to a power (<var> ** <exp>) and multiplying a variable by "
117
+ "itself (<var> * <var>) are not supported"
118
+ )
112
119
  if (
113
120
  all(isinstance(var, ast.Name) for var in variables)
114
121
  and variables[0].id == variables[1].id
115
122
  ):
116
- raise ClassiqValueError(_REPEATED_VARIABLES_ERROR_MESSAGE)
123
+ raise ClassiqValueError(
124
+ f"Expression {ast.unparse(expr)!r} is not supported ({error_suffix})"
125
+ )
117
126
 
118
127
  @staticmethod
119
128
  def _check_multiple_comparators(node: ast.Compare) -> None:
@@ -135,7 +144,11 @@ class ExpressionValidator(ast.NodeVisitor):
135
144
  )
136
145
 
137
146
  def validate_Compare(self, node: ast.Compare) -> None: # noqa: N802
138
- self._check_repeated_variables((node.left, node.comparators[0]))
147
+ self._check_repeated_variables(
148
+ (node.left, node.comparators[0]),
149
+ node,
150
+ "both sides of the comparison are identical",
151
+ )
139
152
  self._check_multiple_comparators(node)
140
153
 
141
154
  def visit_Compare(self, node: ast.Compare) -> None:
@@ -143,7 +156,9 @@ class ExpressionValidator(ast.NodeVisitor):
143
156
  self.generic_visit(node)
144
157
 
145
158
  def validate_BinOp(self, node: ast.BinOp) -> None: # noqa: N802
146
- self._check_repeated_variables((node.left, node.right))
159
+ self._check_repeated_variables(
160
+ (node.left, node.right), node, "both sides of the operation are identical"
161
+ )
147
162
 
148
163
  def visit_BinOp(self, node: ast.BinOp) -> None:
149
164
  self.validate_BinOp(node)
@@ -151,7 +166,11 @@ class ExpressionValidator(ast.NodeVisitor):
151
166
 
152
167
  def validate_Call(self, node: ast.Call) -> None: # noqa: N802
153
168
  if len(node.args) >= 2:
154
- self._check_repeated_variables((node.args[0], node.args[1]))
169
+ self._check_repeated_variables(
170
+ (node.args[0], node.args[1]),
171
+ node,
172
+ "the first two call arguments are identical",
173
+ )
155
174
  node_id = AstNodeRewrite().extract_node_id(node)
156
175
  if node_id not in self._supported_functions:
157
176
  raise ClassiqValueError(f"{node_id} not in supported functions")
@@ -1,5 +1,6 @@
1
1
  # This file was generated automatically - do not edit manually
2
2
 
3
+
3
4
  from enum import IntEnum
4
5
 
5
6
 
@@ -21,6 +21,7 @@ from classiq.interface.model.quantum_function_call import QuantumFunctionCall
21
21
  from classiq.interface.model.quantum_function_declaration import (
22
22
  NamedParamsQuantumFunctionDeclaration,
23
23
  PositionalArg,
24
+ QuantumOperandDeclaration,
24
25
  )
25
26
  from classiq.interface.model.quantum_statement import QuantumStatement
26
27
  from classiq.interface.model.variable_declaration_statement import (
@@ -213,26 +214,43 @@ def _generate_arg_id(
213
214
  arg_name = arg_declaration.name
214
215
  if isinstance(arg_declaration, ClassicalParameterDeclaration):
215
216
  return {arg_name: evaluated_classical_param_to_str(arg_value)}
216
- return {arg_name: _evaluated_arg_to_str(arg_value)}
217
+ if isinstance(arg_declaration, PortDeclaration):
218
+ return {arg_name: _evaluated_port_to_str(arg_value)}
219
+ if isinstance(arg_declaration, QuantumOperandDeclaration):
220
+ return {arg_name: _evaluated_operand_to_str(arg_value)}
217
221
 
218
222
 
219
- @singledispatch
220
223
  def _evaluated_arg_to_str(arg: Any) -> str:
221
224
  if isinstance(arg, str):
222
225
  return arg
223
226
  return str(uuid.uuid4())
224
227
 
225
228
 
226
- @_evaluated_arg_to_str.register
229
+ @singledispatch
230
+ def _evaluated_port_to_str(arg: Any) -> str:
231
+ return _evaluated_arg_to_str(arg)
232
+
233
+
234
+ @_evaluated_port_to_str.register
227
235
  def _evaluated_quantum_symbol_to_str(port: QuantumSymbol) -> str:
228
236
  return port.quantum_type.model_dump_json(exclude_none=True, exclude={"name"})
229
237
 
230
238
 
231
- @_evaluated_arg_to_str.register
239
+ @_evaluated_port_to_str.register
232
240
  def _evaluated_symbol_to_str(port: Symbol) -> str:
233
241
  return repr(port)
234
242
 
235
243
 
236
- @_evaluated_arg_to_str.register
237
- def _evaluated_operand_to_str(operand: FunctionClosure) -> str:
244
+ @singledispatch
245
+ def _evaluated_operand_to_str(arg: Any) -> str:
246
+ return _evaluated_arg_to_str(arg)
247
+
248
+
249
+ @_evaluated_operand_to_str.register
250
+ def _evaluated_one_operand_to_str(operand: FunctionClosure) -> str:
238
251
  return operand.closure_id
252
+
253
+
254
+ @_evaluated_operand_to_str.register
255
+ def _evaluated_list_operands_to_str(operands: list) -> str:
256
+ return json.dumps([_evaluated_operand_to_str(ope) for ope in operands])
@@ -0,0 +1,207 @@
1
+ from collections.abc import Sequence
2
+ from typing import (
3
+ Generic,
4
+ cast,
5
+ )
6
+
7
+ from classiq.interface.debug_info.debug_info import FunctionDebugInfo
8
+ from classiq.interface.generator.generated_circuit_data import OperationLevel
9
+ from classiq.interface.model.classical_parameter_declaration import (
10
+ ClassicalParameterDeclaration,
11
+ )
12
+ from classiq.interface.model.handle_binding import HandleBinding
13
+ from classiq.interface.model.port_declaration import PortDeclaration
14
+ from classiq.interface.model.quantum_function_call import ArgValue, QuantumFunctionCall
15
+ from classiq.interface.model.quantum_function_declaration import (
16
+ NamedParamsQuantumFunctionDeclaration,
17
+ PositionalArg,
18
+ )
19
+ from classiq.interface.model.quantum_statement import QuantumStatement
20
+ from classiq.interface.model.variable_declaration_statement import (
21
+ VariableDeclarationStatement,
22
+ )
23
+
24
+ from classiq.model_expansions.capturing.propagated_var_stack import (
25
+ validate_args_are_not_propagated,
26
+ )
27
+ from classiq.model_expansions.closure import FunctionClosure
28
+ from classiq.model_expansions.evaluators.argument_types import (
29
+ add_information_from_output_arguments,
30
+ )
31
+ from classiq.model_expansions.evaluators.parameter_types import (
32
+ evaluate_parameter_types_from_args,
33
+ )
34
+ from classiq.model_expansions.function_builder import (
35
+ FunctionContext,
36
+ )
37
+ from classiq.model_expansions.generative_functions import emit_operands_as_declarative
38
+ from classiq.model_expansions.quantum_operations.emitter import (
39
+ Emitter,
40
+ QuantumStatementT,
41
+ )
42
+ from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
43
+ from classiq.qmod.builtins.functions import allocate, free
44
+
45
+
46
+ class CallEmitter(Generic[QuantumStatementT], Emitter[QuantumStatementT]):
47
+ @staticmethod
48
+ def _should_wrap(body: Sequence[QuantumStatement]) -> bool:
49
+ # This protects shadowing of captured variables (i.e, bad user code) by wrapping the body in a function
50
+ # I'm sure there are better ways to handle it, but this is the simplest way to do it for now
51
+ return any(isinstance(stmt, VariableDeclarationStatement) for stmt in body)
52
+
53
+ def _create_expanded_wrapping_function(
54
+ self, name: str, body: Sequence[QuantumStatement]
55
+ ) -> QuantumFunctionCall:
56
+ wrapping_function = FunctionClosure.create(
57
+ name=name, body=body, scope=Scope(parent=self._current_scope)
58
+ )
59
+ return self._create_quantum_function_call(wrapping_function, list())
60
+
61
+ def _emit_quantum_function_call(
62
+ self, function: FunctionClosure, args: list[ArgValue]
63
+ ) -> QuantumFunctionCall:
64
+ call = self._create_quantum_function_call(function, args)
65
+ self._builder.emit_statement(call)
66
+ return call
67
+
68
+ def _create_quantum_function_call(
69
+ self, function: FunctionClosure, args: list[ArgValue]
70
+ ) -> QuantumFunctionCall:
71
+ evaluated_args = [self._interpreter.evaluate(arg) for arg in args]
72
+ new_declaration = self._prepare_fully_typed_declaration(
73
+ function, evaluated_args
74
+ )
75
+ new_positional_arg_decls = new_declaration.positional_arg_declarations
76
+ is_atomic = function.is_atomic
77
+ new_function_name = function.name
78
+ if not is_atomic: # perform monomorphization per interpreted parameters set
79
+ self._add_params_to_scope(
80
+ new_positional_arg_decls, evaluated_args, function
81
+ )
82
+ context = self._expand_operation(
83
+ function.with_new_declaration(new_declaration)
84
+ )
85
+ function_context = cast(FunctionContext, context)
86
+ closure_id = function_context.closure.closure_id
87
+ function_def = self._expanded_functions.get(closure_id)
88
+ if function_def is None:
89
+ function_def = self._builder.create_definition(function_context)
90
+ self._expanded_functions[closure_id] = function_def
91
+ self._top_level_scope[function_def.name] = Evaluated(
92
+ value=function_context.closure.with_new_declaration(function_def)
93
+ )
94
+ new_declaration = function_def
95
+ new_function_name = function_def.name
96
+ compilation_metadata = self._functions_compilation_metadata.get(
97
+ function.name
98
+ )
99
+ if compilation_metadata is not None:
100
+ self._expanded_functions_compilation_metadata[new_function_name] = (
101
+ compilation_metadata
102
+ )
103
+
104
+ new_positional_args = self._get_new_positional_args(
105
+ evaluated_args, is_atomic, new_positional_arg_decls
106
+ )
107
+ new_call = QuantumFunctionCall(
108
+ function=new_function_name,
109
+ positional_args=new_positional_args,
110
+ )
111
+ is_allocate_or_free = (
112
+ new_call.func_name == allocate.func_decl.name
113
+ or new_call.func_name == free.func_decl.name
114
+ )
115
+ parameters = {
116
+ arg_decl.name: FunctionDebugInfo.param_controller(value=evaluated_arg.value)
117
+ for arg_decl, evaluated_arg in zip(new_positional_arg_decls, evaluated_args)
118
+ if isinstance(arg_decl, ClassicalParameterDeclaration)
119
+ }
120
+
121
+ port_to_passed_variable_map = {
122
+ arg_decl.name: str(evaluated_arg.value.handle)
123
+ for arg_decl, evaluated_arg in zip(new_positional_arg_decls, evaluated_args)
124
+ if isinstance(arg_decl, PortDeclaration)
125
+ }
126
+ self._interpreter._model.debug_info[new_call.uuid] = FunctionDebugInfo(
127
+ name=new_call.func_name,
128
+ level=OperationLevel.QMOD_FUNCTION_CALL,
129
+ parameters=parameters,
130
+ is_allocate_or_free=is_allocate_or_free,
131
+ port_to_passed_variable_map=port_to_passed_variable_map,
132
+ )
133
+ new_call.set_func_decl(new_declaration)
134
+ return new_call
135
+
136
+ @staticmethod
137
+ def _add_params_to_scope(
138
+ parameters: Sequence[PositionalArg],
139
+ arguments: Sequence[Evaluated],
140
+ closure: FunctionClosure,
141
+ ) -> None:
142
+ for parameter, argument in zip(parameters, arguments):
143
+ if isinstance(argument.value, QuantumSymbol):
144
+ assert isinstance(parameter, PortDeclaration)
145
+ closure.scope[parameter.name] = Evaluated(
146
+ QuantumSymbol(
147
+ handle=HandleBinding(name=parameter.name),
148
+ quantum_type=parameter.quantum_type,
149
+ ),
150
+ defining_function=closure,
151
+ )
152
+ else:
153
+ closure.scope[parameter.name] = argument
154
+
155
+ def _get_new_positional_args(
156
+ self,
157
+ evaluated_args: list[Evaluated],
158
+ is_atomic: bool,
159
+ new_positional_arg_decls: Sequence[PositionalArg],
160
+ ) -> list[ArgValue]:
161
+ evaluated_args = add_information_from_output_arguments(
162
+ new_positional_arg_decls, evaluated_args
163
+ )
164
+ if is_atomic:
165
+ return [
166
+ emit_operands_as_declarative(self._interpreter, param, arg)
167
+ for param, arg in zip(new_positional_arg_decls, evaluated_args)
168
+ ]
169
+
170
+ positional_args = [
171
+ arg.emit() for arg in evaluated_args if isinstance(arg.value, QuantumSymbol)
172
+ ]
173
+
174
+ propagated_variables = self._propagated_var_stack.get_propagated_variables(
175
+ flatten=True
176
+ )
177
+ validate_args_are_not_propagated(positional_args, propagated_variables)
178
+ positional_args.extend(propagated_variables)
179
+
180
+ return positional_args
181
+
182
+ def _prepare_fully_typed_declaration(
183
+ self, function: FunctionClosure, evaluated_args: list[Evaluated]
184
+ ) -> NamedParamsQuantumFunctionDeclaration:
185
+ """
186
+ Given, for example,
187
+ def my_func(x: int, q: QArray["x"], p: QArray[]) -> None:
188
+ ...
189
+ def main(...):
190
+ ...
191
+ allocate(5, s)
192
+ my_func(3, r, s)
193
+ The code below will evaluate x to be 3, q to be of size 3 and p to be of size 5.
194
+ Note that it requires a scope for the parameter declaration space, which is
195
+ different from the call scope. For example, the former uses r,s and the latter
196
+ uses p, q.
197
+ """
198
+ with self._scope_guard(Scope(parent=self._current_scope)):
199
+ # The signature scope is passed as a separate argument to avoid contaminating the statement execution scope
200
+ return NamedParamsQuantumFunctionDeclaration(
201
+ name=function.name,
202
+ positional_arg_declarations=evaluate_parameter_types_from_args(
203
+ function,
204
+ function.signature_scope,
205
+ evaluated_args,
206
+ ),
207
+ )
@@ -5,7 +5,7 @@ from classiq.interface.model.quantum_function_call import QuantumFunctionCall
5
5
  from classiq.interface.model.quantum_statement import QuantumStatement
6
6
 
7
7
  from classiq.model_expansions.closure import FunctionClosure
8
- from classiq.model_expansions.quantum_operations.emitter import Emitter
8
+ from classiq.model_expansions.quantum_operations.call_emitter import CallEmitter
9
9
  from classiq.model_expansions.scope import Scope
10
10
 
11
11
 
@@ -16,7 +16,7 @@ def _is_all_identity_calls(body: Sequence[QuantumStatement]) -> bool:
16
16
  )
17
17
 
18
18
 
19
- class ClassicalIfEmitter(Emitter[ClassicalIf]):
19
+ class ClassicalIfEmitter(CallEmitter[ClassicalIf]):
20
20
  def emit(self, classical_if: ClassicalIf, /) -> None:
21
21
  with self._propagated_var_stack.capture_variables(classical_if):
22
22
  self._emit_propagated(classical_if)
@@ -26,7 +26,6 @@ from classiq.interface.model.quantum_function_call import QuantumFunctionCall
26
26
  from classiq.interface.model.quantum_type import (
27
27
  QuantumBit,
28
28
  QuantumBitvector,
29
- QuantumType,
30
29
  )
31
30
  from classiq.interface.model.statement_block import ConcreteQuantumStatement
32
31
  from classiq.interface.model.variable_declaration_statement import (
@@ -47,6 +46,7 @@ from classiq.model_expansions.quantum_operations.expression_operation import (
47
46
  ExpressionOperationEmitter,
48
47
  )
49
48
  from classiq.model_expansions.scope import Scope
49
+ from classiq.model_expansions.transformers.var_splitter import SymbolParts
50
50
  from classiq.qmod.builtins.functions.standard_gates import X
51
51
 
52
52
 
@@ -54,7 +54,9 @@ class ControlEmitter(ExpressionOperationEmitter[Control]):
54
54
  def emit(self, control: Control, /) -> None:
55
55
  condition = self._evaluate_op_expression(control)
56
56
 
57
- arrays_with_subscript = self._get_symbols_to_split(condition)
57
+ arrays_with_subscript = self.split_symbols(
58
+ condition, self._counted_name_allocator.allocate
59
+ )
58
60
  if len(arrays_with_subscript) > 0:
59
61
  if control.is_generative():
60
62
  with self._propagated_var_stack.capture_variables(control):
@@ -308,12 +310,12 @@ class ControlEmitter(ExpressionOperationEmitter[Control]):
308
310
  raise ClassiqExpansionError(_condition_err_msg(condition_val))
309
311
 
310
312
  def _get_updated_op_split_symbols(
311
- self, op: Control, symbol_mapping: dict[HandleBinding, tuple[str, QuantumType]]
313
+ self, op: Control, symbol_parts: SymbolParts
312
314
  ) -> Control:
313
- new_body = self._rewrite(op.body, symbol_mapping)
315
+ new_body = self.rewrite(op.body, symbol_parts)
314
316
  new_else = None
315
317
  if op.else_block is not None:
316
- new_else = self._rewrite(op.else_block, symbol_mapping)
318
+ new_else = self.rewrite(op.else_block, symbol_parts)
317
319
  return op.model_copy(update=dict(body=new_body, else_block=new_else))
318
320
 
319
321