classiq 0.42.2__py3-none-any.whl → 0.43.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- classiq/__init__.py +2 -6
- classiq/_internals/api_wrapper.py +6 -12
- classiq/_internals/authentication/token_manager.py +5 -2
- classiq/_internals/jobs.py +5 -10
- classiq/analyzer/rb.py +3 -3
- classiq/applications/chemistry/chemistry_model_constructor.py +12 -8
- classiq/applications/combinatorial_helpers/pauli_helpers/pauli_utils.py +2 -2
- classiq/applications/finance/finance_model_constructor.py +16 -13
- classiq/applications/qsvm/__init__.py +1 -3
- classiq/applications/qsvm/qsvm_model_constructor.py +7 -6
- classiq/exceptions.py +9 -4
- classiq/execution/execution_session.py +5 -2
- classiq/execution/qnn.py +1 -1
- classiq/executor.py +0 -2
- classiq/interface/_version.py +1 -1
- classiq/interface/chemistry/operator.py +19 -5
- classiq/interface/executor/constants.py +1 -0
- classiq/interface/finance/function_input.py +16 -10
- classiq/interface/generator/application_apis/chemistry_declarations.py +2 -2
- classiq/interface/generator/application_apis/qsvm_declarations.py +4 -2
- classiq/interface/generator/arith/argument_utils.py +20 -3
- classiq/interface/generator/arith/arithmetic_expression_validator.py +3 -26
- classiq/interface/generator/arith/binary_ops.py +8 -14
- classiq/interface/generator/arith/extremum_operations.py +30 -0
- classiq/interface/generator/arith/number_utils.py +1 -1
- classiq/interface/generator/arith/unary_ops.py +1 -3
- classiq/interface/generator/compiler_keywords.py +1 -1
- classiq/interface/generator/expressions/atomic_expression_functions.py +13 -3
- classiq/interface/generator/expressions/enums/__init__.py +0 -20
- classiq/interface/generator/expressions/enums/finance_functions.py +11 -18
- classiq/interface/generator/expressions/non_symbolic_expr.py +119 -0
- classiq/interface/generator/expressions/qmod_qarray_proxy.py +52 -37
- classiq/interface/generator/expressions/qmod_qscalar_proxy.py +16 -11
- classiq/interface/generator/expressions/qmod_sized_proxy.py +5 -5
- classiq/interface/generator/function_param_list_without_self_reference.py +0 -10
- classiq/interface/generator/function_params.py +0 -4
- classiq/interface/generator/functions/__init__.py +0 -20
- classiq/interface/generator/functions/builtins/core_library/exponentiation_functions.py +2 -2
- classiq/interface/generator/functions/builtins/open_lib_functions.py +530 -1
- classiq/interface/generator/functions/classical_type.py +22 -69
- classiq/interface/generator/functions/port_declaration.py +0 -11
- classiq/interface/generator/model/__init__.py +0 -1
- classiq/interface/generator/model/model.py +9 -185
- classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +3 -1
- classiq/interface/generator/types/builtin_enum_declarations.py +69 -0
- classiq/interface/generator/types/builtin_struct_declarations/pauli_struct_declarations.py +2 -2
- classiq/interface/generator/types/enum_declaration.py +57 -0
- classiq/interface/jobs.py +36 -65
- classiq/interface/model/bind_operation.py +3 -0
- classiq/interface/model/classical_parameter_declaration.py +3 -0
- classiq/interface/model/handle_binding.py +7 -0
- classiq/interface/model/inplace_binary_operation.py +13 -15
- classiq/interface/model/model.py +8 -20
- classiq/interface/model/native_function_definition.py +0 -17
- classiq/interface/model/quantum_function_call.py +63 -182
- classiq/interface/model/quantum_type.py +71 -10
- classiq/interface/server/routes.py +0 -6
- classiq/qmod/__init__.py +3 -3
- classiq/qmod/builtins/__init__.py +10 -1
- classiq/qmod/builtins/classical_execution_primitives.py +4 -2
- classiq/qmod/builtins/enums.py +177 -0
- classiq/qmod/builtins/functions.py +1 -2
- classiq/qmod/builtins/operations.py +2 -4
- classiq/qmod/builtins/structs.py +16 -17
- classiq/qmod/declaration_inferrer.py +23 -20
- classiq/qmod/model_state_container.py +2 -0
- classiq/qmod/native/pretty_printer.py +31 -13
- classiq/qmod/pretty_print/pretty_printer.py +52 -27
- classiq/qmod/qmod_constant.py +7 -3
- classiq/qmod/qmod_parameter.py +2 -1
- classiq/qmod/qmod_struct.py +9 -33
- classiq/qmod/qmod_variable.py +55 -22
- classiq/qmod/quantum_callable.py +6 -1
- classiq/qmod/quantum_expandable.py +29 -11
- classiq/qmod/quantum_function.py +8 -4
- classiq/qmod/semantics/annotation.py +38 -0
- classiq/qmod/semantics/error_manager.py +49 -0
- classiq/qmod/semantics/static_semantics_visitor.py +308 -0
- classiq/qmod/semantics/validation/func_call_validation.py +149 -0
- classiq/qmod/semantics/validation/types_validation.py +21 -0
- classiq/qmod/symbolic.py +6 -6
- classiq/qmod/symbolic_expr.py +26 -11
- classiq/qmod/utilities.py +23 -1
- {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/METADATA +2 -2
- {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/RECORD +88 -103
- classiq/_internals/_qfunc_ext.py +0 -6
- classiq/applications/libraries/ampltitude_estimation_library.py +0 -11
- classiq/interface/generator/credit_risk_example/linear_gci.py +0 -122
- classiq/interface/generator/credit_risk_example/weighted_adder.py +0 -69
- classiq/interface/generator/expressions/enums/chemistry.py +0 -28
- classiq/interface/generator/expressions/enums/classical_enum.py +0 -20
- classiq/interface/generator/expressions/enums/ladder_operator.py +0 -6
- classiq/interface/generator/expressions/enums/optimizers.py +0 -9
- classiq/interface/generator/expressions/enums/pauli.py +0 -8
- classiq/interface/generator/expressions/enums/qsvm_feature_map_entanglement.py +0 -9
- classiq/interface/generator/functions/foreign_function_definition.py +0 -114
- classiq/interface/generator/functions/function_implementation.py +0 -107
- classiq/interface/generator/functions/native_function_definition.py +0 -155
- classiq/interface/generator/functions/quantum_function_declaration.py +0 -69
- classiq/interface/generator/functions/register.py +0 -44
- classiq/interface/generator/functions/register_mapping_data.py +0 -106
- classiq/interface/generator/inequality_mixer.py +0 -51
- classiq/interface/generator/model/classical_main_validator.py +0 -106
- classiq/interface/generator/range_mixer.py +0 -56
- classiq/interface/generator/state_propagator.py +0 -74
- classiq/interface/model/resolvers/function_call_resolver.py +0 -64
- classiq/interface/model/validations/__init__.py +0 -0
- classiq/interface/model/validations/handle_validation_base.py +0 -55
- classiq/interface/model/validations/handles_validator.py +0 -153
- classiq/interface/model/validations/port_to_wire_name_generator.py +0 -12
- /classiq/{interface/generator/credit_risk_example → qmod/semantics}/__init__.py +0 -0
- /classiq/{interface/model/resolvers → qmod/semantics/validation}/__init__.py +0 -0
- {classiq-0.42.2.dist-info → classiq-0.43.1.dist-info}/WHEEL +0 -0
@@ -1,69 +0,0 @@
|
|
1
|
-
from typing import Dict, Set
|
2
|
-
|
3
|
-
import pydantic
|
4
|
-
from pydantic import BaseModel
|
5
|
-
|
6
|
-
from classiq.interface.generator.arith.register_user_input import RegisterUserInput
|
7
|
-
from classiq.interface.generator.function_params import (
|
8
|
-
ArithmeticIODict,
|
9
|
-
IOName,
|
10
|
-
PortDirection,
|
11
|
-
)
|
12
|
-
from classiq.interface.generator.functions.port_declaration import (
|
13
|
-
SynthesisPortDeclaration,
|
14
|
-
)
|
15
|
-
from classiq.interface.helpers.validation_helpers import validate_nameables_mapping
|
16
|
-
|
17
|
-
|
18
|
-
class SynthesisQuantumFunctionDeclaration(BaseModel):
|
19
|
-
"""
|
20
|
-
Facilitates the creation of a common quantum function interface object.
|
21
|
-
"""
|
22
|
-
|
23
|
-
name: str = pydantic.Field(description="The name of the function")
|
24
|
-
|
25
|
-
port_declarations: Dict[IOName, SynthesisPortDeclaration] = pydantic.Field(
|
26
|
-
description="The input and output ports of the function.",
|
27
|
-
default_factory=dict,
|
28
|
-
)
|
29
|
-
|
30
|
-
@property
|
31
|
-
def input_set(self) -> Set[IOName]:
|
32
|
-
return set(self.inputs.keys())
|
33
|
-
|
34
|
-
@property
|
35
|
-
def output_set(self) -> Set[IOName]:
|
36
|
-
return set(self.outputs.keys())
|
37
|
-
|
38
|
-
@property
|
39
|
-
def inputs(self) -> ArithmeticIODict:
|
40
|
-
return _ports_to_registers(self.port_declarations, PortDirection.Input)
|
41
|
-
|
42
|
-
@property
|
43
|
-
def outputs(self) -> ArithmeticIODict:
|
44
|
-
return _ports_to_registers(self.port_declarations, PortDirection.Output)
|
45
|
-
|
46
|
-
@pydantic.validator("port_declarations")
|
47
|
-
def _validate_port_declarations_names(
|
48
|
-
cls, port_declarations: Dict[IOName, SynthesisPortDeclaration]
|
49
|
-
) -> Dict[IOName, SynthesisPortDeclaration]:
|
50
|
-
validate_nameables_mapping(port_declarations, "Port")
|
51
|
-
return port_declarations
|
52
|
-
|
53
|
-
class Config:
|
54
|
-
extra = pydantic.Extra.forbid
|
55
|
-
|
56
|
-
|
57
|
-
def _ports_to_registers(
|
58
|
-
port_declarations: Dict[IOName, SynthesisPortDeclaration], direction: PortDirection
|
59
|
-
) -> ArithmeticIODict:
|
60
|
-
return {
|
61
|
-
name: RegisterUserInput(
|
62
|
-
name=name,
|
63
|
-
size=port_decl.size,
|
64
|
-
is_signed=port_decl.is_signed,
|
65
|
-
fraction_places=port_decl.fraction_places,
|
66
|
-
)
|
67
|
-
for name, port_decl in port_declarations.items()
|
68
|
-
if port_decl.direction.includes_port_direction(direction)
|
69
|
-
}
|
@@ -1,44 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from operator import attrgetter
|
4
|
-
from typing import Iterable, Tuple
|
5
|
-
|
6
|
-
import pydantic
|
7
|
-
from pydantic import BaseModel
|
8
|
-
|
9
|
-
from classiq.interface.helpers.custom_pydantic_types import PydanticNonEmptyString
|
10
|
-
|
11
|
-
from classiq.exceptions import ClassiqValueError
|
12
|
-
|
13
|
-
QubitsType = Tuple[pydantic.NonNegativeInt, ...]
|
14
|
-
|
15
|
-
|
16
|
-
class Register(BaseModel):
|
17
|
-
"""
|
18
|
-
A user-defined custom register.
|
19
|
-
"""
|
20
|
-
|
21
|
-
name: PydanticNonEmptyString = pydantic.Field(
|
22
|
-
description="The name of the custom register",
|
23
|
-
)
|
24
|
-
|
25
|
-
qubits: QubitsType = pydantic.Field(
|
26
|
-
description="A tuple of qubits as integers as indexed within a custom function code",
|
27
|
-
)
|
28
|
-
|
29
|
-
@property
|
30
|
-
def width(self) -> pydantic.PositiveInt:
|
31
|
-
"""The number of qubits of the custom register"""
|
32
|
-
return len(self.qubits)
|
33
|
-
|
34
|
-
@pydantic.validator("qubits")
|
35
|
-
def validate_qubits(cls, qubits: QubitsType) -> QubitsType:
|
36
|
-
if len(qubits) == 0:
|
37
|
-
raise ClassiqValueError("qubits field must be non-empty.")
|
38
|
-
if len(set(qubits)) != len(qubits):
|
39
|
-
raise ClassiqValueError("All qubits of a register must be distinct.")
|
40
|
-
return qubits
|
41
|
-
|
42
|
-
|
43
|
-
def get_register_names(reg_list: Iterable[Register]) -> Iterable[str]:
|
44
|
-
return map(attrgetter("name"), reg_list)
|
@@ -1,106 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import itertools
|
4
|
-
from typing import Any, Dict, Iterable, List, Tuple
|
5
|
-
|
6
|
-
import pydantic
|
7
|
-
|
8
|
-
from classiq.interface.generator.functions.register import Register, get_register_names
|
9
|
-
from classiq.interface.generator.register_role import RegisterRole as Role
|
10
|
-
from classiq.interface.helpers.custom_pydantic_types import PydanticNonEmptyString
|
11
|
-
|
12
|
-
from classiq.exceptions import ClassiqValueError
|
13
|
-
|
14
|
-
REGISTER_NOT_FOUND_ERROR = "Register name not found"
|
15
|
-
|
16
|
-
|
17
|
-
class RegisterMappingData(pydantic.BaseModel):
|
18
|
-
input_registers: List[Register] = pydantic.Field(default_factory=list)
|
19
|
-
output_registers: List[Register] = pydantic.Field(default_factory=list)
|
20
|
-
zero_input_registers: List[Register] = pydantic.Field(default_factory=list)
|
21
|
-
|
22
|
-
@pydantic.root_validator()
|
23
|
-
def validate_mapping(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
24
|
-
input_registers = values.get("input_registers", list())
|
25
|
-
output_registers = values.get("output_registers", list())
|
26
|
-
zero_input_registers = values.get("zero_input_registers", list())
|
27
|
-
|
28
|
-
input_qubits = cls._get_qubit_range(input_registers)
|
29
|
-
output_qubits = cls._get_qubit_range(output_registers)
|
30
|
-
zero_input_qubits = cls._get_qubit_range(zero_input_registers)
|
31
|
-
|
32
|
-
all_input_qubits = sorted(input_qubits + zero_input_qubits)
|
33
|
-
if not cls._validate_no_overlap(all_input_qubits):
|
34
|
-
raise ClassiqValueError("overlapping input qubits are not allowed")
|
35
|
-
if not cls._validate_no_overlap(output_qubits):
|
36
|
-
raise ClassiqValueError("overlapping output qubits are not allowed")
|
37
|
-
|
38
|
-
if not output_qubits == all_input_qubits:
|
39
|
-
raise ClassiqValueError(
|
40
|
-
"output registers should be included within the input / zero input registers"
|
41
|
-
)
|
42
|
-
|
43
|
-
return values
|
44
|
-
|
45
|
-
@pydantic.validator("input_registers", "output_registers")
|
46
|
-
def validate_input_registers_are_distinct(
|
47
|
-
cls, field_value: List[Register]
|
48
|
-
) -> List[Register]:
|
49
|
-
if len(field_value) != len({io_register.name for io_register in field_value}):
|
50
|
-
raise ClassiqValueError(
|
51
|
-
"The names of PortDirection registers must be distinct."
|
52
|
-
)
|
53
|
-
return field_value
|
54
|
-
|
55
|
-
@staticmethod
|
56
|
-
def _validate_no_overlap(reg_list: List[int]) -> bool:
|
57
|
-
return len(reg_list) == len(set(reg_list))
|
58
|
-
|
59
|
-
@staticmethod
|
60
|
-
def _get_qubit_range(registers: Iterable[Register]) -> List[int]:
|
61
|
-
return sorted(itertools.chain.from_iterable(reg.qubits for reg in registers))
|
62
|
-
|
63
|
-
@property
|
64
|
-
def input_names(self) -> Iterable[str]:
|
65
|
-
return get_register_names(self.input_registers)
|
66
|
-
|
67
|
-
@property
|
68
|
-
def output_names(self) -> Iterable[str]:
|
69
|
-
return get_register_names(self.output_registers)
|
70
|
-
|
71
|
-
def validate_equal_mappings(self, other: RegisterMappingData) -> None:
|
72
|
-
if any(
|
73
|
-
[
|
74
|
-
self.input_registers != other.input_registers,
|
75
|
-
self.output_registers != other.output_registers,
|
76
|
-
self.zero_input_registers != other.zero_input_registers,
|
77
|
-
]
|
78
|
-
):
|
79
|
-
raise ClassiqValueError(
|
80
|
-
"Interface should be identical in all implementations"
|
81
|
-
)
|
82
|
-
|
83
|
-
def get_input_register(self, name: PydanticNonEmptyString) -> Register:
|
84
|
-
for reg in self.input_registers:
|
85
|
-
if reg.name == name:
|
86
|
-
return reg
|
87
|
-
raise ClassiqValueError(REGISTER_NOT_FOUND_ERROR)
|
88
|
-
|
89
|
-
def get_output_register(self, name: PydanticNonEmptyString) -> Register:
|
90
|
-
for reg in self.output_registers:
|
91
|
-
if reg.name == name:
|
92
|
-
return reg
|
93
|
-
raise ClassiqValueError(REGISTER_NOT_FOUND_ERROR)
|
94
|
-
|
95
|
-
@staticmethod
|
96
|
-
def from_registers_dict(
|
97
|
-
regs_dict: Dict[Role, Tuple[Register, ...]]
|
98
|
-
) -> RegisterMappingData:
|
99
|
-
return RegisterMappingData(
|
100
|
-
input_registers=list(regs_dict[Role.INPUT]),
|
101
|
-
output_registers=list(regs_dict[Role.OUTPUT]),
|
102
|
-
zero_input_registers=list(regs_dict[Role.ZERO_INPUT]),
|
103
|
-
)
|
104
|
-
|
105
|
-
class Config:
|
106
|
-
extra = "forbid"
|
@@ -1,51 +0,0 @@
|
|
1
|
-
import pydantic
|
2
|
-
|
3
|
-
from classiq.interface.generator import function_params
|
4
|
-
from classiq.interface.generator.arith.argument_utils import RegisterOrConst
|
5
|
-
from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
|
6
|
-
from classiq.interface.generator.parameters import ParameterFloatType
|
7
|
-
|
8
|
-
DATA_REG_INPUT_NAME: str = "data_reg_input"
|
9
|
-
BOUND_REG_INPUT_NAME: str = "bound_reg_input"
|
10
|
-
|
11
|
-
DATA_REG_OUTPUT_NAME: str = "data_reg_output"
|
12
|
-
BOUND_REG_OUTPUT_NAME: str = "bound_reg_output"
|
13
|
-
|
14
|
-
|
15
|
-
class InequalityMixer(function_params.FunctionParams):
|
16
|
-
"""
|
17
|
-
Mixing a fixed point number variable below a given upper bound or above a given
|
18
|
-
lower bound. i.e. after applying this function the variable will hold a
|
19
|
-
superposition position of all the valid values.
|
20
|
-
"""
|
21
|
-
|
22
|
-
data_reg_input: RegisterArithmeticInfo = pydantic.Field(
|
23
|
-
description="The input variable to mix."
|
24
|
-
)
|
25
|
-
|
26
|
-
bound_reg_input: RegisterOrConst = pydantic.Field(
|
27
|
-
description="Fixed number or variable that define the upper or lower bound for"
|
28
|
-
" the mixing operation. In case of a fixed number bound, the value"
|
29
|
-
" must be positive."
|
30
|
-
)
|
31
|
-
|
32
|
-
mixer_parameter: ParameterFloatType = pydantic.Field(
|
33
|
-
description="The parameter used for rotation gates in the mixer.",
|
34
|
-
is_exec_param=True,
|
35
|
-
)
|
36
|
-
|
37
|
-
is_less_inequality: bool = pydantic.Field(
|
38
|
-
default=True,
|
39
|
-
description="Whether to mix below or above a certain bound."
|
40
|
-
"Less inequality mixes between 0 and the given bound."
|
41
|
-
"Greater inequality mixes between the bound and the maximal number allowed by"
|
42
|
-
" the number of qubits (i.e 2^n - 1).",
|
43
|
-
)
|
44
|
-
|
45
|
-
def _create_ios(self) -> None:
|
46
|
-
self._inputs = {DATA_REG_INPUT_NAME: self.data_reg_input}
|
47
|
-
self._outputs = {DATA_REG_OUTPUT_NAME: self.data_reg_input}
|
48
|
-
|
49
|
-
if isinstance(self.bound_reg_input, RegisterArithmeticInfo):
|
50
|
-
self._inputs[BOUND_REG_INPUT_NAME] = self.bound_reg_input
|
51
|
-
self._outputs[BOUND_REG_OUTPUT_NAME] = self.bound_reg_input
|
@@ -1,106 +0,0 @@
|
|
1
|
-
import ast
|
2
|
-
from typing import Dict, List
|
3
|
-
|
4
|
-
from classiq.executor import DEFAULT_RESULT_NAME
|
5
|
-
|
6
|
-
STANDARD_CMAIN_BODY_LENGTH = 2 # assignment of sample call, save statement
|
7
|
-
|
8
|
-
|
9
|
-
class NonStandardClassicalCodeError(Exception):
|
10
|
-
pass
|
11
|
-
|
12
|
-
|
13
|
-
# `is_standard_cmain` and `extract_sample_params` could easily be merged to one function, as they
|
14
|
-
# are doing similar tasks, but we decided to separate them for the sake of a better interface
|
15
|
-
def is_standard_cmain(model_classical_execution_code: str) -> bool:
|
16
|
-
try:
|
17
|
-
classical_body = _get_classical_body(model_classical_execution_code)
|
18
|
-
if len(classical_body) != STANDARD_CMAIN_BODY_LENGTH:
|
19
|
-
return False
|
20
|
-
|
21
|
-
_assert_sample_call(classical_body)
|
22
|
-
_assert_save_statement(classical_body)
|
23
|
-
|
24
|
-
return True
|
25
|
-
except NonStandardClassicalCodeError:
|
26
|
-
return False
|
27
|
-
|
28
|
-
|
29
|
-
def extract_sample_params(model_classical_execution_code: str) -> Dict[str, float]:
|
30
|
-
classical_main = _get_classical_body(model_classical_execution_code)
|
31
|
-
|
32
|
-
qmain_params: Dict[str, float] = {}
|
33
|
-
sample_call = _get_sample_call(classical_main)
|
34
|
-
if len(sample_call.args) == 1 and isinstance(sample_call.args[0], ast.Dict):
|
35
|
-
ast_dict = sample_call.args[0]
|
36
|
-
qmain_params = dict(
|
37
|
-
zip(
|
38
|
-
[k.value for k in ast_dict.keys if isinstance(k, ast.Constant)],
|
39
|
-
[v.value for v in ast_dict.values if isinstance(v, ast.Constant)],
|
40
|
-
)
|
41
|
-
)
|
42
|
-
|
43
|
-
return qmain_params
|
44
|
-
|
45
|
-
|
46
|
-
def has_classical_exec(model_classical_execution_code: str) -> bool:
|
47
|
-
return model_classical_execution_code != ""
|
48
|
-
|
49
|
-
|
50
|
-
def _get_classical_body(model_classical_execution_code: str) -> List[ast.stmt]:
|
51
|
-
if not has_classical_exec(model_classical_execution_code):
|
52
|
-
raise NonStandardClassicalCodeError
|
53
|
-
return ast.parse(model_classical_execution_code).body
|
54
|
-
|
55
|
-
|
56
|
-
def _assert_sample_call(classical_body: List[ast.stmt]) -> None:
|
57
|
-
_get_sample_call(classical_body)
|
58
|
-
|
59
|
-
|
60
|
-
def _get_sample_call(
|
61
|
-
classical_body: List[ast.stmt],
|
62
|
-
) -> ast.Call:
|
63
|
-
classical_call = classical_body[0]
|
64
|
-
if not isinstance(classical_call, ast.Assign):
|
65
|
-
raise NonStandardClassicalCodeError
|
66
|
-
|
67
|
-
if len(classical_call.targets) != 1:
|
68
|
-
raise NonStandardClassicalCodeError
|
69
|
-
target = classical_call.targets[0]
|
70
|
-
if not isinstance(target, ast.Name) or target.id != DEFAULT_RESULT_NAME:
|
71
|
-
raise NonStandardClassicalCodeError
|
72
|
-
|
73
|
-
invoked_expression = classical_call.value
|
74
|
-
if not isinstance(invoked_expression, ast.Call):
|
75
|
-
raise NonStandardClassicalCodeError
|
76
|
-
if (
|
77
|
-
not isinstance(invoked_expression.func, ast.Name)
|
78
|
-
or invoked_expression.func.id != "sample"
|
79
|
-
):
|
80
|
-
raise NonStandardClassicalCodeError
|
81
|
-
|
82
|
-
return invoked_expression
|
83
|
-
|
84
|
-
|
85
|
-
def _assert_save_statement(classical_body: List[ast.stmt]) -> None:
|
86
|
-
save_statement = classical_body[1]
|
87
|
-
if not isinstance(save_statement, ast.Expr) or not isinstance(
|
88
|
-
save_statement.value, ast.Call
|
89
|
-
):
|
90
|
-
raise NonStandardClassicalCodeError
|
91
|
-
|
92
|
-
call = save_statement.value
|
93
|
-
if not isinstance(call.func, ast.Name) or call.func.id != "save":
|
94
|
-
raise NonStandardClassicalCodeError
|
95
|
-
|
96
|
-
if not len(call.args) == 1:
|
97
|
-
raise NonStandardClassicalCodeError
|
98
|
-
|
99
|
-
if (
|
100
|
-
not isinstance(call.args[0], ast.Dict)
|
101
|
-
or not isinstance(call.args[0].keys[0], ast.Constant)
|
102
|
-
or call.args[0].keys[0].value != DEFAULT_RESULT_NAME
|
103
|
-
or not isinstance(call.args[0].values[0], ast.Name)
|
104
|
-
or call.args[0].values[0].id != DEFAULT_RESULT_NAME
|
105
|
-
):
|
106
|
-
raise NonStandardClassicalCodeError
|
@@ -1,56 +0,0 @@
|
|
1
|
-
import pydantic
|
2
|
-
|
3
|
-
from classiq.interface.generator.arith.argument_utils import RegisterOrConst
|
4
|
-
from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
|
5
|
-
from classiq.interface.generator.function_params import FunctionParams
|
6
|
-
from classiq.interface.generator.parameters import ParameterFloatType
|
7
|
-
|
8
|
-
DATA_REG_INPUT_NAME: str = "data_reg_input"
|
9
|
-
LOWER_BOUND_REG_INPUT_NAME: str = "lower_bound_reg_input"
|
10
|
-
UPPER_BOUND_REG_INPUT_NAME: str = "upper_bound_reg_input"
|
11
|
-
|
12
|
-
|
13
|
-
DATA_REG_OUTPUT_NAME: str = "data_reg_output"
|
14
|
-
LOWER_BOUND_REG_OUTPUT_NAME: str = "lower_bound_reg_output"
|
15
|
-
UPPER_BOUND_REG_OUTPUT_NAME: str = "upper_bound_reg_output"
|
16
|
-
|
17
|
-
|
18
|
-
class RangeMixer(FunctionParams):
|
19
|
-
"""
|
20
|
-
Mixing a fixed point number variable between a given lower and upper bounds.
|
21
|
-
I.e. after applying this function the variable will hold a
|
22
|
-
superposition of all the valid values.
|
23
|
-
"""
|
24
|
-
|
25
|
-
data_reg_input: RegisterArithmeticInfo = pydantic.Field(
|
26
|
-
description="The input variable to mix."
|
27
|
-
)
|
28
|
-
|
29
|
-
lower_bound_reg_input: RegisterOrConst = pydantic.Field(
|
30
|
-
description="Fixed number or variable that define the lower bound for"
|
31
|
-
" the mixing operation. In case of a fixed number bound, the value"
|
32
|
-
" must be positive."
|
33
|
-
)
|
34
|
-
|
35
|
-
upper_bound_reg_input: RegisterOrConst = pydantic.Field(
|
36
|
-
description="Fixed number or variable that define the upper bound for"
|
37
|
-
" the mixing operation. In case of a fixed number bound, the value"
|
38
|
-
" must be positive."
|
39
|
-
)
|
40
|
-
|
41
|
-
mixer_parameter: ParameterFloatType = pydantic.Field(
|
42
|
-
description="The parameter used for rotation gates in the mixer.",
|
43
|
-
is_exec_param=True,
|
44
|
-
)
|
45
|
-
|
46
|
-
def _create_ios(self) -> None:
|
47
|
-
self._inputs = {DATA_REG_INPUT_NAME: self.data_reg_input}
|
48
|
-
self._outputs = {DATA_REG_OUTPUT_NAME: self.data_reg_input}
|
49
|
-
|
50
|
-
if isinstance(self.lower_bound_reg_input, RegisterArithmeticInfo):
|
51
|
-
self._inputs[LOWER_BOUND_REG_INPUT_NAME] = self.lower_bound_reg_input
|
52
|
-
self._outputs[LOWER_BOUND_REG_OUTPUT_NAME] = self.lower_bound_reg_input
|
53
|
-
|
54
|
-
if isinstance(self.upper_bound_reg_input, RegisterArithmeticInfo):
|
55
|
-
self._inputs[UPPER_BOUND_REG_INPUT_NAME] = self.upper_bound_reg_input
|
56
|
-
self._outputs[UPPER_BOUND_REG_OUTPUT_NAME] = self.upper_bound_reg_input
|
@@ -1,74 +0,0 @@
|
|
1
|
-
from typing import Any, Dict, List
|
2
|
-
|
3
|
-
import numpy as np
|
4
|
-
import pydantic
|
5
|
-
|
6
|
-
from classiq.interface.generator import function_params
|
7
|
-
from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
|
8
|
-
from classiq.interface.generator.complex_type import Complex
|
9
|
-
from classiq.interface.generator.function_params import (
|
10
|
-
DEFAULT_INPUT_NAME,
|
11
|
-
DEFAULT_OUTPUT_NAME,
|
12
|
-
)
|
13
|
-
|
14
|
-
from classiq.exceptions import ClassiqValueError
|
15
|
-
|
16
|
-
|
17
|
-
class StatePropagator(function_params.FunctionParams):
|
18
|
-
"""
|
19
|
-
Creates a quantum circuit that propagates the start state vector to the end state vector,
|
20
|
-
both are assumed to be pure states.
|
21
|
-
The default start state vector is |000...0>.
|
22
|
-
"""
|
23
|
-
|
24
|
-
end_state_vector: List[Complex] = pydantic.Field(
|
25
|
-
description="The desired state vector at the end of the operator."
|
26
|
-
" Must be of size 2**num_qubits. Does not have to be "
|
27
|
-
"normalized"
|
28
|
-
)
|
29
|
-
|
30
|
-
start_state_vector: List[Complex] = pydantic.Field(
|
31
|
-
default_factory=list,
|
32
|
-
description="The state vector at the input of the operator."
|
33
|
-
" Must be of size 2**num_qubits. Does not have to be"
|
34
|
-
" normalized",
|
35
|
-
)
|
36
|
-
|
37
|
-
@pydantic.validator("start_state_vector", always=True)
|
38
|
-
def validate_start_state(
|
39
|
-
cls, start_state_vector: List[Complex], values: Dict[str, Any]
|
40
|
-
) -> List[Complex]:
|
41
|
-
end_state_vector = values.get("end_state_vector")
|
42
|
-
if end_state_vector is None:
|
43
|
-
raise ClassiqValueError(
|
44
|
-
"Cannot validate start_start_vector without end_state_vector"
|
45
|
-
)
|
46
|
-
|
47
|
-
num_qubits = cls._num_qubits(end_state_vector)
|
48
|
-
if len(start_state_vector) == 0:
|
49
|
-
default_start_state_vector = [Complex(0.0) for _ in range(2**num_qubits)]
|
50
|
-
default_start_state_vector[0] = Complex(1.0)
|
51
|
-
start_state_vector = default_start_state_vector
|
52
|
-
|
53
|
-
if len(start_state_vector) != len(end_state_vector):
|
54
|
-
raise ClassiqValueError(
|
55
|
-
"Start and end state vectors are of non-equal length"
|
56
|
-
)
|
57
|
-
|
58
|
-
return start_state_vector
|
59
|
-
|
60
|
-
@staticmethod
|
61
|
-
def _num_qubits(vector: List[Complex]) -> int:
|
62
|
-
return int(np.log2(len(vector)))
|
63
|
-
|
64
|
-
def _create_ios(self) -> None:
|
65
|
-
self._inputs = {
|
66
|
-
DEFAULT_INPUT_NAME: RegisterArithmeticInfo(
|
67
|
-
size=self._num_qubits(self.start_state_vector)
|
68
|
-
)
|
69
|
-
}
|
70
|
-
self._outputs = {
|
71
|
-
DEFAULT_OUTPUT_NAME: RegisterArithmeticInfo(
|
72
|
-
size=self._num_qubits(self.end_state_vector)
|
73
|
-
)
|
74
|
-
}
|
@@ -1,64 +0,0 @@
|
|
1
|
-
import itertools
|
2
|
-
from typing import Any, Mapping
|
3
|
-
|
4
|
-
from classiq.interface.generator.visitor import Visitor
|
5
|
-
from classiq.interface.model.control import Control
|
6
|
-
from classiq.interface.model.invert import Invert
|
7
|
-
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
8
|
-
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
9
|
-
from classiq.interface.model.quantum_function_declaration import (
|
10
|
-
QuantumFunctionDeclaration,
|
11
|
-
)
|
12
|
-
from classiq.interface.model.repeat import Repeat
|
13
|
-
from classiq.interface.model.within_apply_operation import WithinApply
|
14
|
-
|
15
|
-
|
16
|
-
class FunctionCallResolver(Visitor):
|
17
|
-
def __init__(
|
18
|
-
self,
|
19
|
-
quantum_function_dict: Mapping[str, QuantumFunctionDeclaration],
|
20
|
-
) -> None:
|
21
|
-
self._quantum_function_dict = quantum_function_dict
|
22
|
-
|
23
|
-
def visit_Invert(self, invert: Invert) -> None:
|
24
|
-
for fc in invert.body:
|
25
|
-
self.visit(fc)
|
26
|
-
|
27
|
-
def visit_Repeat(self, repeat: Repeat) -> None:
|
28
|
-
for fc in repeat.body:
|
29
|
-
self.visit(fc)
|
30
|
-
|
31
|
-
def visit_Control(self, control: Control) -> None:
|
32
|
-
for fc in control.body:
|
33
|
-
self.visit(fc)
|
34
|
-
|
35
|
-
def visit_WithinApply(self, within_apply: WithinApply) -> None:
|
36
|
-
for fc in itertools.chain(within_apply.compute, within_apply.action):
|
37
|
-
self.visit(fc)
|
38
|
-
|
39
|
-
def visit_QuantumFunctionCall(self, fc: QuantumFunctionCall) -> None:
|
40
|
-
fc.resolve_function_decl(self._quantum_function_dict, check_operands=True)
|
41
|
-
self.visit_BaseModel(fc)
|
42
|
-
|
43
|
-
def visit_NativeFunctionDefinition(
|
44
|
-
self, func_def: NativeFunctionDefinition
|
45
|
-
) -> None:
|
46
|
-
curr_dict = self._quantum_function_dict
|
47
|
-
self._quantum_function_dict = {
|
48
|
-
**self._quantum_function_dict,
|
49
|
-
**func_def.operand_declarations,
|
50
|
-
}
|
51
|
-
self.visit_BaseModel(func_def)
|
52
|
-
self._quantum_function_dict = curr_dict
|
53
|
-
|
54
|
-
|
55
|
-
def resolve_function_calls(
|
56
|
-
root: Any,
|
57
|
-
quantum_function_dict: Mapping[str, QuantumFunctionDeclaration],
|
58
|
-
) -> None:
|
59
|
-
FunctionCallResolver(
|
60
|
-
{
|
61
|
-
**QuantumFunctionDeclaration.BUILTIN_FUNCTION_DECLARATIONS,
|
62
|
-
**quantum_function_dict,
|
63
|
-
},
|
64
|
-
).visit(root)
|
File without changes
|
@@ -1,55 +0,0 @@
|
|
1
|
-
import abc
|
2
|
-
from typing import Dict, Mapping, Type
|
3
|
-
|
4
|
-
from classiq.interface.generator.functions.port_declaration import (
|
5
|
-
PortDeclarationDirection,
|
6
|
-
)
|
7
|
-
from classiq.interface.model.port_declaration import PortDeclaration
|
8
|
-
from classiq.interface.model.validation_handle import HandleState, ValidationHandle
|
9
|
-
|
10
|
-
EXPECTED_TERMINAL_STATES: Dict[PortDeclarationDirection, HandleState] = {
|
11
|
-
PortDeclarationDirection.Output: HandleState.INITIALIZED,
|
12
|
-
PortDeclarationDirection.Inout: HandleState.INITIALIZED,
|
13
|
-
}
|
14
|
-
|
15
|
-
|
16
|
-
class HandleValidationBase(abc.ABC):
|
17
|
-
def __init__(
|
18
|
-
self,
|
19
|
-
port_declarations: Mapping[str, PortDeclaration],
|
20
|
-
) -> None:
|
21
|
-
self._port_declarations = port_declarations.values()
|
22
|
-
|
23
|
-
def report_errored_handles(self, exception_type: Type[Exception]) -> None:
|
24
|
-
self._validate_terminal_handle_state()
|
25
|
-
|
26
|
-
errored_handles = {
|
27
|
-
name: state.errors
|
28
|
-
for name, state in self._validation_handles_state.items()
|
29
|
-
if state.state is HandleState.ERRORED
|
30
|
-
}
|
31
|
-
if errored_handles:
|
32
|
-
raise exception_type(
|
33
|
-
"\n".join(
|
34
|
-
f"Handle {handle_name!r} was errored with {'. '.join(errors)!r}"
|
35
|
-
for handle_name, errors in errored_handles.items()
|
36
|
-
)
|
37
|
-
)
|
38
|
-
|
39
|
-
def _validate_terminal_handle_state(self) -> None:
|
40
|
-
for port_decl in self._port_declarations:
|
41
|
-
handle_state = self._validation_handles_state[port_decl.name]
|
42
|
-
expected_terminal_state = EXPECTED_TERMINAL_STATES.get(port_decl.direction)
|
43
|
-
if (
|
44
|
-
expected_terminal_state is not None
|
45
|
-
and handle_state.state is not expected_terminal_state
|
46
|
-
and handle_state.state is not HandleState.ERRORED
|
47
|
-
):
|
48
|
-
handle_state.append_error(
|
49
|
-
f"At the end of the function, in port {port_decl.name} is expected to be {expected_terminal_state} but it isn't"
|
50
|
-
)
|
51
|
-
|
52
|
-
@property
|
53
|
-
@abc.abstractmethod
|
54
|
-
def _validation_handles_state(self) -> Mapping[str, ValidationHandle]:
|
55
|
-
raise NotImplementedError
|