classiq 0.38.0__py3-none-any.whl → 0.39.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- classiq/__init__.py +21 -22
- classiq/_internals/api_wrapper.py +13 -1
- classiq/_internals/client.py +12 -2
- classiq/analyzer/analyzer.py +3 -1
- classiq/applications/__init__.py +1 -8
- classiq/applications/chemistry/__init__.py +6 -0
- classiq/{applications_model_constructors → applications}/combinatorial_helpers/arithmetic/arithmetic_expression.py +1 -1
- classiq/{applications_model_constructors → applications}/combinatorial_helpers/combinatorial_problem_utils.py +26 -6
- classiq/{applications_model_constructors → applications}/combinatorial_helpers/encoding_mapping.py +1 -1
- classiq/{applications_model_constructors → applications}/combinatorial_helpers/encoding_utils.py +1 -1
- classiq/{applications_model_constructors → applications}/combinatorial_helpers/memory.py +2 -4
- classiq/{applications_model_constructors → applications}/combinatorial_helpers/optimization_model.py +6 -10
- classiq/{applications_model_constructors → applications}/combinatorial_helpers/pauli_helpers/pauli_utils.py +10 -0
- classiq/{applications_model_constructors → applications}/combinatorial_helpers/pyomo_utils.py +4 -2
- classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/encoding.py +3 -10
- classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/fixed_variables.py +4 -6
- classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/ising_converter.py +3 -5
- classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/penalty_support.py +3 -7
- classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/slack_variables.py +4 -6
- classiq/applications/combinatorial_optimization/__init__.py +9 -3
- classiq/{applications_model_constructors → applications/combinatorial_optimization}/combinatorial_optimization_model_constructor.py +7 -8
- classiq/applications/finance/__init__.py +3 -2
- classiq/{applications_model_constructors → applications/finance}/finance_model_constructor.py +24 -14
- classiq/applications/grover/__init__.py +11 -0
- classiq/applications/libraries/qmci_library.py +35 -0
- classiq/applications/qsvm/__init__.py +5 -1
- classiq/execution/all_hardware_devices.py +13 -0
- classiq/executor.py +2 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/result.py +1 -5
- classiq/interface/applications/qsvm.py +4 -2
- classiq/interface/ast_node.py +23 -0
- classiq/interface/combinatorial_optimization/examples/mht.py +8 -3
- classiq/interface/executor/execution_request.py +2 -37
- classiq/interface/executor/vqe_result.py +1 -1
- classiq/interface/generator/builtin_api_builder.py +0 -5
- classiq/interface/generator/constant.py +2 -3
- classiq/interface/generator/expressions/expression.py +2 -4
- classiq/interface/generator/expressions/qmod_qscalar_proxy.py +1 -1
- classiq/interface/generator/functions/__init__.py +2 -2
- classiq/interface/generator/functions/builtins/__init__.py +15 -0
- classiq/interface/generator/functions/builtins/core_library/__init__.py +14 -0
- classiq/interface/generator/functions/builtins/internal_operators.py +62 -0
- classiq/interface/generator/functions/{core_lib_declarations/quantum_functions/std_lib_functions.py → builtins/open_lib_functions.py} +109 -83
- classiq/interface/generator/functions/builtins/quantum_operators.py +37 -0
- classiq/interface/generator/functions/classical_type.py +2 -4
- classiq/interface/generator/functions/function_declaration.py +2 -2
- classiq/interface/generator/hartree_fock.py +10 -2
- classiq/interface/generator/model/classical_main_validator.py +1 -1
- classiq/interface/generator/model/model.py +1 -1
- classiq/interface/generator/quantum_function_call.py +1 -1
- classiq/interface/generator/types/struct_declaration.py +2 -4
- classiq/interface/model/call_synthesis_data.py +3 -3
- classiq/interface/model/classical_if.py +13 -0
- classiq/interface/model/classical_parameter_declaration.py +2 -3
- classiq/interface/model/control.py +16 -0
- classiq/interface/model/handle_binding.py +3 -2
- classiq/interface/model/invert.py +10 -0
- classiq/interface/model/model.py +2 -1
- classiq/interface/model/power.py +12 -0
- classiq/interface/model/quantum_function_call.py +9 -4
- classiq/interface/model/quantum_if_operation.py +3 -3
- classiq/interface/model/quantum_lambda_function.py +3 -9
- classiq/interface/model/quantum_statement.py +3 -2
- classiq/interface/model/quantum_type.py +2 -4
- classiq/interface/model/quantum_variable_declaration.py +2 -2
- classiq/interface/model/repeat.py +13 -0
- classiq/interface/model/resolvers/function_call_resolver.py +26 -0
- classiq/interface/model/statement_block.py +21 -4
- classiq/interface/model/validations/handles_validator.py +8 -12
- classiq/interface/model/within_apply_operation.py +4 -4
- classiq/interface/server/routes.py +0 -4
- classiq/qmod/builtins/classical_functions.py +9 -9
- classiq/qmod/builtins/functions.py +153 -226
- classiq/qmod/builtins/operations.py +160 -13
- classiq/qmod/native/pretty_printer.py +49 -20
- classiq/qmod/qmod_constant.py +26 -2
- classiq/qmod/qmod_parameter.py +2 -1
- classiq/qmod/qmod_variable.py +48 -15
- classiq/qmod/quantum_callable.py +1 -0
- classiq/qmod/quantum_expandable.py +6 -7
- classiq/qmod/quantum_function.py +4 -0
- classiq/qmod/symbolic.py +2 -2
- {classiq-0.38.0.dist-info → classiq-0.39.0.dist-info}/METADATA +1 -1
- {classiq-0.38.0.dist-info → classiq-0.39.0.dist-info}/RECORD +107 -124
- classiq/applications/benchmarking/__init__.py +0 -9
- classiq/applications/benchmarking/mirror_benchmarking.py +0 -70
- classiq/applications/numpy_utils.py +0 -37
- classiq/applications_model_constructors/__init__.py +0 -25
- classiq/applications_model_constructors/combinatorial_helpers/multiple_comp_basis_sp.py +0 -34
- classiq/applications_model_constructors/libraries/qmci_library.py +0 -107
- classiq/builtin_functions/__init__.py +0 -43
- classiq/builtin_functions/amplitude_loading.py +0 -3
- classiq/builtin_functions/binary_ops.py +0 -1
- classiq/builtin_functions/exponentiation.py +0 -5
- classiq/builtin_functions/qpe.py +0 -4
- classiq/builtin_functions/qsvm.py +0 -7
- classiq/builtin_functions/range_types.py +0 -5
- classiq/builtin_functions/standard_gates.py +0 -1
- classiq/builtin_functions/state_preparation.py +0 -6
- classiq/builtin_functions/suzuki_trotter.py +0 -3
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/__init__.py +0 -18
- classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +0 -95
- classiq/model/__init__.py +0 -14
- classiq/model/composite_function_generator.py +0 -33
- classiq/model/function_handler.py +0 -462
- classiq/model/logic_flow.py +0 -149
- classiq/model/logic_flow_change_handler.py +0 -71
- classiq/model/model.py +0 -229
- classiq/quantum_functions/__init__.py +0 -17
- classiq/quantum_functions/annotation_parser.py +0 -205
- classiq/quantum_functions/decorators.py +0 -22
- classiq/quantum_functions/function_library.py +0 -181
- classiq/quantum_functions/function_parser.py +0 -74
- classiq/quantum_functions/quantum_function.py +0 -236
- /classiq/{applications_model_constructors → applications/chemistry}/chemistry_model_constructor.py +0 -0
- /classiq/{applications_model_constructors → applications}/combinatorial_helpers/__init__.py +0 -0
- /classiq/{applications_model_constructors → applications}/combinatorial_helpers/allowed_constraints.py +0 -0
- /classiq/{applications_model_constructors → applications}/combinatorial_helpers/arithmetic/__init__.py +0 -0
- /classiq/{applications_model_constructors → applications}/combinatorial_helpers/arithmetic/isolation.py +0 -0
- /classiq/{applications_model_constructors → applications}/combinatorial_helpers/pauli_helpers/__init__.py +0 -0
- /classiq/{applications_model_constructors → applications}/combinatorial_helpers/pauli_helpers/pauli_sparsing.py +0 -0
- /classiq/{applications_model_constructors → applications}/combinatorial_helpers/py.typed +0 -0
- /classiq/{applications_model_constructors/combinatorial_helpers/transformations → applications/combinatorial_helpers/solvers}/__init__.py +0 -0
- /classiq/{applications_model_constructors → applications}/combinatorial_helpers/sympy_utils.py +0 -0
- /classiq/{applications_model_constructors/libraries → applications/combinatorial_helpers/transformations}/__init__.py +0 -0
- /classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/penalty.py +0 -0
- /classiq/{applications_model_constructors → applications}/combinatorial_helpers/transformations/sign_seperation.py +0 -0
- /classiq/{applications_model_constructors → applications/grover}/grover_model_constructor.py +0 -0
- /classiq/{interface/generator/functions/core_lib_declarations → applications/libraries}/__init__.py +0 -0
- /classiq/{applications_model_constructors → applications}/libraries/ampltitude_estimation_library.py +0 -0
- /classiq/{applications_model_constructors → applications/qsvm}/qsvm_model_constructor.py +0 -0
- /classiq/interface/generator/functions/{core_lib_declarations/quantum_functions → builtins/core_library}/atomic_quantum_functions.py +0 -0
- /classiq/interface/generator/functions/{core_lib_declarations/quantum_functions → builtins/core_library}/chemistry_functions.py +0 -0
- /classiq/interface/generator/functions/{core_lib_declarations/quantum_functions → builtins/core_library}/exponentiation_functions.py +0 -0
- /classiq/{quantum_register.py → interface/model/quantum_register.py} +0 -0
- {classiq-0.38.0.dist-info → classiq-0.39.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
|
classiq/model/logic_flow.py
DELETED
@@ -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
|