classiq 0.36.0__py3-none-any.whl → 0.37.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 +1 -0
- classiq/_internals/api_wrapper.py +24 -6
- classiq/_internals/authentication/device.py +6 -3
- classiq/_internals/authentication/token_manager.py +21 -5
- classiq/_internals/client.py +7 -2
- classiq/_internals/config.py +12 -0
- classiq/_internals/host_checker.py +1 -1
- classiq/_internals/jobs.py +3 -1
- classiq/_internals/type_validation.py +3 -6
- classiq/analyzer/analyzer.py +1 -0
- classiq/analyzer/rb.py +3 -5
- classiq/applications_model_constructors/chemistry_model_constructor.py +42 -67
- classiq/applications_model_constructors/grover_model_constructor.py +27 -18
- classiq/exceptions.py +5 -0
- classiq/execution/jobs.py +13 -4
- classiq/executor.py +3 -2
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +0 -6
- classiq/interface/analyzer/result.py +0 -4
- classiq/interface/backend/backend_preferences.py +2 -2
- classiq/interface/backend/quantum_backend_providers.py +1 -1
- classiq/interface/execution/resource_estimator.py +7 -0
- classiq/interface/execution/result.py +5 -0
- classiq/interface/executor/register_initialization.py +3 -1
- classiq/interface/executor/vqe_result.py +1 -0
- classiq/interface/generator/ansatz_library.py +3 -3
- classiq/interface/generator/arith/argument_utils.py +4 -4
- classiq/interface/generator/arith/arithmetic.py +4 -2
- classiq/interface/generator/arith/arithmetic_arg_type_validator.py +11 -5
- classiq/interface/generator/arith/arithmetic_expression_parser.py +8 -7
- classiq/interface/generator/arith/arithmetic_operations.py +7 -0
- classiq/interface/generator/arith/arithmetic_param_getters.py +97 -16
- classiq/interface/generator/arith/arithmetic_result_builder.py +13 -3
- classiq/interface/generator/arith/binary_ops.py +8 -10
- classiq/interface/generator/arith/extremum_operations.py +2 -2
- classiq/interface/generator/arith/number_utils.py +20 -23
- classiq/interface/generator/arith/register_user_input.py +3 -1
- classiq/interface/generator/arith/unary_ops.py +9 -13
- classiq/interface/generator/expressions/atomic_expression_functions.py +2 -0
- classiq/interface/generator/expressions/expression.py +7 -2
- classiq/interface/generator/expressions/qmod_qnum_proxy.py +22 -0
- classiq/interface/generator/expressions/qmod_sized_proxy.py +2 -12
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/atomic_quantum_functions.py +63 -3
- classiq/interface/generator/functions/core_lib_declarations/quantum_functions/std_lib_functions.py +143 -17
- classiq/interface/generator/functions/core_lib_declarations/quantum_operators.py +41 -16
- classiq/interface/generator/functions/native_function_definition.py +3 -3
- classiq/interface/generator/model/constraints.py +3 -3
- classiq/interface/generator/model/preferences/preferences.py +13 -9
- classiq/interface/generator/noise_properties.py +5 -5
- classiq/interface/generator/qpe.py +5 -5
- classiq/interface/generator/quantum_function_call.py +5 -3
- classiq/interface/generator/randomized_benchmarking.py +5 -3
- classiq/interface/generator/visitor.py +1 -2
- classiq/interface/hardware.py +1 -1
- classiq/interface/helpers/custom_pydantic_types.py +6 -0
- classiq/interface/model/{modular_addition_operation.py → inplace_binary_operation.py} +16 -2
- classiq/interface/model/native_function_definition.py +2 -24
- classiq/interface/model/operator_synthesis_data.py +6 -0
- classiq/interface/model/quantum_expressions/amplitude_loading_operation.py +8 -4
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +9 -5
- classiq/interface/model/quantum_expressions/control_state.py +38 -0
- classiq/interface/model/quantum_expressions/quantum_expression.py +21 -11
- classiq/interface/model/quantum_function_call.py +81 -6
- classiq/interface/model/quantum_function_declaration.py +3 -3
- classiq/interface/model/quantum_if_operation.py +95 -0
- classiq/interface/model/resolvers/function_call_resolver.py +1 -1
- classiq/interface/model/validations/handles_validator.py +42 -15
- classiq/interface/server/routes.py +10 -6
- classiq/model/function_handler.pyi +86 -86
- classiq/model/model.py +1 -0
- classiq/qmod/__init__.py +6 -1
- classiq/qmod/builtins/__init__.py +13 -1
- classiq/qmod/builtins/classical_execution_primitives.py +109 -0
- classiq/qmod/builtins/classical_functions.py +68 -0
- classiq/qmod/builtins/functions.py +88 -18
- classiq/qmod/builtins/operations.py +60 -35
- classiq/qmod/classical_function.py +40 -0
- classiq/qmod/declaration_inferrer.py +5 -2
- classiq/qmod/qmod_variable.py +17 -10
- classiq/qmod/quantum_callable.py +24 -3
- classiq/qmod/quantum_expandable.py +131 -21
- classiq/qmod/quantum_function.py +12 -2
- classiq/qmod/symbolic.py +182 -107
- classiq/qmod/symbolic_expr.py +11 -10
- classiq/qmod/symbolic_type.py +8 -0
- classiq/quantum_functions/decorators.py +2 -4
- classiq/quantum_functions/function_library.py +1 -0
- {classiq-0.36.0.dist-info → classiq-0.37.0.dist-info}/METADATA +1 -1
- {classiq-0.36.0.dist-info → classiq-0.37.0.dist-info}/RECORD +90 -82
- classiq/interface/model/local_variable_declaration.py +0 -7
- {classiq-0.36.0.dist-info → classiq-0.37.0.dist-info}/WHEEL +0 -0
@@ -6,12 +6,26 @@ from classiq.interface.helpers.pydantic_model_helpers import nameables_to_dict
|
|
6
6
|
from classiq.interface.model.handle_binding import HandleBinding
|
7
7
|
from classiq.interface.model.quantum_statement import QuantumOperation
|
8
8
|
|
9
|
+
from classiq._internals.enum_utils import StrEnum
|
9
10
|
from classiq.exceptions import ClassiqValueError
|
10
11
|
|
11
12
|
|
12
|
-
class
|
13
|
+
class BinaryOperation(StrEnum):
|
14
|
+
Addition = "addition"
|
15
|
+
Xor = "xor"
|
16
|
+
|
17
|
+
@property
|
18
|
+
def internal_function(self) -> str:
|
19
|
+
return {
|
20
|
+
BinaryOperation.Addition: "modular_add",
|
21
|
+
BinaryOperation.Xor: "integer_xor",
|
22
|
+
}[self]
|
23
|
+
|
24
|
+
|
25
|
+
class InplaceBinaryOperation(QuantumOperation):
|
13
26
|
target: HandleBinding
|
14
27
|
value: HandleBinding
|
28
|
+
operation: BinaryOperation
|
15
29
|
|
16
30
|
@property
|
17
31
|
def wiring_inouts(self) -> Mapping[str, HandleBinding]:
|
@@ -20,5 +34,5 @@ class ModularAdditionOperation(QuantumOperation):
|
|
20
34
|
@pydantic.validator("target", "value")
|
21
35
|
def validate_handle(cls, handle: HandleBinding) -> HandleBinding:
|
22
36
|
if not handle.is_bindable():
|
23
|
-
raise ClassiqValueError(f"Cannot bind '{handle}'")
|
37
|
+
raise ClassiqValueError(f"Cannot bind '{handle!r}'")
|
24
38
|
return handle
|
@@ -1,9 +1,7 @@
|
|
1
|
-
from typing import
|
1
|
+
from typing import List
|
2
2
|
|
3
3
|
import pydantic
|
4
4
|
|
5
|
-
from classiq.interface.model.local_variable_declaration import LocalVariableDeclaration
|
6
|
-
from classiq.interface.model.port_declaration import PortDeclaration
|
7
5
|
from classiq.interface.model.quantum_function_call import ConcreteQuantumStatement
|
8
6
|
from classiq.interface.model.quantum_function_declaration import (
|
9
7
|
QuantumFunctionDeclaration,
|
@@ -28,12 +26,8 @@ class NativeFunctionDefinition(QuantumFunctionDeclaration):
|
|
28
26
|
default_factory=list, description="List of function calls to perform."
|
29
27
|
)
|
30
28
|
|
31
|
-
local_handles: List[LocalVariableDeclaration] = pydantic.Field(
|
32
|
-
default_factory=list, description="List of local handles."
|
33
|
-
)
|
34
|
-
|
35
29
|
def validate_body(self) -> None:
|
36
|
-
handle_validator = HandleValidator(self.port_declarations
|
30
|
+
handle_validator = HandleValidator(self.port_declarations)
|
37
31
|
|
38
32
|
for statement in self.body:
|
39
33
|
if isinstance(statement, VariableDeclarationStatement):
|
@@ -42,19 +36,3 @@ class NativeFunctionDefinition(QuantumFunctionDeclaration):
|
|
42
36
|
handle_validator.handle_call(statement)
|
43
37
|
|
44
38
|
handle_validator.report_errored_handles(ClassiqValueError)
|
45
|
-
|
46
|
-
@pydantic.validator("local_handles")
|
47
|
-
def validate_local_handles(
|
48
|
-
cls, local_handles: List[LocalVariableDeclaration], values: Dict[str, Any]
|
49
|
-
) -> List[LocalVariableDeclaration]:
|
50
|
-
ports: Optional[Dict[str, PortDeclaration]] = values.get("port_declarations")
|
51
|
-
if ports is None:
|
52
|
-
return local_handles
|
53
|
-
|
54
|
-
intersection = {handle.name for handle in local_handles} & ports.keys()
|
55
|
-
if intersection:
|
56
|
-
raise ClassiqValueError(
|
57
|
-
f"The names {intersection} are both local handles and ports"
|
58
|
-
)
|
59
|
-
|
60
|
-
return local_handles
|
@@ -40,3 +40,9 @@ class ControlOperatorSynthesisData(OperatorSynthesisData):
|
|
40
40
|
return {
|
41
41
|
"control_states": [self._ctrl_state],
|
42
42
|
}
|
43
|
+
|
44
|
+
|
45
|
+
class ComputeOperatorSynthesisData(OperatorSynthesisData):
|
46
|
+
@property
|
47
|
+
def call_kwargs(self) -> Dict[str, Any]:
|
48
|
+
return {"should_control": False}
|
@@ -12,7 +12,7 @@ from classiq.interface.model.handle_binding import (
|
|
12
12
|
SubscriptHandleBinding,
|
13
13
|
)
|
14
14
|
from classiq.interface.model.quantum_expressions.quantum_expression import (
|
15
|
-
|
15
|
+
QuantumAssignmentOperation,
|
16
16
|
)
|
17
17
|
from classiq.interface.model.quantum_type import QuantumBit, QuantumNumeric, QuantumType
|
18
18
|
|
@@ -27,7 +27,7 @@ VAR_DOMAIN_ILLEGAL = (
|
|
27
27
|
)
|
28
28
|
|
29
29
|
|
30
|
-
class AmplitudeLoadingOperation(
|
30
|
+
class AmplitudeLoadingOperation(QuantumAssignmentOperation):
|
31
31
|
_result_type: QuantumType = pydantic.PrivateAttr(default_factory=QuantumBit)
|
32
32
|
|
33
33
|
@property
|
@@ -40,7 +40,11 @@ class AmplitudeLoadingOperation(QuantumExpressionOperation):
|
|
40
40
|
return dict()
|
41
41
|
return {AMPLITUDE_IO_NAME: self.var_handles[0]}
|
42
42
|
|
43
|
-
def initialize_var_types(
|
43
|
+
def initialize_var_types(
|
44
|
+
self,
|
45
|
+
var_types: Dict[str, QuantumType],
|
46
|
+
machine_precision: int,
|
47
|
+
) -> None:
|
44
48
|
if len(var_types) != 1:
|
45
49
|
raise ClassiqValueError(MULTI_VARS_UNSUPPORTED_ERROR)
|
46
50
|
var_type = var_types[self.var_handles[0].name]
|
@@ -49,7 +53,7 @@ class AmplitudeLoadingOperation(QuantumExpressionOperation):
|
|
49
53
|
and var_type.fraction_digits == var_type.size_in_bits
|
50
54
|
):
|
51
55
|
raise ClassiqValueError(VAR_DOMAIN_ILLEGAL)
|
52
|
-
super().initialize_var_types(var_types)
|
56
|
+
super().initialize_var_types(var_types, machine_precision)
|
53
57
|
|
54
58
|
@classmethod
|
55
59
|
def result_name(cls) -> str:
|
@@ -12,20 +12,24 @@ from classiq.interface.model.handle_binding import (
|
|
12
12
|
SubscriptHandleBinding,
|
13
13
|
)
|
14
14
|
from classiq.interface.model.quantum_expressions.quantum_expression import (
|
15
|
-
|
15
|
+
QuantumAssignmentOperation,
|
16
16
|
)
|
17
17
|
from classiq.interface.model.quantum_type import QuantumType
|
18
18
|
|
19
19
|
|
20
|
-
class ArithmeticOperation(
|
20
|
+
class ArithmeticOperation(QuantumAssignmentOperation):
|
21
21
|
inplace_result: bool = pydantic.Field(
|
22
22
|
description="Determines whether the result variable is initialized",
|
23
23
|
)
|
24
24
|
|
25
|
-
def initialize_var_types(
|
26
|
-
|
25
|
+
def initialize_var_types(
|
26
|
+
self,
|
27
|
+
var_types: Dict[str, QuantumType],
|
28
|
+
machine_precision: int,
|
29
|
+
) -> None:
|
30
|
+
super().initialize_var_types(var_types, machine_precision)
|
27
31
|
self._result_type = compute_arithmetic_result_type(
|
28
|
-
self.expression.expr, var_types
|
32
|
+
self.expression.expr, var_types, machine_precision
|
29
33
|
)
|
30
34
|
|
31
35
|
@property
|
@@ -0,0 +1,38 @@
|
|
1
|
+
from classiq.exceptions import ClassiqValueError
|
2
|
+
|
3
|
+
|
4
|
+
def min_unsigned_bit_length(number: int) -> int:
|
5
|
+
if number < 0:
|
6
|
+
raise ClassiqValueError(
|
7
|
+
f"Quantum register is not signed but control value " # noqa: B907
|
8
|
+
f"'{number}' is negative"
|
9
|
+
)
|
10
|
+
return 1 if number == 0 else number.bit_length()
|
11
|
+
|
12
|
+
|
13
|
+
def min_signed_bit_length(number: int) -> int:
|
14
|
+
pos_val = abs(number)
|
15
|
+
is_whole = pos_val & (pos_val - 1) == 0
|
16
|
+
if number <= 0 and is_whole:
|
17
|
+
return min_unsigned_bit_length(pos_val)
|
18
|
+
return min_unsigned_bit_length(pos_val) + 1
|
19
|
+
|
20
|
+
|
21
|
+
def min_bit_length(number: int, is_signed: bool) -> int:
|
22
|
+
return (
|
23
|
+
min_signed_bit_length(number) if is_signed else min_unsigned_bit_length(number)
|
24
|
+
)
|
25
|
+
|
26
|
+
|
27
|
+
def to_twos_complement(value: int, bits: int, is_signed: bool) -> str:
|
28
|
+
required_bits = min_bit_length(value, is_signed)
|
29
|
+
if bits < required_bits:
|
30
|
+
raise ClassiqValueError(
|
31
|
+
f"Cannot express '{value}' using {bits} bits: " # noqa: B907
|
32
|
+
f"at least {required_bits} bits are required"
|
33
|
+
)
|
34
|
+
if value >= 0:
|
35
|
+
return bin(value)[2:].zfill(bits)[::-1]
|
36
|
+
mask = (1 << bits) - 1
|
37
|
+
value = (abs(value) ^ mask) + 1
|
38
|
+
return bin(value)[:1:-1].rjust(bits, "1")
|
@@ -4,6 +4,9 @@ from typing import Dict, List, Mapping, Optional, Set, Union
|
|
4
4
|
|
5
5
|
import pydantic
|
6
6
|
|
7
|
+
from classiq.interface.generator.arith.arithmetic_expression_validator import (
|
8
|
+
DEFAULT_SUPPORTED_FUNC_NAMES,
|
9
|
+
)
|
7
10
|
from classiq.interface.generator.expressions.expression import Expression
|
8
11
|
from classiq.interface.generator.expressions.sympy_supported_expressions import (
|
9
12
|
SYMPY_SUPPORTED_EXPRESSIONS,
|
@@ -25,25 +28,19 @@ class VarRefCollector(ast.NodeVisitor):
|
|
25
28
|
def generic_visit(self, node: ast.AST) -> None:
|
26
29
|
if isinstance(node, ast.Name) and node.id not in set(
|
27
30
|
SYMPY_SUPPORTED_EXPRESSIONS
|
28
|
-
):
|
31
|
+
) | set(DEFAULT_SUPPORTED_FUNC_NAMES):
|
29
32
|
self.var_names.add(node.id)
|
30
33
|
super().generic_visit(node)
|
31
34
|
|
32
35
|
|
33
36
|
class QuantumExpressionOperation(QuantumOperation):
|
34
37
|
expression: Expression = pydantic.Field()
|
35
|
-
result_var: HandleBinding = pydantic.Field(
|
36
|
-
description="The variable storing the expression result"
|
37
|
-
)
|
38
38
|
_var_handles: List[HandleBinding] = pydantic.PrivateAttr(
|
39
39
|
default_factory=list,
|
40
40
|
)
|
41
41
|
_var_types: Dict[str, QuantumType] = pydantic.PrivateAttr(
|
42
42
|
default_factory=dict,
|
43
43
|
)
|
44
|
-
_result_type: Optional[QuantumType] = pydantic.PrivateAttr(
|
45
|
-
default=None,
|
46
|
-
)
|
47
44
|
|
48
45
|
@property
|
49
46
|
def var_handles(self) -> List[HandleBinding]:
|
@@ -56,7 +53,11 @@ class QuantumExpressionOperation(QuantumOperation):
|
|
56
53
|
def var_types(self) -> Dict[str, QuantumType]:
|
57
54
|
return self._var_types
|
58
55
|
|
59
|
-
def initialize_var_types(
|
56
|
+
def initialize_var_types(
|
57
|
+
self,
|
58
|
+
var_types: Dict[str, QuantumType],
|
59
|
+
machine_precision: int,
|
60
|
+
) -> None:
|
60
61
|
assert len(var_types) == len(self.var_handles)
|
61
62
|
self._var_types = var_types
|
62
63
|
|
@@ -68,15 +69,24 @@ class QuantumExpressionOperation(QuantumOperation):
|
|
68
69
|
]:
|
69
70
|
return nameables_to_dict(self.var_handles)
|
70
71
|
|
71
|
-
|
72
|
-
|
73
|
-
|
72
|
+
|
73
|
+
class QuantumAssignmentOperation(QuantumExpressionOperation):
|
74
|
+
result_var: HandleBinding = pydantic.Field(
|
75
|
+
description="The variable storing the expression result"
|
76
|
+
)
|
77
|
+
_result_type: Optional[QuantumType] = pydantic.PrivateAttr(
|
78
|
+
default=None,
|
79
|
+
)
|
74
80
|
|
75
81
|
@property
|
76
82
|
def result_type(self) -> QuantumType:
|
77
83
|
assert self._result_type is not None
|
78
84
|
return self._result_type
|
79
85
|
|
86
|
+
@property
|
87
|
+
def wiring_outputs(self) -> Mapping[str, HandleBinding]:
|
88
|
+
return {self.result_name(): self.result_var}
|
89
|
+
|
80
90
|
@classmethod
|
81
91
|
@abc.abstractmethod
|
82
92
|
def result_name(cls) -> str:
|
@@ -23,7 +23,7 @@ from classiq.interface.model.handle_binding import (
|
|
23
23
|
SlicedHandleBinding,
|
24
24
|
SubscriptHandleBinding,
|
25
25
|
)
|
26
|
-
from classiq.interface.model.
|
26
|
+
from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
|
27
27
|
from classiq.interface.model.numeric_reinterpretation import (
|
28
28
|
NumericReinterpretationOperation,
|
29
29
|
)
|
@@ -38,6 +38,7 @@ from classiq.interface.model.quantum_function_declaration import (
|
|
38
38
|
QuantumFunctionDeclaration,
|
39
39
|
QuantumOperandDeclaration,
|
40
40
|
)
|
41
|
+
from classiq.interface.model.quantum_if_operation import QuantumIfOperation
|
41
42
|
from classiq.interface.model.quantum_statement import QuantumOperation
|
42
43
|
from classiq.interface.model.validation_handle import get_unique_handle_names
|
43
44
|
from classiq.interface.model.variable_declaration_statement import (
|
@@ -288,7 +289,8 @@ class QuantumFunctionCall(QuantumOperation):
|
|
288
289
|
|
289
290
|
def resolve_function_decl(
|
290
291
|
self,
|
291
|
-
function_dict: Mapping[str,
|
292
|
+
function_dict: Mapping[str, QuantumFunctionDeclaration],
|
293
|
+
check_operands: bool,
|
292
294
|
) -> None:
|
293
295
|
if self._func_decl is None:
|
294
296
|
func_decl = function_dict.get(self.func_name)
|
@@ -312,6 +314,8 @@ class QuantumFunctionCall(QuantumOperation):
|
|
312
314
|
set(self.func_decl.operand_declarations.keys()),
|
313
315
|
self.func_name,
|
314
316
|
)
|
317
|
+
if check_operands:
|
318
|
+
_check_operands_against_declaration(self, self.func_decl, function_dict)
|
315
319
|
|
316
320
|
for name, op in self.operands.items():
|
317
321
|
op_decl = self.func_decl.operand_declarations[name]
|
@@ -352,7 +356,8 @@ ConcreteQuantumStatement = Union[
|
|
352
356
|
VariableDeclarationStatement,
|
353
357
|
BindOperation,
|
354
358
|
NumericReinterpretationOperation,
|
355
|
-
|
359
|
+
InplaceBinaryOperation,
|
360
|
+
QuantumIfOperation,
|
356
361
|
]
|
357
362
|
|
358
363
|
|
@@ -402,6 +407,7 @@ QuantumCallable = Union[str, QuantumLambdaFunction]
|
|
402
407
|
QuantumOperand = Union[QuantumCallable, List[QuantumCallable], LambdaListComprehension]
|
403
408
|
|
404
409
|
QuantumFunctionCall.update_forward_refs()
|
410
|
+
QuantumIfOperation.update_forward_refs(QuantumOperand=QuantumOperand)
|
405
411
|
|
406
412
|
|
407
413
|
def get_lambda_defs(operand: QuantumOperand) -> List[QuantumCallable]:
|
@@ -441,16 +447,85 @@ def _check_ports_against_declaration(
|
|
441
447
|
)
|
442
448
|
|
443
449
|
|
450
|
+
def _check_operand_against_declaration(
|
451
|
+
call: QuantumFunctionCall,
|
452
|
+
operand_decl: QuantumOperandDeclaration,
|
453
|
+
operand_argument: QuantumOperand,
|
454
|
+
function_dict: Mapping[str, QuantumFunctionDeclaration],
|
455
|
+
in_list: bool = False,
|
456
|
+
) -> None:
|
457
|
+
if isinstance(operand_argument, list):
|
458
|
+
if in_list:
|
459
|
+
raise ClassiqValueError(
|
460
|
+
f"{str(operand_argument)!r} argument to {call.func_decl.name!r} is not "
|
461
|
+
f"a valid operand. Nested operand lists are not permitted"
|
462
|
+
)
|
463
|
+
for arg in operand_argument:
|
464
|
+
_check_operand_against_declaration(
|
465
|
+
call, operand_decl, arg, function_dict, in_list=True
|
466
|
+
)
|
467
|
+
return
|
468
|
+
operand_arg_decl: QuantumFunctionDeclaration
|
469
|
+
if isinstance(operand_argument, str):
|
470
|
+
if operand_argument not in function_dict:
|
471
|
+
raise ClassiqValueError(
|
472
|
+
f"{operand_argument!r} argument to {call.func_decl.name!r} is not a "
|
473
|
+
f"registered function"
|
474
|
+
)
|
475
|
+
operand_arg_decl = function_dict[operand_argument]
|
476
|
+
elif isinstance(operand_argument, QuantumLambdaFunction):
|
477
|
+
if operand_argument.func_decl is None:
|
478
|
+
return
|
479
|
+
operand_arg_decl = operand_argument.func_decl
|
480
|
+
elif isinstance(operand_argument, LambdaListComprehension):
|
481
|
+
if operand_argument.func.func_decl is None:
|
482
|
+
return
|
483
|
+
operand_arg_decl = operand_argument.func.func_decl
|
484
|
+
else:
|
485
|
+
raise ClassiqValueError(
|
486
|
+
f"{str(operand_argument)!r} argument to {call.func_decl.name!r} is not a "
|
487
|
+
f"valid operand"
|
488
|
+
)
|
489
|
+
num_arg_parameters = len(operand_arg_decl.get_positional_arg_decls())
|
490
|
+
num_decl_parameters = len(operand_decl.get_positional_arg_decls())
|
491
|
+
if num_arg_parameters != num_decl_parameters:
|
492
|
+
raise ClassiqValueError(
|
493
|
+
f"Signature of argument {operand_argument!r} to {call.func_decl.name!r} "
|
494
|
+
f"does not match the signature of parameter {operand_decl.name!r}. "
|
495
|
+
f"{operand_decl.name!r} accepts {num_decl_parameters} parameters but "
|
496
|
+
f"{operand_argument!r} accepts {num_arg_parameters} parameters"
|
497
|
+
)
|
498
|
+
|
499
|
+
|
500
|
+
def _check_operands_against_declaration(
|
501
|
+
call: QuantumFunctionCall,
|
502
|
+
decl: QuantumFunctionDeclaration,
|
503
|
+
function_dict: Mapping[str, QuantumFunctionDeclaration],
|
504
|
+
) -> None:
|
505
|
+
for operand_parameter, operand_argument in call.operands.items():
|
506
|
+
_check_operand_against_declaration(
|
507
|
+
call,
|
508
|
+
decl.operand_declarations[operand_parameter],
|
509
|
+
operand_argument,
|
510
|
+
function_dict,
|
511
|
+
)
|
512
|
+
|
513
|
+
|
444
514
|
def _check_params_against_declaration(
|
445
515
|
call_params: Set[str],
|
446
516
|
param_decls: Set[str],
|
447
517
|
callee_name: str,
|
448
518
|
) -> None:
|
449
519
|
unknown_params = call_params - param_decls
|
450
|
-
if unknown_params:
|
451
|
-
|
452
|
-
f"
|
520
|
+
if any(re.match(r"arg\d+", param) for param in unknown_params):
|
521
|
+
error_msg = (
|
522
|
+
f"Unsupported passing of named function {callee_name!r} as an operand."
|
523
|
+
"\nSuggestion: replace the named function with lambda function."
|
453
524
|
)
|
525
|
+
else:
|
526
|
+
error_msg = f"Unknown parameters {unknown_params} in call to {callee_name!r}."
|
527
|
+
if unknown_params:
|
528
|
+
raise ClassiqValueError(error_msg)
|
454
529
|
|
455
530
|
missing_params = param_decls - call_params
|
456
531
|
if missing_params:
|
@@ -83,9 +83,9 @@ class QuantumFunctionDeclaration(FunctionDeclaration):
|
|
83
83
|
default_factory=list
|
84
84
|
)
|
85
85
|
|
86
|
-
BUILTIN_FUNCTION_DECLARATIONS: ClassVar[
|
87
|
-
|
88
|
-
|
86
|
+
BUILTIN_FUNCTION_DECLARATIONS: ClassVar[Dict[str, "QuantumFunctionDeclaration"]] = (
|
87
|
+
{}
|
88
|
+
)
|
89
89
|
|
90
90
|
@property
|
91
91
|
def input_set(self) -> Set[str]:
|
@@ -0,0 +1,95 @@
|
|
1
|
+
from typing import TYPE_CHECKING, Optional
|
2
|
+
|
3
|
+
import pydantic
|
4
|
+
from sympy import Equality
|
5
|
+
from sympy.core.numbers import Integer
|
6
|
+
|
7
|
+
from classiq.interface.generator.expressions.expression import Expression
|
8
|
+
from classiq.interface.generator.expressions.qmod_qnum_proxy import QmodQNumProxy
|
9
|
+
from classiq.interface.model.quantum_expressions.control_state import (
|
10
|
+
min_bit_length,
|
11
|
+
to_twos_complement,
|
12
|
+
)
|
13
|
+
from classiq.interface.model.quantum_expressions.quantum_expression import (
|
14
|
+
QuantumExpressionOperation,
|
15
|
+
)
|
16
|
+
|
17
|
+
from classiq.exceptions import ClassiqValueError
|
18
|
+
|
19
|
+
if TYPE_CHECKING:
|
20
|
+
from classiq.interface.model.quantum_function_call import QuantumOperand
|
21
|
+
|
22
|
+
|
23
|
+
QUANTUM_IF_INOUT_NAME = "ctrl"
|
24
|
+
QUANTUM_IF_CONDITION_ARG_ERROR_MESSAGE_FORMAT = (
|
25
|
+
"quantum_if condition must be of the form '<quantum-variable> == "
|
26
|
+
"<classical-integer-expression>', but condition's {}-hand side was {!r}"
|
27
|
+
)
|
28
|
+
|
29
|
+
|
30
|
+
class QuantumIfOperation(QuantumExpressionOperation):
|
31
|
+
then: "QuantumOperand"
|
32
|
+
_ctrl: Optional[QmodQNumProxy] = pydantic.PrivateAttr(
|
33
|
+
default=None,
|
34
|
+
)
|
35
|
+
_ctrl_val: Optional[int] = pydantic.PrivateAttr(
|
36
|
+
default=None,
|
37
|
+
)
|
38
|
+
|
39
|
+
@property
|
40
|
+
def condition(self) -> Expression:
|
41
|
+
return self.expression
|
42
|
+
|
43
|
+
@property
|
44
|
+
def ctrl(self) -> QmodQNumProxy:
|
45
|
+
assert self._ctrl is not None
|
46
|
+
return self._ctrl
|
47
|
+
|
48
|
+
@property
|
49
|
+
def ctrl_val(self) -> int:
|
50
|
+
assert self._ctrl_val is not None
|
51
|
+
return self._ctrl_val
|
52
|
+
|
53
|
+
def resolve_condition(self) -> None:
|
54
|
+
condition = self.condition.value.value
|
55
|
+
if not isinstance(condition, Equality):
|
56
|
+
raise ClassiqValueError(
|
57
|
+
f"quantum_if condition must be an equality, was {str(condition)!r}"
|
58
|
+
)
|
59
|
+
ctrl, ctrl_val = condition.args
|
60
|
+
if isinstance(ctrl, Integer) and isinstance(ctrl_val, QmodQNumProxy):
|
61
|
+
ctrl, ctrl_val = ctrl_val, ctrl
|
62
|
+
if not isinstance(ctrl, QmodQNumProxy):
|
63
|
+
raise ClassiqValueError(
|
64
|
+
QUANTUM_IF_CONDITION_ARG_ERROR_MESSAGE_FORMAT.format("left", str(ctrl))
|
65
|
+
)
|
66
|
+
if not isinstance(ctrl_val, Integer):
|
67
|
+
raise ClassiqValueError(
|
68
|
+
QUANTUM_IF_CONDITION_ARG_ERROR_MESSAGE_FORMAT.format(
|
69
|
+
"right", str(ctrl_val)
|
70
|
+
)
|
71
|
+
)
|
72
|
+
self._ctrl, self._ctrl_val = ctrl, int(ctrl_val)
|
73
|
+
|
74
|
+
@property
|
75
|
+
def ctrl_state(self) -> str:
|
76
|
+
is_signed = self.ctrl.is_signed
|
77
|
+
fraction_places = self.ctrl.fraction_digits
|
78
|
+
ctrl_size = len(self.ctrl)
|
79
|
+
if not is_signed and self.ctrl_val < 0:
|
80
|
+
raise ClassiqValueError(
|
81
|
+
f"Variable {str(self.ctrl)!r} is not signed but control value "
|
82
|
+
f"{self.ctrl_val} is negative"
|
83
|
+
)
|
84
|
+
required_qubits = min_bit_length(self.ctrl_val, is_signed)
|
85
|
+
if ctrl_size < required_qubits:
|
86
|
+
raise ClassiqValueError(
|
87
|
+
f"Variable {str(self.ctrl)!r} has {ctrl_size} qubits but control value "
|
88
|
+
f"{str(self.ctrl_val)!r} requires at least {required_qubits} qubits"
|
89
|
+
)
|
90
|
+
if fraction_places != 0:
|
91
|
+
raise ClassiqValueError(
|
92
|
+
f"quantum-if on a non-integer quantum variable {str(self.ctrl)!r} is "
|
93
|
+
f"not supported at the moment"
|
94
|
+
)
|
95
|
+
return to_twos_complement(self.ctrl_val, ctrl_size, is_signed)
|
@@ -16,7 +16,7 @@ class FunctionCallResolver(Visitor):
|
|
16
16
|
self._quantum_function_dict = quantum_function_dict
|
17
17
|
|
18
18
|
def visit_QuantumFunctionCall(self, fc: QuantumFunctionCall) -> None:
|
19
|
-
fc.resolve_function_decl(self._quantum_function_dict)
|
19
|
+
fc.resolve_function_decl(self._quantum_function_dict, check_operands=True)
|
20
20
|
self.visit_BaseModel(fc)
|
21
21
|
|
22
22
|
def visit_NativeFunctionDefinition(
|
@@ -1,13 +1,20 @@
|
|
1
|
-
from typing import Dict,
|
1
|
+
from typing import Dict, Mapping, Set, Union
|
2
2
|
|
3
3
|
from classiq.interface.generator.function_params import PortDirection
|
4
|
+
from classiq.interface.generator.functions.core_lib_declarations.quantum_operators import (
|
5
|
+
APPLY,
|
6
|
+
OPERAND_FIELD_NAME,
|
7
|
+
)
|
4
8
|
from classiq.interface.model.handle_binding import (
|
5
9
|
HandleBinding,
|
6
10
|
SlicedHandleBinding,
|
7
11
|
SubscriptHandleBinding,
|
8
12
|
)
|
9
|
-
from classiq.interface.model.local_variable_declaration import LocalVariableDeclaration
|
10
13
|
from classiq.interface.model.port_declaration import PortDeclaration
|
14
|
+
from classiq.interface.model.quantum_function_call import (
|
15
|
+
QuantumFunctionCall,
|
16
|
+
QuantumLambdaFunction,
|
17
|
+
)
|
11
18
|
from classiq.interface.model.quantum_statement import QuantumOperation
|
12
19
|
from classiq.interface.model.validation_handle import HandleState, ValidationHandle
|
13
20
|
from classiq.interface.model.validations.handle_validation_base import (
|
@@ -17,23 +24,21 @@ from classiq.interface.model.variable_declaration_statement import (
|
|
17
24
|
VariableDeclarationStatement,
|
18
25
|
)
|
19
26
|
|
27
|
+
from classiq.exceptions import ClassiqValueError
|
28
|
+
|
20
29
|
|
21
30
|
def _initialize_handles_to_state(
|
22
31
|
port_declarations: Mapping[str, PortDeclaration],
|
23
|
-
local_handles: Iterable[LocalVariableDeclaration],
|
24
32
|
) -> Dict[str, ValidationHandle]:
|
25
33
|
handles_to_state: Dict[str, ValidationHandle] = dict()
|
26
34
|
|
27
35
|
for port_decl in port_declarations.values():
|
28
36
|
handles_to_state[port_decl.name] = ValidationHandle(
|
29
|
-
initial_state=
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
for local_handle in local_handles:
|
35
|
-
handles_to_state[local_handle.name] = ValidationHandle(
|
36
|
-
initial_state=HandleState.UNINITIALIZED
|
37
|
+
initial_state=(
|
38
|
+
HandleState.INITIALIZED
|
39
|
+
if port_decl.direction.includes_port_direction(PortDirection.Input)
|
40
|
+
else HandleState.UNINITIALIZED
|
41
|
+
)
|
37
42
|
)
|
38
43
|
|
39
44
|
return handles_to_state
|
@@ -43,19 +48,18 @@ class HandleValidator(HandleValidationBase):
|
|
43
48
|
def __init__(
|
44
49
|
self,
|
45
50
|
port_declarations: Mapping[str, PortDeclaration],
|
46
|
-
local_handles: Iterable[LocalVariableDeclaration],
|
47
51
|
) -> None:
|
48
52
|
super().__init__(port_declarations)
|
49
53
|
self._port_declarations = port_declarations.values()
|
50
|
-
self._handles_to_state = _initialize_handles_to_state(
|
51
|
-
port_declarations, local_handles
|
52
|
-
)
|
54
|
+
self._handles_to_state = _initialize_handles_to_state(port_declarations)
|
53
55
|
|
54
56
|
@property
|
55
57
|
def _validation_handles_state(self) -> Mapping[str, ValidationHandle]:
|
56
58
|
return self._handles_to_state
|
57
59
|
|
58
60
|
def handle_call(self, call: QuantumOperation) -> None:
|
61
|
+
if isinstance(call, QuantumFunctionCall) and call.function == APPLY.name:
|
62
|
+
self._handle_apply(call)
|
59
63
|
self._handle_inputs(call.wiring_inputs)
|
60
64
|
self._handle_outputs(call.wiring_outputs)
|
61
65
|
self._handle_inouts(call.wiring_inouts)
|
@@ -125,3 +129,26 @@ class HandleValidator(HandleValidationBase):
|
|
125
129
|
self._handles_to_state[handle].append_error(
|
126
130
|
f"Invalid use of inout handle {handle!r}, used both in slice or subscript and whole"
|
127
131
|
)
|
132
|
+
|
133
|
+
def _handle_apply(self, call: QuantumFunctionCall) -> None:
|
134
|
+
operand = call.operands[OPERAND_FIELD_NAME]
|
135
|
+
if not isinstance(operand, QuantumLambdaFunction):
|
136
|
+
return
|
137
|
+
local_variables: Set[str] = set()
|
138
|
+
output_capturing_variables: Set[str] = set()
|
139
|
+
for statement in operand.body:
|
140
|
+
if isinstance(statement, VariableDeclarationStatement):
|
141
|
+
local_variables.add(statement.name)
|
142
|
+
elif isinstance(statement, QuantumOperation):
|
143
|
+
for handle in statement.wiring_outputs.values():
|
144
|
+
if (
|
145
|
+
handle.name in local_variables
|
146
|
+
or handle.name in output_capturing_variables
|
147
|
+
):
|
148
|
+
continue
|
149
|
+
output_capturing_variables.add(handle.name)
|
150
|
+
self._handles_to_state[handle.name].initialize()
|
151
|
+
else:
|
152
|
+
raise ClassiqValueError(
|
153
|
+
f"Unknown statement type {type(statement).__name__}"
|
154
|
+
)
|
@@ -4,9 +4,8 @@ LEGACY_EXECUTE_PREFIX = "/execute"
|
|
4
4
|
EXECUTION_PREFIX = "/execution"
|
5
5
|
CONVERSION_PREFIX = "/conversion"
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
EXECUTION_SERVICE_PREFIX = "/execution/v1"
|
7
|
+
EXECUTION_NON_VERSIONED_PREFIX = "/execution/v1"
|
8
|
+
SYNTHESIS_NON_VERSIONED_PREFIX = "/synthesis/v1"
|
10
9
|
|
11
10
|
ANALYZER_CIRCUIT_PAGE = "circuit"
|
12
11
|
DEFAULT_IDE_FE_APP = "https://platform.classiq.io/"
|
@@ -51,10 +50,11 @@ TASK_PREDICT_SUFFIX = TASKS_SUFFIX + "/predict"
|
|
51
50
|
TASK_RB_SUFFIX = TASKS_SUFFIX + RB
|
52
51
|
TASKS_GENERATE_FULL_PATH = TASKS_GENERATE_SUFFIX
|
53
52
|
|
54
|
-
|
55
53
|
EXECUTION_JOBS_SUFFIX = "/jobs"
|
56
54
|
EXECUTION_JOBS_FULL_PATH = EXECUTION_PREFIX + EXECUTION_JOBS_SUFFIX
|
57
|
-
|
55
|
+
EXECUTION_JOBS_NON_VERSIONED_FULL_PATH = (
|
56
|
+
EXECUTION_NON_VERSIONED_PREFIX + EXECUTION_JOBS_SUFFIX
|
57
|
+
)
|
58
58
|
EXECUTE_QUANTUM_PROGRAM_FULL_PATH = LEGACY_EXECUTE_PREFIX + QUANTUM_PROGRAM_SUFFIX
|
59
59
|
EXECUTE_ESTIMATE_FULL_PATH = LEGACY_EXECUTE_PREFIX + ESTIMATE_SUFFIX
|
60
60
|
|
@@ -62,8 +62,12 @@ ANALYZER_FULL_PATH = ANALYZER_PREFIX + TASKS_SUFFIX
|
|
62
62
|
ANALYZER_RB_FULL_PATH = ANALYZER_PREFIX + TASK_RB_SUFFIX
|
63
63
|
GENERATE_RESOURCE_ESTIMATOR_REPORT = "/resource_estimator_report"
|
64
64
|
|
65
|
+
TASKS_SOLVE_EXACT_SUFFIX = "/tasks/solve_exact"
|
66
|
+
|
65
67
|
GENERATE_HAMILTONIAN_SUFFIX = "/generate_hamiltonian"
|
66
|
-
GENERATE_HAMILTONIAN_FULL_PATH =
|
68
|
+
GENERATE_HAMILTONIAN_FULL_PATH = (
|
69
|
+
SYNTHESIS_NON_VERSIONED_PREFIX + GENERATE_HAMILTONIAN_SUFFIX
|
70
|
+
)
|
67
71
|
|
68
72
|
FINANCE_GENERATE_MODEL_PATH = MODEL_GENERATE_PREFIX + "/finance"
|
69
73
|
|