classiq 0.66.1__py3-none-any.whl → 0.68.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/_internals/api_wrapper.py +5 -1
- classiq/_internals/async_utils.py +1 -1
- classiq/_internals/authentication/password_manager.py +1 -1
- classiq/_internals/client.py +1 -1
- classiq/applications/finance/finance_model_constructor.py +9 -0
- classiq/applications/grover/grover_model_constructor.py +10 -0
- classiq/applications/qnn/qlayer.py +8 -2
- classiq/applications/qsvm/qsvm_model_constructor.py +9 -0
- classiq/execution/execution_session.py +7 -3
- classiq/executor.py +7 -2
- classiq/interface/_version.py +1 -1
- classiq/interface/ast_node.py +1 -1
- classiq/interface/chemistry/operator.py +1 -1
- classiq/interface/debug_info/debug_info.py +27 -0
- classiq/interface/exceptions.py +2 -5
- classiq/interface/generator/arith/argument_utils.py +1 -1
- classiq/interface/generator/arith/arithmetic.py +99 -2
- classiq/interface/generator/arith/arithmetic_expression_parser.py +1 -1
- classiq/interface/generator/arith/binary_ops.py +3 -0
- classiq/interface/generator/function_param_list_without_self_reference.py +2 -0
- classiq/interface/generator/functions/type_name.py +2 -2
- classiq/interface/generator/generated_circuit_data.py +38 -3
- classiq/interface/generator/hardware_efficient_ansatz.py +1 -1
- classiq/interface/generator/hva.py +1 -1
- classiq/interface/generator/model/preferences/preferences.py +8 -1
- classiq/interface/generator/quantum_program.py +18 -1
- classiq/interface/generator/reset.py +14 -0
- classiq/interface/generator/types/enum_declaration.py +33 -2
- classiq/interface/generator/ucc.py +1 -1
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/classical_if.py +2 -2
- classiq/interface/model/control.py +2 -2
- classiq/interface/model/invert.py +2 -2
- classiq/interface/model/power.py +2 -2
- classiq/interface/model/quantum_function_call.py +4 -0
- classiq/interface/model/quantum_statement.py +13 -0
- classiq/interface/model/repeat.py +2 -2
- classiq/interface/model/statement_block.py +1 -1
- classiq/interface/model/within_apply_operation.py +2 -2
- classiq/model_expansions/atomic_expression_functions_defs.py +2 -1
- classiq/model_expansions/capturing/captured_vars.py +184 -54
- classiq/model_expansions/closure.py +6 -3
- classiq/model_expansions/evaluators/control.py +14 -38
- classiq/model_expansions/function_builder.py +19 -14
- classiq/model_expansions/generative_functions.py +7 -11
- classiq/model_expansions/interpreters/base_interpreter.py +14 -5
- classiq/model_expansions/interpreters/generative_interpreter.py +87 -26
- classiq/model_expansions/quantum_operations/allocate.py +9 -3
- classiq/model_expansions/quantum_operations/assignment_result_processor.py +52 -0
- classiq/model_expansions/quantum_operations/bind.py +67 -14
- classiq/model_expansions/quantum_operations/block_evaluator.py +76 -0
- classiq/model_expansions/quantum_operations/call_emitter.py +79 -10
- classiq/model_expansions/quantum_operations/classicalif.py +10 -6
- classiq/model_expansions/quantum_operations/composite_emitter.py +27 -0
- classiq/model_expansions/quantum_operations/emitter.py +24 -3
- classiq/model_expansions/quantum_operations/expression_evaluator.py +33 -0
- classiq/model_expansions/quantum_operations/handle_evaluator.py +28 -0
- classiq/model_expansions/quantum_operations/quantum_function_call.py +3 -2
- classiq/model_expansions/quantum_operations/repeat.py +9 -3
- classiq/model_expansions/quantum_operations/variable_decleration.py +13 -2
- classiq/model_expansions/sympy_conversion/expression_to_sympy.py +1 -1
- classiq/open_library/functions/__init__.py +1 -2
- classiq/open_library/functions/amplitude_amplification.py +12 -9
- classiq/open_library/functions/discrete_sine_cosine_transform.py +16 -13
- classiq/open_library/functions/grover.py +11 -15
- classiq/open_library/functions/modular_exponentiation.py +7 -13
- classiq/open_library/functions/state_preparation.py +16 -17
- classiq/open_library/functions/swap_test.py +1 -1
- classiq/open_library/functions/utility_functions.py +10 -2
- classiq/qmod/builtins/functions/__init__.py +3 -0
- classiq/qmod/builtins/functions/chemistry.py +6 -38
- classiq/qmod/builtins/functions/mid_circuit_measurement.py +15 -0
- classiq/qmod/quantum_expandable.py +30 -6
- classiq/qmod/quantum_function.py +4 -0
- classiq/qmod/semantics/annotation/call_annotation.py +8 -2
- classiq/qmod/semantics/annotation/model_annotation.py +9 -0
- classiq/qmod/semantics/error_manager.py +0 -6
- classiq/qmod/semantics/static_semantics_visitor.py +0 -347
- classiq/qmod/semantics/validation/types_validation.py +1 -1
- classiq/qmod/symbolic.py +2 -1
- classiq/qmod/utilities.py +2 -1
- classiq/qmod/write_qmod.py +10 -7
- classiq/synthesis.py +20 -7
- {classiq-0.66.1.dist-info → classiq-0.68.0.dist-info}/METADATA +1 -1
- {classiq-0.66.1.dist-info → classiq-0.68.0.dist-info}/RECORD +86 -81
- classiq/model_expansions/quantum_operations/shallow_emitter.py +0 -166
- classiq/qmod/semantics/validation/func_call_validation.py +0 -99
- classiq/qmod/semantics/validation/handle_validation.py +0 -85
- {classiq-0.66.1.dist-info → classiq-0.68.0.dist-info}/WHEEL +0 -0
@@ -3,6 +3,7 @@ from typing import Any, Optional, Protocol, TypeVar
|
|
3
3
|
|
4
4
|
import httpx
|
5
5
|
import pydantic
|
6
|
+
from pydantic.main import IncEx
|
6
7
|
|
7
8
|
import classiq.interface.executor.execution_result
|
8
9
|
import classiq.interface.pyomo_extension
|
@@ -72,11 +73,12 @@ class ApiWrapper:
|
|
72
73
|
model: pydantic.BaseModel,
|
73
74
|
use_versioned_url: bool = True,
|
74
75
|
http_client: Optional[httpx.AsyncClient] = None,
|
76
|
+
exclude: Optional[IncEx] = None,
|
75
77
|
) -> dict:
|
76
78
|
# TODO: we can't use model.dict() - it doesn't serialize complex class.
|
77
79
|
# This was added because JSON serializer doesn't serialize complex type, and pydantic does.
|
78
80
|
# We should add support for smarter json serialization.
|
79
|
-
body = json.loads(model.model_dump_json())
|
81
|
+
body = json.loads(model.model_dump_json(exclude=exclude))
|
80
82
|
return await cls._call_task(
|
81
83
|
http_method,
|
82
84
|
url,
|
@@ -135,6 +137,7 @@ class ApiWrapper:
|
|
135
137
|
url=routes.EXECUTION_SESSIONS_PREFIX,
|
136
138
|
model=circuit,
|
137
139
|
http_client=http_client,
|
140
|
+
exclude={"debug_info"},
|
138
141
|
)
|
139
142
|
return raw_result["id"]
|
140
143
|
|
@@ -164,6 +167,7 @@ class ApiWrapper:
|
|
164
167
|
url=routes.CONVERSION_GENERATED_CIRCUIT_TO_EXECUTION_INPUT_FULL,
|
165
168
|
model=circuit,
|
166
169
|
http_client=http_client,
|
170
|
+
exclude={"debug_info"},
|
167
171
|
)
|
168
172
|
|
169
173
|
@classmethod
|
@@ -58,7 +58,7 @@ def enable_jupyter_notebook() -> None:
|
|
58
58
|
|
59
59
|
|
60
60
|
def _make_iterable_interval(
|
61
|
-
interval_sec: Union[SupportsFloat, Iterable[SupportsFloat]]
|
61
|
+
interval_sec: Union[SupportsFloat, Iterable[SupportsFloat]],
|
62
62
|
) -> Iterable[float]:
|
63
63
|
if isinstance(interval_sec, Iterable):
|
64
64
|
return map(float, interval_sec)
|
@@ -117,7 +117,7 @@ class DummyPasswordManager(PasswordManager):
|
|
117
117
|
|
118
118
|
class FilePasswordManager(PasswordManager):
|
119
119
|
_CLASSIQ_CREDENTIALS_FILE_PATH: str = "{}/.classiq-credentials".format(
|
120
|
-
os.getenv("HOME")
|
120
|
+
os.getenv("CLASSIQ_DIR", os.getenv("HOME"))
|
121
121
|
)
|
122
122
|
|
123
123
|
def __init__(self) -> None:
|
classiq/_internals/client.py
CHANGED
@@ -89,7 +89,7 @@ P = ParamSpec("P")
|
|
89
89
|
|
90
90
|
|
91
91
|
def try_again_on_failure(
|
92
|
-
func: Callable[P, Awaitable[Ret]]
|
92
|
+
func: Callable[P, Awaitable[Ret]],
|
93
93
|
) -> Callable[P, Awaitable[Ret]]:
|
94
94
|
def check_approved_api_error(error_message: str) -> bool:
|
95
95
|
for approved_api_error in APPROVED_API_ERROR_MESSAGES_FOR_RESTART:
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import warnings
|
1
2
|
from math import floor, log
|
2
3
|
from typing import Union
|
3
4
|
|
@@ -32,6 +33,14 @@ def construct_finance_model(
|
|
32
33
|
finance_function_input: FinanceFunctionInput,
|
33
34
|
phase_port_size: int,
|
34
35
|
) -> SerializedModel:
|
36
|
+
warnings.warn(
|
37
|
+
"Function 'construct_finance_model' has been deprecated and will no longer"
|
38
|
+
"be supported starting on 03/02/2025 the earliest\nHint: It is now possible to "
|
39
|
+
"implement Option Pricing in pure Qmod. For example, see the Option Pricing notebook on the "
|
40
|
+
"Classiq library at https://github.com/Classiq/classiq-library/blob/main/applications/finance/option_pricing/option_pricing.ipynb",
|
41
|
+
category=DeprecationWarning,
|
42
|
+
stacklevel=2,
|
43
|
+
)
|
35
44
|
if isinstance(finance_model_input, LogNormalModelInput):
|
36
45
|
finance_model = f"struct_literal(LogNormalModel, num_qubits={finance_model_input.num_qubits}, mu={finance_model_input.mu}, sigma={finance_model_input.sigma})"
|
37
46
|
finance_function = "log_normal_finance"
|
@@ -1,3 +1,5 @@
|
|
1
|
+
import warnings
|
2
|
+
|
1
3
|
from classiq.interface.generator.expressions.expression import Expression
|
2
4
|
from classiq.interface.generator.functions.port_declaration import (
|
3
5
|
PortDeclarationDirection,
|
@@ -90,6 +92,14 @@ def construct_grover_model(
|
|
90
92
|
expression: str,
|
91
93
|
num_reps: int = 1,
|
92
94
|
) -> SerializedModel:
|
95
|
+
warnings.warn(
|
96
|
+
"Function 'construct_grover_model' has been deprecated and will no longer"
|
97
|
+
"be supported starting on 03/02/2025 the earliest\nHint: It is now possible to "
|
98
|
+
"implement the Grover algorithm in pure Qmod. For example, see the Grover notebook on the "
|
99
|
+
"Classiq library at https://github.com/Classiq/classiq-library/blob/main/algorithms/grover/3_sat_grover/3_sat_grover.ipynb",
|
100
|
+
category=DeprecationWarning,
|
101
|
+
stacklevel=2,
|
102
|
+
)
|
93
103
|
predicate_port_decls = grover_main_port_declarations(
|
94
104
|
definitions, PortDeclarationDirection.Inout
|
95
105
|
)
|
@@ -244,9 +244,11 @@ class QLayer(nn.Module):
|
|
244
244
|
|
245
245
|
self.weight = Parameter(value)
|
246
246
|
|
247
|
-
|
248
|
-
|
247
|
+
def _make_execute(
|
248
|
+
self, quantum_program: SerializedQuantumProgram
|
249
|
+
) -> ExecuteFunction:
|
249
250
|
session = ExecutionSession(quantum_program)
|
251
|
+
self._session = session
|
250
252
|
|
251
253
|
def execute(_ignored: Any, arguments: MultipleArguments) -> ResultsCollection:
|
252
254
|
result: ResultsCollection = []
|
@@ -266,3 +268,7 @@ class QLayer(nn.Module):
|
|
266
268
|
self._post_process,
|
267
269
|
self._epsilon,
|
268
270
|
)
|
271
|
+
|
272
|
+
def _cleanup(self) -> None:
|
273
|
+
if hasattr(self, "_session"):
|
274
|
+
self._session.close()
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import warnings
|
1
2
|
from typing import Any
|
2
3
|
|
3
4
|
from classiq.interface.applications.qsvm import DataList, LabelsInt
|
@@ -92,6 +93,14 @@ def construct_qsvm_model(
|
|
92
93
|
feature_map_function_name: str,
|
93
94
|
**kwargs: Any,
|
94
95
|
) -> SerializedModel:
|
96
|
+
warnings.warn(
|
97
|
+
"Function 'construct_qsvm_model' has been deprecated and will no longer"
|
98
|
+
"be supported starting on 03/02/2025 the earliest\nHint: It is now possible to "
|
99
|
+
"implement QSVM in pure Qmod. For example, see the QSVM notebook on the "
|
100
|
+
"Classiq library at https://github.com/Classiq/classiq-library/blob/main/algorithms/qml/qsvm/qsvm.ipynb",
|
101
|
+
category=DeprecationWarning,
|
102
|
+
stacklevel=2,
|
103
|
+
)
|
95
104
|
qsvm_qmod = Model(
|
96
105
|
functions=[
|
97
106
|
NativeFunctionDefinition(
|
@@ -1,4 +1,3 @@
|
|
1
|
-
import json
|
2
1
|
import random
|
3
2
|
from types import TracebackType
|
4
3
|
from typing import Callable, Optional, Union, cast
|
@@ -16,7 +15,10 @@ from classiq.interface.executor.result import (
|
|
16
15
|
)
|
17
16
|
from classiq.interface.generator.arith import number_utils
|
18
17
|
from classiq.interface.generator.functions.qmod_python_interface import QmodPyStruct
|
19
|
-
from classiq.interface.generator.quantum_program import
|
18
|
+
from classiq.interface.generator.quantum_program import (
|
19
|
+
OMIT_DEBUG_INFO_FLAG,
|
20
|
+
QuantumProgram,
|
21
|
+
)
|
20
22
|
from classiq.interface.model.quantum_type import QuantumBit, QuantumNumeric
|
21
23
|
|
22
24
|
from classiq._internals import async_utils
|
@@ -46,7 +48,9 @@ def _deserialize_program(program: Program) -> QuantumProgram:
|
|
46
48
|
return (
|
47
49
|
program
|
48
50
|
if isinstance(program, QuantumProgram)
|
49
|
-
else QuantumProgram.
|
51
|
+
else QuantumProgram.model_validate_json(
|
52
|
+
program, context={OMIT_DEBUG_INFO_FLAG: True}
|
53
|
+
)
|
50
54
|
)
|
51
55
|
|
52
56
|
|
classiq/executor.py
CHANGED
@@ -10,7 +10,10 @@ from classiq.interface.executor.execution_preferences import ExecutionPreference
|
|
10
10
|
from classiq.interface.executor.quantum_code import QuantumCode
|
11
11
|
from classiq.interface.executor.quantum_instruction_set import QuantumInstructionSet
|
12
12
|
from classiq.interface.executor.result import ExecutionDetails
|
13
|
-
from classiq.interface.generator.quantum_program import
|
13
|
+
from classiq.interface.generator.quantum_program import (
|
14
|
+
OMIT_DEBUG_INFO_FLAG,
|
15
|
+
QuantumProgram,
|
16
|
+
)
|
14
17
|
|
15
18
|
from classiq._internals import async_utils
|
16
19
|
from classiq._internals.api_wrapper import ApiWrapper
|
@@ -27,7 +30,9 @@ BackendPreferencesAndResult: TypeAlias = tuple[
|
|
27
30
|
def _parse_serialized_qprog(
|
28
31
|
quantum_program: SerializedQuantumProgram,
|
29
32
|
) -> QuantumProgram:
|
30
|
-
return QuantumProgram.model_validate_json(
|
33
|
+
return QuantumProgram.model_validate_json(
|
34
|
+
quantum_program, context={OMIT_DEBUG_INFO_FLAG: True}
|
35
|
+
)
|
31
36
|
|
32
37
|
|
33
38
|
async def execute_async(quantum_program: SerializedQuantumProgram) -> ExecutionJob:
|
classiq/interface/_version.py
CHANGED
classiq/interface/ast_node.py
CHANGED
@@ -20,7 +20,7 @@ class ASTNode(HashablePydanticBaseModel):
|
|
20
20
|
return self
|
21
21
|
|
22
22
|
|
23
|
-
def
|
23
|
+
def reset_lists(
|
24
24
|
ast_node: ASTNodeType, statement_block_fields: list[str]
|
25
25
|
) -> ASTNodeType:
|
26
26
|
return ast_node.model_copy(update={field: [] for field in statement_block_fields})
|
@@ -69,7 +69,7 @@ class PauliOperator(HashablePydanticBaseModel, VersionedModel):
|
|
69
69
|
|
70
70
|
@staticmethod
|
71
71
|
def _validate_monomial_coefficient(
|
72
|
-
coeff: Union[sympy.Expr, ParameterComplexType]
|
72
|
+
coeff: Union[sympy.Expr, ParameterComplexType],
|
73
73
|
) -> ParameterComplexType:
|
74
74
|
if isinstance(coeff, str):
|
75
75
|
validate_expression_str(coeff)
|
@@ -32,6 +32,7 @@ class FunctionDebugInfo(BaseModel):
|
|
32
32
|
statement_type: Union[StatementType, None] = None
|
33
33
|
is_allocate_or_free: bool = Field(default=False)
|
34
34
|
is_inverse: bool = Field(default=False)
|
35
|
+
release_by_inverse: bool = Field(default=False)
|
35
36
|
port_to_passed_variable_map: dict[str, str] = Field(default_factory=dict)
|
36
37
|
node: Optional[ConcreteQuantumStatement] = None
|
37
38
|
|
@@ -86,3 +87,29 @@ class DebugInfoCollection(BaseModel):
|
|
86
87
|
if (debug_info := self.get(key)) is None:
|
87
88
|
return None
|
88
89
|
return self.blackbox_data.get(debug_info.name)
|
90
|
+
|
91
|
+
|
92
|
+
def get_back_refs(
|
93
|
+
debug_info: FunctionDebugInfo, collected_debug_info: DebugInfoCollection
|
94
|
+
) -> list[ConcreteQuantumStatement]:
|
95
|
+
back_refs: list[ConcreteQuantumStatement] = []
|
96
|
+
while (node := debug_info.node) is not None:
|
97
|
+
back_refs.insert(0, node)
|
98
|
+
if node.back_ref is None:
|
99
|
+
break
|
100
|
+
next_debug_info = collected_debug_info.get(node.back_ref)
|
101
|
+
if next_debug_info is None:
|
102
|
+
break
|
103
|
+
debug_info = next_debug_info
|
104
|
+
return back_refs
|
105
|
+
|
106
|
+
|
107
|
+
def new_function_debug_info_by_node(
|
108
|
+
node: ConcreteQuantumStatement,
|
109
|
+
) -> FunctionDebugInfo:
|
110
|
+
return FunctionDebugInfo(
|
111
|
+
name="",
|
112
|
+
parameters=dict(),
|
113
|
+
level=OperationLevel.QMOD_STATEMENT,
|
114
|
+
node=node._as_back_ref(),
|
115
|
+
)
|
classiq/interface/exceptions.py
CHANGED
@@ -6,7 +6,8 @@ _logger = logging.getLogger(__name__)
|
|
6
6
|
|
7
7
|
CLASSIQ_SLACK_COMMUNITY_LINK = (
|
8
8
|
"\nIf you need further assistance, please reach out on our Community Slack channel "
|
9
|
-
"at: https://short.classiq.io/join-slack"
|
9
|
+
"at: https://short.classiq.io/join-slack or open a support ticket at: "
|
10
|
+
"https://classiq-community.freshdesk.com/support/tickets/new"
|
10
11
|
)
|
11
12
|
|
12
13
|
|
@@ -177,10 +178,6 @@ class ClassiqExecutorInvalidHamiltonianError(ClassiqCombOptError):
|
|
177
178
|
super().__init__("Invalid hamiltonian")
|
178
179
|
|
179
180
|
|
180
|
-
class ClassiqSemanticError(ClassiqError):
|
181
|
-
pass
|
182
|
-
|
183
|
-
|
184
181
|
class ClassiqDeprecationWarning(FutureWarning):
|
185
182
|
pass
|
186
183
|
|
@@ -91,7 +91,7 @@ def unsigned_integer_interpretation(
|
|
91
91
|
int_val <<= fraction_digits_diff
|
92
92
|
|
93
93
|
# extend sign bit
|
94
|
-
if
|
94
|
+
if value < 0:
|
95
95
|
bin_val = number_utils.binary_string(int_val)
|
96
96
|
bin_val += "1" * (register.size - len(bin_val))
|
97
97
|
int_val = number_utils.binary_to_int(bin_val[::-1])
|
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
import ast
|
2
|
+
from typing import Any, Final, Optional, cast
|
2
3
|
|
3
4
|
import networkx as nx
|
4
5
|
import pydantic
|
@@ -32,6 +33,8 @@ ARITHMETIC_EXPRESSION_TARGET_NAME: Final[str] = "arithmetic_target"
|
|
32
33
|
ARITHMETIC_EXPRESSION_RESULT_NAME: Final[str] = "expression_result"
|
33
34
|
ARITHMETIC_EXPRESSION_GARBAGE_NAME: Final[str] = "expression_garbage"
|
34
35
|
|
36
|
+
TARGET_ASSIGNMENT_ERROR = "Expression does not support target assignment"
|
37
|
+
|
35
38
|
|
36
39
|
def is_zero(expr: str) -> bool:
|
37
40
|
return is_constant(expr) and float(expr) == 0
|
@@ -60,7 +63,7 @@ class Arithmetic(ArithmeticExpressionABC):
|
|
60
63
|
degree or operation_allows_target(id2op(node))
|
61
64
|
for node, degree in graph.out_degree
|
62
65
|
):
|
63
|
-
raise ClassiqValueError(
|
66
|
+
raise ClassiqValueError(TARGET_ASSIGNMENT_ERROR)
|
64
67
|
|
65
68
|
def _create_ios(self) -> None:
|
66
69
|
self._inputs = {
|
@@ -93,6 +96,9 @@ def get_arithmetic_params(
|
|
93
96
|
machine_precision: int,
|
94
97
|
enable_target: bool = False,
|
95
98
|
) -> Arithmetic:
|
99
|
+
expr_str, var_types = _substitute_quantum_subscripts(
|
100
|
+
expr_str, var_types, machine_precision
|
101
|
+
)
|
96
102
|
return Arithmetic(
|
97
103
|
expression=expr_str,
|
98
104
|
definitions={
|
@@ -119,3 +125,94 @@ def compute_arithmetic_result_type(
|
|
119
125
|
return register_info_to_quantum_type(
|
120
126
|
arith_param.outputs[ARITHMETIC_EXPRESSION_RESULT_NAME]
|
121
127
|
)
|
128
|
+
|
129
|
+
|
130
|
+
def aggregate_numeric_types(
|
131
|
+
numeric_types: list[QuantumNumeric],
|
132
|
+
) -> RegisterArithmeticInfo:
|
133
|
+
if all(
|
134
|
+
numeric_type.size_in_bits == 1
|
135
|
+
and numeric_type.sign_value
|
136
|
+
and numeric_type.fraction_digits_value == 1
|
137
|
+
for numeric_type in numeric_types
|
138
|
+
):
|
139
|
+
return RegisterArithmeticInfo(size=1, is_signed=True, fraction_places=1)
|
140
|
+
int_size = max(
|
141
|
+
numeric_type.size_in_bits
|
142
|
+
- int(numeric_type.sign_value)
|
143
|
+
- numeric_type.fraction_digits_value
|
144
|
+
for numeric_type in numeric_types
|
145
|
+
)
|
146
|
+
is_signed = any(numeric_type.sign_value for numeric_type in numeric_types)
|
147
|
+
frac_size = max(
|
148
|
+
numeric_type.fraction_digits_value for numeric_type in numeric_types
|
149
|
+
)
|
150
|
+
total_size = int_size + int(is_signed) + frac_size
|
151
|
+
return RegisterArithmeticInfo(
|
152
|
+
size=total_size, is_signed=is_signed, fraction_places=frac_size
|
153
|
+
)
|
154
|
+
|
155
|
+
|
156
|
+
class _QuantumSubscriptRemover(ast.NodeTransformer):
|
157
|
+
def __init__(self, machine_precision: int) -> None:
|
158
|
+
self._machine_precision = machine_precision
|
159
|
+
self.substitutions_types: dict[str, QuantumNumeric] = {}
|
160
|
+
|
161
|
+
def visit_Call(self, node: ast.Call) -> ast.expr:
|
162
|
+
if not isinstance(node.func, ast.Name) or node.func.id != "Piecewise":
|
163
|
+
return node
|
164
|
+
items = [
|
165
|
+
cast(float, cast(ast.Num, cast(ast.Tuple, arg).elts[0]).value)
|
166
|
+
for arg in node.args
|
167
|
+
]
|
168
|
+
numeric_types = [
|
169
|
+
compute_arithmetic_result_type(str(num), {}, self._machine_precision)
|
170
|
+
for num in items
|
171
|
+
]
|
172
|
+
unified_numeric_type = register_info_to_quantum_type(
|
173
|
+
aggregate_numeric_types(numeric_types)
|
174
|
+
)
|
175
|
+
substitution_var_name = f"__lut__{len(self.substitutions_types)}__"
|
176
|
+
self.substitutions_types[substitution_var_name] = unified_numeric_type
|
177
|
+
return ast.Name(id=substitution_var_name)
|
178
|
+
|
179
|
+
|
180
|
+
class _NameCollector(ast.NodeVisitor):
|
181
|
+
def __init__(self) -> None:
|
182
|
+
self.names: set[str] = set()
|
183
|
+
|
184
|
+
def visit_Name(self, node: ast.Name) -> None:
|
185
|
+
self.names.add(node.id)
|
186
|
+
|
187
|
+
|
188
|
+
def _substitute_quantum_subscripts(
|
189
|
+
expr_str: str, var_types: dict[str, QuantumType], machine_precision: int
|
190
|
+
) -> tuple[str, dict[str, QuantumType]]:
|
191
|
+
"""
|
192
|
+
Remove quantum lookup expressions ([1, 2, 3, 4][n]) from an arithmetic expression
|
193
|
+
for the purpose of calculating its numeric attributes.
|
194
|
+
Each quantum lookup expression is replaced by a numeric value with equivalent
|
195
|
+
numeric properties.
|
196
|
+
|
197
|
+
Args:
|
198
|
+
expr_str: arithmetic expression
|
199
|
+
var_types: quantum variable type mapping
|
200
|
+
machine_precision: global machine precision
|
201
|
+
|
202
|
+
Returns:
|
203
|
+
1. the reduced expression
|
204
|
+
2. updated type mapping
|
205
|
+
"""
|
206
|
+
expr_ast = ast.parse(expr_str)
|
207
|
+
subscript_remover = _QuantumSubscriptRemover(machine_precision)
|
208
|
+
expr_ast = subscript_remover.visit(expr_ast)
|
209
|
+
var_types_substituted = var_types | subscript_remover.substitutions_types
|
210
|
+
expr_str_substituted = ast.unparse(expr_ast)
|
211
|
+
names_collector = _NameCollector()
|
212
|
+
names_collector.visit(ast.parse(expr_str_substituted))
|
213
|
+
var_types_substituted = {
|
214
|
+
var_name: var_type
|
215
|
+
for var_name, var_type in var_types_substituted.items()
|
216
|
+
if var_name in names_collector.names
|
217
|
+
}
|
218
|
+
return expr_str_substituted, var_types_substituted
|
@@ -398,6 +398,7 @@ class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
|
398
398
|
right_arg=-self.effective_right_arg,
|
399
399
|
output_size=self.output_size,
|
400
400
|
inplace_arg=self.inplace_arg,
|
401
|
+
machine_precision=self.machine_precision,
|
401
402
|
)
|
402
403
|
return adder_params.garbage_output_size()
|
403
404
|
|
@@ -406,6 +407,7 @@ class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
|
406
407
|
output_size=self.negation_output_size,
|
407
408
|
inplace=self.should_inplace_negation,
|
408
409
|
bypass_bounds_validation=True,
|
410
|
+
machine_precision=self.machine_precision,
|
409
411
|
)
|
410
412
|
negation_result = negation_params.result_register
|
411
413
|
if self.output_size is None and max(self.effective_right_arg.bounds) > 0:
|
@@ -425,6 +427,7 @@ class Subtractor(InplacableBinaryOpParams[RegisterOrConst, RegisterOrConst]):
|
|
425
427
|
right_arg=negation_result,
|
426
428
|
output_size=self.output_size,
|
427
429
|
inplace_arg=self.arg_to_inplace_adder,
|
430
|
+
machine_precision=self.machine_precision,
|
428
431
|
)
|
429
432
|
negation_garbage_size = negation_params.garbage_output_size() * int(
|
430
433
|
not self.should_uncompute_negation
|
@@ -65,6 +65,7 @@ from classiq.interface.generator.piecewise_linear_amplitude_loading import (
|
|
65
65
|
from classiq.interface.generator.qft import QFT
|
66
66
|
from classiq.interface.generator.qsvm import QSVMFeatureMap
|
67
67
|
from classiq.interface.generator.randomized_benchmarking import RandomizedBenchmarking
|
68
|
+
from classiq.interface.generator.reset import Reset
|
68
69
|
from classiq.interface.generator.standard_gates.standard_gates_param_list import (
|
69
70
|
standard_gate_function_param_library,
|
70
71
|
)
|
@@ -150,6 +151,7 @@ function_param_library_without_self_reference: FunctionParamLibrary = (
|
|
150
151
|
PiecewiseLinearRotationAmplitudeLoading,
|
151
152
|
HadamardTransform,
|
152
153
|
Copy,
|
154
|
+
Reset,
|
153
155
|
},
|
154
156
|
standard_gate_function_param_library.param_list,
|
155
157
|
oracle_function_param_library.param_list,
|
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Any, Literal, Optional
|
|
3
3
|
|
4
4
|
import pydantic
|
5
5
|
|
6
|
-
from classiq.interface.exceptions import
|
6
|
+
from classiq.interface.exceptions import ClassiqExpansionError
|
7
7
|
from classiq.interface.generator.expressions.qmod_qstruct_proxy import QmodQStructProxy
|
8
8
|
from classiq.interface.generator.functions.classical_type import (
|
9
9
|
ClassicalType,
|
@@ -59,7 +59,7 @@ class TypeName(ClassicalType, QuantumType):
|
|
59
59
|
@property
|
60
60
|
def fields(self) -> Mapping[str, "ConcreteQuantumType"]:
|
61
61
|
if self._assigned_fields is None:
|
62
|
-
raise
|
62
|
+
raise ClassiqExpansionError(f"Type {self.name!r} is undefined")
|
63
63
|
return self._assigned_fields
|
64
64
|
|
65
65
|
@property
|
@@ -11,6 +11,7 @@ from classiq.interface.generator.register_role import RegisterRole
|
|
11
11
|
from classiq.interface.generator.synthesis_metadata.synthesis_execution_data import (
|
12
12
|
ExecutionData,
|
13
13
|
)
|
14
|
+
from classiq.interface.model.statement_block import StatementBlock
|
14
15
|
|
15
16
|
from classiq.model_expansions.capturing.mangling_utils import (
|
16
17
|
demangle_capture_name,
|
@@ -22,7 +23,7 @@ _logger = logging.getLogger(__name__)
|
|
22
23
|
ParameterName = str
|
23
24
|
IOQubitMapping: TypeAlias = dict[str, tuple[int, ...]]
|
24
25
|
|
25
|
-
CLASSIQ_HIERARCHY_SEPARATOR: Literal["
|
26
|
+
CLASSIQ_HIERARCHY_SEPARATOR: Literal["__"] = "__"
|
26
27
|
|
27
28
|
VISUALIZATION_HIDE_LIST = [
|
28
29
|
"apply_to_all",
|
@@ -135,6 +136,8 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
135
136
|
level: OperationLevel = pydantic.Field(default=OperationLevel.UNKNOWN)
|
136
137
|
parameters: list[OperationParameter] = list()
|
137
138
|
port_to_passed_variable_map: dict[str, str] = pydantic.Field(default={})
|
139
|
+
release_by_inverse: bool = pydantic.Field(default=False)
|
140
|
+
back_refs: StatementBlock = pydantic.Field(default_factory=list)
|
138
141
|
|
139
142
|
model_config = ConfigDict(extra="allow")
|
140
143
|
# Temporary field to store the override debug info for parallel old/new visualization
|
@@ -166,7 +169,7 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
166
169
|
|
167
170
|
@staticmethod
|
168
171
|
def create_parameters_from_dict(
|
169
|
-
parameters: dict[str, str]
|
172
|
+
parameters: dict[str, str],
|
170
173
|
) -> list[OperationParameter]:
|
171
174
|
return [
|
172
175
|
OperationParameter(label=key, value=value)
|
@@ -227,14 +230,46 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
227
230
|
)
|
228
231
|
|
229
232
|
def inverse(self) -> "FunctionDebugInfoInterface":
|
230
|
-
|
233
|
+
if self.override_debug_info is not None:
|
234
|
+
self.override_debug_info = self.override_debug_info.inverse()
|
235
|
+
return self
|
236
|
+
inverse_generated_function = (
|
237
|
+
self.generated_function.model_copy(
|
238
|
+
update=dict(registers=self._inverse_registers)
|
239
|
+
)
|
240
|
+
if self.generated_function
|
241
|
+
else None
|
242
|
+
)
|
243
|
+
inverted_children = [child.inverse() for child in reversed(self.children)]
|
231
244
|
return self.model_copy(
|
232
245
|
update=dict(
|
233
246
|
is_inverse=not self.is_inverse,
|
234
247
|
children=inverted_children,
|
248
|
+
generated_function=inverse_generated_function,
|
235
249
|
)
|
236
250
|
)
|
237
251
|
|
252
|
+
@property
|
253
|
+
def _inverse_registers(self) -> list[GeneratedRegister]:
|
254
|
+
return [
|
255
|
+
reg.model_copy(update=dict(role=self._inverse_register_role(reg.role)))
|
256
|
+
for reg in self.registers
|
257
|
+
]
|
258
|
+
|
259
|
+
def _inverse_register_role(self, role: RegisterRole) -> RegisterRole:
|
260
|
+
if role is RegisterRole.INPUT:
|
261
|
+
return RegisterRole.OUTPUT
|
262
|
+
if role is RegisterRole.EXPLICIT_ZERO_INPUT or role is RegisterRole.ZERO_INPUT:
|
263
|
+
if self.release_by_inverse:
|
264
|
+
return RegisterRole.ZERO_OUTPUT
|
265
|
+
return RegisterRole.OUTPUT
|
266
|
+
if role is RegisterRole.AUXILIARY:
|
267
|
+
return RegisterRole.AUXILIARY
|
268
|
+
if role is RegisterRole.OUTPUT or role is RegisterRole.GARBAGE_OUTPUT:
|
269
|
+
return RegisterRole.INPUT
|
270
|
+
if role is RegisterRole.ZERO_OUTPUT:
|
271
|
+
return RegisterRole.ZERO_INPUT
|
272
|
+
|
238
273
|
|
239
274
|
def _get_absolute_from_relative(
|
240
275
|
absolute_qubits: tuple[int, ...], relative_qubits: tuple[int, ...]
|
@@ -60,7 +60,7 @@ class HardwareEfficientAnsatz(function_params.FunctionParams):
|
|
60
60
|
description='List of gates for the two qubit gates entangling layer, e.g. ["cx", "cry"]',
|
61
61
|
)
|
62
62
|
parameter_prefix: str = pydantic.Field(
|
63
|
-
default="
|
63
|
+
default="hea_param_",
|
64
64
|
description="Prefix for the generated parameters",
|
65
65
|
)
|
66
66
|
|
@@ -36,7 +36,7 @@ if TYPE_CHECKING:
|
|
36
36
|
PydanticBackendName = str
|
37
37
|
else:
|
38
38
|
PydanticBackendName = Annotated[
|
39
|
-
str, Field(strict=True,
|
39
|
+
str, Field(strict=True, pattern="^([.A-Za-z0-9_-][ .A-Za-z0-9_-]*)$")
|
40
40
|
]
|
41
41
|
|
42
42
|
|
@@ -262,6 +262,13 @@ class Preferences(pydantic.BaseModel, extra="forbid"):
|
|
262
262
|
|
263
263
|
return output_format
|
264
264
|
|
265
|
+
@pydantic.field_validator("backend_name")
|
266
|
+
@classmethod
|
267
|
+
def validate_backend_name(cls, backend_name: Optional[str]) -> Optional[str]:
|
268
|
+
if backend_name is None:
|
269
|
+
return backend_name
|
270
|
+
return backend_name.rstrip()
|
271
|
+
|
265
272
|
@pydantic.model_validator(mode="after")
|
266
273
|
def validate_backend(self) -> Self:
|
267
274
|
backend_name = self.backend_name
|
@@ -1,9 +1,11 @@
|
|
1
1
|
import uuid
|
2
2
|
from datetime import datetime, timezone
|
3
3
|
from pathlib import Path
|
4
|
-
from typing import Optional, Union
|
4
|
+
from typing import Any, Optional, Union
|
5
5
|
|
6
6
|
import pydantic
|
7
|
+
from pydantic import model_validator
|
8
|
+
from pydantic_core.core_schema import ValidationInfo
|
7
9
|
from typing_extensions import TypeAlias
|
8
10
|
|
9
11
|
from classiq.interface.exceptions import (
|
@@ -36,6 +38,8 @@ from classiq.interface.ide.visual_model import CircuitMetrics
|
|
36
38
|
RegisterName: TypeAlias = str
|
37
39
|
InitialConditions: TypeAlias = dict[RegisterName, int]
|
38
40
|
|
41
|
+
OMIT_DEBUG_INFO_FLAG = "omit_debug_info"
|
42
|
+
|
39
43
|
|
40
44
|
class TranspiledCircuitData(CircuitCodeInterface):
|
41
45
|
depth: int
|
@@ -71,6 +75,19 @@ class QuantumProgram(VersionedModel, CircuitCodeInterface):
|
|
71
75
|
program_id: str = pydantic.Field(default_factory=get_uuid_as_str)
|
72
76
|
execution_primitives_input: Optional[PrimitivesInput] = pydantic.Field(default=None)
|
73
77
|
|
78
|
+
@model_validator(mode="before")
|
79
|
+
@classmethod
|
80
|
+
def remove_debug_info(
|
81
|
+
cls, data: dict[str, Any], info: ValidationInfo
|
82
|
+
) -> dict[str, Any]:
|
83
|
+
if (
|
84
|
+
isinstance(data, dict)
|
85
|
+
and info.context is not None
|
86
|
+
and info.context.get(OMIT_DEBUG_INFO_FLAG, False)
|
87
|
+
):
|
88
|
+
data.pop("debug_info", None)
|
89
|
+
return data
|
90
|
+
|
74
91
|
def _hardware_agnostic_program_code(self) -> CodeAndSyntax:
|
75
92
|
circuit_code = self.program_circuit.get_code_by_priority()
|
76
93
|
if circuit_code is not None:
|