classiq 0.54.0__py3-none-any.whl → 0.55.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/interface/_version.py +1 -1
- classiq/interface/generator/functions/builtins/internal_operators.py +9 -1
- classiq/interface/generator/generated_circuit_data.py +0 -1
- classiq/interface/generator/model/preferences/preferences.py +4 -0
- classiq/interface/generator/types/compilation_metadata.py +5 -0
- classiq/interface/ide/visual_model.py +3 -1
- classiq/interface/model/control.py +22 -1
- classiq/interface/model/model.py +4 -0
- classiq/interface/model/native_function_definition.py +1 -1
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +2 -26
- classiq/interface/model/quantum_statement.py +3 -0
- classiq/model_expansions/closure.py +74 -11
- classiq/model_expansions/function_builder.py +0 -6
- classiq/model_expansions/generative_functions.py +2 -2
- classiq/model_expansions/interpreter.py +22 -25
- classiq/model_expansions/quantum_operations/control.py +79 -20
- classiq/model_expansions/quantum_operations/emitter.py +24 -8
- classiq/model_expansions/quantum_operations/expression_operation.py +25 -1
- classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +3 -26
- classiq/model_expansions/quantum_operations/quantum_function_call.py +2 -32
- classiq/model_expansions/quantum_operations/within_apply.py +0 -16
- classiq/model_expansions/scope_initialization.py +0 -1
- classiq/qmod/builtins/operations.py +113 -17
- classiq/qmod/create_model_function.py +10 -12
- classiq/qmod/model_state_container.py +5 -1
- classiq/qmod/native/pretty_printer.py +6 -1
- classiq/qmod/pretty_print/pretty_printer.py +25 -11
- classiq/qmod/quantum_function.py +25 -19
- classiq/qmod/synthesize_separately.py +1 -2
- {classiq-0.54.0.dist-info → classiq-0.55.0.dist-info}/METADATA +1 -1
- {classiq-0.54.0.dist-info → classiq-0.55.0.dist-info}/RECORD +32 -32
- classiq/model_expansions/call_to_model_converter.py +0 -190
- {classiq-0.54.0.dist-info → classiq-0.55.0.dist-info}/WHEEL +0 -0
classiq/interface/_version.py
CHANGED
@@ -3,6 +3,14 @@ CONTROL_OPERATOR_NAME = "control"
|
|
3
3
|
INVERT_OPERATOR_NAME = "invert"
|
4
4
|
REPEAT_OPERATOR_NAME = "iteration"
|
5
5
|
POWER_OPERATOR_NAME = "power"
|
6
|
-
COMPUTE_OPERATOR_NAME = "compute"
|
7
6
|
UNCOMPUTE_OPERATOR_NAME = "uncompute"
|
8
7
|
WITHIN_APPLY_NAME = "within_apply"
|
8
|
+
|
9
|
+
All_BUILTINS_OPERATORS = {
|
10
|
+
CONTROL_OPERATOR_NAME,
|
11
|
+
INVERT_OPERATOR_NAME,
|
12
|
+
REPEAT_OPERATOR_NAME,
|
13
|
+
POWER_OPERATOR_NAME,
|
14
|
+
UNCOMPUTE_OPERATOR_NAME,
|
15
|
+
WITHIN_APPLY_NAME,
|
16
|
+
}
|
@@ -59,7 +59,6 @@ class GeneratedFunction(pydantic.BaseModel):
|
|
59
59
|
registers: list[GeneratedRegister] = list()
|
60
60
|
depth: Optional[int] = pydantic.Field(default=None)
|
61
61
|
width: Optional[int] = pydantic.Field(default=None)
|
62
|
-
released_auxiliary_qubits: list[int] = list()
|
63
62
|
dangling_inputs: dict[str, GeneratedRegister] = dict()
|
64
63
|
dangling_outputs: dict[str, GeneratedRegister] = dict()
|
65
64
|
|
@@ -154,6 +154,10 @@ class Preferences(pydantic.BaseModel, extra="forbid"):
|
|
154
154
|
"Setting this option to False can potentially speed up the synthesis, and is "
|
155
155
|
"recommended for executing iterative algorithms.",
|
156
156
|
)
|
157
|
+
synthesize_all_separately: bool = pydantic.Field(
|
158
|
+
default=False,
|
159
|
+
description="If true, all functions will be synthesized separately",
|
160
|
+
)
|
157
161
|
output_format: PydanticConstrainedQuantumFormatList = pydantic.Field(
|
158
162
|
default=[QuantumFormat.QASM],
|
159
163
|
description="The quantum circuit output format(s). ",
|
@@ -1,3 +1,4 @@
|
|
1
|
+
from collections import Counter
|
1
2
|
from typing import Any, Optional
|
2
3
|
|
3
4
|
import pydantic
|
@@ -23,7 +24,7 @@ class OperationType(StrEnum):
|
|
23
24
|
class OperationData(pydantic.BaseModel):
|
24
25
|
approximated_depth: Optional[int] = None
|
25
26
|
width: int
|
26
|
-
gate_count:
|
27
|
+
gate_count: Counter[str] = pydantic.Field(default_factory=dict)
|
27
28
|
|
28
29
|
|
29
30
|
class CircuitMetrics(pydantic.BaseModel):
|
@@ -104,6 +105,7 @@ class AtomicGate(StrEnum):
|
|
104
105
|
|
105
106
|
class Operation(pydantic.BaseModel):
|
106
107
|
name: str
|
108
|
+
details: str = pydantic.Field(default="")
|
107
109
|
children: list["Operation"]
|
108
110
|
operation_data: Optional[OperationData] = None
|
109
111
|
operation_links: OperationLinks
|
@@ -1,10 +1,12 @@
|
|
1
|
-
from typing import TYPE_CHECKING, Literal
|
1
|
+
from typing import TYPE_CHECKING, Literal, Optional
|
2
2
|
|
3
3
|
import pydantic
|
4
4
|
|
5
|
+
from classiq.interface.generator.arith.arithmetic import compute_arithmetic_result_type
|
5
6
|
from classiq.interface.model.quantum_expressions.quantum_expression import (
|
6
7
|
QuantumExpressionOperation,
|
7
8
|
)
|
9
|
+
from classiq.interface.model.quantum_type import QuantumType
|
8
10
|
|
9
11
|
if TYPE_CHECKING:
|
10
12
|
from classiq.interface.model.statement_block import StatementBlock
|
@@ -13,8 +15,12 @@ if TYPE_CHECKING:
|
|
13
15
|
class Control(QuantumExpressionOperation):
|
14
16
|
kind: Literal["Control"]
|
15
17
|
body: "StatementBlock"
|
18
|
+
else_block: Optional["StatementBlock"] = None
|
16
19
|
|
17
20
|
_ctrl_size: int = pydantic.PrivateAttr(default=0)
|
21
|
+
_result_type: Optional[QuantumType] = pydantic.PrivateAttr(
|
22
|
+
default=None,
|
23
|
+
)
|
18
24
|
|
19
25
|
@property
|
20
26
|
def ctrl_size(self) -> int:
|
@@ -22,3 +28,18 @@ class Control(QuantumExpressionOperation):
|
|
22
28
|
|
23
29
|
def set_ctrl_size(self, ctrl_size: int) -> None:
|
24
30
|
self._ctrl_size = ctrl_size
|
31
|
+
|
32
|
+
@property
|
33
|
+
def result_type(self) -> QuantumType:
|
34
|
+
assert self._result_type is not None
|
35
|
+
return self._result_type
|
36
|
+
|
37
|
+
def initialize_var_types(
|
38
|
+
self,
|
39
|
+
var_types: dict[str, QuantumType],
|
40
|
+
machine_precision: int,
|
41
|
+
) -> None:
|
42
|
+
super().initialize_var_types(var_types, machine_precision)
|
43
|
+
self._result_type = compute_arithmetic_result_type(
|
44
|
+
self.expression.expr, var_types, machine_precision
|
45
|
+
)
|
classiq/interface/model/model.py
CHANGED
@@ -15,6 +15,7 @@ from classiq.interface.generator.functions.port_declaration import (
|
|
15
15
|
from classiq.interface.generator.model.constraints import Constraints
|
16
16
|
from classiq.interface.generator.model.preferences.preferences import Preferences
|
17
17
|
from classiq.interface.generator.quantum_function_call import SUFFIX_RANDOMIZER
|
18
|
+
from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
|
18
19
|
from classiq.interface.generator.types.enum_declaration import EnumDeclaration
|
19
20
|
from classiq.interface.generator.types.qstruct_declaration import QStructDeclaration
|
20
21
|
from classiq.interface.generator.types.struct_declaration import StructDeclaration
|
@@ -98,6 +99,9 @@ class Model(VersionedModel, ASTNode):
|
|
98
99
|
debug_info: DebugInfoCollection = pydantic.Field(
|
99
100
|
default_factory=DebugInfoCollection
|
100
101
|
)
|
102
|
+
functions_compilation_metadata: dict[str, CompilationMetadata] = pydantic.Field(
|
103
|
+
default_factory=dict
|
104
|
+
)
|
101
105
|
|
102
106
|
@property
|
103
107
|
def main_func(self) -> NativeFunctionDefinition:
|
@@ -29,5 +29,5 @@ class NativeFunctionDefinition(NamedParamsQuantumFunctionDeclaration):
|
|
29
29
|
default_factory=list, description="List of function calls to perform."
|
30
30
|
)
|
31
31
|
synthesis_data: FunctionSynthesisData = pydantic.Field(
|
32
|
-
default_factory=FunctionSynthesisData,
|
32
|
+
default_factory=FunctionSynthesisData, deprecated=True, exclude=True
|
33
33
|
)
|
@@ -1,8 +1,5 @@
|
|
1
1
|
from collections.abc import Mapping, Sequence
|
2
|
-
from typing import Literal
|
3
|
-
|
4
|
-
import pydantic
|
5
|
-
from pydantic_core.core_schema import ValidationInfo
|
2
|
+
from typing import Literal
|
6
3
|
|
7
4
|
from classiq.interface.enum_utils import StrEnum
|
8
5
|
from classiq.interface.generator.arith.arithmetic import (
|
@@ -29,28 +26,7 @@ class ArithmeticOperationKind(StrEnum):
|
|
29
26
|
class ArithmeticOperation(QuantumAssignmentOperation):
|
30
27
|
kind: Literal["ArithmeticOperation"]
|
31
28
|
|
32
|
-
|
33
|
-
description="Determines whether the result variable is initialized",
|
34
|
-
default=None,
|
35
|
-
exclude=True,
|
36
|
-
)
|
37
|
-
|
38
|
-
operation_kind: ArithmeticOperationKind = pydantic.Field(
|
39
|
-
default=None, validate_default=True
|
40
|
-
)
|
41
|
-
|
42
|
-
@pydantic.field_validator("operation_kind", mode="before")
|
43
|
-
@classmethod
|
44
|
-
def _propagate_inplace_result(
|
45
|
-
cls, operation_kind: Optional[ArithmeticOperationKind], info: ValidationInfo
|
46
|
-
) -> ArithmeticOperationKind:
|
47
|
-
if operation_kind is None:
|
48
|
-
operation_kind = (
|
49
|
-
ArithmeticOperationKind.InplaceXor
|
50
|
-
if info.data["inplace_result"]
|
51
|
-
else ArithmeticOperationKind.Assignment
|
52
|
-
)
|
53
|
-
return operation_kind
|
29
|
+
operation_kind: ArithmeticOperationKind
|
54
30
|
|
55
31
|
@property
|
56
32
|
def is_inplace(self) -> bool:
|
@@ -75,6 +75,9 @@ class QuantumOperation(QuantumStatement):
|
|
75
75
|
def set_generative_block(self, block_name: str, py_callable: Callable) -> None:
|
76
76
|
self._generative_blocks[block_name] = py_callable
|
77
77
|
|
78
|
+
def remove_generative_block(self, block_name: str) -> None:
|
79
|
+
self._generative_blocks.pop(block_name)
|
80
|
+
|
78
81
|
def get_generative_block(self, block_name: str) -> Callable:
|
79
82
|
return self._generative_blocks[block_name]
|
80
83
|
|
@@ -1,14 +1,21 @@
|
|
1
|
+
import json
|
2
|
+
import uuid
|
1
3
|
from collections import defaultdict
|
2
|
-
from collections.abc import Sequence
|
4
|
+
from collections.abc import Collection, Sequence
|
3
5
|
from dataclasses import dataclass, field
|
4
|
-
from functools import cached_property
|
6
|
+
from functools import cached_property, singledispatch
|
7
|
+
from symtable import Symbol
|
5
8
|
from typing import Any, Optional, Union
|
6
9
|
|
7
10
|
from typing_extensions import Self
|
8
11
|
|
9
|
-
from classiq.interface.exceptions import
|
12
|
+
from classiq.interface.exceptions import (
|
13
|
+
ClassiqInternalExpansionError,
|
14
|
+
)
|
15
|
+
from classiq.interface.generator.functions.builtins.internal_operators import (
|
16
|
+
All_BUILTINS_OPERATORS,
|
17
|
+
)
|
10
18
|
from classiq.interface.generator.visitor import Visitor
|
11
|
-
from classiq.interface.model.native_function_definition import FunctionSynthesisData
|
12
19
|
from classiq.interface.model.port_declaration import PortDeclaration
|
13
20
|
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
14
21
|
from classiq.interface.model.quantum_function_declaration import (
|
@@ -20,8 +27,14 @@ from classiq.interface.model.variable_declaration_statement import (
|
|
20
27
|
VariableDeclarationStatement,
|
21
28
|
)
|
22
29
|
|
30
|
+
from classiq import ClassicalParameterDeclaration
|
23
31
|
from classiq.model_expansions.expression_renamer import ExpressionRenamer
|
24
|
-
from classiq.model_expansions.scope import
|
32
|
+
from classiq.model_expansions.scope import (
|
33
|
+
Evaluated,
|
34
|
+
QuantumSymbol,
|
35
|
+
Scope,
|
36
|
+
evaluated_to_str as evaluated_classical_param_to_str,
|
37
|
+
)
|
25
38
|
from classiq.qmod.builtins.functions import permute
|
26
39
|
from classiq.qmod.quantum_function import GenerativeQFunc
|
27
40
|
|
@@ -52,7 +65,21 @@ class FunctionClosure(Closure):
|
|
52
65
|
is_lambda: bool = False
|
53
66
|
is_atomic: bool = False
|
54
67
|
signature_scope: Scope = field(default_factory=Scope)
|
55
|
-
|
68
|
+
|
69
|
+
# creates a unique id for the function closure based on the arguments values.
|
70
|
+
# The closure is changing across the interpreter flow so it's closure_id may change
|
71
|
+
@property
|
72
|
+
def closure_id(self) -> str:
|
73
|
+
# builtins operators have side effects, so generate a unique id for than
|
74
|
+
# may create a bugs. Therefore, with each call to closure_id a new id is
|
75
|
+
# created
|
76
|
+
if self.is_lambda or self.name in All_BUILTINS_OPERATORS:
|
77
|
+
signature = str(uuid.uuid4())
|
78
|
+
else:
|
79
|
+
signature = _generate_closure_id(
|
80
|
+
self.positional_arg_declarations, self.scope.data.values()
|
81
|
+
)
|
82
|
+
return f"{self.name}__{signature}"
|
56
83
|
|
57
84
|
@property
|
58
85
|
def body(self) -> Sequence[QuantumStatement]:
|
@@ -77,7 +104,6 @@ class FunctionClosure(Closure):
|
|
77
104
|
expr_renamer: Optional[ExpressionRenamer] = None,
|
78
105
|
is_lambda: bool = False,
|
79
106
|
is_atomic: bool = False,
|
80
|
-
synthesis_data: Optional[FunctionSynthesisData] = None,
|
81
107
|
**kwargs: Any,
|
82
108
|
) -> Self:
|
83
109
|
if expr_renamer:
|
@@ -90,9 +116,6 @@ class FunctionClosure(Closure):
|
|
90
116
|
body = expr_renamer.visit(body)
|
91
117
|
|
92
118
|
blocks = {"body": body} if body is not None else {}
|
93
|
-
synthesis_data = (
|
94
|
-
synthesis_data if synthesis_data is not None else FunctionSynthesisData()
|
95
|
-
)
|
96
119
|
return cls(
|
97
120
|
name,
|
98
121
|
blocks,
|
@@ -100,7 +123,6 @@ class FunctionClosure(Closure):
|
|
100
123
|
positional_arg_declarations,
|
101
124
|
is_lambda,
|
102
125
|
is_atomic,
|
103
|
-
synthesis_data=synthesis_data,
|
104
126
|
**kwargs,
|
105
127
|
)
|
106
128
|
|
@@ -171,3 +193,44 @@ class VariableCollector(Visitor):
|
|
171
193
|
defining_function = lambda_environment[var].defining_function
|
172
194
|
if defining_function is not None:
|
173
195
|
self._variables[var].add(defining_function.name)
|
196
|
+
|
197
|
+
|
198
|
+
def _generate_closure_id(
|
199
|
+
args_declaration: Sequence[PositionalArg], evaluated_args: Collection[Evaluated]
|
200
|
+
) -> str:
|
201
|
+
args_signature: dict = {}
|
202
|
+
for arg_declara, eval_arg in zip(args_declaration, evaluated_args):
|
203
|
+
args_signature |= _generate_arg_id(arg_declara, eval_arg)
|
204
|
+
return json.dumps(args_signature)
|
205
|
+
|
206
|
+
|
207
|
+
def _generate_arg_id(
|
208
|
+
arg_declaration: PositionalArg, evaluated_arg: Evaluated
|
209
|
+
) -> dict[str, str]:
|
210
|
+
arg_value = evaluated_arg.value
|
211
|
+
arg_name = arg_declaration.name
|
212
|
+
if isinstance(arg_declaration, ClassicalParameterDeclaration):
|
213
|
+
return {arg_name: evaluated_classical_param_to_str(arg_value)}
|
214
|
+
return {arg_name: _evaluated_arg_to_str(arg_value)}
|
215
|
+
|
216
|
+
|
217
|
+
@singledispatch
|
218
|
+
def _evaluated_arg_to_str(arg: Any) -> str:
|
219
|
+
if isinstance(arg, str):
|
220
|
+
return arg
|
221
|
+
return str(uuid.uuid4())
|
222
|
+
|
223
|
+
|
224
|
+
@_evaluated_arg_to_str.register
|
225
|
+
def _evaluated_quantum_symbol_to_str(port: QuantumSymbol) -> str:
|
226
|
+
return port.quantum_type.model_dump_json(exclude_none=True, exclude={"name"})
|
227
|
+
|
228
|
+
|
229
|
+
@_evaluated_arg_to_str.register
|
230
|
+
def _evaluated_symbol_to_str(port: Symbol) -> str:
|
231
|
+
return repr(port)
|
232
|
+
|
233
|
+
|
234
|
+
@_evaluated_arg_to_str.register
|
235
|
+
def _evaluated_operand_to_str(operand: FunctionClosure) -> str:
|
236
|
+
return operand.closure_id
|
@@ -19,7 +19,6 @@ from classiq.interface.generator.functions.port_declaration import (
|
|
19
19
|
)
|
20
20
|
from classiq.interface.model.model import MAIN_FUNCTION_NAME
|
21
21
|
from classiq.interface.model.native_function_definition import (
|
22
|
-
FunctionSynthesisData,
|
23
22
|
NativeFunctionDefinition,
|
24
23
|
)
|
25
24
|
from classiq.interface.model.port_declaration import PortDeclaration
|
@@ -76,10 +75,6 @@ class FunctionContext(OperationContext[FunctionClosure]):
|
|
76
75
|
def is_lambda(self) -> bool:
|
77
76
|
return self.closure.is_lambda
|
78
77
|
|
79
|
-
@property
|
80
|
-
def synthesis_data(self) -> FunctionSynthesisData:
|
81
|
-
return self.closure.synthesis_data
|
82
|
-
|
83
78
|
|
84
79
|
class OperationBuilder:
|
85
80
|
def __init__(self) -> None:
|
@@ -176,7 +171,6 @@ class OperationBuilder:
|
|
176
171
|
name=name,
|
177
172
|
body=function_context.body,
|
178
173
|
positional_arg_declarations=new_parameters,
|
179
|
-
synthesis_data=function_context.synthesis_data,
|
180
174
|
)
|
181
175
|
|
182
176
|
|
@@ -186,7 +186,7 @@ def _expand_operand_as_declarative(
|
|
186
186
|
|
187
187
|
|
188
188
|
def _register_declarative_function(interpreter: "Interpreter", func_name: str) -> None:
|
189
|
-
if func_name in nameables_to_dict(interpreter._expanded_functions):
|
189
|
+
if func_name in nameables_to_dict(list(interpreter._expanded_functions.values())):
|
190
190
|
return
|
191
191
|
|
192
192
|
for user_gen_func in interpreter._generative_functions:
|
@@ -199,7 +199,7 @@ def _register_declarative_function(interpreter: "Interpreter", func_name: str) -
|
|
199
199
|
dec_func = QFunc(user_gen_func._py_callable)
|
200
200
|
dec_func.expand()
|
201
201
|
dec_func_def = QMODULE.native_defs[func_name]
|
202
|
-
interpreter._expanded_functions
|
202
|
+
interpreter._expanded_functions[func_name] = dec_func_def
|
203
203
|
_DecFuncVisitor(interpreter).visit(dec_func_def)
|
204
204
|
|
205
205
|
|
@@ -12,6 +12,7 @@ from classiq.interface.exceptions import (
|
|
12
12
|
ClassiqInternalExpansionError,
|
13
13
|
)
|
14
14
|
from classiq.interface.generator.expressions.expression import Expression
|
15
|
+
from classiq.interface.generator.types.compilation_metadata import CompilationMetadata
|
15
16
|
from classiq.interface.model.bind_operation import BindOperation
|
16
17
|
from classiq.interface.model.classical_if import ClassicalIf
|
17
18
|
from classiq.interface.model.control import Control
|
@@ -46,7 +47,6 @@ from classiq.interface.model.variable_declaration_statement import (
|
|
46
47
|
)
|
47
48
|
from classiq.interface.model.within_apply_operation import WithinApply
|
48
49
|
|
49
|
-
from classiq.model_expansions.call_to_model_converter import BlockFunctionInfo
|
50
50
|
from classiq.model_expansions.capturing.propagated_var_stack import PropagatedVarStack
|
51
51
|
from classiq.model_expansions.closure import (
|
52
52
|
Closure,
|
@@ -108,7 +108,7 @@ class Interpreter:
|
|
108
108
|
self._model = model
|
109
109
|
self._current_scope = Scope()
|
110
110
|
self._builder = OperationBuilder()
|
111
|
-
self._expanded_functions:
|
111
|
+
self._expanded_functions: dict[str, NativeFunctionDefinition] = {}
|
112
112
|
self._propagated_var_stack = PropagatedVarStack(
|
113
113
|
self._current_scope, self._builder
|
114
114
|
)
|
@@ -119,10 +119,11 @@ class Interpreter:
|
|
119
119
|
generative_functions = []
|
120
120
|
self._generative_functions = generative_functions
|
121
121
|
init_top_level_scope(model, generative_functions, self._current_scope)
|
122
|
-
|
122
|
+
self._expanded_functions_compilation_metadata: dict[
|
123
|
+
str, CompilationMetadata
|
124
|
+
] = dict()
|
123
125
|
self._counted_name_allocator = CountedNameAllocator()
|
124
126
|
self._error_manager: ErrorManager = ErrorManager()
|
125
|
-
self._synthesized_separately_blocks: dict[str, BlockFunctionInfo] = {}
|
126
127
|
|
127
128
|
@contextmanager
|
128
129
|
def _scope_guard(self, scope: Scope) -> Iterator[None]:
|
@@ -153,14 +154,13 @@ class Interpreter:
|
|
153
154
|
main_closure.positional_arg_declarations, main_closure
|
154
155
|
)
|
155
156
|
context = self._expand_operation(main_closure)
|
156
|
-
self._expanded_functions.
|
157
|
+
self._expanded_functions[main_closure.closure_id] = (
|
157
158
|
self._builder.create_definition(cast(FunctionContext, context))
|
158
159
|
)
|
159
160
|
|
160
|
-
def expand(self) ->
|
161
|
+
def expand(self) -> Model:
|
161
162
|
try:
|
162
163
|
with self._error_manager.call("main"):
|
163
|
-
self._synthesized_separately_blocks = {}
|
164
164
|
self._expand_main_func()
|
165
165
|
except Exception as e:
|
166
166
|
if isinstance(e, ClassiqInternalExpansionError) or debug_mode.get():
|
@@ -172,20 +172,18 @@ class Interpreter:
|
|
172
172
|
finally:
|
173
173
|
self._error_manager.report_errors(ClassiqExpansionError)
|
174
174
|
|
175
|
-
return (
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
),
|
188
|
-
self._synthesized_separately_blocks,
|
175
|
+
return Model(
|
176
|
+
constraints=self._model.constraints,
|
177
|
+
preferences=self._model.preferences,
|
178
|
+
classical_execution_code=self._model.classical_execution_code,
|
179
|
+
execution_preferences=self._model.execution_preferences,
|
180
|
+
functions=list(self._expanded_functions.values()),
|
181
|
+
constants=self._model.constants,
|
182
|
+
enums=self._model.enums,
|
183
|
+
types=self._model.types,
|
184
|
+
qstructs=self._model.qstructs,
|
185
|
+
debug_info=self._model.debug_info,
|
186
|
+
functions_compilation_metadata=self._expanded_functions_compilation_metadata,
|
189
187
|
)
|
190
188
|
|
191
189
|
@singledispatchmethod
|
@@ -346,9 +344,8 @@ class Interpreter:
|
|
346
344
|
|
347
345
|
def _expand_operation(self, operation: Closure) -> OperationContext:
|
348
346
|
with self._builder.operation_context(operation) as context:
|
349
|
-
if (
|
350
|
-
|
351
|
-
and operation.synthesis_data.should_synthesize_separately
|
347
|
+
if isinstance(operation, FunctionClosure) and (
|
348
|
+
self._expanded_functions.get(operation.closure_id) is not None
|
352
349
|
):
|
353
350
|
pass
|
354
351
|
elif isinstance(operation, FunctionClosure) and operation.name == "permute":
|
@@ -385,5 +382,5 @@ class Interpreter:
|
|
385
382
|
return (
|
386
383
|
self._model.functions
|
387
384
|
+ [gen_func.func_decl for gen_func in self._generative_functions]
|
388
|
-
+ self._expanded_functions
|
385
|
+
+ list(self._expanded_functions.values())
|
389
386
|
)
|
@@ -9,6 +9,7 @@ from classiq.interface.exceptions import (
|
|
9
9
|
from classiq.interface.generator.compiler_keywords import INPLACE_ARITH_AUX_VAR_PREFIX
|
10
10
|
from classiq.interface.generator.expressions.expression import Expression
|
11
11
|
from classiq.interface.generator.expressions.expression_types import ExpressionValue
|
12
|
+
from classiq.interface.generator.expressions.qmod_qarray_proxy import QmodQArrayProxy
|
12
13
|
from classiq.interface.generator.expressions.qmod_qscalar_proxy import QmodQNumProxy
|
13
14
|
from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
|
14
15
|
from classiq.interface.generator.functions.builtins.internal_operators import (
|
@@ -21,6 +22,7 @@ from classiq.interface.model.quantum_expressions.arithmetic_operation import (
|
|
21
22
|
ArithmeticOperation,
|
22
23
|
ArithmeticOperationKind,
|
23
24
|
)
|
25
|
+
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
24
26
|
from classiq.interface.model.quantum_type import QuantumBit, QuantumBitvector
|
25
27
|
from classiq.interface.model.statement_block import ConcreteQuantumStatement
|
26
28
|
from classiq.interface.model.variable_declaration_statement import (
|
@@ -40,6 +42,7 @@ from classiq.model_expansions.quantum_operations.expression_operation import (
|
|
40
42
|
ExpressionOperationEmitter,
|
41
43
|
)
|
42
44
|
from classiq.model_expansions.scope import Scope
|
45
|
+
from classiq.qmod.builtins.functions.standard_gates import X
|
43
46
|
|
44
47
|
ARRAY_CAST_SUFFIX = HANDLE_ID_SEPARATOR + "array_cast"
|
45
48
|
|
@@ -47,22 +50,42 @@ ARRAY_CAST_SUFFIX = HANDLE_ID_SEPARATOR + "array_cast"
|
|
47
50
|
class ControlEmitter(ExpressionOperationEmitter[Control]):
|
48
51
|
def emit(self, control: Control, /) -> None:
|
49
52
|
condition = self._evaluate_op_expression(control)
|
50
|
-
control = control.model_copy(update=dict(expression=condition))
|
51
53
|
|
52
54
|
arrays_with_subscript = self._get_symbols_to_split(condition)
|
53
55
|
if len(arrays_with_subscript) > 0:
|
54
56
|
self._emit_with_split(control, condition, arrays_with_subscript)
|
55
57
|
return
|
56
58
|
|
59
|
+
control = self._evaluate_types_in_expression(control, condition)
|
57
60
|
condition_val = condition.value.value
|
58
61
|
if isinstance(condition_val, QmodSizedProxy):
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
+
if control.else_block is None:
|
63
|
+
self._validate_canonical_condition(condition_val)
|
64
|
+
self._emit_canonical_control(control)
|
65
|
+
return
|
66
|
+
else:
|
67
|
+
self._interpreter.emit_statement(
|
68
|
+
control.model_copy(
|
69
|
+
update=dict(
|
70
|
+
expression=self._uncanonize_condition(condition_val)
|
71
|
+
)
|
72
|
+
)
|
73
|
+
)
|
74
|
+
return
|
62
75
|
|
63
|
-
self._validate_condition(
|
76
|
+
self._validate_condition(control)
|
64
77
|
self._emit_with_boolean(control)
|
65
78
|
|
79
|
+
def _uncanonize_condition(self, condition_val: QmodSizedProxy) -> Expression:
|
80
|
+
lhs = (
|
81
|
+
" & ".join(
|
82
|
+
f"{condition_val.handle}[{idx}]" for idx in range(condition_val.size)
|
83
|
+
)
|
84
|
+
if isinstance(condition_val, QmodQArrayProxy)
|
85
|
+
else condition_val.handle
|
86
|
+
)
|
87
|
+
return Expression(expr=f"{lhs} == 1")
|
88
|
+
|
66
89
|
def _emit_canonical_control(self, control: Control) -> None:
|
67
90
|
# canonical means control(q, body) where q is a single quantum variable
|
68
91
|
control = self._evaluate_types_in_expression(control, control.expression)
|
@@ -129,9 +152,10 @@ class ControlEmitter(ExpressionOperationEmitter[Control]):
|
|
129
152
|
condition_val = control.expression.value.value
|
130
153
|
if self._is_simple_equality(condition_val):
|
131
154
|
ctrl, ctrl_state = resolve_num_condition(condition_val)
|
132
|
-
|
133
|
-
|
134
|
-
|
155
|
+
if control.else_block is None or ctrl.size == 1:
|
156
|
+
self._emit_with_x_gates(control, ctrl, ctrl_state)
|
157
|
+
return
|
158
|
+
self._emit_with_arithmetic(control)
|
135
159
|
|
136
160
|
@staticmethod
|
137
161
|
def _is_simple_equality(condition_val: ExpressionValue) -> TypeGuard[Equality]:
|
@@ -151,13 +175,37 @@ class ControlEmitter(ExpressionOperationEmitter[Control]):
|
|
151
175
|
|
152
176
|
def _create_canonical_control_op(
|
153
177
|
self, control: Control, handle_name: str
|
154
|
-
) ->
|
178
|
+
) -> list[ConcreteQuantumStatement]:
|
155
179
|
handle_expr = self._interpreter.evaluate(Expression(expr=handle_name)).emit()
|
156
|
-
|
180
|
+
control_then = control.model_copy(
|
181
|
+
update=dict(expression=handle_expr, else_block=None)
|
182
|
+
)
|
183
|
+
if control.else_block is None:
|
184
|
+
return [control_then]
|
185
|
+
else_compute_call = QuantumFunctionCall(
|
186
|
+
function="X", positional_args=[HandleBinding(name=handle_name)]
|
187
|
+
)
|
188
|
+
else_compute_call.set_func_decl(X.func_decl)
|
189
|
+
control_else_inner = control.model_copy(
|
190
|
+
update=dict(
|
191
|
+
expression=handle_expr, body=control.else_block, else_block=None
|
192
|
+
),
|
193
|
+
deep=True,
|
194
|
+
)
|
195
|
+
control_else = WithinApply(
|
196
|
+
compute=[else_compute_call],
|
197
|
+
action=[control_else_inner],
|
198
|
+
)
|
199
|
+
if control_else_inner.is_generative():
|
200
|
+
control_else_inner.set_generative_block(
|
201
|
+
"body", control_else_inner.get_generative_block("else_block")
|
202
|
+
)
|
203
|
+
control_else_inner.remove_generative_block("else_block")
|
204
|
+
return [control_then, control_else]
|
157
205
|
|
158
|
-
def
|
206
|
+
def _control_with_x_gates(
|
159
207
|
self, control: Control, ctrl: QmodSizedProxy, ctrl_state: str
|
160
|
-
) ->
|
208
|
+
) -> ConcreteQuantumStatement:
|
161
209
|
compute_op: list[ConcreteQuantumStatement] = []
|
162
210
|
|
163
211
|
x_gate_value = self._get_x_gate_value(ctrl_state)
|
@@ -175,12 +223,19 @@ class ControlEmitter(ExpressionOperationEmitter[Control]):
|
|
175
223
|
cast_decl, bind_op = self._get_array_cast_ops(ctrl)
|
176
224
|
self._interpreter.emit_statement(cast_decl)
|
177
225
|
compute_op.append(bind_op)
|
178
|
-
|
226
|
+
control_ops = self._create_canonical_control_op(
|
227
|
+
control, str(cast_decl.name)
|
228
|
+
)
|
179
229
|
else:
|
180
|
-
|
230
|
+
control_ops = self._create_canonical_control_op(control, str(ctrl.handle))
|
231
|
+
|
232
|
+
return WithinApply(compute=compute_op, action=control_ops)
|
181
233
|
|
234
|
+
def _emit_with_x_gates(
|
235
|
+
self, control: Control, ctrl: QmodSizedProxy, ctrl_state: str
|
236
|
+
) -> None:
|
182
237
|
self._interpreter.emit_statement(
|
183
|
-
|
238
|
+
self._control_with_x_gates(control, ctrl, ctrl_state)
|
184
239
|
)
|
185
240
|
|
186
241
|
@staticmethod
|
@@ -217,14 +272,18 @@ class ControlEmitter(ExpressionOperationEmitter[Control]):
|
|
217
272
|
self._interpreter.emit_statement(
|
218
273
|
WithinApply(
|
219
274
|
compute=[arith_expression],
|
220
|
-
action=
|
275
|
+
action=self._create_canonical_control_op(control, aux_var),
|
221
276
|
)
|
222
277
|
)
|
223
278
|
|
224
|
-
|
225
|
-
|
226
|
-
if not
|
227
|
-
|
279
|
+
def _validate_condition(self, control: Control) -> None:
|
280
|
+
condition_value = control.expression.value.value
|
281
|
+
if not (
|
282
|
+
isinstance(condition_value, Boolean)
|
283
|
+
or self._all_vars_boolean(control)
|
284
|
+
and self._is_res_boolean(control)
|
285
|
+
):
|
286
|
+
raise ClassiqExpansionError(_condition_err_msg(condition_value))
|
228
287
|
|
229
288
|
@staticmethod
|
230
289
|
def _validate_canonical_condition(condition_val: ExpressionValue) -> None:
|