classiq 0.84.0__py3-none-any.whl → 0.86.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/applications/combinatorial_optimization/combinatorial_problem.py +24 -45
- classiq/evaluators/classical_expression.py +32 -15
- 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 +53 -6
- classiq/interface/_version.py +1 -1
- classiq/interface/analyzer/analysis_params.py +1 -1
- classiq/interface/analyzer/result.py +1 -1
- classiq/interface/debug_info/debug_info.py +0 -4
- 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/atomic_expression_functions.py +5 -0
- 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/builtins/internal_operators.py +2 -0
- classiq/interface/generator/functions/classical_function_declaration.py +0 -4
- classiq/interface/generator/functions/classical_type.py +0 -32
- classiq/interface/generator/functions/concrete_types.py +20 -0
- classiq/interface/generator/generated_circuit_data.py +7 -10
- 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 +45 -0
- classiq/interface/ide/visual_model.py +84 -2
- classiq/interface/model/bounds.py +12 -2
- classiq/interface/model/quantum_expressions/arithmetic_operation.py +7 -4
- classiq/interface/model/quantum_type.py +67 -33
- classiq/interface/model/variable_declaration_statement.py +33 -6
- 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 +10 -6
- classiq/model_expansions/function_builder.py +4 -1
- classiq/model_expansions/generative_functions.py +15 -2
- classiq/model_expansions/interpreters/base_interpreter.py +7 -0
- classiq/model_expansions/interpreters/generative_interpreter.py +18 -1
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +63 -21
- classiq/model_expansions/quantum_operations/bounds.py +7 -1
- classiq/model_expansions/quantum_operations/call_emitter.py +5 -2
- classiq/model_expansions/quantum_operations/classical_var_emitter.py +16 -0
- classiq/model_expansions/quantum_operations/variable_decleration.py +30 -10
- classiq/model_expansions/scope.py +7 -0
- 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/transformers/var_splitter.py +1 -1
- classiq/model_expansions/visitors/boolean_expression_transformers.py +1 -1
- classiq/open_library/functions/__init__.py +0 -2
- classiq/open_library/functions/qaoa_penalty.py +8 -1
- classiq/open_library/functions/state_preparation.py +1 -32
- classiq/qmod/__init__.py +2 -0
- classiq/qmod/builtins/operations.py +66 -2
- classiq/qmod/classical_variable.py +74 -0
- classiq/qmod/declaration_inferrer.py +5 -3
- classiq/qmod/native/pretty_printer.py +18 -14
- classiq/qmod/pretty_print/pretty_printer.py +34 -15
- classiq/qmod/qfunc.py +2 -19
- classiq/qmod/qmod_variable.py +5 -8
- classiq/qmod/quantum_expandable.py +1 -1
- classiq/qmod/quantum_function.py +42 -2
- classiq/qmod/symbolic_type.py +2 -1
- classiq/qmod/write_qmod.py +3 -1
- {classiq-0.84.0.dist-info → classiq-0.86.0.dist-info}/METADATA +1 -1
- {classiq-0.84.0.dist-info → classiq-0.86.0.dist-info}/RECORD +86 -62
- classiq/interface/model/quantum_variable_declaration.py +0 -7
- /classiq/{model_expansions/sympy_conversion/arithmetics.py → evaluators/qmod_expression_visitors/sympy_wrappers.py} +0 -0
- {classiq-0.84.0.dist-info → classiq-0.86.0.dist-info}/WHEEL +0 -0
@@ -1,10 +1,15 @@
|
|
1
1
|
import inspect
|
2
2
|
import random
|
3
|
+
import warnings
|
3
4
|
from types import TracebackType
|
4
|
-
from typing import Callable, Optional, Union, cast
|
5
|
+
from typing import Any, Callable, Optional, Union, cast
|
5
6
|
|
6
7
|
from classiq.interface.chemistry.operator import PauliOperator, pauli_integers_to_str
|
7
|
-
from classiq.interface.exceptions import
|
8
|
+
from classiq.interface.exceptions import (
|
9
|
+
ClassiqDeprecationWarning,
|
10
|
+
ClassiqError,
|
11
|
+
ClassiqValueError,
|
12
|
+
)
|
8
13
|
from classiq.interface.execution.primitives import (
|
9
14
|
EstimateInput,
|
10
15
|
MinimizeClassicalCostInput,
|
@@ -71,6 +76,20 @@ def parse_params(params: ExecutionParams) -> ParsedExecutionParams:
|
|
71
76
|
return result
|
72
77
|
|
73
78
|
|
79
|
+
def _hamiltonian_deprecation_warning(hamiltonian: Any) -> None:
|
80
|
+
if isinstance(hamiltonian, list):
|
81
|
+
warnings.warn(
|
82
|
+
(
|
83
|
+
"Parameter type list[PauliTerm] to 'ExecutionSession' methods is "
|
84
|
+
"deprecated and will no longer be supported starting on 21/7/2025 "
|
85
|
+
"at the earliest. Instead, send a 'SparsePauliOp' (see "
|
86
|
+
"https://docs.classiq.io/latest/qmod-reference/language-reference/classical-types/#hamiltonians)."
|
87
|
+
),
|
88
|
+
ClassiqDeprecationWarning,
|
89
|
+
stacklevel=3,
|
90
|
+
)
|
91
|
+
|
92
|
+
|
74
93
|
class ExecutionSession:
|
75
94
|
"""
|
76
95
|
A session for executing a quantum program.
|
@@ -228,11 +247,18 @@ class ExecutionSession:
|
|
228
247
|
Returns:
|
229
248
|
EstimationResult: The result of the estimation.
|
230
249
|
"""
|
231
|
-
|
250
|
+
_hamiltonian_deprecation_warning(hamiltonian)
|
251
|
+
job = self.submit_estimate(
|
252
|
+
hamiltonian=hamiltonian, parameters=parameters, _check_deprecation=False
|
253
|
+
)
|
232
254
|
return job.get_estimate_result(_http_client=self._async_client)
|
233
255
|
|
234
256
|
def submit_estimate(
|
235
|
-
self,
|
257
|
+
self,
|
258
|
+
hamiltonian: Hamiltonian,
|
259
|
+
parameters: Optional[ExecutionParams] = None,
|
260
|
+
*,
|
261
|
+
_check_deprecation: bool = True,
|
236
262
|
) -> ExecutionJob:
|
237
263
|
"""
|
238
264
|
Initiates an execution job with the `estimate` primitive.
|
@@ -247,6 +273,8 @@ class ExecutionSession:
|
|
247
273
|
Returns:
|
248
274
|
The execution job.
|
249
275
|
"""
|
276
|
+
if _check_deprecation:
|
277
|
+
_hamiltonian_deprecation_warning(hamiltonian)
|
250
278
|
execution_primitives_input = PrimitivesInput(
|
251
279
|
estimate=EstimateInput(
|
252
280
|
hamiltonian=self._hamiltonian_to_pauli_operator(hamiltonian),
|
@@ -270,11 +298,18 @@ class ExecutionSession:
|
|
270
298
|
Returns:
|
271
299
|
List[EstimationResult]: The results of all the estimation iterations.
|
272
300
|
"""
|
273
|
-
|
301
|
+
_hamiltonian_deprecation_warning(hamiltonian)
|
302
|
+
job = self.submit_batch_estimate(
|
303
|
+
hamiltonian=hamiltonian, parameters=parameters, _check_deprecation=False
|
304
|
+
)
|
274
305
|
return job.get_batch_estimate_result(_http_client=self._async_client)
|
275
306
|
|
276
307
|
def submit_batch_estimate(
|
277
|
-
self,
|
308
|
+
self,
|
309
|
+
hamiltonian: Hamiltonian,
|
310
|
+
parameters: list[ExecutionParams],
|
311
|
+
*,
|
312
|
+
_check_deprecation: bool = True,
|
278
313
|
) -> ExecutionJob:
|
279
314
|
"""
|
280
315
|
Initiates an execution job with the `batch_estimate` primitive.
|
@@ -289,6 +324,8 @@ class ExecutionSession:
|
|
289
324
|
Returns:
|
290
325
|
The execution job.
|
291
326
|
"""
|
327
|
+
if _check_deprecation:
|
328
|
+
_hamiltonian_deprecation_warning(hamiltonian)
|
292
329
|
execution_primitives_input = PrimitivesInput(
|
293
330
|
estimate=EstimateInput(
|
294
331
|
hamiltonian=self._hamiltonian_to_pauli_operator(hamiltonian),
|
@@ -322,11 +359,13 @@ class ExecutionSession:
|
|
322
359
|
A list of tuples, each containing the estimated cost and the corresponding parameters for that iteration.
|
323
360
|
`cost` is a float, and `parameters` is a dictionary matching the execution parameter format.
|
324
361
|
"""
|
362
|
+
_hamiltonian_deprecation_warning(cost_function)
|
325
363
|
job = self.submit_minimize(
|
326
364
|
cost_function=cost_function,
|
327
365
|
initial_params=initial_params,
|
328
366
|
max_iteration=max_iteration,
|
329
367
|
quantile=quantile,
|
368
|
+
_check_deprecation=False,
|
330
369
|
)
|
331
370
|
result = job.get_minimization_result(_http_client=self._async_client)
|
332
371
|
|
@@ -340,6 +379,8 @@ class ExecutionSession:
|
|
340
379
|
initial_params: ExecutionParams,
|
341
380
|
max_iteration: int,
|
342
381
|
quantile: float = 1.0,
|
382
|
+
*,
|
383
|
+
_check_deprecation: bool = True,
|
343
384
|
) -> ExecutionJob:
|
344
385
|
"""
|
345
386
|
Initiates an execution job with the `minimize` primitive.
|
@@ -363,6 +404,8 @@ class ExecutionSession:
|
|
363
404
|
Returns:
|
364
405
|
The execution job.
|
365
406
|
"""
|
407
|
+
if _check_deprecation:
|
408
|
+
_hamiltonian_deprecation_warning(cost_function)
|
366
409
|
if len(initial_params) != 1:
|
367
410
|
raise ClassiqValueError(
|
368
411
|
"The initial parameters must be a dictionary with a single key-value pair."
|
@@ -461,6 +504,10 @@ class ExecutionSession:
|
|
461
504
|
raise NotImplementedError(
|
462
505
|
"Filtering is only supported on a single value per model output"
|
463
506
|
)
|
507
|
+
if len(legal_bitstrings) == 0:
|
508
|
+
raise ClassiqValueError(
|
509
|
+
f"The condition was false for every possible value of {output_name}"
|
510
|
+
)
|
464
511
|
else:
|
465
512
|
raise NotImplementedError(
|
466
513
|
"Filtering is only supported on QuantumBit and QuantumNumeric"
|
classiq/interface/_version.py
CHANGED
@@ -14,7 +14,7 @@ from classiq.interface.generator.model.preferences.preferences import (
|
|
14
14
|
from classiq.interface.hardware import Provider
|
15
15
|
from classiq.interface.helpers.custom_pydantic_types import PydanticNonEmptyString
|
16
16
|
|
17
|
-
MAX_KB_OF_FILE =
|
17
|
+
MAX_KB_OF_FILE = 50000
|
18
18
|
MAX_FILE_LENGTH = MAX_KB_OF_FILE * 1024
|
19
19
|
|
20
20
|
MAX_QUBITS = 100
|
@@ -9,7 +9,6 @@ from classiq.interface.generator.generated_circuit_data import (
|
|
9
9
|
FunctionDebugInfoInterface,
|
10
10
|
StatementType,
|
11
11
|
)
|
12
|
-
from classiq.interface.model.block import Block
|
13
12
|
from classiq.interface.model.handle_binding import ConcreteHandleBinding
|
14
13
|
from classiq.interface.model.port_declaration import PortDeclaration
|
15
14
|
from classiq.interface.model.quantum_function_call import ArgValue
|
@@ -82,9 +81,6 @@ def get_back_refs(
|
|
82
81
|
) -> list[ConcreteQuantumStatement]:
|
83
82
|
back_refs: list[ConcreteQuantumStatement] = []
|
84
83
|
while (node := debug_info.node) is not None:
|
85
|
-
# For backwards compatibility, we make sure that the back_ref is not a block
|
86
|
-
# Remove this check when we start saving blocks in the debug info collection.
|
87
|
-
assert not isinstance(node, Block)
|
88
84
|
if len(back_refs) > 0 and node.back_ref == back_refs[0].back_ref:
|
89
85
|
break
|
90
86
|
back_refs.insert(0, node)
|
@@ -95,8 +95,8 @@ class QuantumCode(QuantumBaseCode):
|
|
95
95
|
) -> Optional[ExecutionData]:
|
96
96
|
if (
|
97
97
|
synthesis_execution_data is not None
|
98
|
-
and
|
99
|
-
):
|
98
|
+
and synthesis_execution_data.function_execution
|
99
|
+
) and values.data.get("syntax") is not QuantumInstructionSet.QASM:
|
100
100
|
raise ClassiqValueError("Only QASM supports the requested configuration")
|
101
101
|
|
102
102
|
return synthesis_execution_data
|
@@ -63,6 +63,10 @@ def is_constant(expr: Union[str, Expr]) -> bool:
|
|
63
63
|
return False
|
64
64
|
|
65
65
|
|
66
|
+
def is_variable(expr: str) -> bool:
|
67
|
+
return IDENITIFIER_REGEX.fullmatch(expr) is not None
|
68
|
+
|
69
|
+
|
66
70
|
class ExpressionValidator(ast.NodeVisitor):
|
67
71
|
def __init__(
|
68
72
|
self,
|
@@ -95,7 +99,7 @@ class ExpressionValidator(ast.NodeVisitor):
|
|
95
99
|
@staticmethod
|
96
100
|
def _get_adjusted_expression(expression: str) -> str:
|
97
101
|
# This works around the simplification of the trivial expressions such as a + 0, 1 * a, etc.
|
98
|
-
if
|
102
|
+
if is_variable(expression) or is_constant(expression):
|
99
103
|
return f"0 + {expression}"
|
100
104
|
return expression
|
101
105
|
|
@@ -20,7 +20,6 @@ from classiq.interface.exceptions import ClassiqValueError
|
|
20
20
|
from classiq.interface.generator.arith import argument_utils, number_utils
|
21
21
|
from classiq.interface.generator.arith.argument_utils import (
|
22
22
|
RegisterOrConst,
|
23
|
-
as_arithmetic_info,
|
24
23
|
)
|
25
24
|
from classiq.interface.generator.arith.arithmetic_operations import (
|
26
25
|
MODULO_WITH_FRACTION_PLACES_ERROR_MSG,
|
@@ -33,6 +32,13 @@ from classiq.interface.generator.arith.register_user_input import RegisterArithm
|
|
33
32
|
from classiq.interface.generator.arith.unary_ops import Negation
|
34
33
|
from classiq.interface.generator.function_params import get_zero_input_name
|
35
34
|
|
35
|
+
from classiq.model_expansions.arithmetic import NumericAttributes
|
36
|
+
from classiq.model_expansions.arithmetic_compute_result_attrs import (
|
37
|
+
compute_result_attrs_add,
|
38
|
+
compute_result_attrs_assign,
|
39
|
+
compute_result_attrs_subtract,
|
40
|
+
)
|
41
|
+
|
36
42
|
LeftDataT = TypeVar("LeftDataT")
|
37
43
|
RightDataT = TypeVar("RightDataT")
|
38
44
|
_NumericArgumentInplaceErrorMessage: str = "Cannot inplace the numeric argument {}"
|
@@ -268,40 +274,35 @@ class Adder(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
|
268
274
|
output_name = "sum"
|
269
275
|
|
270
276
|
def _get_result_register(self) -> RegisterArithmeticInfo:
|
271
|
-
|
277
|
+
left_attrs = NumericAttributes.from_type_or_constant(
|
272
278
|
self.left_arg, self.machine_precision
|
273
279
|
)
|
274
|
-
|
280
|
+
right_attrs = NumericAttributes.from_type_or_constant(
|
275
281
|
self.right_arg, self.machine_precision
|
276
282
|
)
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
283
|
+
|
284
|
+
if self._is_zero(self.left_arg) or self._is_zero(self.right_arg):
|
285
|
+
other_arg_attrs = (
|
286
|
+
right_attrs if self._is_zero(self.left_arg) else left_attrs
|
287
|
+
)
|
288
|
+
result_attrs = compute_result_attrs_assign(
|
289
|
+
other_arg_attrs, self.machine_precision
|
290
|
+
)
|
291
|
+
else:
|
292
|
+
result_attrs = compute_result_attrs_add(
|
293
|
+
left_attrs, right_attrs, self.machine_precision
|
294
|
+
)
|
295
|
+
|
287
296
|
return RegisterArithmeticInfo(
|
288
|
-
size=self.output_size or
|
289
|
-
fraction_places=
|
290
|
-
is_signed=self._include_sign and
|
291
|
-
bounds=
|
297
|
+
size=self.output_size or result_attrs.size,
|
298
|
+
fraction_places=result_attrs.fraction_digits,
|
299
|
+
is_signed=self._include_sign and result_attrs.is_signed,
|
300
|
+
bounds=result_attrs.bounds if self._include_sign else None,
|
292
301
|
)
|
293
302
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
return self.right_arg.size
|
298
|
-
return as_arithmetic_info(self.right_arg).size
|
299
|
-
elif isinstance(self.right_arg, float) and self.right_arg == 0.0:
|
300
|
-
assert isinstance(self.left_arg, RegisterArithmeticInfo)
|
301
|
-
return self.left_arg.size
|
302
|
-
|
303
|
-
integer_part_size = number_utils.bounds_to_integer_part_size(lb, ub)
|
304
|
-
return integer_part_size + fraction_places
|
303
|
+
@staticmethod
|
304
|
+
def _is_zero(arg: RegisterOrConst) -> bool:
|
305
|
+
return isinstance(arg, float) and arg == 0
|
305
306
|
|
306
307
|
|
307
308
|
class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
@@ -337,41 +338,32 @@ class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
|
337
338
|
return self._is_arg_trimmed_register(self.right_arg, self.machine_precision)
|
338
339
|
|
339
340
|
def _get_result_register(self) -> RegisterArithmeticInfo:
|
340
|
-
|
341
|
-
|
342
|
-
- argument_utils.upper_bound(self.effective_right_arg),
|
343
|
-
argument_utils.upper_bound(self.effective_left_arg)
|
344
|
-
- argument_utils.lower_bound(self.effective_right_arg),
|
341
|
+
left_attrs = NumericAttributes.from_type_or_constant(
|
342
|
+
self.left_arg, self.machine_precision
|
345
343
|
)
|
346
|
-
|
347
|
-
|
348
|
-
|
344
|
+
right_attrs = NumericAttributes.from_type_or_constant(
|
345
|
+
self.right_arg, self.machine_precision
|
346
|
+
)
|
347
|
+
result_attrs = compute_result_attrs_subtract(
|
348
|
+
left_attrs, right_attrs, self.machine_precision
|
349
349
|
)
|
350
350
|
|
351
|
-
size = self.output_size or
|
352
|
-
is_signed = self._include_sign and
|
351
|
+
size = self.output_size or result_attrs.size
|
352
|
+
is_signed = self._include_sign and result_attrs.is_signed
|
353
353
|
return RegisterArithmeticInfo(
|
354
354
|
size=size,
|
355
|
-
fraction_places=
|
355
|
+
fraction_places=result_attrs.fraction_digits,
|
356
356
|
is_signed=is_signed,
|
357
357
|
bounds=self._legal_bounds(
|
358
|
-
bounds,
|
358
|
+
result_attrs.bounds,
|
359
359
|
RegisterArithmeticInfo.get_maximal_bounds(
|
360
|
-
size=size,
|
360
|
+
size=size,
|
361
|
+
is_signed=is_signed,
|
362
|
+
fraction_places=result_attrs.fraction_digits,
|
361
363
|
),
|
362
364
|
),
|
363
365
|
)
|
364
366
|
|
365
|
-
def _get_output_size(
|
366
|
-
self, bounds: tuple[float, float], fraction_places: int
|
367
|
-
) -> int:
|
368
|
-
if isinstance(self.right_arg, float) and self.effective_right_arg == 0:
|
369
|
-
assert isinstance(self.effective_left_arg, RegisterArithmeticInfo)
|
370
|
-
return self.effective_left_arg.size
|
371
|
-
integer_part_size = number_utils.bounds_to_integer_part_size(*bounds)
|
372
|
-
size_needed = integer_part_size + fraction_places
|
373
|
-
return size_needed
|
374
|
-
|
375
367
|
def garbage_output_size(self) -> pydantic.NonNegativeInt:
|
376
368
|
if (
|
377
369
|
not self.left_arg_is_trimmed_register
|
@@ -79,10 +79,11 @@ def size(float_value: float) -> int:
|
|
79
79
|
|
80
80
|
|
81
81
|
def _is_extra_sign_bit_needed(*, lb: float, ub: float) -> bool:
|
82
|
-
|
82
|
+
fractions = max(fraction_places(lb), fraction_places(ub))
|
83
|
+
integer_lb = lb * 2**fractions
|
83
84
|
max_represented_number = (
|
84
85
|
2 ** (len(binary_string(integer_lb)) - 1) - 1
|
85
|
-
) / 2
|
86
|
+
) / 2**fractions
|
86
87
|
return ub > max_represented_number
|
87
88
|
|
88
89
|
|
@@ -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):
|
@@ -20,9 +20,14 @@ CLASSIQ_EXPR_FUNCTIONS = {
|
|
20
20
|
"get_field",
|
21
21
|
}
|
22
22
|
|
23
|
+
MEASUREMENT_FUNCTIONS = {
|
24
|
+
"measure",
|
25
|
+
}
|
26
|
+
|
23
27
|
SUPPORTED_CLASSIQ_BUILTIN_FUNCTIONS = {
|
24
28
|
*CLASSIQ_BUILTIN_CLASSICAL_FUNCTIONS,
|
25
29
|
*CLASSIQ_EXPR_FUNCTIONS,
|
30
|
+
*MEASUREMENT_FUNCTIONS,
|
26
31
|
}
|
27
32
|
|
28
33
|
SUPPORTED_CLASSIQ_SYMPY_WRAPPERS = {
|
@@ -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
|
+
)
|
@@ -6,6 +6,7 @@ CLASSICAL_IF_OPERATOR_NAME = "classical_if"
|
|
6
6
|
POWER_OPERATOR_NAME = "power"
|
7
7
|
UNCOMPUTE_OPERATOR_NAME = "uncompute"
|
8
8
|
WITHIN_APPLY_NAME = "within_apply"
|
9
|
+
BLOCK_OPERATOR_NAME = "block"
|
9
10
|
|
10
11
|
All_BUILTINS_OPERATORS = {
|
11
12
|
CONTROL_OPERATOR_NAME,
|
@@ -14,4 +15,5 @@ All_BUILTINS_OPERATORS = {
|
|
14
15
|
POWER_OPERATOR_NAME,
|
15
16
|
UNCOMPUTE_OPERATOR_NAME,
|
16
17
|
WITHIN_APPLY_NAME,
|
18
|
+
BLOCK_OPERATOR_NAME,
|
17
19
|
}
|
@@ -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()
|
@@ -53,3 +53,23 @@ QuantumBitvector.model_rebuild()
|
|
53
53
|
TypeName.model_rebuild()
|
54
54
|
QStructDeclaration.model_rebuild()
|
55
55
|
RegisterQuantumType.model_rebuild()
|
56
|
+
|
57
|
+
ConcreteType = Annotated[
|
58
|
+
Union[
|
59
|
+
Integer,
|
60
|
+
Real,
|
61
|
+
Bool,
|
62
|
+
StructMetaType,
|
63
|
+
TypeName,
|
64
|
+
ClassicalArray,
|
65
|
+
ClassicalTuple,
|
66
|
+
VQEResult,
|
67
|
+
Histogram,
|
68
|
+
Estimation,
|
69
|
+
IQAERes,
|
70
|
+
QuantumBit,
|
71
|
+
QuantumBitvector,
|
72
|
+
QuantumNumeric,
|
73
|
+
],
|
74
|
+
Field(discriminator="kind"),
|
75
|
+
]
|