classiq 0.85.0__py3-none-any.whl → 0.86.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/applications/combinatorial_optimization/combinatorial_problem.py +4 -3
- classiq/evaluators/qmod_annotated_expression.py +207 -0
- classiq/evaluators/qmod_expression_visitors/__init__.py +0 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_bwc.py +134 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_evaluator.py +232 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_renamer.py +44 -0
- classiq/evaluators/qmod_expression_visitors/qmod_expression_simplifier.py +308 -0
- classiq/evaluators/qmod_node_evaluators/__init__.py +0 -0
- classiq/evaluators/qmod_node_evaluators/attribute_evaluation.py +112 -0
- classiq/evaluators/qmod_node_evaluators/binary_op_evaluation.py +132 -0
- classiq/evaluators/qmod_node_evaluators/bool_op_evaluation.py +70 -0
- classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +311 -0
- classiq/evaluators/qmod_node_evaluators/compare_evaluation.py +107 -0
- classiq/evaluators/qmod_node_evaluators/constant_evaluation.py +67 -0
- classiq/evaluators/qmod_node_evaluators/list_evaluation.py +107 -0
- classiq/evaluators/qmod_node_evaluators/measurement_evaluation.py +25 -0
- classiq/evaluators/qmod_node_evaluators/name_evaluation.py +50 -0
- classiq/evaluators/qmod_node_evaluators/struct_instantiation_evaluation.py +66 -0
- classiq/evaluators/qmod_node_evaluators/subscript_evaluation.py +225 -0
- classiq/evaluators/qmod_node_evaluators/unary_op_evaluation.py +58 -0
- classiq/evaluators/qmod_node_evaluators/utils.py +80 -0
- classiq/execution/execution_session.py +4 -0
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +1 -1
- classiq/interface/analyzer/result.py +1 -1
- classiq/interface/executor/quantum_code.py +2 -2
- classiq/interface/generator/arith/arithmetic_expression_validator.py +5 -1
- classiq/interface/generator/arith/binary_ops.py +43 -51
- classiq/interface/generator/arith/number_utils.py +3 -2
- classiq/interface/generator/arith/register_user_input.py +15 -0
- classiq/interface/generator/arith/unary_ops.py +32 -28
- classiq/interface/generator/expressions/expression_types.py +2 -2
- classiq/interface/generator/expressions/proxies/classical/qmod_struct_instance.py +7 -0
- classiq/interface/generator/functions/classical_function_declaration.py +0 -4
- classiq/interface/generator/functions/classical_type.py +0 -32
- classiq/interface/generator/generated_circuit_data.py +2 -0
- classiq/interface/generator/quantum_program.py +6 -1
- classiq/interface/generator/synthesis_metadata/synthesis_execution_data.py +29 -0
- classiq/interface/ide/operation_registry.py +2 -2
- classiq/interface/ide/visual_model.py +22 -1
- classiq/interface/model/quantum_type.py +67 -33
- classiq/model_expansions/arithmetic.py +115 -0
- classiq/model_expansions/arithmetic_compute_result_attrs.py +71 -0
- classiq/model_expansions/atomic_expression_functions_defs.py +5 -5
- classiq/model_expansions/generative_functions.py +15 -2
- classiq/model_expansions/interpreters/base_interpreter.py +7 -0
- classiq/model_expansions/interpreters/generative_interpreter.py +2 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +5 -2
- classiq/model_expansions/scope_initialization.py +2 -0
- classiq/model_expansions/sympy_conversion/sympy_to_python.py +1 -1
- classiq/model_expansions/transformers/type_modifier_inference.py +5 -0
- classiq/model_expansions/visitors/boolean_expression_transformers.py +1 -1
- classiq/qmod/builtins/operations.py +1 -1
- classiq/qmod/declaration_inferrer.py +5 -3
- classiq/qmod/write_qmod.py +3 -1
- {classiq-0.85.0.dist-info → classiq-0.86.1.dist-info}/METADATA +1 -1
- {classiq-0.85.0.dist-info → classiq-0.86.1.dist-info}/RECORD +59 -37
- /classiq/{model_expansions/sympy_conversion/arithmetics.py → evaluators/qmod_expression_visitors/sympy_wrappers.py} +0 -0
- {classiq-0.85.0.dist-info → classiq-0.86.1.dist-info}/WHEEL +0 -0
@@ -11,6 +11,8 @@ from classiq.interface.helpers.hashable_pydantic_base_model import (
|
|
11
11
|
HashablePydanticBaseModel,
|
12
12
|
)
|
13
13
|
|
14
|
+
MAX_REGISTER_SIZE = 10000
|
15
|
+
|
14
16
|
|
15
17
|
class RegisterArithmeticInfo(HashablePydanticBaseModel):
|
16
18
|
size: pydantic.PositiveInt = pydantic.Field(default=1)
|
@@ -56,6 +58,19 @@ class RegisterArithmeticInfo(HashablePydanticBaseModel):
|
|
56
58
|
fraction_places = info.data.get("fraction_places", 0)
|
57
59
|
if not isinstance(size, int):
|
58
60
|
raise ClassiqValueError("RegisterArithmeticInfo must have an integer size")
|
61
|
+
if not isinstance(fraction_places, int):
|
62
|
+
raise ClassiqValueError(
|
63
|
+
"RegisterArithmeticInfo must have an integer fraction_places"
|
64
|
+
)
|
65
|
+
|
66
|
+
if size > MAX_REGISTER_SIZE:
|
67
|
+
raise ValueError(
|
68
|
+
f"Register size {size} exceeds maximum size {MAX_REGISTER_SIZE}."
|
69
|
+
)
|
70
|
+
if fraction_places > size:
|
71
|
+
raise ValueError(
|
72
|
+
f"Number of fraction places {fraction_places} exceeds register size {size}."
|
73
|
+
)
|
59
74
|
|
60
75
|
maximal_bounds = cls.get_maximal_bounds(
|
61
76
|
size=size, is_signed=is_signed, fraction_places=fraction_places
|
@@ -1,11 +1,11 @@
|
|
1
1
|
from collections.abc import Iterable
|
2
|
-
from typing import
|
2
|
+
from typing import Final, Optional
|
3
3
|
|
4
4
|
import pydantic
|
5
5
|
from pydantic import ConfigDict
|
6
6
|
|
7
7
|
from classiq.interface.exceptions import ClassiqValueError
|
8
|
-
from classiq.interface.generator.arith import
|
8
|
+
from classiq.interface.generator.arith import number_utils
|
9
9
|
from classiq.interface.generator.arith.arithmetic_operations import (
|
10
10
|
MODULO_WITH_FRACTION_PLACES_ERROR_MSG,
|
11
11
|
ArithmeticOperationParams,
|
@@ -13,6 +13,12 @@ from classiq.interface.generator.arith.arithmetic_operations import (
|
|
13
13
|
from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
|
14
14
|
from classiq.interface.generator.function_params import get_zero_input_name
|
15
15
|
|
16
|
+
from classiq.model_expansions.arithmetic import NumericAttributes
|
17
|
+
from classiq.model_expansions.arithmetic_compute_result_attrs import (
|
18
|
+
compute_result_attrs_bitwise_invert,
|
19
|
+
compute_result_attrs_negate,
|
20
|
+
)
|
21
|
+
|
16
22
|
UNARY_ARG_NAME: Final[str] = "arg"
|
17
23
|
|
18
24
|
|
@@ -65,13 +71,17 @@ class BitwiseInvert(UnaryOpParams):
|
|
65
71
|
output_name = "inverted"
|
66
72
|
|
67
73
|
def _get_result_register(self) -> RegisterArithmeticInfo:
|
68
|
-
|
69
|
-
|
70
|
-
|
74
|
+
arg_attrs = NumericAttributes.from_register_arithmetic_info(
|
75
|
+
self.arg, self.machine_precision
|
76
|
+
)
|
77
|
+
result_attrs = compute_result_attrs_bitwise_invert(
|
78
|
+
arg_attrs, self.machine_precision
|
79
|
+
)
|
80
|
+
|
71
81
|
return RegisterArithmeticInfo(
|
72
|
-
size=self.output_size or
|
73
|
-
fraction_places=
|
74
|
-
is_signed=
|
82
|
+
size=self.output_size or result_attrs.size,
|
83
|
+
fraction_places=result_attrs.fraction_digits,
|
84
|
+
is_signed=result_attrs.is_signed and self._include_sign,
|
75
85
|
)
|
76
86
|
|
77
87
|
|
@@ -81,40 +91,34 @@ class Negation(UnaryOpParams):
|
|
81
91
|
) # True for efficient subtraction
|
82
92
|
output_name = "negated"
|
83
93
|
|
84
|
-
@staticmethod
|
85
|
-
def _expected_result_size(arg: RegisterArithmeticInfo) -> pydantic.PositiveInt:
|
86
|
-
if arg.size == 1:
|
87
|
-
return 1
|
88
|
-
return arg.fraction_places + number_utils.bounds_to_integer_part_size(
|
89
|
-
*(-bound for bound in arg.bounds)
|
90
|
-
)
|
91
|
-
|
92
94
|
def _get_result_register(self) -> RegisterArithmeticInfo:
|
93
|
-
|
94
|
-
|
95
|
-
|
95
|
+
arg_attrs = NumericAttributes.from_register_arithmetic_info(
|
96
|
+
self.arg, self.machine_precision
|
97
|
+
)
|
98
|
+
result_attrs = compute_result_attrs_negate(arg_attrs, self.machine_precision)
|
99
|
+
is_signed = result_attrs.is_signed and self._include_sign
|
100
|
+
bounds = result_attrs.bounds
|
96
101
|
if self.output_size and not self.bypass_bounds_validation:
|
97
|
-
if
|
102
|
+
if result_attrs.fraction_digits:
|
98
103
|
raise ValueError(MODULO_WITH_FRACTION_PLACES_ERROR_MSG)
|
99
104
|
max_bounds = RegisterArithmeticInfo.get_maximal_bounds(
|
100
105
|
size=self.output_size, is_signed=False, fraction_places=0
|
101
106
|
)
|
102
107
|
bounds = number_utils.bounds_cut(bounds, max_bounds)
|
103
108
|
return RegisterArithmeticInfo(
|
104
|
-
size=self.output_size or
|
105
|
-
fraction_places=
|
109
|
+
size=self.output_size or result_attrs.size,
|
110
|
+
fraction_places=result_attrs.fraction_digits,
|
106
111
|
is_signed=is_signed,
|
107
112
|
bypass_bounds_validation=self.bypass_bounds_validation,
|
108
113
|
bounds=bounds,
|
109
114
|
)
|
110
115
|
|
111
116
|
def zero_input_for_extension(self) -> pydantic.NonNegativeInt:
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
) - self.arg.size
|
117
|
+
arg_integers = self.arg.size - self.arg.fraction_places
|
118
|
+
result_integers = (
|
119
|
+
self.result_register.size - self.result_register.fraction_places
|
120
|
+
)
|
121
|
+
return result_integers - arg_integers
|
118
122
|
|
119
123
|
|
120
124
|
class Sign(UnaryOpParams):
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from typing import Union
|
2
2
|
|
3
|
-
from sympy import
|
3
|
+
from sympy import Basic
|
4
4
|
from sympy.logic.boolalg import Boolean
|
5
5
|
|
6
6
|
from classiq.interface.generator.expressions.handle_identifier import HandleIdentifier
|
@@ -31,5 +31,5 @@ Proxies = Union[
|
|
31
31
|
QmodSizedProxy,
|
32
32
|
ClassicalProxy,
|
33
33
|
]
|
34
|
-
RuntimeExpression = Union[AnyClassicalValue,
|
34
|
+
RuntimeExpression = Union[AnyClassicalValue, Basic, Boolean]
|
35
35
|
ExpressionValue = Union[RuntimeConstant, Proxies, RuntimeExpression]
|
@@ -41,3 +41,10 @@ class QmodStructInstance:
|
|
41
41
|
for field_name, field_value in self._fields.items()
|
42
42
|
)
|
43
43
|
return f"{self.struct_declaration.name}({fields})"
|
44
|
+
|
45
|
+
def __eq__(self, other: Any) -> bool:
|
46
|
+
return (
|
47
|
+
isinstance(other, QmodStructInstance)
|
48
|
+
and self.struct_declaration.name == other.struct_declaration.name
|
49
|
+
and self._fields == other._fields
|
50
|
+
)
|
@@ -28,10 +28,6 @@ class ClassicalFunctionDeclaration(FunctionDeclaration):
|
|
28
28
|
default=None,
|
29
29
|
)
|
30
30
|
|
31
|
-
BUILTIN_FUNCTION_DECLARATIONS: ClassVar[
|
32
|
-
dict[str, "ClassicalFunctionDeclaration"]
|
33
|
-
] = {}
|
34
|
-
|
35
31
|
FOREIGN_FUNCTION_DECLARATIONS: ClassVar[
|
36
32
|
dict[str, "ClassicalFunctionDeclaration"]
|
37
33
|
] = {}
|
@@ -113,7 +113,6 @@ class StructMetaType(ClassicalType):
|
|
113
113
|
class ClassicalArray(ClassicalType):
|
114
114
|
kind: Literal["array"]
|
115
115
|
element_type: "ConcreteClassicalType"
|
116
|
-
size: Optional[int] = pydantic.Field(exclude=True, default=None)
|
117
116
|
length: Optional[Expression] = None
|
118
117
|
|
119
118
|
@pydantic.model_validator(mode="before")
|
@@ -121,37 +120,6 @@ class ClassicalArray(ClassicalType):
|
|
121
120
|
def _set_kind(cls, values: Any) -> dict[str, Any]:
|
122
121
|
return values_with_discriminator(values, "kind", "array")
|
123
122
|
|
124
|
-
@pydantic.model_validator(mode="before")
|
125
|
-
@classmethod
|
126
|
-
def _set_length(cls, values: Any) -> Any:
|
127
|
-
if isinstance(values, dict):
|
128
|
-
size = values.get("size")
|
129
|
-
length = values.get("length")
|
130
|
-
else:
|
131
|
-
size = values.size
|
132
|
-
length = values.length
|
133
|
-
if size is not None:
|
134
|
-
if isinstance(values, dict):
|
135
|
-
values["length"] = Expression(expr=str(size))
|
136
|
-
else:
|
137
|
-
values.length = Expression(expr=str(size))
|
138
|
-
elif length is not None:
|
139
|
-
if isinstance(length, dict):
|
140
|
-
expr = length["expr"]
|
141
|
-
else:
|
142
|
-
expr = length.expr
|
143
|
-
expr_size: Optional[int] = None
|
144
|
-
try: # noqa: SIM105
|
145
|
-
expr_size = int(expr)
|
146
|
-
except ValueError:
|
147
|
-
pass
|
148
|
-
if expr_size is not None:
|
149
|
-
if isinstance(values, dict):
|
150
|
-
values["size"] = expr_size
|
151
|
-
else:
|
152
|
-
values.size = expr_size
|
153
|
-
return values
|
154
|
-
|
155
123
|
@property
|
156
124
|
def has_length(self) -> bool:
|
157
125
|
return self.length is not None and self.length.is_evaluated()
|
@@ -146,6 +146,7 @@ class StatementType(StrEnum):
|
|
146
146
|
INPLACE_ADD = "inplace add"
|
147
147
|
REPEAT = "repeat"
|
148
148
|
BLOCK = "block"
|
149
|
+
IF = "if"
|
149
150
|
|
150
151
|
|
151
152
|
# Mapping between statement kind (or sub-kind) and statement type (visualization name)
|
@@ -167,6 +168,7 @@ STATEMENTS_NAME: dict[str, StatementType] = {
|
|
167
168
|
ArithmeticOperationKind.InplaceAdd.value: StatementType.INPLACE_ADD,
|
168
169
|
"Repeat": StatementType.REPEAT,
|
169
170
|
"Block": StatementType.BLOCK,
|
171
|
+
"ClassicalIf": StatementType.IF,
|
170
172
|
}
|
171
173
|
|
172
174
|
|
@@ -76,6 +76,8 @@ class QuantumProgram(VersionedModel, CircuitCodeInterface):
|
|
76
76
|
execution_primitives_input: Optional[PrimitivesInput] = pydantic.Field(default=None)
|
77
77
|
synthesis_warnings: Optional[list[str]] = pydantic.Field(default=None)
|
78
78
|
should_warn: bool = pydantic.Field(default=False)
|
79
|
+
# Unique identifier for the circuit (since the program_id might change when running show). Used for the circuit store.
|
80
|
+
circuit_id: str = pydantic.Field(default_factory=get_uuid_as_str)
|
79
81
|
|
80
82
|
def __str__(self) -> str:
|
81
83
|
return self.model_dump_json(indent=2)
|
@@ -168,7 +170,10 @@ class QuantumProgram(VersionedModel, CircuitCodeInterface):
|
|
168
170
|
|
169
171
|
@property
|
170
172
|
def _can_use_transpiled_code(self) -> bool:
|
171
|
-
return
|
173
|
+
return (
|
174
|
+
self.data.execution_data is None
|
175
|
+
or not self.data.execution_data.function_execution
|
176
|
+
)
|
172
177
|
|
173
178
|
@property
|
174
179
|
def program_circuit(self) -> CircuitCodeInterface:
|
@@ -3,6 +3,7 @@ from typing import Optional
|
|
3
3
|
|
4
4
|
import pydantic
|
5
5
|
import sympy
|
6
|
+
from typing_extensions import Self
|
6
7
|
|
7
8
|
from classiq.interface.backend.pydantic_backend import PydanticExecutionParameter
|
8
9
|
from classiq.interface.generator.parameters import ParameterType
|
@@ -34,3 +35,31 @@ class ExecutionData(pydantic.BaseModel):
|
|
34
35
|
if function_execution_data.power_vars is not None
|
35
36
|
)
|
36
37
|
)
|
38
|
+
|
39
|
+
def to_inverse(self) -> Self:
|
40
|
+
return type(self)(
|
41
|
+
function_execution={
|
42
|
+
self._inverse_name(name): value
|
43
|
+
for name, value in self.function_execution.items()
|
44
|
+
}
|
45
|
+
)
|
46
|
+
|
47
|
+
def to_control(self) -> Self:
|
48
|
+
return type(self)(
|
49
|
+
function_execution={
|
50
|
+
self._control_name(name): value
|
51
|
+
for name, value in self.function_execution.items()
|
52
|
+
}
|
53
|
+
)
|
54
|
+
|
55
|
+
@staticmethod
|
56
|
+
def _inverse_name(name: str) -> str:
|
57
|
+
# see inverse of qiskit.circuit.Instruction
|
58
|
+
if name.endswith("_dg"):
|
59
|
+
return name[:-3]
|
60
|
+
return f"{name}_dg"
|
61
|
+
|
62
|
+
@staticmethod
|
63
|
+
def _control_name(name: str) -> str:
|
64
|
+
# see inverse of qiskit.circuit.QuantumCircuit
|
65
|
+
return f"c_{name}"
|
@@ -12,9 +12,9 @@ class OperationRegistry:
|
|
12
12
|
|
13
13
|
def build_operation(self, **kwargs: Any) -> Operation:
|
14
14
|
operation = Operation(**kwargs)
|
15
|
-
return self.
|
15
|
+
return self._add_operation(operation)
|
16
16
|
|
17
|
-
def
|
17
|
+
def _add_operation(self, op: Operation) -> Operation:
|
18
18
|
"""
|
19
19
|
Adds an operation to the global dictionaries for operations.
|
20
20
|
if operation already exist in the registry, it returns the existing operation.
|
@@ -6,7 +6,7 @@ from itertools import count
|
|
6
6
|
from typing import Any, Optional
|
7
7
|
|
8
8
|
import pydantic
|
9
|
-
from pydantic import ConfigDict
|
9
|
+
from pydantic import ConfigDict, field_validator
|
10
10
|
|
11
11
|
from classiq.interface.enum_utils import StrEnum
|
12
12
|
from classiq.interface.generator.generated_circuit_data import (
|
@@ -50,6 +50,8 @@ class OperationData(pydantic.BaseModel):
|
|
50
50
|
width: int
|
51
51
|
gate_count: Counter[str] = pydantic.Field(default_factory=dict)
|
52
52
|
|
53
|
+
model_config = ConfigDict(frozen=True)
|
54
|
+
|
53
55
|
|
54
56
|
class CircuitMetrics(pydantic.BaseModel):
|
55
57
|
depth: int
|
@@ -83,6 +85,21 @@ class OperationLinks(pydantic.BaseModel):
|
|
83
85
|
inputs: list[OperationLink]
|
84
86
|
outputs: list[OperationLink]
|
85
87
|
|
88
|
+
model_config = ConfigDict(frozen=True)
|
89
|
+
|
90
|
+
@field_validator("inputs", "outputs", mode="after")
|
91
|
+
@classmethod
|
92
|
+
def sort_links(cls, v: list[OperationLink]) -> Any:
|
93
|
+
"""
|
94
|
+
sorting the input/output links on creation
|
95
|
+
the sort is done by 'label-qubits-type'
|
96
|
+
since hash is non-deterministic between runs
|
97
|
+
"""
|
98
|
+
return sorted(v, key=hash)
|
99
|
+
|
100
|
+
def __hash__(self) -> int:
|
101
|
+
return hash(json.dumps(self.model_dump(exclude_none=True), sort_keys=True))
|
102
|
+
|
86
103
|
@cached_property
|
87
104
|
def input_width(self) -> int:
|
88
105
|
return sum(len(link.qubits) for link in self.inputs)
|
@@ -131,6 +148,7 @@ class AtomicGate(StrEnum):
|
|
131
148
|
|
132
149
|
class Operation(pydantic.BaseModel):
|
133
150
|
name: str
|
151
|
+
inner_label: Optional[str] = None
|
134
152
|
_id: int = pydantic.PrivateAttr(default_factory=_operation_id_counter.next_id)
|
135
153
|
qasm_name: str = pydantic.Field(default="")
|
136
154
|
details: str = pydantic.Field(default="")
|
@@ -152,6 +170,9 @@ class Operation(pydantic.BaseModel):
|
|
152
170
|
is_daggered: bool = pydantic.Field(default=False)
|
153
171
|
expanded: bool = pydantic.Field(default=False)
|
154
172
|
show_expanded_label: bool = pydantic.Field(default=False)
|
173
|
+
is_low_level_fallback: bool = pydantic.Field(default=False)
|
174
|
+
|
175
|
+
model_config = ConfigDict(frozen=True)
|
155
176
|
|
156
177
|
@property
|
157
178
|
def id(self) -> int:
|
@@ -85,6 +85,35 @@ class QuantumScalar(QuantumType):
|
|
85
85
|
def get_proxy(self, handle: "HandleBinding") -> QmodQScalarProxy:
|
86
86
|
return QmodQScalarProxy(handle, size=self.size_in_bits)
|
87
87
|
|
88
|
+
@property
|
89
|
+
def has_sign(self) -> bool:
|
90
|
+
raise NotImplementedError
|
91
|
+
|
92
|
+
@property
|
93
|
+
def sign_value(self) -> bool:
|
94
|
+
raise NotImplementedError
|
95
|
+
|
96
|
+
@property
|
97
|
+
def has_fraction_digits(self) -> bool:
|
98
|
+
raise NotImplementedError
|
99
|
+
|
100
|
+
@property
|
101
|
+
def fraction_digits_value(self) -> int:
|
102
|
+
raise NotImplementedError
|
103
|
+
|
104
|
+
def get_effective_bounds(
|
105
|
+
self, machine_precision: Optional[int] = None
|
106
|
+
) -> tuple[float, float]:
|
107
|
+
raise NotImplementedError
|
108
|
+
|
109
|
+
@property
|
110
|
+
def is_qbit(self) -> bool:
|
111
|
+
return (
|
112
|
+
self.size_in_bits == 1
|
113
|
+
and self.fraction_digits_value == 0
|
114
|
+
and not self.sign_value
|
115
|
+
)
|
116
|
+
|
88
117
|
|
89
118
|
class QuantumBit(QuantumScalar):
|
90
119
|
kind: Literal["qbit"]
|
@@ -117,6 +146,27 @@ class QuantumBit(QuantumScalar):
|
|
117
146
|
def is_evaluated(self) -> bool:
|
118
147
|
return True
|
119
148
|
|
149
|
+
@property
|
150
|
+
def has_sign(self) -> bool:
|
151
|
+
return True
|
152
|
+
|
153
|
+
@property
|
154
|
+
def sign_value(self) -> bool:
|
155
|
+
return False
|
156
|
+
|
157
|
+
@property
|
158
|
+
def has_fraction_digits(self) -> bool:
|
159
|
+
return True
|
160
|
+
|
161
|
+
@property
|
162
|
+
def fraction_digits_value(self) -> int:
|
163
|
+
return 0
|
164
|
+
|
165
|
+
def get_effective_bounds(
|
166
|
+
self, machine_precision: Optional[int] = None
|
167
|
+
) -> tuple[float, float]:
|
168
|
+
return (0, 1)
|
169
|
+
|
120
170
|
|
121
171
|
class QuantumBitvector(QuantumType):
|
122
172
|
element_type: "ConcreteQuantumType" = Field(
|
@@ -243,14 +293,6 @@ class QuantumNumeric(QuantumScalar):
|
|
243
293
|
0 if self.fraction_digits is None else self.fraction_digits.to_int_value()
|
244
294
|
)
|
245
295
|
|
246
|
-
@property
|
247
|
-
def is_qbit(self) -> bool:
|
248
|
-
return (
|
249
|
-
self.size_in_bits == 1
|
250
|
-
and self.fraction_digits_value == 0
|
251
|
-
and not self.sign_value
|
252
|
-
)
|
253
|
-
|
254
296
|
def _update_size_in_bits_from_declaration(self) -> None:
|
255
297
|
if self.size is not None and self.size.is_evaluated():
|
256
298
|
self._size_in_bits = self.size.to_int_value()
|
@@ -302,20 +344,8 @@ class QuantumNumeric(QuantumScalar):
|
|
302
344
|
exprs.append(self.fraction_digits)
|
303
345
|
return exprs
|
304
346
|
|
305
|
-
def get_bounds(
|
306
|
-
self
|
307
|
-
) -> Optional[tuple[float, float]]:
|
308
|
-
if (
|
309
|
-
self._bounds is None
|
310
|
-
or machine_precision is None
|
311
|
-
or self.fraction_digits_value <= machine_precision
|
312
|
-
):
|
313
|
-
return self._bounds
|
314
|
-
|
315
|
-
return (
|
316
|
-
number_utils.limit_fraction_places(self._bounds[0], machine_precision),
|
317
|
-
number_utils.limit_fraction_places(self._bounds[1], machine_precision),
|
318
|
-
)
|
347
|
+
def get_bounds(self) -> Optional[tuple[float, float]]:
|
348
|
+
return self._bounds
|
319
349
|
|
320
350
|
def set_bounds(self, bounds: Optional[tuple[float, float]]) -> None:
|
321
351
|
self._bounds = bounds
|
@@ -323,19 +353,23 @@ class QuantumNumeric(QuantumScalar):
|
|
323
353
|
def reset_bounds(self) -> None:
|
324
354
|
self.set_bounds(None)
|
325
355
|
|
326
|
-
def get_maximal_bounds(
|
356
|
+
def get_maximal_bounds(self) -> tuple[float, float]:
|
357
|
+
return RegisterArithmeticInfo.get_maximal_bounds(
|
358
|
+
size=self.size_in_bits,
|
359
|
+
is_signed=self.sign_value,
|
360
|
+
fraction_places=self.fraction_digits_value,
|
361
|
+
)
|
362
|
+
|
363
|
+
def get_effective_bounds(
|
327
364
|
self, machine_precision: Optional[int] = None
|
328
365
|
) -> tuple[float, float]:
|
329
|
-
|
330
|
-
fraction_digits = self.fraction_digits_value
|
331
|
-
if machine_precision is not None and fraction_digits > machine_precision:
|
332
|
-
size -= fraction_digits - machine_precision
|
333
|
-
fraction_digits = machine_precision
|
366
|
+
bounds = self.get_bounds() or self.get_maximal_bounds()
|
334
367
|
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
368
|
+
if machine_precision is None or machine_precision >= self.fraction_digits_value:
|
369
|
+
return bounds
|
370
|
+
return (
|
371
|
+
number_utils.limit_fraction_places(bounds[0], machine_precision),
|
372
|
+
number_utils.limit_fraction_places(bounds[1], machine_precision),
|
339
373
|
)
|
340
374
|
|
341
375
|
|
@@ -359,7 +393,7 @@ def register_info_to_quantum_type(reg_info: RegisterArithmeticInfo) -> QuantumNu
|
|
359
393
|
result.set_size_in_bits(reg_info.size)
|
360
394
|
result.is_signed = Expression(expr=str(reg_info.is_signed))
|
361
395
|
result.fraction_digits = Expression(expr=str(reg_info.fraction_places))
|
362
|
-
result.set_bounds(reg_info.bounds)
|
396
|
+
result.set_bounds(tuple(reg_info.bounds)) # type: ignore[arg-type]
|
363
397
|
return result
|
364
398
|
|
365
399
|
|
@@ -0,0 +1,115 @@
|
|
1
|
+
from dataclasses import dataclass
|
2
|
+
from typing import Optional, Union
|
3
|
+
|
4
|
+
from classiq.interface.generator.arith import number_utils
|
5
|
+
from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
|
6
|
+
from classiq.interface.model.quantum_type import (
|
7
|
+
QuantumScalar,
|
8
|
+
register_info_to_quantum_type,
|
9
|
+
)
|
10
|
+
|
11
|
+
|
12
|
+
@dataclass
|
13
|
+
class NumericAttributes:
|
14
|
+
size: int
|
15
|
+
is_signed: bool
|
16
|
+
fraction_digits: int
|
17
|
+
bounds: tuple[float, float]
|
18
|
+
|
19
|
+
def __init__(
|
20
|
+
self,
|
21
|
+
size: int,
|
22
|
+
is_signed: bool,
|
23
|
+
fraction_digits: int,
|
24
|
+
bounds: Optional[tuple[float, float]] = None,
|
25
|
+
trim_bounds: bool = False,
|
26
|
+
) -> None:
|
27
|
+
self.size = size
|
28
|
+
self.is_signed = is_signed
|
29
|
+
self.fraction_digits = fraction_digits
|
30
|
+
if bounds is None:
|
31
|
+
bounds = RegisterArithmeticInfo.get_maximal_bounds(
|
32
|
+
size=size,
|
33
|
+
is_signed=is_signed,
|
34
|
+
fraction_places=fraction_digits,
|
35
|
+
)
|
36
|
+
if trim_bounds:
|
37
|
+
bounds = (
|
38
|
+
number_utils.limit_fraction_places(bounds[0], fraction_digits),
|
39
|
+
number_utils.limit_fraction_places(bounds[1], fraction_digits),
|
40
|
+
)
|
41
|
+
self.bounds = bounds
|
42
|
+
|
43
|
+
@property
|
44
|
+
def lb(self) -> float:
|
45
|
+
return self.bounds[0]
|
46
|
+
|
47
|
+
@property
|
48
|
+
def ub(self) -> float:
|
49
|
+
return self.bounds[1]
|
50
|
+
|
51
|
+
@classmethod
|
52
|
+
def from_bounds(
|
53
|
+
cls, lb: float, ub: float, fraction_places: int, machine_precision: int
|
54
|
+
) -> "NumericAttributes":
|
55
|
+
size, is_signed, fraction_digits = number_utils.bounds_to_attributes(
|
56
|
+
lb, ub, fraction_places, machine_precision
|
57
|
+
)
|
58
|
+
return cls(
|
59
|
+
size=size,
|
60
|
+
is_signed=is_signed,
|
61
|
+
fraction_digits=fraction_digits,
|
62
|
+
bounds=(lb, ub),
|
63
|
+
)
|
64
|
+
|
65
|
+
@classmethod
|
66
|
+
def from_constant(
|
67
|
+
cls,
|
68
|
+
value: float,
|
69
|
+
machine_precision: Optional[int] = None,
|
70
|
+
) -> "NumericAttributes":
|
71
|
+
if machine_precision is not None:
|
72
|
+
value = number_utils.limit_fraction_places(value, machine_precision)
|
73
|
+
|
74
|
+
return cls(
|
75
|
+
size=number_utils.size(value),
|
76
|
+
is_signed=value < 0,
|
77
|
+
fraction_digits=number_utils.fraction_places(value),
|
78
|
+
bounds=(value, value),
|
79
|
+
)
|
80
|
+
|
81
|
+
@classmethod
|
82
|
+
def from_quantum_scalar(
|
83
|
+
cls,
|
84
|
+
quantum_type: QuantumScalar,
|
85
|
+
machine_precision: Optional[int] = None,
|
86
|
+
) -> "NumericAttributes":
|
87
|
+
return cls(
|
88
|
+
size=quantum_type.size_in_bits,
|
89
|
+
is_signed=quantum_type.sign_value,
|
90
|
+
fraction_digits=quantum_type.fraction_digits_value,
|
91
|
+
bounds=quantum_type.get_effective_bounds(machine_precision),
|
92
|
+
)
|
93
|
+
|
94
|
+
@classmethod
|
95
|
+
def from_register_arithmetic_info(
|
96
|
+
cls,
|
97
|
+
register: RegisterArithmeticInfo,
|
98
|
+
machine_precision: Optional[int] = None,
|
99
|
+
) -> "NumericAttributes":
|
100
|
+
return cls.from_quantum_scalar(
|
101
|
+
quantum_type=register_info_to_quantum_type(register),
|
102
|
+
machine_precision=machine_precision,
|
103
|
+
)
|
104
|
+
|
105
|
+
@classmethod
|
106
|
+
def from_type_or_constant(
|
107
|
+
cls,
|
108
|
+
from_: Union[float, QuantumScalar, RegisterArithmeticInfo],
|
109
|
+
machine_precision: Optional[int] = None,
|
110
|
+
) -> "NumericAttributes":
|
111
|
+
if isinstance(from_, QuantumScalar):
|
112
|
+
return cls.from_quantum_scalar(from_, machine_precision)
|
113
|
+
if isinstance(from_, RegisterArithmeticInfo):
|
114
|
+
return cls.from_register_arithmetic_info(from_, machine_precision)
|
115
|
+
return cls.from_constant(from_, machine_precision)
|