classiq 0.38.0__py3-none-any.whl → 0.40.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. classiq/__init__.py +22 -22
  2. classiq/_internals/api_wrapper.py +13 -1
  3. classiq/_internals/client.py +12 -2
  4. classiq/analyzer/analyzer.py +3 -1
  5. classiq/applications/__init__.py +1 -8
  6. classiq/applications/chemistry/__init__.py +6 -0
  7. classiq/{applications_model_constructors → applications/chemistry}/chemistry_model_constructor.py +1 -1
  8. classiq/{applications_model_constructors → applications}/combinatorial_helpers/arithmetic/arithmetic_expression.py +1 -1
  9. classiq/{applications_model_constructors → applications}/combinatorial_helpers/combinatorial_problem_utils.py +25 -6
  10. classiq/{applications_model_constructors → applications}/combinatorial_helpers/encoding_mapping.py +1 -1
  11. classiq/{applications_model_constructors → applications}/combinatorial_helpers/encoding_utils.py +1 -1
  12. classiq/{applications_model_constructors → applications}/combinatorial_helpers/memory.py +2 -4
  13. classiq/{applications_model_constructors → applications}/combinatorial_helpers/optimization_model.py +13 -16
  14. classiq/{applications_model_constructors → applications}/combinatorial_helpers/pyomo_utils.py +4 -2
  15. classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/encoding.py +3 -10
  16. classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/fixed_variables.py +4 -6
  17. classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/ising_converter.py +3 -5
  18. classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/penalty_support.py +3 -7
  19. classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/slack_variables.py +4 -6
  20. classiq/applications/combinatorial_optimization/__init__.py +11 -3
  21. classiq/{applications_model_constructors → applications/combinatorial_optimization}/combinatorial_optimization_model_constructor.py +9 -10
  22. classiq/applications/finance/__init__.py +3 -2
  23. classiq/{applications_model_constructors → applications/finance}/finance_model_constructor.py +24 -14
  24. classiq/applications/grover/__init__.py +11 -0
  25. classiq/applications/libraries/qmci_library.py +35 -0
  26. classiq/applications/qsvm/__init__.py +5 -1
  27. classiq/execution/all_hardware_devices.py +13 -0
  28. classiq/executor.py +2 -1
  29. classiq/interface/_version.py +1 -1
  30. classiq/interface/analyzer/result.py +1 -5
  31. classiq/interface/applications/qsvm.py +4 -2
  32. classiq/interface/ast_node.py +23 -0
  33. classiq/interface/backend/backend_preferences.py +5 -5
  34. classiq/interface/backend/quantum_backend_providers.py +7 -7
  35. classiq/interface/combinatorial_optimization/examples/mht.py +8 -3
  36. classiq/interface/executor/execution_preferences.py +4 -9
  37. classiq/interface/executor/execution_request.py +2 -37
  38. classiq/interface/executor/vqe_result.py +1 -1
  39. classiq/interface/generator/application_apis/chemistry_declarations.py +2 -4
  40. classiq/interface/generator/application_apis/finance_declarations.py +1 -1
  41. classiq/interface/generator/arith/arithmetic_expression_validator.py +2 -0
  42. classiq/interface/generator/builtin_api_builder.py +0 -5
  43. classiq/interface/generator/constant.py +2 -3
  44. classiq/interface/generator/expressions/expression.py +2 -4
  45. classiq/interface/generator/expressions/qmod_qarray_proxy.py +82 -0
  46. classiq/interface/generator/expressions/qmod_qscalar_proxy.py +22 -1
  47. classiq/interface/generator/expressions/qmod_sized_proxy.py +22 -0
  48. classiq/interface/generator/functions/__init__.py +2 -2
  49. classiq/interface/generator/functions/builtins/__init__.py +15 -0
  50. classiq/interface/generator/functions/builtins/core_library/__init__.py +14 -0
  51. classiq/interface/generator/functions/{core_lib_declarations/quantum_functions → builtins/core_library}/atomic_quantum_functions.py +8 -6
  52. classiq/interface/generator/functions/{core_lib_declarations/quantum_functions → builtins/core_library}/exponentiation_functions.py +10 -4
  53. classiq/interface/generator/functions/builtins/internal_operators.py +62 -0
  54. classiq/interface/generator/functions/{core_lib_declarations/quantum_functions/std_lib_functions.py → builtins/open_lib_functions.py} +893 -319
  55. classiq/interface/generator/functions/builtins/quantum_operators.py +37 -0
  56. classiq/interface/generator/functions/classical_type.py +31 -21
  57. classiq/interface/generator/functions/function_declaration.py +2 -2
  58. classiq/interface/generator/hartree_fock.py +10 -2
  59. classiq/interface/generator/model/classical_main_validator.py +1 -1
  60. classiq/interface/generator/model/model.py +1 -1
  61. classiq/interface/generator/model/preferences/preferences.py +4 -2
  62. classiq/interface/generator/quantum_function_call.py +1 -1
  63. classiq/interface/generator/types/struct_declaration.py +2 -4
  64. classiq/interface/model/call_synthesis_data.py +3 -3
  65. classiq/interface/model/classical_if.py +13 -0
  66. classiq/interface/model/classical_parameter_declaration.py +2 -3
  67. classiq/interface/model/{quantum_if_operation.py → control.py} +39 -21
  68. classiq/interface/model/handle_binding.py +3 -2
  69. classiq/interface/model/invert.py +10 -0
  70. classiq/interface/model/model.py +2 -1
  71. classiq/interface/model/power.py +12 -0
  72. classiq/interface/model/quantum_function_call.py +9 -4
  73. classiq/interface/model/quantum_lambda_function.py +3 -9
  74. classiq/interface/model/quantum_statement.py +3 -2
  75. classiq/interface/model/quantum_type.py +8 -9
  76. classiq/interface/model/quantum_variable_declaration.py +2 -2
  77. classiq/interface/model/repeat.py +13 -0
  78. classiq/interface/model/resolvers/function_call_resolver.py +21 -0
  79. classiq/interface/model/statement_block.py +18 -4
  80. classiq/interface/model/validations/handles_validator.py +8 -12
  81. classiq/interface/model/within_apply_operation.py +4 -4
  82. classiq/interface/server/routes.py +0 -4
  83. classiq/qmod/__init__.py +6 -2
  84. classiq/qmod/builtins/classical_functions.py +34 -39
  85. classiq/qmod/builtins/functions.py +287 -300
  86. classiq/qmod/builtins/operations.py +217 -16
  87. classiq/qmod/builtins/structs.py +50 -48
  88. classiq/qmod/declaration_inferrer.py +30 -18
  89. classiq/qmod/native/expression_to_qmod.py +5 -4
  90. classiq/qmod/native/pretty_printer.py +48 -26
  91. classiq/qmod/qmod_constant.py +29 -5
  92. classiq/qmod/qmod_parameter.py +56 -34
  93. classiq/qmod/qmod_struct.py +2 -2
  94. classiq/qmod/qmod_variable.py +87 -43
  95. classiq/qmod/quantum_callable.py +8 -4
  96. classiq/qmod/quantum_expandable.py +25 -20
  97. classiq/qmod/quantum_function.py +29 -2
  98. classiq/qmod/symbolic.py +79 -69
  99. classiq/qmod/symbolic_expr.py +1 -1
  100. classiq/qmod/symbolic_type.py +1 -4
  101. classiq/qmod/utilities.py +29 -0
  102. {classiq-0.38.0.dist-info → classiq-0.40.0.dist-info}/METADATA +1 -1
  103. {classiq-0.38.0.dist-info → classiq-0.40.0.dist-info}/RECORD +122 -141
  104. classiq/applications/benchmarking/__init__.py +0 -9
  105. classiq/applications/benchmarking/mirror_benchmarking.py +0 -70
  106. classiq/applications/numpy_utils.py +0 -37
  107. classiq/applications_model_constructors/__init__.py +0 -25
  108. classiq/applications_model_constructors/combinatorial_helpers/multiple_comp_basis_sp.py +0 -34
  109. classiq/applications_model_constructors/libraries/qmci_library.py +0 -107
  110. classiq/builtin_functions/__init__.py +0 -43
  111. classiq/builtin_functions/amplitude_loading.py +0 -3
  112. classiq/builtin_functions/binary_ops.py +0 -1
  113. classiq/builtin_functions/exponentiation.py +0 -5
  114. classiq/builtin_functions/qpe.py +0 -4
  115. classiq/builtin_functions/qsvm.py +0 -7
  116. classiq/builtin_functions/range_types.py +0 -5
  117. classiq/builtin_functions/standard_gates.py +0 -1
  118. classiq/builtin_functions/state_preparation.py +0 -6
  119. classiq/builtin_functions/suzuki_trotter.py +0 -3
  120. classiq/interface/executor/error_mitigation.py +0 -6
  121. classiq/interface/generator/functions/core_lib_declarations/quantum_functions/__init__.py +0 -18
  122. classiq/interface/generator/functions/core_lib_declarations/quantum_functions/chemistry_functions.py +0 -0
  123. classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +0 -95
  124. classiq/model/__init__.py +0 -14
  125. classiq/model/composite_function_generator.py +0 -33
  126. classiq/model/function_handler.py +0 -462
  127. classiq/model/logic_flow.py +0 -149
  128. classiq/model/logic_flow_change_handler.py +0 -71
  129. classiq/model/model.py +0 -229
  130. classiq/quantum_functions/__init__.py +0 -17
  131. classiq/quantum_functions/annotation_parser.py +0 -205
  132. classiq/quantum_functions/decorators.py +0 -22
  133. classiq/quantum_functions/function_library.py +0 -181
  134. classiq/quantum_functions/function_parser.py +0 -74
  135. classiq/quantum_functions/quantum_function.py +0 -236
  136. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/__init__.py +0 -0
  137. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/allowed_constraints.py +0 -0
  138. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/arithmetic/__init__.py +0 -0
  139. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/arithmetic/isolation.py +0 -0
  140. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/pauli_helpers/__init__.py +0 -0
  141. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/pauli_helpers/pauli_sparsing.py +0 -0
  142. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/pauli_helpers/pauli_utils.py +0 -0
  143. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/py.typed +0 -0
  144. /classiq/{applications_model_constructors/combinatorial_helpers/transformations → applications/combinatorial_helpers/solvers}/__init__.py +0 -0
  145. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/sympy_utils.py +0 -0
  146. /classiq/{applications_model_constructors/libraries → applications/combinatorial_helpers/transformations}/__init__.py +0 -0
  147. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/penalty.py +0 -0
  148. /classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/sign_seperation.py +0 -0
  149. /classiq/{applications_model_constructors → applications/grover}/grover_model_constructor.py +0 -0
  150. /classiq/{interface/generator/functions/core_lib_declarations → applications/libraries}/__init__.py +0 -0
  151. /classiq/{applications_model_constructors → applications}/libraries/ampltitude_estimation_library.py +0 -0
  152. /classiq/{applications_model_constructors → applications/qsvm}/qsvm_model_constructor.py +0 -0
  153. /classiq/{quantum_register.py → interface/model/quantum_register.py} +0 -0
  154. {classiq-0.38.0.dist-info → classiq-0.40.0.dist-info}/WHEEL +0 -0
