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.
Files changed (146) hide show
  1. classiq/__init__.py +0 -1
  2. classiq/_internals/__init__.py +20 -0
  3. classiq/_internals/authentication/authentication.py +11 -0
  4. classiq/analyzer/analyzer.py +12 -10
  5. classiq/applications/combinatorial_helpers/combinatorial_problem_utils.py +1 -1
  6. classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +1 -1
  7. classiq/applications/combinatorial_optimization/combinatorial_optimization_model_constructor.py +1 -1
  8. classiq/applications/libraries/qmci_library.py +4 -9
  9. classiq/execution/execution_session.py +68 -7
  10. classiq/executor.py +14 -2
  11. classiq/interface/_version.py +1 -1
  12. classiq/interface/backend/backend_preferences.py +189 -0
  13. classiq/interface/backend/quantum_backend_providers.py +38 -0
  14. classiq/interface/debug_info/debug_info.py +22 -2
  15. classiq/interface/exceptions.py +16 -1
  16. classiq/interface/executor/execution_preferences.py +18 -0
  17. classiq/interface/generator/application_apis/chemistry_declarations.py +1 -177
  18. classiq/interface/generator/application_apis/combinatorial_optimization_declarations.py +0 -12
  19. classiq/interface/generator/application_apis/finance_declarations.py +8 -43
  20. classiq/interface/generator/application_apis/qsvm_declarations.py +0 -78
  21. classiq/interface/generator/builtin_api_builder.py +0 -3
  22. classiq/interface/generator/functions/__init__.py +0 -2
  23. classiq/interface/generator/functions/builtins/__init__.py +0 -15
  24. classiq/interface/generator/generated_circuit_data.py +2 -0
  25. classiq/interface/generator/hardware/hardware_data.py +37 -0
  26. classiq/interface/generator/model/constraints.py +18 -1
  27. classiq/interface/generator/model/preferences/preferences.py +53 -1
  28. classiq/interface/generator/model/quantum_register.py +1 -1
  29. classiq/interface/generator/quantum_program.py +10 -2
  30. classiq/interface/generator/transpiler_basis_gates.py +4 -0
  31. classiq/interface/generator/types/builtin_enum_declarations.py +136 -21
  32. classiq/interface/generator/types/enum_declaration.py +1 -3
  33. classiq/interface/generator/types/struct_declaration.py +1 -3
  34. classiq/interface/hardware.py +5 -0
  35. classiq/interface/ide/visual_model.py +1 -1
  36. classiq/interface/model/classical_parameter_declaration.py +6 -0
  37. classiq/interface/model/inplace_binary_operation.py +0 -14
  38. classiq/interface/model/model.py +1 -18
  39. classiq/interface/model/port_declaration.py +4 -2
  40. classiq/interface/model/quantum_function_declaration.py +19 -6
  41. classiq/interface/model/quantum_lambda_function.py +11 -1
  42. classiq/interface/model/quantum_variable_declaration.py +1 -1
  43. classiq/model_expansions/__init__.py +0 -0
  44. classiq/model_expansions/atomic_expression_functions_defs.py +250 -0
  45. classiq/model_expansions/capturing/__init__.py +0 -0
  46. classiq/model_expansions/capturing/captured_var_manager.py +50 -0
  47. classiq/model_expansions/capturing/mangling_utils.py +17 -0
  48. classiq/model_expansions/capturing/propagated_var_stack.py +180 -0
  49. classiq/model_expansions/closure.py +160 -0
  50. classiq/model_expansions/debug_flag.py +3 -0
  51. classiq/model_expansions/evaluators/__init__.py +0 -0
  52. classiq/model_expansions/evaluators/arg_type_match.py +160 -0
  53. classiq/model_expansions/evaluators/argument_types.py +42 -0
  54. classiq/model_expansions/evaluators/classical_expression.py +36 -0
  55. classiq/model_expansions/evaluators/control.py +144 -0
  56. classiq/model_expansions/evaluators/parameter_types.py +227 -0
  57. classiq/model_expansions/evaluators/quantum_type_utils.py +235 -0
  58. classiq/model_expansions/evaluators/type_type_match.py +90 -0
  59. classiq/model_expansions/expression_evaluator.py +125 -0
  60. classiq/model_expansions/expression_renamer.py +76 -0
  61. classiq/model_expansions/function_builder.py +192 -0
  62. classiq/model_expansions/generative_functions.py +101 -0
  63. classiq/model_expansions/interpreter.py +365 -0
  64. classiq/model_expansions/model_tables.py +105 -0
  65. classiq/model_expansions/quantum_operations/__init__.py +19 -0
  66. classiq/model_expansions/quantum_operations/bind.py +64 -0
  67. classiq/model_expansions/quantum_operations/classicalif.py +39 -0
  68. classiq/model_expansions/quantum_operations/control.py +235 -0
  69. classiq/model_expansions/quantum_operations/emitter.py +215 -0
  70. classiq/model_expansions/quantum_operations/expression_operation.py +218 -0
  71. classiq/model_expansions/quantum_operations/inplace_binary_operation.py +250 -0
  72. classiq/model_expansions/quantum_operations/invert.py +38 -0
  73. classiq/model_expansions/quantum_operations/power.py +74 -0
  74. classiq/model_expansions/quantum_operations/quantum_assignment_operation.py +174 -0
  75. classiq/model_expansions/quantum_operations/quantum_function_call.py +15 -0
  76. classiq/model_expansions/quantum_operations/repeat.py +33 -0
  77. classiq/model_expansions/quantum_operations/variable_decleration.py +28 -0
  78. classiq/model_expansions/quantum_operations/within_apply.py +46 -0
  79. classiq/model_expansions/scope.py +226 -0
  80. classiq/model_expansions/scope_initialization.py +136 -0
  81. classiq/model_expansions/sympy_conversion/__init__.py +0 -0
  82. classiq/model_expansions/sympy_conversion/arithmetics.py +49 -0
  83. classiq/model_expansions/sympy_conversion/expression_to_sympy.py +150 -0
  84. classiq/model_expansions/sympy_conversion/sympy_to_python.py +113 -0
  85. classiq/model_expansions/utils/__init__.py +0 -0
  86. classiq/model_expansions/utils/counted_name_allocator.py +11 -0
  87. classiq/model_expansions/visitors/__init__.py +0 -0
  88. classiq/model_expansions/visitors/boolean_expression_transformers.py +214 -0
  89. classiq/model_expansions/visitors/variable_references.py +115 -0
  90. classiq/qmod/__init__.py +1 -3
  91. classiq/qmod/builtins/enums.py +33 -2
  92. classiq/qmod/builtins/functions/__init__.py +251 -0
  93. classiq/qmod/builtins/functions/amplitude_estimation.py +27 -0
  94. classiq/qmod/builtins/functions/arithmetic.py +68 -0
  95. classiq/qmod/builtins/functions/benchmarking.py +8 -0
  96. classiq/qmod/builtins/functions/chemistry.py +91 -0
  97. classiq/qmod/builtins/functions/discrete_sine_cosine_transform.py +105 -0
  98. classiq/qmod/builtins/functions/exponentiation.py +111 -0
  99. classiq/qmod/builtins/functions/finance.py +34 -0
  100. classiq/qmod/builtins/functions/grover.py +178 -0
  101. classiq/qmod/builtins/functions/hea.py +59 -0
  102. classiq/qmod/builtins/functions/linear_pauli_rotation.py +65 -0
  103. classiq/qmod/builtins/functions/modular_exponentiation.py +137 -0
  104. classiq/qmod/builtins/functions/operators.py +22 -0
  105. classiq/qmod/builtins/functions/qaoa_penalty.py +116 -0
  106. classiq/qmod/builtins/functions/qft.py +23 -0
  107. classiq/qmod/builtins/functions/qpe.py +39 -0
  108. classiq/qmod/builtins/functions/qsvm.py +24 -0
  109. classiq/qmod/builtins/functions/qsvt.py +136 -0
  110. classiq/qmod/builtins/functions/standard_gates.py +739 -0
  111. classiq/qmod/builtins/functions/state_preparation.py +357 -0
  112. classiq/qmod/builtins/functions/swap_test.py +25 -0
  113. classiq/qmod/builtins/structs.py +50 -28
  114. classiq/qmod/cparam.py +64 -0
  115. classiq/qmod/create_model_function.py +190 -0
  116. classiq/qmod/declaration_inferrer.py +52 -81
  117. classiq/qmod/expression_query.py +16 -0
  118. classiq/qmod/generative.py +48 -0
  119. classiq/qmod/model_state_container.py +1 -2
  120. classiq/qmod/native/pretty_printer.py +7 -11
  121. classiq/qmod/pretty_print/pretty_printer.py +7 -11
  122. classiq/qmod/python_classical_type.py +67 -0
  123. classiq/qmod/qfunc.py +19 -4
  124. classiq/qmod/qmod_parameter.py +15 -64
  125. classiq/qmod/qmod_variable.py +27 -45
  126. classiq/qmod/quantum_callable.py +1 -1
  127. classiq/qmod/quantum_expandable.py +10 -4
  128. classiq/qmod/quantum_function.py +22 -40
  129. classiq/qmod/semantics/error_manager.py +22 -10
  130. classiq/qmod/semantics/static_semantics_visitor.py +10 -12
  131. classiq/qmod/semantics/validation/types_validation.py +6 -7
  132. classiq/qmod/utilities.py +2 -2
  133. classiq/qmod/write_qmod.py +14 -0
  134. classiq/show.py +10 -0
  135. classiq/synthesis.py +46 -2
  136. {classiq-0.45.1.dist-info → classiq-0.46.1.dist-info}/METADATA +1 -1
  137. {classiq-0.45.1.dist-info → classiq-0.46.1.dist-info}/RECORD +138 -74
  138. classiq/interface/generator/functions/builtins/core_library/__init__.py +0 -16
  139. classiq/interface/generator/functions/builtins/core_library/atomic_quantum_functions.py +0 -710
  140. classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +0 -105
  141. classiq/interface/generator/functions/builtins/open_lib_functions.py +0 -2489
  142. classiq/interface/generator/functions/builtins/quantum_operators.py +0 -24
  143. classiq/interface/generator/types/builtin_struct_declarations/__init__.py +0 -1
  144. classiq/interface/generator/types/builtin_struct_declarations/pauli_struct_declarations.py +0 -21
  145. classiq/qmod/builtins/functions.py +0 -1029
  146. {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
+ )