classiq 0.45.1__py3-none-any.whl → 0.46.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- classiq/__init__.py +0 -1
- classiq/_internals/__init__.py +20 -0
- classiq/_internals/authentication/authentication.py +11 -0
- classiq/analyzer/analyzer.py +12 -10
- classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +1 -1
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +1 -1
- classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
- classiq/applications/libraries/qmci_library.py +4 -9
- classiq/execution/execution_session.py +68 -7
- classiq/executor.py +14 -2
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +189 -0
- classiq/interface/backend/quantum_backend_providers.py +38 -0
- classiq/interface/debug_info/debug_info.py +22 -2
- classiq/interface/exceptions.py +16 -1
- classiq/interface/executor/execution_preferences.py +18 -0
- classiq/interface/generator/application_apis/chemistry_declarations.py +1 -177
- classiq/interface/generator/application_apis/combinatorial_optimization_declarations.py +0 -12
- classiq/interface/generator/application_apis/finance_declarations.py +8 -43
- classiq/interface/generator/application_apis/qsvm_declarations.py +0 -78
- classiq/interface/generator/builtin_api_builder.py +0 -3
- classiq/interface/generator/functions/__init__.py +0 -2
- classiq/interface/generator/functions/builtins/__init__.py +0 -15
- classiq/interface/generator/generated_circuit_data.py +2 -0
- classiq/interface/generator/hardware/hardware_data.py +37 -0
- classiq/interface/generator/model/constraints.py +18 -1
- classiq/interface/generator/model/preferences/preferences.py +53 -1
- classiq/interface/generator/model/quantum_register.py +1 -1
- classiq/interface/generator/quantum_program.py +10 -2
- classiq/interface/generator/transpiler_basis_gates.py +4 -0
- classiq/interface/generator/types/builtin_enum_declarations.py +136 -21
- classiq/interface/generator/types/enum_declaration.py +1 -3
- classiq/interface/generator/types/struct_declaration.py +1 -3
- classiq/interface/hardware.py +5 -0
- classiq/interface/ide/visual_model.py +1 -1
- classiq/interface/model/classical_parameter_declaration.py +6 -0
- classiq/interface/model/inplace_binary_operation.py +0 -14
- classiq/interface/model/model.py +1 -18
- classiq/interface/model/port_declaration.py +4 -2
- classiq/interface/model/quantum_function_declaration.py +19 -6
- classiq/interface/model/quantum_lambda_function.py +11 -1
- classiq/interface/model/quantum_variable_declaration.py +1 -1
- classiq/model_expansions/__init__.py +0 -0
- classiq/model_expansions/atomic_expression_functions_defs.py +250 -0
- classiq/model_expansions/capturing/__init__.py +0 -0
- classiq/model_expansions/capturing/captured_var_manager.py +50 -0
- classiq/model_expansions/capturing/mangling_utils.py +17 -0
- classiq/model_expansions/capturing/propagated_var_stack.py +180 -0
- classiq/model_expansions/closure.py +160 -0
- classiq/model_expansions/debug_flag.py +3 -0
- classiq/model_expansions/evaluators/__init__.py +0 -0
- classiq/model_expansions/evaluators/arg_type_match.py +160 -0
- classiq/model_expansions/evaluators/argument_types.py +42 -0
- classiq/model_expansions/evaluators/classical_expression.py +36 -0
- classiq/model_expansions/evaluators/control.py +144 -0
- classiq/model_expansions/evaluators/parameter_types.py +227 -0
- classiq/model_expansions/evaluators/quantum_type_utils.py +235 -0
- classiq/model_expansions/evaluators/type_type_match.py +90 -0
- classiq/model_expansions/expression_evaluator.py +125 -0
- classiq/model_expansions/expression_renamer.py +76 -0
- classiq/model_expansions/function_builder.py +192 -0
- classiq/model_expansions/generative_functions.py +101 -0
- classiq/model_expansions/interpreter.py +365 -0
- classiq/model_expansions/model_tables.py +105 -0
- classiq/model_expansions/quantum_operations/__init__.py +19 -0
- classiq/model_expansions/quantum_operations/bind.py +64 -0
- classiq/model_expansions/quantum_operations/classicalif.py +39 -0
- classiq/model_expansions/quantum_operations/control.py +235 -0
- classiq/model_expansions/quantum_operations/emitter.py +215 -0
- classiq/model_expansions/quantum_operations/expression_operation.py +218 -0
- classiq/model_expansions/quantum_operations/inplace_binary_operation.py +250 -0
- classiq/model_expansions/quantum_operations/invert.py +38 -0
- classiq/model_expansions/quantum_operations/power.py +74 -0
- classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +174 -0
- classiq/model_expansions/quantum_operations/quantum_function_call.py +15 -0
- classiq/model_expansions/quantum_operations/repeat.py +33 -0
- classiq/model_expansions/quantum_operations/variable_decleration.py +28 -0
- classiq/model_expansions/quantum_operations/within_apply.py +46 -0
- classiq/model_expansions/scope.py +226 -0
- classiq/model_expansions/scope_initialization.py +136 -0
- classiq/model_expansions/sympy_conversion/__init__.py +0 -0
- classiq/model_expansions/sympy_conversion/arithmetics.py +49 -0
- classiq/model_expansions/sympy_conversion/expression_to_sympy.py +150 -0
- classiq/model_expansions/sympy_conversion/sympy_to_python.py +113 -0
- classiq/model_expansions/utils/__init__.py +0 -0
- classiq/model_expansions/utils/counted_name_allocator.py +11 -0
- classiq/model_expansions/visitors/__init__.py +0 -0
- classiq/model_expansions/visitors/boolean_expression_transformers.py +214 -0
- classiq/model_expansions/visitors/variable_references.py +115 -0
- classiq/qmod/__init__.py +1 -3
- classiq/qmod/builtins/enums.py +33 -2
- classiq/qmod/builtins/functions/__init__.py +251 -0
- classiq/qmod/builtins/functions/amplitude_estimation.py +27 -0
- classiq/qmod/builtins/functions/arithmetic.py +68 -0
- classiq/qmod/builtins/functions/benchmarking.py +8 -0
- classiq/qmod/builtins/functions/chemistry.py +91 -0
- classiq/qmod/builtins/functions/discrete_sine_cosine_transform.py +105 -0
- classiq/qmod/builtins/functions/exponentiation.py +111 -0
- classiq/qmod/builtins/functions/finance.py +34 -0
- classiq/qmod/builtins/functions/grover.py +178 -0
- classiq/qmod/builtins/functions/hea.py +59 -0
- classiq/qmod/builtins/functions/linear_pauli_rotation.py +65 -0
- classiq/qmod/builtins/functions/modular_exponentiation.py +137 -0
- classiq/qmod/builtins/functions/operators.py +22 -0
- classiq/qmod/builtins/functions/qaoa_penalty.py +116 -0
- classiq/qmod/builtins/functions/qft.py +23 -0
- classiq/qmod/builtins/functions/qpe.py +39 -0
- classiq/qmod/builtins/functions/qsvm.py +24 -0
- classiq/qmod/builtins/functions/qsvt.py +136 -0
- classiq/qmod/builtins/functions/standard_gates.py +739 -0
- classiq/qmod/builtins/functions/state_preparation.py +357 -0
- classiq/qmod/builtins/functions/swap_test.py +25 -0
- classiq/qmod/builtins/structs.py +50 -28
- classiq/qmod/cparam.py +64 -0
- classiq/qmod/create_model_function.py +190 -0
- classiq/qmod/declaration_inferrer.py +52 -81
- classiq/qmod/expression_query.py +16 -0
- classiq/qmod/generative.py +48 -0
- classiq/qmod/model_state_container.py +1 -2
- classiq/qmod/native/pretty_printer.py +7 -11
- classiq/qmod/pretty_print/pretty_printer.py +7 -11
- classiq/qmod/python_classical_type.py +67 -0
- classiq/qmod/qfunc.py +19 -4
- classiq/qmod/qmod_parameter.py +15 -64
- classiq/qmod/qmod_variable.py +27 -45
- classiq/qmod/quantum_callable.py +1 -1
- classiq/qmod/quantum_expandable.py +10 -4
- classiq/qmod/quantum_function.py +22 -40
- classiq/qmod/semantics/error_manager.py +22 -10
- classiq/qmod/semantics/static_semantics_visitor.py +10 -12
- classiq/qmod/semantics/validation/types_validation.py +6 -7
- classiq/qmod/utilities.py +2 -2
- classiq/qmod/write_qmod.py +14 -0
- classiq/show.py +10 -0
- classiq/synthesis.py +46 -2
- {classiq-0.45.1.dist-info → classiq-0.46.1.dist-info}/METADATA +1 -1
- {classiq-0.45.1.dist-info → classiq-0.46.1.dist-info}/RECORD +138 -74
- classiq/interface/generator/functions/builtins/core_library/__init__.py +0 -16
- classiq/interface/generator/functions/builtins/core_library/atomic_quantum_functions.py +0 -710
- classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +0 -105
- classiq/interface/generator/functions/builtins/open_lib_functions.py +0 -2489
- classiq/interface/generator/functions/builtins/quantum_operators.py +0 -24
- classiq/interface/generator/types/builtin_struct_declarations/__init__.py +0 -1
- classiq/interface/generator/types/builtin_struct_declarations/pauli_struct_declarations.py +0 -21
- classiq/qmod/builtins/functions.py +0 -1029
- {classiq-0.45.1.dist-info → classiq-0.46.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,105 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from enum import IntEnum
|
3
|
+
from typing import ClassVar, Dict, Generic, List, Optional, TypeVar
|
4
|
+
|
5
|
+
from classiq.interface.generator.expressions.handle_identifier import HandleIdentifier
|
6
|
+
from classiq.interface.generator.functions.classical_type import QmodPyObject
|
7
|
+
from classiq.interface.generator.types.enum_declaration import EnumDeclaration
|
8
|
+
from classiq.interface.generator.types.qstruct_declaration import QStructDeclaration
|
9
|
+
from classiq.interface.generator.types.struct_declaration import StructDeclaration
|
10
|
+
|
11
|
+
from classiq.qmod.builtins.enums import BUILTIN_ENUM_DECLARATIONS
|
12
|
+
from classiq.qmod.builtins.structs import BUILTIN_STRUCT_DECLARATIONS
|
13
|
+
from classiq.qmod.model_state_container import QMODULE
|
14
|
+
|
15
|
+
DeclarationType = TypeVar(
|
16
|
+
"DeclarationType", EnumDeclaration, StructDeclaration, QStructDeclaration
|
17
|
+
)
|
18
|
+
|
19
|
+
|
20
|
+
class TypeTable(Generic[DeclarationType], ABC):
|
21
|
+
def __init__(self, user_types: List[DeclarationType]) -> None:
|
22
|
+
self._all_types: Dict[str, DeclarationType] = self.builtins.copy()
|
23
|
+
for t in user_types:
|
24
|
+
assert t.name not in self._all_types # FIXME: issue user error (CAD-7856)
|
25
|
+
self._all_types[t.name] = t
|
26
|
+
|
27
|
+
@property
|
28
|
+
@abstractmethod
|
29
|
+
def builtins(self) -> Dict[str, DeclarationType]:
|
30
|
+
pass
|
31
|
+
|
32
|
+
def __getitem__(self, key: str) -> DeclarationType:
|
33
|
+
return self._all_types[key]
|
34
|
+
|
35
|
+
def __setitem__(self, key: str, value: DeclarationType) -> None:
|
36
|
+
self._all_types[key] = value
|
37
|
+
|
38
|
+
def __contains__(self, key: str) -> bool:
|
39
|
+
return key in self._all_types
|
40
|
+
|
41
|
+
def all_types(self) -> Dict[str, DeclarationType]:
|
42
|
+
return self._all_types
|
43
|
+
|
44
|
+
|
45
|
+
class EnumTable(TypeTable[EnumDeclaration]):
|
46
|
+
def __init__(self, user_types: List[EnumDeclaration]) -> None:
|
47
|
+
super().__init__(user_types)
|
48
|
+
|
49
|
+
@property
|
50
|
+
def builtins(self) -> Dict[str, EnumDeclaration]:
|
51
|
+
return BUILTIN_ENUM_DECLARATIONS
|
52
|
+
|
53
|
+
@property
|
54
|
+
def enums(self) -> Dict[str, IntEnum]:
|
55
|
+
return {
|
56
|
+
enum_decl.name: enum_decl.create_enum()
|
57
|
+
for enum_decl in self.all_types().values()
|
58
|
+
}
|
59
|
+
|
60
|
+
|
61
|
+
class StructTable(TypeTable[StructDeclaration]):
|
62
|
+
@property
|
63
|
+
def builtins(self) -> Dict[str, StructDeclaration]:
|
64
|
+
return BUILTIN_STRUCT_DECLARATIONS
|
65
|
+
|
66
|
+
|
67
|
+
class QStructTable(TypeTable[QStructDeclaration]):
|
68
|
+
@property
|
69
|
+
def builtins(self) -> Dict[str, QStructDeclaration]:
|
70
|
+
return {}
|
71
|
+
|
72
|
+
|
73
|
+
class SymbolTable:
|
74
|
+
enum_table: ClassVar[EnumTable] = EnumTable([])
|
75
|
+
type_table: ClassVar[StructTable] = StructTable([])
|
76
|
+
qstruct_table: ClassVar[QStructTable] = QStructTable([])
|
77
|
+
|
78
|
+
@classmethod
|
79
|
+
def init_user_enums(cls, user_enums: List[EnumDeclaration]) -> None:
|
80
|
+
cls.enum_table = EnumTable(user_enums)
|
81
|
+
|
82
|
+
@classmethod
|
83
|
+
def init_user_types(cls, user_types: List[StructDeclaration]) -> None:
|
84
|
+
cls.type_table = StructTable(user_types)
|
85
|
+
|
86
|
+
@classmethod
|
87
|
+
def init_user_qstructs(cls, user_qstructs: List[QStructDeclaration]) -> None:
|
88
|
+
cls.qstruct_table = QStructTable(user_qstructs)
|
89
|
+
QMODULE.qstruct_decls = {
|
90
|
+
qstruct_decl.name: qstruct_decl for qstruct_decl in user_qstructs
|
91
|
+
}
|
92
|
+
|
93
|
+
|
94
|
+
class HandleTable:
|
95
|
+
_handle_map: Dict[HandleIdentifier, QmodPyObject] = {}
|
96
|
+
|
97
|
+
@classmethod
|
98
|
+
def get_handle_object(cls, hid: HandleIdentifier) -> Optional[QmodPyObject]:
|
99
|
+
return cls._handle_map.get(hid)
|
100
|
+
|
101
|
+
@classmethod
|
102
|
+
def set_handle_object(cls, qmod_object: QmodPyObject) -> HandleIdentifier:
|
103
|
+
hid = HandleIdentifier(id(qmod_object))
|
104
|
+
cls._handle_map[hid] = qmod_object
|
105
|
+
return hid
|
@@ -0,0 +1,19 @@
|
|
1
|
+
from classiq.model_expansions.quantum_operations.bind import BindEmitter
|
2
|
+
from classiq.model_expansions.quantum_operations.classicalif import ClassicalIfEmitter
|
3
|
+
from classiq.model_expansions.quantum_operations.control import ControlEmitter
|
4
|
+
from classiq.model_expansions.quantum_operations.inplace_binary_operation import (
|
5
|
+
InplaceBinaryOperationEmitter,
|
6
|
+
)
|
7
|
+
from classiq.model_expansions.quantum_operations.invert import InvertEmitter
|
8
|
+
from classiq.model_expansions.quantum_operations.power import PowerEmitter
|
9
|
+
from classiq.model_expansions.quantum_operations.quantum_assignment_operation import (
|
10
|
+
QuantumAssignmentOperationEmitter,
|
11
|
+
)
|
12
|
+
from classiq.model_expansions.quantum_operations.quantum_function_call import (
|
13
|
+
QuantumFunctionCallEmitter,
|
14
|
+
)
|
15
|
+
from classiq.model_expansions.quantum_operations.repeat import RepeatEmitter
|
16
|
+
from classiq.model_expansions.quantum_operations.variable_decleration import (
|
17
|
+
VariableDeclarationStatementEmitter,
|
18
|
+
)
|
19
|
+
from classiq.model_expansions.quantum_operations.within_apply import WithinApplyEmitter
|
@@ -0,0 +1,64 @@
|
|
1
|
+
from typing import List
|
2
|
+
|
3
|
+
from classiq.interface.exceptions import ClassiqExpansionError
|
4
|
+
from classiq.interface.model.bind_operation import BindOperation
|
5
|
+
|
6
|
+
from classiq.model_expansions.evaluators.parameter_types import (
|
7
|
+
evaluate_types_in_quantum_symbols,
|
8
|
+
)
|
9
|
+
from classiq.model_expansions.evaluators.quantum_type_utils import (
|
10
|
+
set_size,
|
11
|
+
validate_bind_targets,
|
12
|
+
)
|
13
|
+
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
14
|
+
from classiq.model_expansions.scope import QuantumSymbol
|
15
|
+
|
16
|
+
|
17
|
+
class BindEmitter(Emitter[BindOperation]):
|
18
|
+
def emit(self, bind: BindOperation, /) -> None:
|
19
|
+
with self._propagated_var_stack.capture_variables(bind):
|
20
|
+
pass
|
21
|
+
inputs: List[QuantumSymbol] = [
|
22
|
+
self._interpreter.evaluate(arg).as_type(QuantumSymbol)
|
23
|
+
for arg in bind.in_handles
|
24
|
+
]
|
25
|
+
outputs: List[QuantumSymbol] = [
|
26
|
+
self._interpreter.evaluate(arg).as_type(QuantumSymbol)
|
27
|
+
for arg in bind.out_handles
|
28
|
+
]
|
29
|
+
inputs = evaluate_types_in_quantum_symbols(inputs, self._current_scope)
|
30
|
+
outputs = evaluate_types_in_quantum_symbols(outputs, self._current_scope)
|
31
|
+
validate_bind_targets(bind, self._current_scope)
|
32
|
+
unsized_outputs = [
|
33
|
+
output for output in outputs if not output.quantum_type.has_size_in_bits
|
34
|
+
]
|
35
|
+
|
36
|
+
if len(unsized_outputs) > 1:
|
37
|
+
raise ClassiqExpansionError(
|
38
|
+
f"Cannot perform the split operation {bind.in_handles[0].name} -> {{{', '.join(out_handle.name for out_handle in bind.out_handles)}}}:\n"
|
39
|
+
f"Quantum variables {', '.join(str(out_handle.handle) for out_handle in unsized_outputs)} are used as bind outputs, but their size cannot be inferred."
|
40
|
+
)
|
41
|
+
|
42
|
+
input_size = sum(input.quantum_type.size_in_bits for input in inputs)
|
43
|
+
output_size = sum(
|
44
|
+
output.quantum_type.size_in_bits
|
45
|
+
for output in outputs
|
46
|
+
if output.quantum_type.has_size_in_bits
|
47
|
+
)
|
48
|
+
|
49
|
+
if len(unsized_outputs) == 1:
|
50
|
+
set_size(
|
51
|
+
unsized_outputs[0].quantum_type,
|
52
|
+
input_size - output_size,
|
53
|
+
str(unsized_outputs[0].handle),
|
54
|
+
)
|
55
|
+
|
56
|
+
else:
|
57
|
+
if input_size != output_size:
|
58
|
+
raise ClassiqExpansionError(
|
59
|
+
f"The total size for the input and output of the bind operation must be the same. The in size is {input_size} and the out size is {output_size}"
|
60
|
+
)
|
61
|
+
|
62
|
+
self._builder.emit_statement(
|
63
|
+
BindOperation(in_handles=bind.in_handles, out_handles=bind.out_handles)
|
64
|
+
)
|
@@ -0,0 +1,39 @@
|
|
1
|
+
from typing import List
|
2
|
+
|
3
|
+
from classiq.interface.model.classical_if import ClassicalIf
|
4
|
+
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
5
|
+
from classiq.interface.model.statement_block import ConcreteQuantumStatement
|
6
|
+
|
7
|
+
from classiq.model_expansions.closure import FunctionClosure
|
8
|
+
from classiq.model_expansions.quantum_operations.emitter import Emitter
|
9
|
+
from classiq.model_expansions.scope import Scope
|
10
|
+
|
11
|
+
|
12
|
+
def _is_all_identity_calls(body: List[ConcreteQuantumStatement]) -> bool:
|
13
|
+
return all(
|
14
|
+
isinstance(stmt, QuantumFunctionCall) and stmt.func_name.lower() == "identity"
|
15
|
+
for stmt in body
|
16
|
+
)
|
17
|
+
|
18
|
+
|
19
|
+
class ClassicalIfEmitter(Emitter[ClassicalIf]):
|
20
|
+
def emit(self, classical_if: ClassicalIf, /) -> None:
|
21
|
+
condition = self._interpreter.evaluate(classical_if.condition).as_type(bool)
|
22
|
+
body: List[ConcreteQuantumStatement] = (
|
23
|
+
classical_if.then if condition else classical_if.else_
|
24
|
+
)
|
25
|
+
if _is_all_identity_calls(body):
|
26
|
+
return
|
27
|
+
|
28
|
+
if not self._should_wrap(body):
|
29
|
+
for stmt in body:
|
30
|
+
self._interpreter.emit_statement(stmt)
|
31
|
+
return
|
32
|
+
|
33
|
+
then_else_func = FunctionClosure.create(
|
34
|
+
name="then" if condition else "else",
|
35
|
+
body=body,
|
36
|
+
scope=Scope(parent=self._current_scope),
|
37
|
+
)
|
38
|
+
with self._propagated_var_stack.capture_variables(classical_if):
|
39
|
+
self._emit_quantum_function_call(then_else_func, list())
|
@@ -0,0 +1,235 @@
|
|
1
|
+
from typing import List, Tuple
|
2
|
+
|
3
|
+
from sympy import Equality
|
4
|
+
from sympy.logic.boolalg import Boolean
|
5
|
+
from typing_extensions import TypeGuard
|
6
|
+
|
7
|
+
from classiq.interface.exceptions import (
|
8
|
+
ClassiqExpansionError,
|
9
|
+
ClassiqInternalExpansionError,
|
10
|
+
)
|
11
|
+
from classiq.interface.generator.compiler_keywords import INPLACE_ARITH_AUX_VAR_PREFIX
|
12
|
+
from classiq.interface.generator.expressions.expression import Expression
|
13
|
+
from classiq.interface.generator.expressions.expression_types import ExpressionValue
|
14
|
+
from classiq.interface.generator.expressions.qmod_qscalar_proxy import QmodQNumProxy
|
15
|
+
from classiq.interface.generator.expressions.qmod_sized_proxy import QmodSizedProxy
|
16
|
+
from classiq.interface.generator.functions.builtins.internal_operators import (
|
17
|
+
CONTROL_OPERATOR_NAME,
|
18
|
+
)
|
19
|
+
from classiq.interface.model.bind_operation import BindOperation
|
20
|
+
from classiq.interface.model.control import Control
|
21
|
+
from classiq.interface.model.handle_binding import HANDLE_ID_SEPARATOR, HandleBinding
|
22
|
+
from classiq.interface.model.quantum_expressions.arithmetic_operation import (
|
23
|
+
ArithmeticOperation,
|
24
|
+
)
|
25
|
+
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
26
|
+
from classiq.interface.model.quantum_type import QuantumBit, QuantumBitvector
|
27
|
+
from classiq.interface.model.statement_block import ConcreteQuantumStatement
|
28
|
+
from classiq.interface.model.variable_declaration_statement import (
|
29
|
+
VariableDeclarationStatement,
|
30
|
+
)
|
31
|
+
from classiq.interface.model.within_apply_operation import WithinApply
|
32
|
+
|
33
|
+
from classiq.model_expansions.capturing.propagated_var_stack import (
|
34
|
+
validate_args_are_not_propagated,
|
35
|
+
)
|
36
|
+
from classiq.model_expansions.closure import Closure
|
37
|
+
from classiq.model_expansions.evaluators.control import (
|
38
|
+
resolve_num_condition,
|
39
|
+
type_name,
|
40
|
+
)
|
41
|
+
from classiq.model_expansions.quantum_operations.expression_operation import (
|
42
|
+
ExpressionOperationEmitter,
|
43
|
+
)
|
44
|
+
from classiq.model_expansions.scope import Scope
|
45
|
+
from classiq.qmod.builtins.functions import inplace_prepare_int
|
46
|
+
|
47
|
+
ARRAY_CAST_SUFFIX = HANDLE_ID_SEPARATOR + "array_cast"
|
48
|
+
|
49
|
+
|
50
|
+
class ControlEmitter(ExpressionOperationEmitter[Control]):
|
51
|
+
def emit(self, control: Control, /) -> None:
|
52
|
+
condition = self._evaluate_op_expression(control)
|
53
|
+
control = control.copy(update=dict(expression=condition))
|
54
|
+
|
55
|
+
arrays_with_subscript = self._get_symbols_to_split(condition)
|
56
|
+
if len(arrays_with_subscript) > 0:
|
57
|
+
self._emit_with_split(control, condition, arrays_with_subscript)
|
58
|
+
return
|
59
|
+
|
60
|
+
condition_val = condition.value.value
|
61
|
+
if isinstance(condition_val, QmodSizedProxy):
|
62
|
+
self._validate_canonical_condition(condition_val)
|
63
|
+
self._emit_canonical_control(control)
|
64
|
+
return
|
65
|
+
|
66
|
+
self._validate_condition(condition_val)
|
67
|
+
self._emit_with_boolean(control)
|
68
|
+
|
69
|
+
def _emit_canonical_control(self, control: Control) -> None:
|
70
|
+
# canonical means control(q, body) where q is a single quantum variable
|
71
|
+
control = self._evaluate_types_in_expression(control, control.expression)
|
72
|
+
if self._should_wrap_control(control):
|
73
|
+
self._emit_wrapped(control)
|
74
|
+
return
|
75
|
+
self._emit_as_operation(control)
|
76
|
+
|
77
|
+
def _should_wrap_control(self, control: Control) -> bool:
|
78
|
+
# TODO we can return back to the general case (as in _should_wrap function)
|
79
|
+
# once we implement the "smart control" pass to blocks:
|
80
|
+
# Control(q, body) -> WithinApply(
|
81
|
+
# compute=aux:=QBit(), allocate(1, aux), Control(q, [X(aux)]),
|
82
|
+
# action=Control(aux, body)
|
83
|
+
# )
|
84
|
+
# We also need to be able to nest multiple Control statements to a single one
|
85
|
+
return len(control.body) > 1
|
86
|
+
|
87
|
+
def _emit_as_operation(self, control: Control) -> None:
|
88
|
+
control_operation = Closure(
|
89
|
+
name=CONTROL_OPERATOR_NAME,
|
90
|
+
blocks=dict(body=control.body),
|
91
|
+
scope=Scope(parent=self._current_scope),
|
92
|
+
)
|
93
|
+
with self._propagated_var_stack.capture_variables(control):
|
94
|
+
context = self._expand_operation(control_operation)
|
95
|
+
validate_args_are_not_propagated(
|
96
|
+
control.var_handles,
|
97
|
+
self._propagated_var_stack.get_propagated_variables(),
|
98
|
+
)
|
99
|
+
self._update_control_state(control)
|
100
|
+
self._builder.emit_statement(
|
101
|
+
control.copy(update=dict(body=context.statements("body")))
|
102
|
+
)
|
103
|
+
|
104
|
+
def _emit_wrapped(self, control: Control) -> None:
|
105
|
+
with self._propagated_var_stack.capture_variables(control):
|
106
|
+
wrapping_function = self._create_expanded_wrapping_function(
|
107
|
+
CONTROL_OPERATOR_NAME, control.body
|
108
|
+
)
|
109
|
+
validate_args_are_not_propagated(
|
110
|
+
control.var_handles,
|
111
|
+
self._propagated_var_stack.get_propagated_variables(),
|
112
|
+
)
|
113
|
+
self._update_control_state(control)
|
114
|
+
self._builder.emit_statement(
|
115
|
+
control.copy(update=dict(body=[wrapping_function]))
|
116
|
+
)
|
117
|
+
|
118
|
+
@staticmethod
|
119
|
+
def _update_control_state(control: Control) -> None:
|
120
|
+
condition_val = control.expression.value.value
|
121
|
+
if not isinstance(condition_val, QmodSizedProxy):
|
122
|
+
raise ClassiqInternalExpansionError("Control is not in canonical form")
|
123
|
+
control.set_ctrl_size(condition_val.size)
|
124
|
+
|
125
|
+
def _emit_with_boolean(self, control: Control) -> None:
|
126
|
+
condition_val = control.expression.value.value
|
127
|
+
if self._is_simple_equality(condition_val):
|
128
|
+
ctrl, ctrl_state = resolve_num_condition(condition_val)
|
129
|
+
self._emit_with_x_gates(control, ctrl, ctrl_state)
|
130
|
+
else:
|
131
|
+
self._emit_with_arithmetic(control)
|
132
|
+
|
133
|
+
@staticmethod
|
134
|
+
def _is_simple_equality(condition_val: ExpressionValue) -> TypeGuard[Equality]:
|
135
|
+
# Note that we don't support equalities with non-integer values yet
|
136
|
+
return isinstance(condition_val, Equality) and (
|
137
|
+
(
|
138
|
+
condition_val.args[0].is_Atom
|
139
|
+
and not isinstance(condition_val.args[0], QmodSizedProxy)
|
140
|
+
and isinstance(condition_val.args[1], QmodSizedProxy)
|
141
|
+
)
|
142
|
+
or (
|
143
|
+
condition_val.args[1].is_Atom
|
144
|
+
and not isinstance(condition_val.args[1], QmodSizedProxy)
|
145
|
+
and isinstance(condition_val.args[0], QmodSizedProxy)
|
146
|
+
)
|
147
|
+
)
|
148
|
+
|
149
|
+
def _create_canonical_control_op(
|
150
|
+
self, control: Control, handle_name: str
|
151
|
+
) -> Control:
|
152
|
+
handle_expr = self._interpreter.evaluate(Expression(expr=handle_name)).emit()
|
153
|
+
return control.copy(update=dict(expression=handle_expr))
|
154
|
+
|
155
|
+
def _emit_with_x_gates(
|
156
|
+
self, control: Control, ctrl: QmodSizedProxy, ctrl_state: str
|
157
|
+
) -> None:
|
158
|
+
compute_op: List[ConcreteQuantumStatement] = []
|
159
|
+
|
160
|
+
x_gate_value = self._get_x_gate_value(ctrl_state)
|
161
|
+
if x_gate_value != 0:
|
162
|
+
prepare_int_call = QuantumFunctionCall(
|
163
|
+
function=inplace_prepare_int.func_decl.name,
|
164
|
+
positional_args=[Expression(expr=str(x_gate_value)), ctrl.handle],
|
165
|
+
)
|
166
|
+
prepare_int_call.set_func_decl(inplace_prepare_int.func_decl)
|
167
|
+
compute_op.append(prepare_int_call)
|
168
|
+
|
169
|
+
if isinstance(ctrl, QmodQNumProxy):
|
170
|
+
# Canonical control does not accept QNum, so we have to cast it
|
171
|
+
cast_decl, bind_op = self._get_array_cast_ops(ctrl)
|
172
|
+
self._interpreter.emit_statement(cast_decl)
|
173
|
+
compute_op.append(bind_op)
|
174
|
+
control_op = self._create_canonical_control_op(control, str(cast_decl.name))
|
175
|
+
else:
|
176
|
+
control_op = self._create_canonical_control_op(control, str(ctrl.handle))
|
177
|
+
|
178
|
+
self._interpreter.emit_statement(
|
179
|
+
WithinApply(compute=compute_op, action=[control_op])
|
180
|
+
)
|
181
|
+
|
182
|
+
@staticmethod
|
183
|
+
def _get_x_gate_value(ctrl_state: str) -> int:
|
184
|
+
x_gate_value = 0
|
185
|
+
for idx, bit in enumerate(ctrl_state):
|
186
|
+
x_gate_value += int(bit == "0") << idx
|
187
|
+
return x_gate_value
|
188
|
+
|
189
|
+
def _get_array_cast_ops(
|
190
|
+
self, ctrl: QmodQNumProxy
|
191
|
+
) -> Tuple[VariableDeclarationStatement, BindOperation]:
|
192
|
+
array_cast = self._counted_name_allocator.allocate(
|
193
|
+
f"{ctrl.handle}{ARRAY_CAST_SUFFIX}"
|
194
|
+
)
|
195
|
+
cast_decl = VariableDeclarationStatement(
|
196
|
+
name=array_cast, quantum_type=QuantumBitvector()
|
197
|
+
)
|
198
|
+
bind_op = BindOperation(
|
199
|
+
in_handles=[ctrl.handle], out_handles=[HandleBinding(name=array_cast)]
|
200
|
+
)
|
201
|
+
return cast_decl, bind_op
|
202
|
+
|
203
|
+
def _emit_with_arithmetic(self, control: Control) -> None:
|
204
|
+
aux_var = self._counted_name_allocator.allocate(INPLACE_ARITH_AUX_VAR_PREFIX)
|
205
|
+
self._interpreter.emit_statement(
|
206
|
+
VariableDeclarationStatement(name=aux_var, quantum_type=QuantumBit())
|
207
|
+
)
|
208
|
+
arith_expression = ArithmeticOperation(
|
209
|
+
result_var=HandleBinding(name=aux_var),
|
210
|
+
expression=control.expression,
|
211
|
+
inplace_result=False,
|
212
|
+
)
|
213
|
+
self._interpreter.emit_statement(
|
214
|
+
WithinApply(
|
215
|
+
compute=[arith_expression],
|
216
|
+
action=[self._create_canonical_control_op(control, aux_var)],
|
217
|
+
)
|
218
|
+
)
|
219
|
+
|
220
|
+
@staticmethod
|
221
|
+
def _validate_condition(condition_val: ExpressionValue) -> None:
|
222
|
+
if not isinstance(condition_val, Boolean):
|
223
|
+
raise ClassiqExpansionError(_condition_err_msg(condition_val))
|
224
|
+
|
225
|
+
@staticmethod
|
226
|
+
def _validate_canonical_condition(condition_val: ExpressionValue) -> None:
|
227
|
+
if isinstance(condition_val, QmodQNumProxy):
|
228
|
+
raise ClassiqExpansionError(_condition_err_msg(condition_val))
|
229
|
+
|
230
|
+
|
231
|
+
def _condition_err_msg(condition_val: ExpressionValue) -> str:
|
232
|
+
return (
|
233
|
+
f"Control condition {str(condition_val)!r} must be a qubit, an array of "
|
234
|
+
f"qubits, or a boolean expression, but is {type_name(condition_val)}"
|
235
|
+
)
|
@@ -0,0 +1,215 @@
|
|
1
|
+
from abc import abstractmethod
|
2
|
+
from typing import TYPE_CHECKING, Generic, List, Sequence, TypeVar, cast
|
3
|
+
|
4
|
+
from classiq.interface.debug_info.debug_info import FunctionDebugInfo
|
5
|
+
from classiq.interface.ide.visual_model import OperationLevel
|
6
|
+
from classiq.interface.model.classical_parameter_declaration import (
|
7
|
+
ClassicalParameterDeclaration,
|
8
|
+
)
|
9
|
+
from classiq.interface.model.handle_binding import HandleBinding
|
10
|
+
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
11
|
+
from classiq.interface.model.port_declaration import PortDeclaration
|
12
|
+
from classiq.interface.model.quantum_function_call import ArgValue, QuantumFunctionCall
|
13
|
+
from classiq.interface.model.quantum_function_declaration import (
|
14
|
+
NamedParamsQuantumFunctionDeclaration,
|
15
|
+
PositionalArg,
|
16
|
+
)
|
17
|
+
from classiq.interface.model.quantum_statement import QuantumStatement
|
18
|
+
from classiq.interface.model.variable_declaration_statement import (
|
19
|
+
VariableDeclarationStatement,
|
20
|
+
)
|
21
|
+
|
22
|
+
from classiq.model_expansions.capturing.propagated_var_stack import (
|
23
|
+
PropagatedVarStack,
|
24
|
+
validate_args_are_not_propagated,
|
25
|
+
)
|
26
|
+
from classiq.model_expansions.closure import FunctionClosure
|
27
|
+
from classiq.model_expansions.evaluators.argument_types import (
|
28
|
+
add_information_from_output_arguments,
|
29
|
+
)
|
30
|
+
from classiq.model_expansions.evaluators.parameter_types import (
|
31
|
+
evaluate_parameter_types_from_args,
|
32
|
+
)
|
33
|
+
from classiq.model_expansions.function_builder import (
|
34
|
+
FunctionContext,
|
35
|
+
OperationBuilder,
|
36
|
+
)
|
37
|
+
from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
|
38
|
+
from classiq.model_expansions.utils.counted_name_allocator import CountedNameAllocator
|
39
|
+
from classiq.qmod.builtins.functions import allocate, free
|
40
|
+
|
41
|
+
if TYPE_CHECKING:
|
42
|
+
from classiq.model_expansions.interpreter import Interpreter
|
43
|
+
|
44
|
+
|
45
|
+
QuantumStatementT = TypeVar("QuantumStatementT", bound=QuantumStatement)
|
46
|
+
|
47
|
+
|
48
|
+
class Emitter(Generic[QuantumStatementT]):
|
49
|
+
def __init__(self, interpreter: "Interpreter") -> None:
|
50
|
+
self._interpreter = interpreter
|
51
|
+
|
52
|
+
self._scope_guard = self._interpreter._scope_guard
|
53
|
+
self._expand_operation = self._interpreter._expand_operation
|
54
|
+
self._machine_precision = self._interpreter._model.preferences.machine_precision
|
55
|
+
|
56
|
+
@abstractmethod
|
57
|
+
def emit(self, statement: QuantumStatementT, /) -> None:
|
58
|
+
pass
|
59
|
+
|
60
|
+
@property
|
61
|
+
def _propagated_var_stack(self) -> PropagatedVarStack:
|
62
|
+
return self._interpreter._propagated_var_stack
|
63
|
+
|
64
|
+
@property
|
65
|
+
def _builder(self) -> OperationBuilder:
|
66
|
+
return self._interpreter._builder
|
67
|
+
|
68
|
+
@property
|
69
|
+
def _current_scope(self) -> Scope:
|
70
|
+
return self._interpreter._current_scope
|
71
|
+
|
72
|
+
@property
|
73
|
+
def _expanded_functions(self) -> List[NativeFunctionDefinition]:
|
74
|
+
return self._interpreter._expanded_functions
|
75
|
+
|
76
|
+
@property
|
77
|
+
def _counted_name_allocator(self) -> CountedNameAllocator:
|
78
|
+
return self._interpreter._counted_name_allocator
|
79
|
+
|
80
|
+
@staticmethod
|
81
|
+
def _should_wrap(body: Sequence[QuantumStatement]) -> bool:
|
82
|
+
# This protects shadowing of captured variables (i.e, bad user code) by wrapping the body in a function
|
83
|
+
# I'm sure there are better ways to handle it, but this is the simplest way to do it for now
|
84
|
+
return any(isinstance(stmt, VariableDeclarationStatement) for stmt in body)
|
85
|
+
|
86
|
+
def _create_expanded_wrapping_function(
|
87
|
+
self, name: str, body: Sequence[QuantumStatement]
|
88
|
+
) -> QuantumFunctionCall:
|
89
|
+
wrapping_function = FunctionClosure.create(
|
90
|
+
name=name, body=body, scope=Scope(parent=self._current_scope)
|
91
|
+
)
|
92
|
+
return self._create_quantum_function_call(wrapping_function, list())
|
93
|
+
|
94
|
+
def _emit_quantum_function_call(
|
95
|
+
self, function: FunctionClosure, args: List[ArgValue]
|
96
|
+
) -> QuantumFunctionCall:
|
97
|
+
call = self._create_quantum_function_call(function, args)
|
98
|
+
self._builder.emit_statement(call)
|
99
|
+
return call
|
100
|
+
|
101
|
+
def _create_quantum_function_call(
|
102
|
+
self, function: FunctionClosure, args: List[ArgValue]
|
103
|
+
) -> QuantumFunctionCall:
|
104
|
+
evaluated_args = [self._interpreter.evaluate(arg) for arg in args]
|
105
|
+
new_declaration = self._prepare_fully_typed_declaration(
|
106
|
+
function, evaluated_args
|
107
|
+
)
|
108
|
+
new_positional_arg_decls = new_declaration.positional_arg_declarations
|
109
|
+
is_atomic = function.is_atomic
|
110
|
+
if not is_atomic: # perform monomorphization
|
111
|
+
self._add_params_to_scope(
|
112
|
+
new_positional_arg_decls, evaluated_args, function
|
113
|
+
)
|
114
|
+
context = self._expand_operation(
|
115
|
+
function.with_new_declaration(new_declaration)
|
116
|
+
)
|
117
|
+
self._expanded_functions.append(
|
118
|
+
self._builder.create_definition(cast(FunctionContext, context))
|
119
|
+
)
|
120
|
+
new_positional_args = self._get_new_positional_args(
|
121
|
+
evaluated_args, is_atomic, new_positional_arg_decls
|
122
|
+
)
|
123
|
+
new_call = QuantumFunctionCall(
|
124
|
+
function=(
|
125
|
+
new_declaration.name if is_atomic else self._expanded_functions[-1].name
|
126
|
+
),
|
127
|
+
positional_args=new_positional_args,
|
128
|
+
)
|
129
|
+
is_allocate_or_free = (
|
130
|
+
new_call.func_name == allocate.func_decl.name
|
131
|
+
or new_call.func_name == free.func_decl.name
|
132
|
+
)
|
133
|
+
parameters = {
|
134
|
+
arg_decl.name: FunctionDebugInfo.param_controller(value=valuated_arg.value)
|
135
|
+
for arg_decl, valuated_arg in zip(new_positional_arg_decls, evaluated_args)
|
136
|
+
if isinstance(arg_decl, ClassicalParameterDeclaration)
|
137
|
+
}
|
138
|
+
|
139
|
+
self._interpreter._model.debug_info[new_call.uuid] = FunctionDebugInfo(
|
140
|
+
name=new_call.func_name,
|
141
|
+
level=OperationLevel.QMOD_FUNCTION_CALL,
|
142
|
+
parameters=parameters,
|
143
|
+
is_allocate_or_free=is_allocate_or_free,
|
144
|
+
)
|
145
|
+
if is_atomic:
|
146
|
+
new_call.set_func_decl(new_declaration)
|
147
|
+
return new_call
|
148
|
+
|
149
|
+
@staticmethod
|
150
|
+
def _add_params_to_scope(
|
151
|
+
parameters: Sequence[PositionalArg],
|
152
|
+
arguments: Sequence[Evaluated],
|
153
|
+
closure: FunctionClosure,
|
154
|
+
) -> None:
|
155
|
+
for parameter, argument in zip(parameters, arguments):
|
156
|
+
if isinstance(argument.value, QuantumSymbol):
|
157
|
+
assert isinstance(parameter, PortDeclaration)
|
158
|
+
closure.scope[parameter.name] = Evaluated(
|
159
|
+
QuantumSymbol(
|
160
|
+
handle=HandleBinding(name=parameter.name),
|
161
|
+
quantum_type=parameter.quantum_type,
|
162
|
+
),
|
163
|
+
defining_function=closure,
|
164
|
+
)
|
165
|
+
else:
|
166
|
+
closure.scope[parameter.name] = argument
|
167
|
+
|
168
|
+
def _get_new_positional_args(
|
169
|
+
self,
|
170
|
+
evaluated_args: List[Evaluated],
|
171
|
+
is_atomic: bool,
|
172
|
+
new_positional_arg_decls: Sequence[PositionalArg],
|
173
|
+
) -> List[ArgValue]:
|
174
|
+
evaluated_args = add_information_from_output_arguments(
|
175
|
+
new_positional_arg_decls, evaluated_args
|
176
|
+
)
|
177
|
+
if is_atomic:
|
178
|
+
return [arg.emit() for arg in evaluated_args]
|
179
|
+
|
180
|
+
positional_args = [
|
181
|
+
arg.emit() for arg in evaluated_args if isinstance(arg.value, QuantumSymbol)
|
182
|
+
]
|
183
|
+
|
184
|
+
propagated_variables = self._propagated_var_stack.get_propagated_variables()
|
185
|
+
validate_args_are_not_propagated(positional_args, propagated_variables)
|
186
|
+
positional_args.extend(propagated_variables)
|
187
|
+
|
188
|
+
return positional_args
|
189
|
+
|
190
|
+
def _prepare_fully_typed_declaration(
|
191
|
+
self, function: FunctionClosure, evaluated_args: List[Evaluated]
|
192
|
+
) -> NamedParamsQuantumFunctionDeclaration:
|
193
|
+
"""
|
194
|
+
Given, for example,
|
195
|
+
def my_func(x: int, q: QArray["x"], p: QArray[]) -> None:
|
196
|
+
...
|
197
|
+
def main(...):
|
198
|
+
...
|
199
|
+
allocate(5, s)
|
200
|
+
my_func(3, r, s)
|
201
|
+
The code below will evaluate x to be 3, q to be of size 3 and p to be of size 5.
|
202
|
+
Note that it requires a scope for the parameter declaration space, which is
|
203
|
+
different from the call scope. For example, the former uses r,s and the latter
|
204
|
+
uses p, q.
|
205
|
+
"""
|
206
|
+
with self._scope_guard(Scope(parent=self._current_scope)):
|
207
|
+
# The signature scope is passed as a separate argument to avoid contaminating the statement execution scope
|
208
|
+
return NamedParamsQuantumFunctionDeclaration(
|
209
|
+
name=function.name,
|
210
|
+
positional_arg_declarations=evaluate_parameter_types_from_args(
|
211
|
+
function,
|
212
|
+
function.signature_scope,
|
213
|
+
evaluated_args,
|
214
|
+
),
|
215
|
+
)
|