classiq 0.32.1__py3-none-any.whl → 0.34.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 +2 -1
- classiq/_analyzer_extras/_ipywidgets_async_extension.py +1 -1
- classiq/_internals/api_wrapper.py +34 -23
- classiq/_internals/jobs.py +41 -14
- classiq/analyzer/__init__.py +3 -1
- classiq/applications/__init__.py +3 -1
- classiq/applications/benchmarking/__init__.py +3 -1
- classiq/applications/chemistry/__init__.py +3 -1
- classiq/applications/combinatorial_optimization/__init__.py +3 -1
- classiq/applications/combinatorial_optimization/examples/__init__.py +3 -1
- classiq/applications/finance/__init__.py +3 -1
- classiq/applications/qnn/__init__.py +3 -1
- classiq/applications/qnn/datasets/__init__.py +3 -1
- classiq/applications/qsvm/qsvm.py +1 -1
- classiq/applications_model_constructors/grover_model_constructor.py +25 -8
- classiq/builtin_functions/__init__.py +3 -1
- classiq/execution/__init__.py +3 -1
- classiq/execution/jobs.py +57 -20
- classiq/executor.py +4 -11
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +1 -1
- classiq/interface/backend/backend_preferences.py +17 -0
- classiq/interface/backend/pydantic_backend.py +8 -0
- classiq/interface/backend/quantum_backend_providers.py +15 -1
- classiq/interface/chemistry/ground_state_problem.py +1 -1
- classiq/interface/chemistry/operator.py +198 -0
- classiq/interface/executor/execution_request.py +5 -12
- classiq/interface/generator/circuit_code/types_and_constants.py +1 -1
- classiq/interface/generator/complex_type.py +4 -1
- classiq/interface/generator/functions/__init__.py +3 -1
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/__init__.py +0 -1
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/atomic_quantum_functions.py +39 -39
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/std_lib_functions.py +251 -87
- classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +5 -54
- classiq/interface/generator/generated_circuit.py +14 -43
- classiq/interface/generator/generated_circuit_data.py +26 -34
- classiq/interface/generator/model/preferences/preferences.py +3 -3
- classiq/interface/generator/partitioned_register.py +1 -1
- classiq/interface/generator/quantum_function_call.py +1 -1
- classiq/interface/generator/validations/validator_functions.py +4 -2
- classiq/interface/hardware.py +3 -2
- classiq/interface/ide/show.py +1 -14
- classiq/interface/model/bind_operation.py +20 -0
- classiq/interface/model/handle_binding.py +8 -0
- classiq/interface/model/native_function_definition.py +15 -5
- classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +8 -3
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +9 -4
- classiq/interface/model/quantum_expressions/quantum_expression.py +10 -5
- classiq/interface/model/quantum_function_call.py +24 -347
- classiq/interface/model/quantum_function_declaration.py +7 -11
- classiq/interface/model/quantum_statement.py +13 -7
- classiq/interface/model/validations/handle_validation_base.py +1 -2
- classiq/interface/model/validations/handles_validator.py +34 -8
- classiq/interface/model/variable_declaration_statement.py +8 -0
- classiq/interface/server/routes.py +11 -16
- classiq/model/__init__.py +3 -1
- classiq/model/function_handler.py +1 -1
- classiq/model/function_handler.pyi +88 -88
- classiq/qmod/declaration_inferrer.py +37 -18
- classiq/qmod/model_state_container.py +6 -3
- classiq/qmod/qmod_builtins.py +892 -4
- classiq/qmod/qmod_parameter.py +24 -8
- classiq/qmod/qmod_variable.py +2 -1
- classiq/qmod/quantum_expandable.py +6 -2
- classiq/qmod/quantum_function.py +11 -10
- classiq/quantum_functions/quantum_function.py +4 -1
- {classiq-0.32.1.dist-info → classiq-0.34.0.dist-info}/METADATA +1 -1
- {classiq-0.32.1.dist-info → classiq-0.34.0.dist-info}/RECORD +69 -72
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/apps_lib_functions.py +0 -262
- classiq/interface/model/clients/__init__.py +0 -0
- classiq/interface/model/clients/qmod/__init__.py +0 -0
- classiq/interface/model/clients/qmod/qmod_builtins.py +0 -908
- classiq/interface/model/semantics.py +0 -15
- {classiq-0.32.1.dist-info → classiq-0.34.0.dist-info}/WHEEL +0 -0
@@ -1,62 +1,29 @@
|
|
1
|
-
import functools
|
2
1
|
import itertools
|
3
2
|
import re
|
4
|
-
from
|
5
|
-
from typing import (
|
6
|
-
TYPE_CHECKING,
|
7
|
-
Any,
|
8
|
-
Collection,
|
9
|
-
Dict,
|
10
|
-
Iterable,
|
11
|
-
List,
|
12
|
-
Mapping,
|
13
|
-
Match,
|
14
|
-
Optional,
|
15
|
-
Sequence,
|
16
|
-
Set,
|
17
|
-
Tuple,
|
18
|
-
Union,
|
19
|
-
)
|
3
|
+
from typing import Any, Dict, List, Mapping, Optional, Sequence, Set, Union
|
20
4
|
|
21
5
|
import pydantic
|
22
|
-
from pydantic import BaseModel
|
6
|
+
from pydantic import BaseModel
|
23
7
|
|
24
|
-
from classiq.interface.generator import function_param_list, function_params as f_params
|
25
8
|
from classiq.interface.generator.arith.arithmetic import Arithmetic
|
26
9
|
from classiq.interface.generator.control_state import ControlState
|
27
10
|
from classiq.interface.generator.expressions.expression import Expression
|
28
|
-
from classiq.interface.generator.function_params import
|
29
|
-
NAME_REGEX,
|
30
|
-
FunctionParams,
|
31
|
-
IOName,
|
32
|
-
PortDirection,
|
33
|
-
)
|
11
|
+
from classiq.interface.generator.function_params import NAME_REGEX
|
34
12
|
from classiq.interface.generator.functions import FunctionDeclaration
|
35
13
|
from classiq.interface.generator.functions.port_declaration import (
|
36
14
|
PortDeclarationDirection,
|
37
15
|
)
|
38
16
|
from classiq.interface.generator.quantum_function_call import (
|
39
17
|
BAD_CALL_NAME_ERROR_MSG,
|
40
|
-
BAD_INPUT_ERROR_MSG,
|
41
|
-
BAD_INPUT_EXPRESSION_MSG,
|
42
|
-
BAD_INPUT_SLICING_MSG,
|
43
|
-
BAD_OUTPUT_ERROR_MSG,
|
44
|
-
BAD_OUTPUT_EXPRESSION_MSG,
|
45
|
-
BAD_OUTPUT_SLICING_MSG,
|
46
|
-
CUSTOM_FUNCTION_SINGLE_IO_ERROR,
|
47
|
-
LEGAL_SLICING,
|
48
18
|
SUFFIX_MARKER,
|
49
19
|
randomize_suffix,
|
50
20
|
)
|
51
|
-
from classiq.interface.generator.slice_parsing_utils import (
|
52
|
-
IO_REGEX,
|
53
|
-
NAME,
|
54
|
-
SLICING,
|
55
|
-
parse_io_slicing,
|
56
|
-
)
|
57
|
-
from classiq.interface.generator.user_defined_function_params import CustomFunction
|
58
21
|
from classiq.interface.helpers.custom_pydantic_types import PydanticNonEmptyString
|
59
|
-
from classiq.interface.model.handle_binding import
|
22
|
+
from classiq.interface.model.handle_binding import (
|
23
|
+
HandleBinding,
|
24
|
+
SlicedHandleBinding,
|
25
|
+
SubscriptHandleBinding,
|
26
|
+
)
|
60
27
|
from classiq.interface.model.quantum_function_declaration import (
|
61
28
|
QuantumFunctionDeclaration,
|
62
29
|
QuantumOperandDeclaration,
|
@@ -64,7 +31,7 @@ from classiq.interface.model.quantum_function_declaration import (
|
|
64
31
|
from classiq.interface.model.quantum_statement import QuantumOperation
|
65
32
|
from classiq.interface.model.validation_handle import get_unique_handle_names
|
66
33
|
|
67
|
-
from classiq.exceptions import
|
34
|
+
from classiq.exceptions import ClassiqError, ClassiqValueError
|
68
35
|
|
69
36
|
|
70
37
|
def _validate_no_duplicated_ports(
|
@@ -140,10 +107,6 @@ class QuantumFunctionCall(QuantumOperation):
|
|
140
107
|
description="The function that is called"
|
141
108
|
)
|
142
109
|
params: Dict[str, Expression] = pydantic.Field(default_factory=dict)
|
143
|
-
function_params: f_params.FunctionParams = pydantic.Field(
|
144
|
-
description="The parameters necessary for defining the function",
|
145
|
-
default_factory=CustomFunction,
|
146
|
-
)
|
147
110
|
strict_zero_ios: bool = pydantic.Field(
|
148
111
|
default=True,
|
149
112
|
description="Enables automated qubit allocation for pre-determined zero inputs "
|
@@ -163,15 +126,17 @@ class QuantumFunctionCall(QuantumOperation):
|
|
163
126
|
default=True,
|
164
127
|
description="False value indicates this call shouldn't be controlled even if the flow is controlled.",
|
165
128
|
)
|
166
|
-
inputs: Dict[
|
129
|
+
inputs: Dict[str, HandleBinding] = pydantic.Field(
|
167
130
|
default_factory=dict,
|
168
131
|
description="A mapping from the input name to the wire it connects to",
|
169
132
|
)
|
170
|
-
inouts: Dict[
|
133
|
+
inouts: Dict[
|
134
|
+
str, Union[SlicedHandleBinding, SubscriptHandleBinding, HandleBinding]
|
135
|
+
] = pydantic.Field(
|
171
136
|
default_factory=dict,
|
172
137
|
description="A mapping from in/out name to the wires that connect to it",
|
173
138
|
)
|
174
|
-
outputs: Dict[
|
139
|
+
outputs: Dict[str, HandleBinding] = pydantic.Field(
|
175
140
|
default_factory=dict,
|
176
141
|
description="A mapping from the output name to the wire it connects to",
|
177
142
|
)
|
@@ -191,7 +156,10 @@ class QuantumFunctionCall(QuantumOperation):
|
|
191
156
|
)
|
192
157
|
|
193
158
|
@property
|
194
|
-
def func_decl(self) ->
|
159
|
+
def func_decl(self) -> QuantumFunctionDeclaration:
|
160
|
+
if self._func_decl is None:
|
161
|
+
raise ClassiqError("Accessing an unresolved quantum function call")
|
162
|
+
|
195
163
|
return self._func_decl
|
196
164
|
|
197
165
|
def set_func_decl(self, fd: Optional[FunctionDeclaration]) -> None:
|
@@ -208,17 +176,19 @@ class QuantumFunctionCall(QuantumOperation):
|
|
208
176
|
return self.function
|
209
177
|
|
210
178
|
@property
|
211
|
-
def wiring_inputs(self) -> Mapping[
|
179
|
+
def wiring_inputs(self) -> Mapping[str, HandleBinding]:
|
212
180
|
return self.inputs
|
213
181
|
|
214
182
|
@property
|
215
183
|
def wiring_inouts(
|
216
184
|
self,
|
217
|
-
) -> Mapping[
|
185
|
+
) -> Mapping[
|
186
|
+
str, Union[SlicedHandleBinding, SubscriptHandleBinding, HandleBinding]
|
187
|
+
]:
|
218
188
|
return self.inouts
|
219
189
|
|
220
190
|
@property
|
221
|
-
def wiring_outputs(self) -> Mapping[
|
191
|
+
def wiring_outputs(self) -> Mapping[str, HandleBinding]:
|
222
192
|
return self.outputs
|
223
193
|
|
224
194
|
def get_positional_args(self) -> List[ArgValue]:
|
@@ -253,41 +223,13 @@ class QuantumFunctionCall(QuantumOperation):
|
|
253
223
|
if isinstance(function, OperandIdentifier):
|
254
224
|
function = function.name
|
255
225
|
|
256
|
-
params = values.get("function_params")
|
257
|
-
if isinstance(params, CustomFunction):
|
258
|
-
if function == CustomFunction.discriminator() and params.name != "":
|
259
|
-
function = params.name
|
260
|
-
|
261
226
|
suffix = f"{SUFFIX_MARKER}_{randomize_suffix()}"
|
262
|
-
if not function
|
227
|
+
if not function:
|
263
228
|
return name if name else suffix
|
264
229
|
return f"{function}_{suffix}"
|
265
230
|
|
266
|
-
@pydantic.root_validator(pre=True)
|
267
|
-
def validate_composite_name(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
268
|
-
if isinstance(values.get("unitary_params"), CustomFunction) and not values.get(
|
269
|
-
"unitary"
|
270
|
-
):
|
271
|
-
raise ClassiqValueError(
|
272
|
-
"`PhaseEstimation` of a user define function (`CustomFunction`) must receive the function name from the `unitary` field"
|
273
|
-
)
|
274
|
-
return values
|
275
|
-
|
276
|
-
@pydantic.root_validator(pre=True)
|
277
|
-
def _parse_function_params(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
278
|
-
f_params.parse_function_params_values(
|
279
|
-
values=values,
|
280
|
-
params_key="function_params",
|
281
|
-
discriminator_key="function",
|
282
|
-
param_classes=function_param_list.function_param_library.param_list,
|
283
|
-
default_parser_class=CustomFunction,
|
284
|
-
)
|
285
|
-
return values
|
286
|
-
|
287
231
|
@property
|
288
232
|
def pos_param_args(self) -> Dict[str, Expression]:
|
289
|
-
if TYPE_CHECKING:
|
290
|
-
assert self.func_decl is not None
|
291
233
|
return dict(
|
292
234
|
zip(
|
293
235
|
self.func_decl.param_decls.keys(),
|
@@ -301,8 +243,6 @@ class QuantumFunctionCall(QuantumOperation):
|
|
301
243
|
|
302
244
|
@property
|
303
245
|
def pos_operand_args(self) -> Dict[str, "QuantumOperand"]:
|
304
|
-
if TYPE_CHECKING:
|
305
|
-
assert self.func_decl is not None
|
306
246
|
return dict(
|
307
247
|
zip(
|
308
248
|
self.func_decl.operand_declarations.keys(),
|
@@ -316,8 +256,6 @@ class QuantumFunctionCall(QuantumOperation):
|
|
316
256
|
|
317
257
|
@property
|
318
258
|
def pos_port_args(self) -> Dict[str, HandleBinding]:
|
319
|
-
if TYPE_CHECKING:
|
320
|
-
assert self.func_decl is not None
|
321
259
|
return dict(
|
322
260
|
zip(
|
323
261
|
self.func_decl.port_declarations.keys(),
|
@@ -330,8 +268,6 @@ class QuantumFunctionCall(QuantumOperation):
|
|
330
268
|
)
|
331
269
|
|
332
270
|
def _update_pos_port_params(self) -> None:
|
333
|
-
if TYPE_CHECKING:
|
334
|
-
assert self.func_decl is not None
|
335
271
|
for name, port_decl in self.func_decl.port_declarations.items():
|
336
272
|
if port_decl.direction == PortDeclarationDirection.Input:
|
337
273
|
self.inputs[name] = self.pos_port_args[name]
|
@@ -345,45 +281,10 @@ class QuantumFunctionCall(QuantumOperation):
|
|
345
281
|
self.operands.update(self.pos_operand_args)
|
346
282
|
self._update_pos_port_params()
|
347
283
|
|
348
|
-
# TODO: note that this checks QuantumFunctionCall input register names
|
349
|
-
# are PARTIAL to FunctionParams input register names, not EQUAL.
|
350
|
-
# We might want to change that.
|
351
|
-
@staticmethod
|
352
|
-
def _validate_input_names(
|
353
|
-
*,
|
354
|
-
params: f_params.FunctionParams,
|
355
|
-
input_names: Collection[IOName],
|
356
|
-
control_states: List[ControlState],
|
357
|
-
strict_zero_ios: bool,
|
358
|
-
) -> None:
|
359
|
-
(
|
360
|
-
invalid_expressions,
|
361
|
-
invalid_slicings,
|
362
|
-
invalid_names,
|
363
|
-
) = QuantumFunctionCall._get_invalid_ios(
|
364
|
-
expressions=input_names,
|
365
|
-
params=params,
|
366
|
-
io=PortDirection.Input,
|
367
|
-
control_states=control_states,
|
368
|
-
strict_zero_ios=strict_zero_ios,
|
369
|
-
)
|
370
|
-
error_msg = []
|
371
|
-
if invalid_expressions:
|
372
|
-
error_msg.append(f"{BAD_INPUT_EXPRESSION_MSG}: {invalid_expressions}")
|
373
|
-
if invalid_names:
|
374
|
-
error_msg.append(f"{BAD_INPUT_ERROR_MSG}: {invalid_names}")
|
375
|
-
if invalid_slicings:
|
376
|
-
error_msg.append(f"{BAD_INPUT_SLICING_MSG}: {invalid_slicings}")
|
377
|
-
if error_msg:
|
378
|
-
raise ValueError("\n".join(error_msg))
|
379
|
-
|
380
284
|
def resolve_function_decl(
|
381
285
|
self,
|
382
286
|
function_dict: Mapping[str, FunctionDeclaration],
|
383
287
|
) -> None:
|
384
|
-
if not isinstance(self.function_params, CustomFunction):
|
385
|
-
return
|
386
|
-
|
387
288
|
if self._func_decl is None:
|
388
289
|
func_decl = function_dict.get(self.func_name)
|
389
290
|
if func_decl is None:
|
@@ -392,9 +293,6 @@ class QuantumFunctionCall(QuantumOperation):
|
|
392
293
|
)
|
393
294
|
self.set_func_decl(func_decl)
|
394
295
|
|
395
|
-
if TYPE_CHECKING:
|
396
|
-
assert self.func_decl is not None
|
397
|
-
|
398
296
|
if self.positional_args:
|
399
297
|
self._reduce_positional_args_to_keywords()
|
400
298
|
|
@@ -425,228 +323,10 @@ class QuantumFunctionCall(QuantumOperation):
|
|
425
323
|
), "when using the Arithmetic function, assign to the expression result register via the target parameter instead of the strict_zero_ios flag"
|
426
324
|
return strict_zero_ios
|
427
325
|
|
428
|
-
@pydantic.validator("control_states")
|
429
|
-
def _validate_control_states(
|
430
|
-
cls, control_states: List[ControlState], values: Dict[str, Any]
|
431
|
-
) -> List[ControlState]:
|
432
|
-
control_names = [ctrl_state.name for ctrl_state in control_states]
|
433
|
-
function_params = values.get("function_params")
|
434
|
-
strict_zero_ios = values.get("strict_zero_ios")
|
435
|
-
if not (
|
436
|
-
isinstance(function_params, FunctionParams)
|
437
|
-
and isinstance(strict_zero_ios, bool)
|
438
|
-
):
|
439
|
-
return control_states
|
440
|
-
all_input_names = [
|
441
|
-
*function_params.inputs_full(strict_zero_ios=strict_zero_ios),
|
442
|
-
*control_names,
|
443
|
-
]
|
444
|
-
all_output_names = [*function_params.outputs, *control_names]
|
445
|
-
if any(
|
446
|
-
cls._has_repetitions(name_list)
|
447
|
-
for name_list in (control_names, all_input_names, all_output_names)
|
448
|
-
):
|
449
|
-
raise ClassiqControlError()
|
450
|
-
return control_states
|
451
|
-
|
452
326
|
@staticmethod
|
453
327
|
def _has_repetitions(name_list: Sequence[str]) -> bool:
|
454
328
|
return len(set(name_list)) < len(name_list)
|
455
329
|
|
456
|
-
@staticmethod
|
457
|
-
def _validate_slices(
|
458
|
-
io: PortDirection,
|
459
|
-
input_names: Collection[IOName],
|
460
|
-
fp: FunctionParams,
|
461
|
-
strict_zero_ios: bool,
|
462
|
-
control_states: List[ControlState],
|
463
|
-
) -> None:
|
464
|
-
name_slice_pairs = [parse_io_slicing(input) for input in input_names]
|
465
|
-
slices_dict: Dict[str, List[slice]] = defaultdict(list)
|
466
|
-
for name, slice_obj in name_slice_pairs:
|
467
|
-
slices_dict[name].append(slice_obj)
|
468
|
-
|
469
|
-
fp_inputs = (
|
470
|
-
fp.inputs_full(strict_zero_ios)
|
471
|
-
if (io == PortDirection.Input)
|
472
|
-
else fp.outputs
|
473
|
-
)
|
474
|
-
widths = {name: reg.size for name, reg in fp_inputs.items()}
|
475
|
-
control_names = {state.name for state in control_states}
|
476
|
-
|
477
|
-
for name in slices_dict:
|
478
|
-
if name in control_names:
|
479
|
-
continue
|
480
|
-
assert name in widths, "Name not in widths"
|
481
|
-
if not QuantumFunctionCall._register_validate_slices(
|
482
|
-
slices_dict[name], widths[name]
|
483
|
-
):
|
484
|
-
raise ValueError(BAD_INPUT_SLICING_MSG)
|
485
|
-
|
486
|
-
@staticmethod
|
487
|
-
def _register_validate_slices(slices: List[slice], reg_width: int) -> bool:
|
488
|
-
widths_separated = [len(range(reg_width)[reg_slice]) for reg_slice in slices]
|
489
|
-
# examples: slice(0), slice(5,None) when width <= 5, slice(5,3)
|
490
|
-
empty_slices = 0 in widths_separated
|
491
|
-
|
492
|
-
max_stop = max(reg_slice.stop or 0 for reg_slice in slices)
|
493
|
-
out_of_range = max_stop > reg_width
|
494
|
-
|
495
|
-
all_widths_separated = sum(widths_separated)
|
496
|
-
all_indices = set(
|
497
|
-
itertools.chain.from_iterable(
|
498
|
-
range(reg_width)[reg_slice] for reg_slice in slices
|
499
|
-
)
|
500
|
-
)
|
501
|
-
all_widths_combined = len(all_indices)
|
502
|
-
overlapping_slices = all_widths_combined != all_widths_separated
|
503
|
-
|
504
|
-
return not any((empty_slices, out_of_range, overlapping_slices))
|
505
|
-
|
506
|
-
@pydantic.validator("inputs")
|
507
|
-
def _validate_inputs(
|
508
|
-
cls, inputs: Mapping[IOName, HandleBinding], values: Dict[str, Any]
|
509
|
-
) -> Mapping[IOName, HandleBinding]:
|
510
|
-
params: Optional[FunctionParams] = values.get("function_params")
|
511
|
-
strict_zero_ios: bool = values.get("strict_zero_ios", True)
|
512
|
-
control_states: List[ControlState] = values.get("control_states", list())
|
513
|
-
if params is None:
|
514
|
-
return dict()
|
515
|
-
if isinstance(params, CustomFunction):
|
516
|
-
if not isinstance(inputs, dict):
|
517
|
-
raise ValueError(CUSTOM_FUNCTION_SINGLE_IO_ERROR)
|
518
|
-
return inputs
|
519
|
-
|
520
|
-
cls._validate_input_names(
|
521
|
-
params=params,
|
522
|
-
input_names=inputs.keys(),
|
523
|
-
control_states=control_states,
|
524
|
-
strict_zero_ios=strict_zero_ios,
|
525
|
-
)
|
526
|
-
|
527
|
-
cls._validate_slices(
|
528
|
-
PortDirection.Input,
|
529
|
-
inputs.keys(),
|
530
|
-
params,
|
531
|
-
strict_zero_ios,
|
532
|
-
control_states,
|
533
|
-
)
|
534
|
-
|
535
|
-
return inputs
|
536
|
-
|
537
|
-
@staticmethod
|
538
|
-
def _validate_output_names(
|
539
|
-
*,
|
540
|
-
params: f_params.FunctionParams,
|
541
|
-
output_names: Collection[IOName],
|
542
|
-
control_states: List[ControlState],
|
543
|
-
strict_zero_ios: bool,
|
544
|
-
) -> None:
|
545
|
-
(
|
546
|
-
invalid_expressions,
|
547
|
-
invalid_slicings,
|
548
|
-
invalid_names,
|
549
|
-
) = QuantumFunctionCall._get_invalid_ios(
|
550
|
-
expressions=output_names,
|
551
|
-
params=params,
|
552
|
-
io=PortDirection.Output,
|
553
|
-
control_states=control_states,
|
554
|
-
strict_zero_ios=strict_zero_ios,
|
555
|
-
)
|
556
|
-
error_msg = []
|
557
|
-
if invalid_expressions:
|
558
|
-
error_msg.append(f"{BAD_OUTPUT_EXPRESSION_MSG}: {invalid_expressions}")
|
559
|
-
if invalid_names:
|
560
|
-
error_msg.append(f"{BAD_OUTPUT_ERROR_MSG}: {invalid_names}")
|
561
|
-
if invalid_slicings:
|
562
|
-
error_msg.append(f"{BAD_OUTPUT_SLICING_MSG}: {invalid_slicings}")
|
563
|
-
if error_msg:
|
564
|
-
raise ValueError("\n".join(error_msg))
|
565
|
-
|
566
|
-
@pydantic.validator("outputs")
|
567
|
-
def _validate_outputs(
|
568
|
-
cls, outputs: Mapping[IOName, HandleBinding], values: Dict[str, Any]
|
569
|
-
) -> Mapping[IOName, HandleBinding]:
|
570
|
-
params = values.get("function_params")
|
571
|
-
strict_zero_ios: bool = values.get("strict_zero_ios", True)
|
572
|
-
control_states = values.get("control_states", list())
|
573
|
-
if params is None:
|
574
|
-
return outputs
|
575
|
-
if isinstance(params, CustomFunction):
|
576
|
-
if not isinstance(outputs, dict):
|
577
|
-
raise ValueError(CUSTOM_FUNCTION_SINGLE_IO_ERROR)
|
578
|
-
return outputs
|
579
|
-
|
580
|
-
cls._validate_output_names(
|
581
|
-
params=params,
|
582
|
-
output_names=outputs.keys(),
|
583
|
-
control_states=control_states,
|
584
|
-
strict_zero_ios=strict_zero_ios,
|
585
|
-
)
|
586
|
-
|
587
|
-
cls._validate_slices(
|
588
|
-
PortDirection.Output,
|
589
|
-
outputs.keys(),
|
590
|
-
params,
|
591
|
-
strict_zero_ios,
|
592
|
-
control_states,
|
593
|
-
)
|
594
|
-
|
595
|
-
return outputs
|
596
|
-
|
597
|
-
@staticmethod
|
598
|
-
def _get_invalid_ios(
|
599
|
-
*,
|
600
|
-
expressions: Iterable[str],
|
601
|
-
params: f_params.FunctionParams,
|
602
|
-
io: f_params.PortDirection,
|
603
|
-
control_states: List[ControlState],
|
604
|
-
strict_zero_ios: bool,
|
605
|
-
) -> Tuple[List[str], List[str], List[str]]:
|
606
|
-
expression_matches: Iterable[Optional[Match]] = map(
|
607
|
-
functools.partial(re.fullmatch, IO_REGEX), expressions
|
608
|
-
)
|
609
|
-
|
610
|
-
valid_matches: List[Match] = []
|
611
|
-
invalid_expressions: List[str] = []
|
612
|
-
for expression, expression_match in zip(expressions, expression_matches):
|
613
|
-
invalid_expressions.append(
|
614
|
-
expression
|
615
|
-
) if expression_match is None else valid_matches.append(expression_match)
|
616
|
-
|
617
|
-
invalid_slicings: List[str] = []
|
618
|
-
invalid_names: List[str] = []
|
619
|
-
valid_names = frozenset(
|
620
|
-
params.inputs_full(strict_zero_ios)
|
621
|
-
if io == PortDirection.Input
|
622
|
-
else params.outputs
|
623
|
-
)
|
624
|
-
for match in valid_matches:
|
625
|
-
name = match.groupdict().get(NAME)
|
626
|
-
if name is None:
|
627
|
-
raise AssertionError("Input/output name validation error")
|
628
|
-
|
629
|
-
slicing = match.groupdict().get(SLICING)
|
630
|
-
if slicing is not None and re.fullmatch(LEGAL_SLICING, slicing) is None:
|
631
|
-
invalid_slicings.append(match.string)
|
632
|
-
|
633
|
-
if name in valid_names:
|
634
|
-
continue
|
635
|
-
elif all(state.name != name for state in control_states):
|
636
|
-
invalid_names.append(name)
|
637
|
-
|
638
|
-
return invalid_expressions, invalid_slicings, invalid_names
|
639
|
-
|
640
|
-
def get_param_exprs(self) -> Dict[str, Expression]:
|
641
|
-
if isinstance(self.function_params, CustomFunction):
|
642
|
-
return self.params
|
643
|
-
else:
|
644
|
-
return {
|
645
|
-
name: Expression(expr=raw_expr)
|
646
|
-
for name, raw_expr in self.function_params
|
647
|
-
if self.function_params.is_field_gen_param(name)
|
648
|
-
}
|
649
|
-
|
650
330
|
@pydantic.root_validator()
|
651
331
|
def validate_handles(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
652
332
|
inputs = values.get("inputs", dict())
|
@@ -659,9 +339,6 @@ class QuantumFunctionCall(QuantumOperation):
|
|
659
339
|
|
660
340
|
return values
|
661
341
|
|
662
|
-
class Config:
|
663
|
-
extra = Extra.forbid
|
664
|
-
|
665
342
|
|
666
343
|
class QuantumLambdaFunction(BaseModel):
|
667
344
|
"""
|
@@ -2,11 +2,7 @@ from typing import Any, ClassVar, Dict, List, Mapping, Sequence, Set, Type, Unio
|
|
2
2
|
|
3
3
|
import pydantic
|
4
4
|
|
5
|
-
from classiq.interface.generator.function_params import
|
6
|
-
ArithmeticIODict,
|
7
|
-
IOName,
|
8
|
-
PortDirection,
|
9
|
-
)
|
5
|
+
from classiq.interface.generator.function_params import ArithmeticIODict, PortDirection
|
10
6
|
from classiq.interface.generator.functions.function_declaration import (
|
11
7
|
FunctionDeclaration,
|
12
8
|
)
|
@@ -56,7 +52,7 @@ PositionalArg = Union[
|
|
56
52
|
|
57
53
|
|
58
54
|
def _ports_to_registers(
|
59
|
-
port_declarations: Dict[
|
55
|
+
port_declarations: Dict[str, PortDeclaration], direction: PortDirection
|
60
56
|
) -> ArithmeticIODict:
|
61
57
|
return {
|
62
58
|
name: quantum_var_to_register(name, port_decl.quantum_type)
|
@@ -70,7 +66,7 @@ class QuantumFunctionDeclaration(FunctionDeclaration):
|
|
70
66
|
Facilitates the creation of a common quantum function interface object.
|
71
67
|
"""
|
72
68
|
|
73
|
-
port_declarations: Dict[
|
69
|
+
port_declarations: Dict[str, PortDeclaration] = pydantic.Field(
|
74
70
|
description="The input and output ports of the function.",
|
75
71
|
default_factory=dict,
|
76
72
|
)
|
@@ -89,11 +85,11 @@ class QuantumFunctionDeclaration(FunctionDeclaration):
|
|
89
85
|
] = {}
|
90
86
|
|
91
87
|
@property
|
92
|
-
def input_set(self) -> Set[
|
88
|
+
def input_set(self) -> Set[str]:
|
93
89
|
return set(self.inputs.keys())
|
94
90
|
|
95
91
|
@property
|
96
|
-
def output_set(self) -> Set[
|
92
|
+
def output_set(self) -> Set[str]:
|
97
93
|
return set(self.outputs.keys())
|
98
94
|
|
99
95
|
@property
|
@@ -138,8 +134,8 @@ class QuantumFunctionDeclaration(FunctionDeclaration):
|
|
138
134
|
|
139
135
|
@pydantic.validator("port_declarations")
|
140
136
|
def _validate_port_declarations_names(
|
141
|
-
cls, port_declarations: Dict[
|
142
|
-
) -> Dict[
|
137
|
+
cls, port_declarations: Dict[str, PortDeclaration]
|
138
|
+
) -> Dict[str, PortDeclaration]:
|
143
139
|
validate_nameables_mapping(port_declarations, "Port")
|
144
140
|
return port_declarations
|
145
141
|
|
@@ -1,26 +1,32 @@
|
|
1
1
|
from typing import Mapping, Union
|
2
2
|
|
3
|
-
from pydantic import BaseModel
|
3
|
+
from pydantic import BaseModel, Extra
|
4
4
|
|
5
|
-
from classiq.interface.
|
6
|
-
|
5
|
+
from classiq.interface.model.handle_binding import (
|
6
|
+
HandleBinding,
|
7
|
+
SlicedHandleBinding,
|
8
|
+
SubscriptHandleBinding,
|
9
|
+
)
|
7
10
|
|
8
11
|
|
9
12
|
class QuantumStatement(BaseModel):
|
10
|
-
|
13
|
+
class Config:
|
14
|
+
extra = Extra.forbid
|
11
15
|
|
12
16
|
|
13
17
|
class QuantumOperation(QuantumStatement):
|
14
18
|
@property
|
15
|
-
def wiring_inputs(self) -> Mapping[
|
19
|
+
def wiring_inputs(self) -> Mapping[str, HandleBinding]:
|
16
20
|
return dict()
|
17
21
|
|
18
22
|
@property
|
19
23
|
def wiring_inouts(
|
20
24
|
self,
|
21
|
-
) -> Mapping[
|
25
|
+
) -> Mapping[
|
26
|
+
str, Union[SlicedHandleBinding, SubscriptHandleBinding, HandleBinding]
|
27
|
+
]:
|
22
28
|
return dict()
|
23
29
|
|
24
30
|
@property
|
25
|
-
def wiring_outputs(self) -> Mapping[
|
31
|
+
def wiring_outputs(self) -> Mapping[str, HandleBinding]:
|
26
32
|
return dict()
|
@@ -1,7 +1,6 @@
|
|
1
1
|
import abc
|
2
2
|
from typing import Dict, Mapping, Type
|
3
3
|
|
4
|
-
from classiq.interface.generator.function_params import IOName
|
5
4
|
from classiq.interface.generator.functions.port_declaration import (
|
6
5
|
PortDeclarationDirection,
|
7
6
|
)
|
@@ -17,7 +16,7 @@ EXPECTED_TERMINAL_STATES: Dict[PortDeclarationDirection, HandleState] = {
|
|
17
16
|
class HandleValidationBase(abc.ABC):
|
18
17
|
def __init__(
|
19
18
|
self,
|
20
|
-
port_declarations: Mapping[
|
19
|
+
port_declarations: Mapping[str, PortDeclaration],
|
21
20
|
) -> None:
|
22
21
|
self._port_declarations = port_declarations.values()
|
23
22
|
|