@@ -1,462 +0,0 @@
1
- import abc
2
- import collections.abc
3
- import functools
4
- from typing import (
5
- Any,
6
- Callable,
7
- Collection,
8
- Dict,
9
- Iterable,
10
- List,
11
- Mapping,
12
- Optional,
13
- Tuple,
14
- Union,
15
- cast,
16
- )
17
-
18
- from classiq.interface.generator import function_param_list, function_params
19
- from classiq.interface.generator.control_state import ControlState
20
- from classiq.interface.generator.function_params import IOName, PortDirection
21
- from classiq.interface.generator.functions.port_declaration import (
22
- PortDeclarationDirection,
23
- SynthesisPortDeclaration,
24
- )
25
- from classiq.interface.generator.identity import Identity
26
- from classiq.interface.generator.quantum_function_call import (
27
- SynthesisQuantumFunctionCall,
28
- WireDict,
29
- )
30
- from classiq.interface.generator.slice_parsing_utils import parse_io_slicing
31
- from classiq.interface.generator.user_defined_function_params import CustomFunction
32
-
33
- from classiq.exceptions import ClassiqValueError, ClassiqWiringError
34
- from classiq.model import logic_flow_change_handler
35
- from classiq.model.logic_flow import LogicFlowBuilder
36
- from classiq.quantum_functions.function_library import (
37
- FunctionLibrary,
38
- QuantumFunction,
39
- SynthesisQuantumFunctionDeclaration,
40
- )
41
- from classiq.quantum_register import QReg, QRegGenericAlias
42
-
43
- SupportedInputArgs = Union[
44
- Mapping[IOName, QReg],
45
- Collection[QReg],
46
- QReg,
47
- ]
48
-
49
- _SAME_INPUT_NAME_ERROR_MSG: str = "Cannot create multiple inputs with the same name"
50
- _INPUT_AS_OUTPUT_ERROR_MSG: str = "Can't connect input directly to output"
51
- ILLEGAL_INPUT_OR_SLICING_ERROR_MSG: str = "is not a valid input name/slice"
52
- ILLEGAL_OUTPUT_ERROR_MSG: str = "Illegal output provided"
53
-
54
- ASSIGNED = "_assigned_"
55
-
56
-
57
- def _get_identity_call_name(name: str, io: PortDirection) -> str:
58
- return f"{name}_{io.value}_Identity"
59
-
60
-
61
- class FunctionHandler(abc.ABC):
62
- def __init__(self) -> None:
63
- self._function_library: Optional[FunctionLibrary] = None
64
- self._port_declarations: Dict[IOName, SynthesisPortDeclaration] = dict()
65
- self._external_port_wiring: Dict[PortDirection, WireDict] = {
66
- PortDirection.Input: dict(),
67
- PortDirection.Output: dict(),
68
- }
69
- self._generated_qregs: Dict[IOName, QReg] = dict()
70
- self._logic_flow_builder: LogicFlowBuilder = LogicFlowBuilder()
71
-
72
- @property
73
- def input_wires(self) -> WireDict:
74
- return self._external_port_wiring[PortDirection.Input]
75
-
76
- @property
77
- def output_wires(self) -> WireDict:
78
- return self._external_port_wiring[PortDirection.Output]
79
-
80
- def _verify_unique_inputs(self, input_names: Iterable[IOName]) -> None:
81
- input_port_declarations = {
82
- name: port_declaration
83
- for name, port_declaration in self._port_declarations.items()
84
- if port_declaration.direction.is_input
85
- }
86
- if not input_port_declarations.keys().isdisjoint(input_names):
87
- raise ClassiqWiringError(_SAME_INPUT_NAME_ERROR_MSG)
88
-
89
- def _verify_no_inputs_as_outputs(self, output_qregs: Iterable[QReg]) -> None:
90
- for qreg in output_qregs:
91
- if any(
92
- qreg.isoverlapping(gen_qreg)
93
- for gen_qreg in self._generated_qregs.values()
94
- ):
95
- raise ClassiqWiringError(f"{_INPUT_AS_OUTPUT_ERROR_MSG} {qreg}")
96
-
97
- @staticmethod
98
- def _parse_control_states(
99
- control_states: Optional[Union[ControlState, Iterable[ControlState]]] = None
100
- ) -> List[ControlState]:
101
- if control_states is None:
102
- return list()
103
- elif isinstance(control_states, ControlState):
104
- return [control_states]
105
- return list(control_states)
106
-
107
- def create_inputs(
108
- self,
109
- inputs: Mapping[IOName, QRegGenericAlias],
110
- ) -> Dict[IOName, QReg]:
111
- self._verify_unique_inputs(inputs.keys())
112
- qregs_dict = {
113
- name: self._create_input_with_identity(name, qreg_type)
114
- for name, qreg_type in inputs.items()
115
- }
116
- self._generated_qregs.update(qregs_dict)
117
- return qregs_dict
118
-
119
- def _create_input_with_identity(
120
- self, name: IOName, qreg_type: QRegGenericAlias
121
- ) -> QReg:
122
- qreg = qreg_type()
123
- self._handle_io_with_identity(PortDirection.Input, name, qreg)
124
- return qreg
125
-
126
- def set_outputs(self, outputs: Mapping[IOName, QReg]) -> None:
127
- for name, qreg in outputs.items():
128
- self._set_output_with_identity(name, qreg)
129
-
130
- def _set_output_with_identity(self, name: IOName, qreg: QReg) -> None:
131
- self._handle_io_with_identity(PortDirection.Output, name, qreg)
132
-
133
- def _handle_io_with_identity(
134
- self, port_direction: PortDirection, name: IOName, qreg: QReg
135
- ) -> None:
136
- # We need to add an Identity call on each input/output of the logic flow,
137
- # since function input/output pins don't support "pin slicing".
138
- # (Which means we cannot use QRegs in the wiring directly - because it gets
139
- # decomposed into 1 bit wirings).
140
- # Adding the identity also indirectly adds support for slicing on IOs
141
- # (via the QReg slicing).
142
- rui = qreg.to_register_user_input(name)
143
- identity_call = SynthesisQuantumFunctionCall(
144
- name=_get_identity_call_name(name, port_direction),
145
- function_params=Identity(arguments=[rui]),
146
- )
147
- self._body.append(identity_call)
148
- wire_name = logic_flow_change_handler.handle_io_connection(
149
- port_direction, identity_call, name
150
- )
151
- if port_direction == PortDirection.Input:
152
- self._logic_flow_builder.connect_func_call_to_qreg(
153
- identity_call, name, qreg
154
- )
155
- else:
156
- self._logic_flow_builder.connect_qreg_to_func_call(
157
- qreg, name, identity_call
158
- )
159
- declaration_direction = PortDeclarationDirection.from_port_direction(
160
- port_direction
161
- )
162
- if (
163
- name in self._port_declarations
164
- and self._port_declarations[name].direction != declaration_direction
165
- ):
166
- declaration_direction = PortDeclarationDirection.Inout
167
- self._port_declarations[name] = SynthesisPortDeclaration(
168
- name=name,
169
- size=rui.size,
170
- direction=declaration_direction,
171
- )
172
- external_port_wiring_dict = dict(self._external_port_wiring[port_direction])
173
- external_port_wiring_dict[name] = wire_name
174
- self._external_port_wiring[port_direction] = external_port_wiring_dict
175
-
176
- def apply(
177
- self,
178
- function_name: Union[
179
- str,
180
- SynthesisQuantumFunctionDeclaration,
181
- QuantumFunction,
182
- ],
183
- in_wires: Optional[SupportedInputArgs] = None,
184
- out_wires: Optional[SupportedInputArgs] = None,
185
- is_inverse: bool = False,
186
- strict_zero_ios: bool = True,
187
- release_by_inverse: bool = False,
188
- control_states: Optional[Union[ControlState, Iterable[ControlState]]] = None,
189
- should_control: bool = True,
190
- power: int = 1,
191
- call_name: Optional[str] = None,
192
- ) -> Dict[IOName, QReg]:
193
- # if there's no function library, create one
194
- if self._function_library is None:
195
- self.create_library()
196
-
197
- if isinstance(function_name, SynthesisQuantumFunctionDeclaration):
198
- function_data = function_name
199
- elif isinstance(function_name, QuantumFunction):
200
- function_data = function_name.function_data
201
- else:
202
- function_data = None
203
-
204
- if function_data:
205
- if function_data.name not in self._function_library.function_dict: # type: ignore[union-attr]
206
- self._function_library.add_function(function_data) # type: ignore[union-attr]
207
-
208
- function_name = function_data.name
209
-
210
- function_name = cast(str, function_name)
211
- return self._add_function_call(
212
- function_name,
213
- self._function_library.get_function(function_name), # type: ignore[union-attr]
214
- in_wires=in_wires,
215
- out_wires=out_wires,
216
- is_inverse=is_inverse,
217
- strict_zero_ios=strict_zero_ios,
218
- release_by_inverse=release_by_inverse,
219
- control_states=control_states,
220
- should_control=should_control,
221
- power=power,
222
- call_name=call_name,
223
- )
224
-
225
- def release_qregs(self, qregs: Union[QReg, Collection[QReg]]) -> None:
226
- if isinstance(qregs, QReg):
227
- qregs = [qregs]
228
- for qreg in qregs:
229
- self._logic_flow_builder.connect_qreg_to_zero(qreg)
230
-
231
- def _add_function_call(
232
- self,
233
- function: str,
234
- params: function_params.FunctionParams,
235
- control_states: Optional[Union[ControlState, Iterable[ControlState]]] = None,
236
- in_wires: Optional[SupportedInputArgs] = None,
237
- out_wires: Optional[SupportedInputArgs] = None,
238
- is_inverse: bool = False,
239
- release_by_inverse: bool = False,
240
- should_control: bool = True,
241
- power: int = 1,
242
- call_name: Optional[str] = None,
243
- strict_zero_ios: bool = True,
244
- ) -> Dict[IOName, QReg]:
245
- if function != type(params).__name__ and not isinstance(params, CustomFunction):
246
- raise ClassiqValueError(
247
- "The FunctionParams type does not match function name"
248
- )
249
-
250
- if (
251
- isinstance(params, CustomFunction)
252
- and self._function_library
253
- and function not in self._function_library.function_dict
254
- ):
255
- raise ClassiqValueError(
256
- "QuantumFunctionCall: The function is not found in included library."
257
- )
258
-
259
- call = SynthesisQuantumFunctionCall(
260
- function=function,
261
- function_params=params,
262
- is_inverse=is_inverse,
263
- release_by_inverse=release_by_inverse,
264
- strict_zero_ios=strict_zero_ios,
265
- control_states=self._parse_control_states(control_states),
266
- should_control=should_control,
267
- power=power,
268
- name=call_name,
269
- )
270
-
271
- if in_wires is not None:
272
- self._connect_in_qregs(call=call, in_wires=in_wires)
273
-
274
- self._body.append(call)
275
-
276
- return self._connect_out_qregs(call=call, out_wires=out_wires or {})
277
-
278
- def _connect_in_qregs(
279
- self,
280
- call: SynthesisQuantumFunctionCall,
281
- in_wires: SupportedInputArgs,
282
- ) -> None:
283
- if isinstance(in_wires, dict):
284
- self._connect_named_in_qregs(call=call, in_wires=in_wires)
285
- elif isinstance(in_wires, QReg):
286
- self._connect_unnamed_in_qregs(call=call, in_wires=[in_wires])
287
- elif isinstance(in_wires, collections.abc.Collection):
288
- self._connect_unnamed_in_qregs(
289
- # mypy doesn't recognize that `dict` wouldn't reach this point
290
- call=call,
291
- in_wires=in_wires, # type: ignore[arg-type]
292
- )
293
- else:
294
- raise ClassiqWiringError(
295
- f"Invalid in_wires type: {type(in_wires).__name__}"
296
- )
297
-
298
- def _connect_unnamed_in_qregs(
299
- self,
300
- call: SynthesisQuantumFunctionCall,
301
- in_wires: Collection[QReg],
302
- ) -> None:
303
- call_inputs = call.function_params.inputs_full(call.strict_zero_ios).keys()
304
- self._connect_named_in_qregs(call, dict(zip(call_inputs, in_wires)))
305
-
306
- def _connect_named_in_qregs(
307
- self,
308
- call: SynthesisQuantumFunctionCall,
309
- in_wires: Dict[IOName, QReg],
310
- ) -> None:
311
- for input_name, in_qreg in in_wires.items():
312
- pin_name, pin_indices = self._get_pin_name_and_indices(input_name, call)
313
- if len(in_qreg) != len(pin_indices):
314
- raise ClassiqWiringError(
315
- f"Incorrect size of input QReg: expected {len(pin_indices)}, actual {len(in_qreg)}"
316
- )
317
- self._logic_flow_builder.connect_qreg_to_func_call(
318
- in_qreg, pin_name, call, pin_indices
319
- )
320
-
321
- @staticmethod
322
- def _get_pin_name_and_indices(
323
- input_name: IOName,
324
- call: SynthesisQuantumFunctionCall,
325
- ) -> Tuple[IOName, range]:
326
- try:
327
- name, slicing = parse_io_slicing(input_name)
328
- except (AssertionError, ValueError) as e:
329
- raise ClassiqWiringError(
330
- f"{input_name} {ILLEGAL_INPUT_OR_SLICING_ERROR_MSG}"
331
- ) from e
332
- pin_info = call.input_regs_dict.get(name)
333
- if pin_info is None:
334
- raise ClassiqWiringError(
335
- f"No register size information on input pin {name}"
336
- )
337
- indices = range(pin_info.size)[slicing]
338
- return name, indices
339
-
340
- def _connect_out_qregs(
341
- self,
342
- call: SynthesisQuantumFunctionCall,
343
- out_wires: SupportedInputArgs,
344
- ) -> Dict[IOName, QReg]:
345
- if isinstance(out_wires, dict):
346
- return self._connect_named_out_qregs(call, out_wires)
347
- elif isinstance(out_wires, QReg):
348
- return self._connect_unnamed_out_qregs(call, [out_wires])
349
- elif isinstance(out_wires, collections.abc.Collection):
350
- return self._connect_unnamed_out_qregs(
351
- # mypy doesn't recognize that `dict` wouldn't reach this point
352
- call,
353
- out_wires, # type: ignore[arg-type]
354
- )
355
- else:
356
- raise ClassiqWiringError(
357
- f"Invalid in_wires type: {type(out_wires).__name__}"
358
- )
359
-
360
- def _connect_unnamed_out_qregs(
361
- self,
362
- call: SynthesisQuantumFunctionCall,
363
- out_wires: Collection[QReg],
364
- ) -> Dict[IOName, QReg]:
365
- call_outputs = call.function_params.outputs.keys()
366
- return self._connect_named_out_qregs(call, dict(zip(call_outputs, out_wires)))
367
-
368
- def _connect_named_out_qregs(
369
- self,
370
- call: SynthesisQuantumFunctionCall,
371
- out_wires: Mapping[IOName, QReg],
372
- ) -> Dict[IOName, QReg]:
373
- if not all(output_name in call.output_regs_dict for output_name in out_wires):
374
- raise ClassiqWiringError(ILLEGAL_OUTPUT_ERROR_MSG)
375
- output_dict = {}
376
- for output_name, reg_user_input in call.output_regs_dict.items():
377
- if reg_user_input is None:
378
- raise ClassiqValueError(
379
- f"No output register information for {output_name}"
380
- )
381
- qreg = out_wires.get(output_name) or QReg.from_arithmetic_info(
382
- reg_user_input
383
- )
384
- self._logic_flow_builder.connect_func_call_to_qreg(call, output_name, qreg)
385
- output_dict[output_name] = qreg
386
- return output_dict
387
-
388
- def __getattr__(self, item: str) -> Callable[..., Any]:
389
- # This is added due to problematic behaviour in deepcopy.
390
- # deepcopy approaches __getattr__ before __init__ is called,
391
- # and therefore self._function_library doesn't exist.
392
- # Thus, we treat _function_library differently.
393
-
394
- if item == "_function_library":
395
- raise AttributeError(
396
- f"{self.__class__.__name__!r} has no attribute {item!r}"
397
- )
398
-
399
- is_builtin_function_name = any(
400
- item == func.__name__
401
- for func in function_param_list.function_param_library.param_list
402
- )
403
-
404
- if is_builtin_function_name:
405
- return functools.partial(self._add_function_call, item)
406
-
407
- is_user_function_name = (
408
- self._function_library is not None
409
- and item in self._function_library.function_names
410
- )
411
-
412
- if is_user_function_name:
413
- return functools.partial(self.apply, item)
414
-
415
- if (
416
- self._function_library is not None
417
- and item in self._function_library.function_factory_names
418
- ):
419
- return functools.partial(
420
- self._function_library.get_function_factory(item),
421
- add_method=functools.partial(
422
- self._function_library.add_function,
423
- override_existing_functions=True,
424
- ),
425
- apply_method=self.apply,
426
- )
427
-
428
- raise AttributeError(f"{self.__class__.__name__!r} has no attribute {item!r}")
429
-
430
- def __dir__(self) -> List[str]:
431
- builtin_func_name = [
432
- func.__name__
433
- for func in function_param_list.function_param_library.param_list
434
- ]
435
- user_func_names = (
436
- list(self._function_library.function_names)
437
- if self._function_library is not None
438
- else list()
439
- )
440
- return list(super().__dir__()) + builtin_func_name + user_func_names
441
-
442
- def include_library(self, library: FunctionLibrary) -> None:
443
- """Includes a function library.
444
-
445
- Args:
446
- library (FunctionLibrary): The function library.
447
- """
448
- if self._function_library is not None:
449
- raise ClassiqValueError("Another function library is already included.")
450
-
451
- self._function_library = library
452
-
453
- @property
454
- @abc.abstractmethod
455
- def _body(
456
- self,
457
- ) -> List[SynthesisQuantumFunctionCall]:
458
- pass
459
-
460
- @abc.abstractmethod
461
- def create_library(self) -> None:
462
- pass
@@ -1,149 +0,0 @@
1
- from dataclasses import dataclass
2
- from typing import Any, Optional, Tuple
3
-
4
- import networkx as nx
5
-
6
- from classiq.interface.generator.function_params import PortDirection
7
- from classiq.interface.generator.quantum_function_call import (
8
- ZERO_INDICATOR,
9
- SynthesisQuantumFunctionCall,
10
- )
11
-
12
- from classiq.exceptions import ClassiqWiringError
13
- from classiq.model import logic_flow_change_handler
14
- from classiq.quantum_register import QReg, Qubit
15
-
16
-
17
- # We need the dataclass to be hashable for inserting it into the graph,
18
- # hence the dataclass is frozen.
19
- @dataclass(frozen=True)
20
- class _Pin:
21
- pin_name: str
22
- index: int
23
- func_call: Optional[SynthesisQuantumFunctionCall]
24
- io: PortDirection # We need to store PortDirection because a function may have an input and an output pin with the same name
25
-
26
- def __str__(self) -> str:
27
- return f"{self.pin_name}[{self.index}]"
28
-
29
-
30
- @dataclass(frozen=True)
31
- class _ZeroPin(_Pin):
32
- def __init__(self, io: PortDirection) -> None:
33
- super().__init__(pin_name=ZERO_INDICATOR, index=0, func_call=None, io=io)
34
-
35
- def __str__(self) -> str:
36
- return ZERO_INDICATOR
37
-
38
-
39
- class _StrictDiGraph(nx.DiGraph):
40
- def add_edge(self, u_of_edge: Any, v_of_edge: Any, **attr: Any) -> None:
41
- if u_of_edge in self and v_of_edge in self[u_of_edge]:
42
- raise ClassiqWiringError(
43
- f"Cannot reconnect an already connected edge: {u_of_edge}, {v_of_edge}"
44
- )
45
- super().add_edge(u_of_edge, v_of_edge, **attr)
46
-
47
-
48
- INVALID_QUBITS_ERROR_MESSAGE = (
49
- "Cannot use a QReg with consumed or uninitialized qubits:"
50
- )
51
-
52
-
53
- class LogicFlowBuilder:
54
- def __init__(self) -> None:
55
- self._logic_flow_graph = _StrictDiGraph()
56
- self._connect_qubit_func = {
57
- PortDirection.Input: self._connect_qubit_to_func_call,
58
- PortDirection.Output: self._connect_func_call_to_qubit,
59
- }
60
-
61
- def _is_qubit_available(self, qubit: Qubit) -> bool:
62
- return qubit in self._logic_flow_graph.nodes
63
-
64
- def _validate_qreg(self, qreg: QReg) -> None:
65
- invalid_qubit_indices = [
66
- i
67
- for i, qubit in enumerate(qreg.qubits)
68
- if not self._is_qubit_available(qubit)
69
- ]
70
- if invalid_qubit_indices:
71
- raise ClassiqWiringError(
72
- f"{INVALID_QUBITS_ERROR_MESSAGE} {invalid_qubit_indices}"
73
- )
74
-
75
- def _verify_no_loops(self, dest_node: SynthesisQuantumFunctionCall) -> None:
76
- if not nx.is_directed_acyclic_graph(self._logic_flow_graph):
77
- raise ClassiqWiringError(f"Cannot wire function {dest_node} to itself")
78
-
79
- def _connect_qubit_to_func_call(
80
- self,
81
- qubit: Qubit,
82
- dest_pin: _Pin,
83
- dest_node: Optional[SynthesisQuantumFunctionCall],
84
- ) -> None:
85
- if dest_node is not None:
86
- self._logic_flow_graph.add_edge(dest_pin, dest_node)
87
- source_node, source_pin = self._get_source_node_and_pin(qubit)
88
- # relabel_nodes replaces a node with another (inplace and keeping the edges)
89
- nx.relabel_nodes(self._logic_flow_graph, {qubit: dest_pin}, copy=False)
90
- logic_flow_change_handler.handle_inner_connection(
91
- source_node,
92
- str(source_pin),
93
- str(dest_pin),
94
- dest_node,
95
- )
96
-
97
- def _get_source_node_and_pin(
98
- self, qubit: Qubit
99
- ) -> Tuple[SynthesisQuantumFunctionCall, _Pin]:
100
- source_pin = next(self._logic_flow_graph.predecessors(qubit))
101
- source_node = next(self._logic_flow_graph.predecessors(source_pin))
102
- return source_node, source_pin
103
-
104
- def _connect_func_call_to_qubit(
105
- self, qubit: Qubit, source_pin: _Pin, source_node: SynthesisQuantumFunctionCall
106
- ) -> None:
107
- self._logic_flow_graph.add_edge(source_node, source_pin)
108
- self._logic_flow_graph.add_edge(source_pin, qubit)
109
-
110
- def _connect_io(
111
- self,
112
- io: PortDirection,
113
- func_node: SynthesisQuantumFunctionCall,
114
- pin_name: str,
115
- qreg: QReg,
116
- pin_indices: Optional[range] = None,
117
- ) -> None:
118
- if pin_indices is None:
119
- pin_indices = range(len(qreg))
120
- pins = [_Pin(pin_name, i, func_node, io) for i in pin_indices]
121
- for pin, qubit in zip(pins, qreg.qubits):
122
- self._connect_qubit_func[io](qubit, pin, func_node)
123
-
124
- def connect_qreg_to_func_call(
125
- self,
126
- source: QReg,
127
- dest_pin_name: str,
128
- dest_func_call: SynthesisQuantumFunctionCall,
129
- pin_indices: Optional[range] = None,
130
- ) -> None:
131
- self._validate_qreg(source)
132
- self._connect_io(
133
- PortDirection.Input, dest_func_call, dest_pin_name, source, pin_indices
134
- )
135
- self._verify_no_loops(dest_func_call)
136
-
137
- def connect_func_call_to_qreg(
138
- self,
139
- source_func_call: SynthesisQuantumFunctionCall,
140
- source_pin_name: str,
141
- dest: QReg,
142
- ) -> None:
143
- self._connect_io(PortDirection.Output, source_func_call, source_pin_name, dest)
144
-
145
- def connect_qreg_to_zero(self, source: QReg) -> None:
146
- for qubit in source.qubits:
147
- self._connect_qubit_to_func_call(
148
- qubit, _ZeroPin(PortDirection.Output), None
149
- )
@@ -1,71 +0,0 @@
1
- from typing import Optional
2
-
3
- from classiq.interface.generator.function_params import PortDirection
4
- from classiq.interface.generator.quantum_function_call import (
5
- ZERO_INDICATOR,
6
- SynthesisQuantumFunctionCall,
7
- )
8
-
9
-
10
- def _get_io_wire_name(
11
- name: str, call: SynthesisQuantumFunctionCall, io: PortDirection
12
- ) -> str:
13
- if io == PortDirection.Input:
14
- return f"{io.name}:{name}->{call.name}:{name}"
15
- else:
16
- return f"{call.name}:{name}->{io.name}:{name}"
17
-
18
-
19
- def _get_wire_name(
20
- source_call: SynthesisQuantumFunctionCall,
21
- source_pin_name: str,
22
- dest_pin_name: str,
23
- dest_call: Optional[SynthesisQuantumFunctionCall],
24
- ) -> str:
25
- if dest_call is None:
26
- assert dest_pin_name == ZERO_INDICATOR
27
- return ZERO_INDICATOR
28
- return f"{source_call.name}:{source_pin_name}->{dest_call.name}:{dest_pin_name}"
29
-
30
-
31
- def _set_model_output(
32
- call: SynthesisQuantumFunctionCall, pin_name: str, wire_name: str
33
- ) -> None:
34
- call_outputs = dict(call.outputs_dict)
35
- call_outputs[pin_name] = wire_name
36
- call.outputs = call_outputs
37
- if wire_name != ZERO_INDICATOR:
38
- call.non_zero_output_wires.append(wire_name)
39
-
40
-
41
- def _set_model_input(
42
- call: Optional[SynthesisQuantumFunctionCall], pin_name: str, wire_name: str
43
- ) -> None:
44
- if call is None:
45
- return
46
- call_inputs = dict(call.inputs_dict)
47
- call_inputs[pin_name] = wire_name
48
- call.inputs = call_inputs
49
- call.non_zero_input_wires.append(wire_name)
50
-
51
-
52
- def handle_inner_connection(
53
- source_call: SynthesisQuantumFunctionCall,
54
- source_pin_name: str,
55
- dest_pin_name: str,
56
- dest_call: Optional[SynthesisQuantumFunctionCall],
57
- ) -> None:
58
- wire_name = _get_wire_name(source_call, source_pin_name, dest_pin_name, dest_call)
59
- _set_model_output(source_call, source_pin_name, wire_name)
60
- _set_model_input(dest_call, dest_pin_name, wire_name)
61
-
62
-
63
- def handle_io_connection(
64
- io_dir: PortDirection, call: SynthesisQuantumFunctionCall, io_name: str
65
- ) -> str:
66
- wire_name = _get_io_wire_name(io_name, call, io_dir)
67
- if io_dir == PortDirection.Input:
68
- _set_model_input(call, io_name, wire_name)
69
- else:
70
- _set_model_output(call, io_name, wire_name)
71
- return wire_name