classiq 0.94.2__py3-none-any.whl → 0.96.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.
Potentially problematic release.
This version of classiq might be problematic. Click here for more details.
- classiq/_internals/api_wrapper.py +0 -83
- classiq/_internals/authentication/auth0.py +32 -3
- classiq/_internals/authentication/authorization_code.py +9 -0
- classiq/_internals/authentication/authorization_flow.py +41 -0
- classiq/_internals/authentication/device.py +31 -50
- classiq/_internals/authentication/hybrid_flow.py +19 -0
- classiq/_internals/authentication/token_manager.py +5 -4
- classiq/applications/__init__.py +2 -2
- classiq/applications/iqae/iqae.py +6 -3
- classiq/applications/qnn/gradients/simple_quantum_gradient.py +1 -1
- classiq/applications/qnn/qlayer.py +1 -1
- classiq/applications/qnn/torch_utils.py +2 -2
- classiq/applications/qsp/qsp.py +6 -5
- classiq/evaluators/qmod_node_evaluators/classical_function_evaluation.py +10 -0
- classiq/execution/__init__.py +0 -3
- classiq/execution/user_budgets.py +0 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/backend_preferences.py +11 -35
- classiq/interface/backend/quantum_backend_providers.py +0 -2
- classiq/interface/exceptions.py +0 -4
- classiq/interface/generator/application_apis/__init__.py +0 -1
- classiq/interface/generator/arith/register_user_input.py +1 -1
- classiq/interface/generator/function_param_list.py +0 -2
- classiq/interface/generator/generated_circuit_data.py +1 -6
- classiq/interface/generator/hardware_efficient_ansatz.py +1 -1
- classiq/interface/generator/quantum_function_call.py +1 -1
- classiq/interface/generator/quantum_program.py +0 -4
- classiq/interface/generator/transpiler_basis_gates.py +3 -0
- classiq/interface/generator/types/builtin_enum_declarations.py +0 -9
- classiq/interface/hardware.py +0 -1
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/block.py +4 -0
- classiq/interface/model/classical_if.py +4 -0
- classiq/interface/model/control.py +7 -0
- classiq/interface/model/invert.py +4 -0
- classiq/interface/model/model_visitor.py +40 -1
- classiq/interface/model/power.py +4 -0
- classiq/interface/model/quantum_statement.py +8 -1
- classiq/interface/model/repeat.py +4 -0
- classiq/interface/model/skip_control.py +4 -0
- classiq/interface/model/within_apply_operation.py +4 -0
- classiq/interface/server/routes.py +0 -12
- classiq/model_expansions/generative_functions.py +6 -8
- classiq/model_expansions/interpreters/base_interpreter.py +1 -1
- classiq/model_expansions/interpreters/frontend_generative_interpreter.py +2 -1
- classiq/model_expansions/visitors/symbolic_param_inference.py +3 -3
- classiq/model_expansions/visitors/uncomputation_signature_inference.py +14 -3
- classiq/open_library/functions/__init__.py +3 -2
- classiq/open_library/functions/amplitude_loading.py +85 -0
- classiq/open_library/functions/lcu.py +47 -18
- classiq/open_library/functions/modular_exponentiation.py +5 -8
- classiq/open_library/functions/qsvt.py +4 -4
- classiq/open_library/functions/state_preparation.py +7 -7
- classiq/qmod/builtins/classical_execution_primitives.py +0 -12
- classiq/qmod/builtins/enums.py +15 -17
- classiq/qmod/builtins/functions/__init__.py +5 -5
- classiq/qmod/builtins/functions/allocation.py +21 -0
- classiq/qmod/builtins/functions/mcx.py +7 -0
- classiq/qmod/builtins/operations.py +125 -23
- classiq/qmod/builtins/structs.py +22 -33
- classiq/qmod/semantics/annotation/call_annotation.py +3 -3
- classiq/qmod/semantics/error_manager.py +7 -8
- classiq/qmod/utilities.py +0 -10
- {classiq-0.94.2.dist-info → classiq-0.96.0.dist-info}/METADATA +1 -1
- {classiq-0.94.2.dist-info → classiq-0.96.0.dist-info}/RECORD +67 -71
- {classiq-0.94.2.dist-info → classiq-0.96.0.dist-info}/WHEEL +1 -1
- classiq/applications/qsvm/__init__.py +0 -8
- classiq/applications/qsvm/qsvm.py +0 -11
- classiq/execution/iqcc.py +0 -128
- classiq/interface/applications/qsvm.py +0 -114
- classiq/interface/execution/iqcc.py +0 -42
- classiq/interface/generator/application_apis/qsvm_declarations.py +0 -6
- classiq/interface/generator/qsvm.py +0 -96
- classiq/open_library/functions/lookup_table.py +0 -58
- classiq/qmod/builtins/functions/qsvm.py +0 -24
- {classiq-0.94.2.dist-info → classiq-0.96.0.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -20,7 +20,6 @@ class ProviderVendor(StrEnum):
|
|
|
20
20
|
OQC = "OQC"
|
|
21
21
|
INTEL = "Intel"
|
|
22
22
|
AQT = "AQT"
|
|
23
|
-
IQCC = "IQCC"
|
|
24
23
|
CINECA = "CINECA"
|
|
25
24
|
|
|
26
25
|
|
|
@@ -35,7 +34,6 @@ class ProviderTypeVendor:
|
|
|
35
34
|
OQC = Literal[ProviderVendor.OQC]
|
|
36
35
|
INTEL = Literal[ProviderVendor.INTEL]
|
|
37
36
|
AQT = Literal[ProviderVendor.AQT]
|
|
38
|
-
IQCC = Literal[ProviderVendor.IQCC]
|
|
39
37
|
CINECA = Literal[ProviderVendor.CINECA]
|
|
40
38
|
|
|
41
39
|
|
classiq/interface/exceptions.py
CHANGED
|
@@ -4,6 +4,5 @@ from classiq.interface.generator.builtin_api_builder import (
|
|
|
4
4
|
|
|
5
5
|
from .arithmetic_declarations import * # noqa: F403
|
|
6
6
|
from .combinatorial_optimization_declarations import * # noqa: F403
|
|
7
|
-
from .qsvm_declarations import * # noqa: F403
|
|
8
7
|
|
|
9
8
|
populate_builtin_declarations(vars().values())
|
|
@@ -19,7 +19,7 @@ class RegisterArithmeticInfo(HashablePydanticBaseModel):
|
|
|
19
19
|
is_signed: bool = pydantic.Field(default=False)
|
|
20
20
|
fraction_places: pydantic.NonNegativeInt = pydantic.Field(default=0)
|
|
21
21
|
bypass_bounds_validation: bool = pydantic.Field(default=False)
|
|
22
|
-
bounds: PydanticFloatTuple = pydantic.Field(
|
|
22
|
+
bounds: PydanticFloatTuple = pydantic.Field(
|
|
23
23
|
default=None,
|
|
24
24
|
validate_default=True,
|
|
25
25
|
)
|
|
@@ -42,7 +42,6 @@ from classiq.interface.generator.hardware_efficient_ansatz import (
|
|
|
42
42
|
from classiq.interface.generator.identity import Identity
|
|
43
43
|
from classiq.interface.generator.mcu import Mcu
|
|
44
44
|
from classiq.interface.generator.mcx import Mcx
|
|
45
|
-
from classiq.interface.generator.qsvm import QSVMFeatureMap
|
|
46
45
|
from classiq.interface.generator.randomized_benchmarking import RandomizedBenchmarking
|
|
47
46
|
from classiq.interface.generator.reset import Reset
|
|
48
47
|
from classiq.interface.generator.standard_gates.standard_gates_param_list import (
|
|
@@ -108,7 +107,6 @@ function_param_library: FunctionParamLibrary = FunctionParamLibrary(
|
|
|
108
107
|
RandomizedBenchmarking,
|
|
109
108
|
UGate,
|
|
110
109
|
AmplitudeLoading,
|
|
111
|
-
QSVMFeatureMap,
|
|
112
110
|
HadamardTransform,
|
|
113
111
|
Copy,
|
|
114
112
|
Reset,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import re
|
|
3
|
-
from typing import Literal,
|
|
3
|
+
from typing import Literal, TypeAlias
|
|
4
4
|
from uuid import UUID
|
|
5
5
|
|
|
6
6
|
import pydantic
|
|
@@ -188,8 +188,6 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
|
188
188
|
back_refs: StatementBlock = Field(default_factory=list)
|
|
189
189
|
|
|
190
190
|
model_config = ConfigDict(extra="allow")
|
|
191
|
-
# Temporary field to store the override debug info for parallel old/new visualization
|
|
192
|
-
override_debug_info: Optional["FunctionDebugInfoInterface"] = None
|
|
193
191
|
|
|
194
192
|
@property
|
|
195
193
|
def is_allocate_or_free(self) -> bool:
|
|
@@ -317,9 +315,6 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
|
317
315
|
)
|
|
318
316
|
|
|
319
317
|
def inverse(self) -> "FunctionDebugInfoInterface":
|
|
320
|
-
if self.override_debug_info is not None:
|
|
321
|
-
self.override_debug_info = self.override_debug_info.inverse()
|
|
322
|
-
return self
|
|
323
318
|
inverse_generated_function = (
|
|
324
319
|
self.generated_function.model_copy(
|
|
325
320
|
update=dict(registers=self._inverse_registers)
|
|
@@ -42,7 +42,7 @@ class HardwareEfficientAnsatz(function_params.FunctionParams):
|
|
|
42
42
|
"If none specified - use connectivity map from the model hardware settings. "
|
|
43
43
|
"If none specified as well, all qubit pairs will be connected.",
|
|
44
44
|
)
|
|
45
|
-
num_qubits: pydantic.PositiveInt = pydantic.Field(
|
|
45
|
+
num_qubits: pydantic.PositiveInt = pydantic.Field(
|
|
46
46
|
default=None,
|
|
47
47
|
description="Number of qubits in the ansatz.",
|
|
48
48
|
validate_default=True,
|
|
@@ -144,7 +144,7 @@ class SynthesisQuantumFunctionCall(BaseModel):
|
|
|
144
144
|
power: PydanticPowerType = pydantic.Field(
|
|
145
145
|
default=1, description="Number of successive calls to the operation"
|
|
146
146
|
)
|
|
147
|
-
name: PydanticNonEmptyString = pydantic.Field(
|
|
147
|
+
name: PydanticNonEmptyString = pydantic.Field(
|
|
148
148
|
default=None,
|
|
149
149
|
validate_default=True,
|
|
150
150
|
description="The name of the function instance. "
|
|
@@ -65,7 +65,6 @@ class QuantumProgram(VersionedModel, CircuitCodeInterface):
|
|
|
65
65
|
transpiled_circuit: TranspiledCircuitData | None = pydantic.Field(default=None)
|
|
66
66
|
creation_time: str = pydantic.Field(default_factory=_get_formatted_utc_current_time)
|
|
67
67
|
synthesis_duration: SynthesisStepDurations | None = pydantic.Field(default=None)
|
|
68
|
-
debug_info: list[FunctionDebugInfoInterface] | None = pydantic.Field(default=None)
|
|
69
68
|
compressed_debug_info: bytes | None = pydantic.Field(default=None)
|
|
70
69
|
program_id: str = pydantic.Field(default_factory=get_uuid_as_str)
|
|
71
70
|
execution_primitives_input: PrimitivesInput | None = pydantic.Field(default=None)
|
|
@@ -169,9 +168,6 @@ class QuantumProgram(VersionedModel, CircuitCodeInterface):
|
|
|
169
168
|
)
|
|
170
169
|
|
|
171
170
|
def get_debug_info(self) -> list[FunctionDebugInfoInterface] | None:
|
|
172
|
-
# Support legacy uncompressed debug info
|
|
173
|
-
if self.debug_info is not None:
|
|
174
|
-
return self.debug_info
|
|
175
171
|
if self.compressed_debug_info is None:
|
|
176
172
|
return None
|
|
177
173
|
decompressed_debug_info_dict_list = decompress(self.compressed_debug_info)
|
|
@@ -64,6 +64,9 @@ DEFAULT_BASIS_GATES: BasisGates = SINGLE_QUBIT_GATES | BASIC_TWO_QUBIT_GATES
|
|
|
64
64
|
ALL_GATES: BasisGates = (
|
|
65
65
|
SINGLE_QUBIT_GATES | TWO_QUBIT_GATES | THREE_QUBIT_GATES | NON_UNITARY_GATES
|
|
66
66
|
)
|
|
67
|
+
ALL_NON_3_QBIT_GATES: BasisGates = (
|
|
68
|
+
SINGLE_QUBIT_GATES | TWO_QUBIT_GATES | NON_UNITARY_GATES
|
|
69
|
+
)
|
|
67
70
|
|
|
68
71
|
ROUTING_TWO_QUBIT_BASIS_GATES: BasisGates = frozenset(
|
|
69
72
|
("cx", "ecr", "rzx", "ryy", "rxx", "rzz", "cy", "cz", "cp", "swap")
|
classiq/interface/hardware.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
INTERFACE_VERSION = "
|
|
1
|
+
INTERFACE_VERSION = "14"
|
classiq/interface/model/block.py
CHANGED
|
@@ -31,6 +31,10 @@ class ClassicalIf(QuantumOperation):
|
|
|
31
31
|
def expressions(self) -> list[Expression]:
|
|
32
32
|
return [self.condition]
|
|
33
33
|
|
|
34
|
+
@property
|
|
35
|
+
def blocks(self) -> dict[str, "StatementBlock"]:
|
|
36
|
+
return {"then": self.then, "else_": self.else_}
|
|
37
|
+
|
|
34
38
|
@property
|
|
35
39
|
def wiring_inputs(self) -> Mapping[str, HandleBinding]:
|
|
36
40
|
return functools.reduce(
|
|
@@ -47,3 +47,10 @@ class Control(QuantumExpressionOperation):
|
|
|
47
47
|
|
|
48
48
|
def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
|
|
49
49
|
return reset_lists(self, ["body", "else_block"])
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def blocks(self) -> dict[str, "StatementBlock"]:
|
|
53
|
+
blocks = {"body": self.body}
|
|
54
|
+
if self.else_block is not None:
|
|
55
|
+
blocks["else_block"] = self.else_block
|
|
56
|
+
return blocks
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
from collections.abc import Collection
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
1
5
|
from classiq.interface.debug_info.debug_info import DebugInfoCollection
|
|
2
|
-
from classiq.interface.generator.visitor import Transformer, Visitor
|
|
6
|
+
from classiq.interface.generator.visitor import RetType, Transformer, Visitor
|
|
7
|
+
from classiq.interface.model.model import Model
|
|
8
|
+
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
|
9
|
+
from classiq.interface.model.quantum_statement import QuantumStatement
|
|
3
10
|
|
|
4
11
|
|
|
5
12
|
class ModelVisitor(Visitor):
|
|
@@ -7,8 +14,40 @@ class ModelVisitor(Visitor):
|
|
|
7
14
|
return
|
|
8
15
|
|
|
9
16
|
|
|
17
|
+
class ModelStatementsVisitor(ModelVisitor):
|
|
18
|
+
def visit_BaseModel(self, node: BaseModel) -> RetType | None:
|
|
19
|
+
if isinstance(node, Model):
|
|
20
|
+
return self.visit(node.functions)
|
|
21
|
+
if isinstance(node, NativeFunctionDefinition):
|
|
22
|
+
return self.visit(node.body)
|
|
23
|
+
if isinstance(node, QuantumStatement):
|
|
24
|
+
for block in node.blocks.values():
|
|
25
|
+
self.visit(block)
|
|
26
|
+
return None
|
|
27
|
+
return super().visit_BaseModel(node)
|
|
28
|
+
|
|
29
|
+
|
|
10
30
|
class ModelTransformer(Transformer):
|
|
11
31
|
def visit_DebugInfoCollection(
|
|
12
32
|
self, debug_info: DebugInfoCollection
|
|
13
33
|
) -> DebugInfoCollection:
|
|
14
34
|
return debug_info
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ModelStatementsTransformer(ModelTransformer):
|
|
38
|
+
def visit_BaseModel(
|
|
39
|
+
self, node: BaseModel, fields_to_skip: Collection[str] | None = None
|
|
40
|
+
) -> RetType:
|
|
41
|
+
if isinstance(node, Model):
|
|
42
|
+
new_functions = self.visit(node.functions)
|
|
43
|
+
return node.model_copy(update=dict(functions=new_functions))
|
|
44
|
+
if isinstance(node, NativeFunctionDefinition):
|
|
45
|
+
new_body = self.visit(node.body)
|
|
46
|
+
return node.model_copy(update=dict(body=new_body))
|
|
47
|
+
if isinstance(node, QuantumStatement):
|
|
48
|
+
new_blocks = {
|
|
49
|
+
block_name: self.visit(block)
|
|
50
|
+
for block_name, block in node.blocks.items()
|
|
51
|
+
}
|
|
52
|
+
return node.model_copy(update=new_blocks)
|
|
53
|
+
return super().visit_BaseModel(node, fields_to_skip)
|
classiq/interface/model/power.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from collections.abc import Callable, Iterable, Mapping, Sequence
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import TYPE_CHECKING, Any
|
|
4
4
|
from uuid import UUID, uuid4
|
|
5
5
|
|
|
6
6
|
import pydantic
|
|
@@ -17,6 +17,9 @@ from classiq.interface.model.handle_binding import (
|
|
|
17
17
|
HandleBinding,
|
|
18
18
|
)
|
|
19
19
|
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from classiq.interface.model.statement_block import StatementBlock
|
|
22
|
+
|
|
20
23
|
|
|
21
24
|
class QuantumStatement(ASTNode):
|
|
22
25
|
kind: str
|
|
@@ -45,6 +48,10 @@ class QuantumStatement(ASTNode):
|
|
|
45
48
|
def expressions(self) -> list[Expression]:
|
|
46
49
|
return []
|
|
47
50
|
|
|
51
|
+
@property
|
|
52
|
+
def blocks(self) -> dict[str, "StatementBlock"]:
|
|
53
|
+
return {}
|
|
54
|
+
|
|
48
55
|
|
|
49
56
|
@dataclass
|
|
50
57
|
class HandleMetadata:
|
|
@@ -16,6 +16,10 @@ class WithinApply(QuantumOperation):
|
|
|
16
16
|
def _as_back_ref(self: ASTNodeType) -> ASTNodeType:
|
|
17
17
|
return reset_lists(self, ["compute", "action"])
|
|
18
18
|
|
|
19
|
+
@property
|
|
20
|
+
def blocks(self) -> dict[str, "StatementBlock"]:
|
|
21
|
+
return {"compute": self.compute, "action": self.action}
|
|
22
|
+
|
|
19
23
|
|
|
20
24
|
class Compute(QuantumOperation):
|
|
21
25
|
kind: Literal["Compute"]
|
|
@@ -4,7 +4,6 @@ EXECUTION_SESSIONS_PREFIX = EXECUTION_PREFIX + "/sessions"
|
|
|
4
4
|
CONVERSION_PREFIX = "/conversion"
|
|
5
5
|
PROVIDERS_PREFIX = "/providers"
|
|
6
6
|
|
|
7
|
-
IQCC_PREFIX = PROVIDERS_PREFIX + "/iqcc"
|
|
8
7
|
USER_BUDGETS_PREFIX = "/user_budgets"
|
|
9
8
|
|
|
10
9
|
|
|
@@ -70,17 +69,6 @@ QASM_TO_QMOD_FULL_PATH = CONVERSION_PREFIX + QASM_TO_QMOD_SUFFIX
|
|
|
70
69
|
|
|
71
70
|
STATIC_SEMANTICS_VALIDATION_PATH = "/validate_static_semantics"
|
|
72
71
|
|
|
73
|
-
IQCC_INIT_AUTH_SUFFIX = "/init_auth"
|
|
74
|
-
IQCC_INIT_AUTH_FULL_PATH = IQCC_PREFIX + IQCC_INIT_AUTH_SUFFIX
|
|
75
|
-
IQCC_PROBE_AUTH_SUFFIX = "/probe_auth"
|
|
76
|
-
IQCC_PROBE_AUTH_FULL_PATH = IQCC_PREFIX + IQCC_PROBE_AUTH_SUFFIX
|
|
77
|
-
IQCC_LIST_AUTH_SCOPES_SUFFIX = "/auth_scopes"
|
|
78
|
-
IQCC_LIST_AUTH_SCOPES_FULL_PATH = IQCC_PREFIX + IQCC_LIST_AUTH_SCOPES_SUFFIX
|
|
79
|
-
IQCC_LIST_AUTH_METHODS_SUFFIX = "/auth_methods"
|
|
80
|
-
IQCC_LIST_AUTH_METHODS_FULL_PATH = IQCC_PREFIX + IQCC_LIST_AUTH_METHODS_SUFFIX
|
|
81
|
-
IQCC_LIST_AUTH_TARGETS_SUFFIX = "/auth_targets"
|
|
82
|
-
IQCC_LIST_AUTH_TARGETS_FULL_PATH = IQCC_PREFIX + IQCC_LIST_AUTH_TARGETS_SUFFIX
|
|
83
|
-
|
|
84
72
|
USER_BUDGETS_SUFFIX = "/all"
|
|
85
73
|
USER_BUDGETS_FULL_PATH = USER_BUDGETS_PREFIX + USER_BUDGETS_SUFFIX
|
|
86
74
|
USER_BUDGET_SET_LIMIT_SUFFIX = "/set_limit"
|
|
@@ -172,20 +172,18 @@ class _InterpreterExpandable(QFunc):
|
|
|
172
172
|
scope_func_decls: dict[str, QuantumFunctionDeclaration] = {}
|
|
173
173
|
for name, evaluated in self._interpreter._builder.current_scope.items():
|
|
174
174
|
value = evaluated.value
|
|
175
|
+
if (
|
|
176
|
+
isinstance(value, list)
|
|
177
|
+
and len(value) > 0
|
|
178
|
+
and isinstance(value[0], FunctionClosure)
|
|
179
|
+
):
|
|
180
|
+
value = value[0]
|
|
175
181
|
if isinstance(value, FunctionClosure):
|
|
176
182
|
scope_func_decls[name] = QuantumFunctionDeclaration(
|
|
177
183
|
name=name,
|
|
178
184
|
positional_arg_declarations=value.positional_arg_declarations,
|
|
179
185
|
)
|
|
180
186
|
continue
|
|
181
|
-
op_param = self._interpreter._builder.current_function.parameters_dict.get(
|
|
182
|
-
name
|
|
183
|
-
)
|
|
184
|
-
if isinstance(op_param, QuantumOperandDeclaration):
|
|
185
|
-
scope_func_decls[name] = QuantumFunctionDeclaration(
|
|
186
|
-
name=name,
|
|
187
|
-
positional_arg_declarations=op_param.positional_arg_declarations,
|
|
188
|
-
)
|
|
189
187
|
return (
|
|
190
188
|
nameables_to_dict(self._interpreter._get_function_declarations())
|
|
191
189
|
| scope_func_decls
|
|
@@ -324,7 +324,7 @@ class BaseInterpreter:
|
|
|
324
324
|
def emit_statement(self, statement: QuantumStatement) -> None:
|
|
325
325
|
source_ref = statement.source_ref
|
|
326
326
|
error_context = (
|
|
327
|
-
self._error_manager.
|
|
327
|
+
self._error_manager.source_ref_context(statement.source_ref)
|
|
328
328
|
if source_ref is not None
|
|
329
329
|
else nullcontext()
|
|
330
330
|
)
|
|
@@ -41,7 +41,8 @@ class FrontendGenerativeInterpreter(GenerativeInterpreter):
|
|
|
41
41
|
if module is None or not module.__name__.startswith("classiq."):
|
|
42
42
|
file_name = os.path.split(frame.filename)[-1]
|
|
43
43
|
if (
|
|
44
|
-
frame
|
|
44
|
+
hasattr(frame, "positions")
|
|
45
|
+
and frame.positions is not None
|
|
45
46
|
and frame.positions.lineno is not None
|
|
46
47
|
and frame.positions.col_offset is not None
|
|
47
48
|
and frame.positions.end_lineno is not None
|
|
@@ -19,7 +19,7 @@ from classiq.interface.model.classical_parameter_declaration import (
|
|
|
19
19
|
AnonClassicalParameterDeclaration,
|
|
20
20
|
)
|
|
21
21
|
from classiq.interface.model.handle_binding import FieldHandleBinding, HandleBinding
|
|
22
|
-
from classiq.interface.model.model_visitor import
|
|
22
|
+
from classiq.interface.model.model_visitor import ModelStatementsVisitor
|
|
23
23
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
|
24
24
|
from classiq.interface.model.quantum_function_call import ArgValue, QuantumFunctionCall
|
|
25
25
|
from classiq.interface.model.quantum_function_declaration import (
|
|
@@ -84,7 +84,7 @@ def _get_param_expressions(param: AnonPositionalArg) -> list[Expression]:
|
|
|
84
84
|
return param.quantum_type.expressions
|
|
85
85
|
|
|
86
86
|
|
|
87
|
-
class SymbolicParamInference(
|
|
87
|
+
class SymbolicParamInference(ModelStatementsVisitor):
|
|
88
88
|
def __init__(
|
|
89
89
|
self,
|
|
90
90
|
functions: list[NativeFunctionDefinition],
|
|
@@ -166,7 +166,7 @@ class SymbolicParamInference(ModelVisitor):
|
|
|
166
166
|
else:
|
|
167
167
|
for expr in _get_expressions(arg):
|
|
168
168
|
self._process_nested_compile_time_expression(expr.expr)
|
|
169
|
-
self.
|
|
169
|
+
self.visit(call.positional_args)
|
|
170
170
|
|
|
171
171
|
def _get_params(self, call: QuantumFunctionCall) -> Sequence[AnonPositionalArg]:
|
|
172
172
|
name = call.func_name
|
|
@@ -18,9 +18,10 @@ from classiq.interface.generator.visitor import NodeType
|
|
|
18
18
|
from classiq.interface.model.allocate import Allocate
|
|
19
19
|
from classiq.interface.model.bind_operation import BindOperation
|
|
20
20
|
from classiq.interface.model.block import Block
|
|
21
|
+
from classiq.interface.model.classical_if import ClassicalIf
|
|
21
22
|
from classiq.interface.model.control import Control
|
|
22
23
|
from classiq.interface.model.invert import Invert
|
|
23
|
-
from classiq.interface.model.model_visitor import
|
|
24
|
+
from classiq.interface.model.model_visitor import ModelStatementsVisitor
|
|
24
25
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
|
25
26
|
from classiq.interface.model.power import Power
|
|
26
27
|
from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
|
|
@@ -48,7 +49,7 @@ class _BoundVars(NamedTuple):
|
|
|
48
49
|
)
|
|
49
50
|
|
|
50
51
|
|
|
51
|
-
class UncomputationSignatureInference(
|
|
52
|
+
class UncomputationSignatureInference(ModelStatementsVisitor):
|
|
52
53
|
"""
|
|
53
54
|
Infers the uncomputation signature of a function (permutation/non-permutation for
|
|
54
55
|
the function, and const/non-const for each parameter).
|
|
@@ -139,7 +140,13 @@ class UncomputationSignatureInference(ModelVisitor):
|
|
|
139
140
|
self._bound_vars_list.append(bound_vars)
|
|
140
141
|
|
|
141
142
|
def visit_ArithmeticOperation(self, arith: ArithmeticOperation) -> None:
|
|
142
|
-
|
|
143
|
+
if arith.classical_assignment:
|
|
144
|
+
if arith.var_handles:
|
|
145
|
+
self._mark_as_non_permutation()
|
|
146
|
+
for handle in arith.var_handles:
|
|
147
|
+
self._mark_as_non_const(handle.name, False)
|
|
148
|
+
else:
|
|
149
|
+
self._mark_as_non_const(arith.result_var.name, True)
|
|
143
150
|
|
|
144
151
|
def visit_AmplitudeLoadingOperation(
|
|
145
152
|
self, amp_load: AmplitudeLoadingOperation
|
|
@@ -172,6 +179,10 @@ class UncomputationSignatureInference(ModelVisitor):
|
|
|
172
179
|
def visit_SkipControl(self, block: SkipControl) -> None:
|
|
173
180
|
self.visit(block.body)
|
|
174
181
|
|
|
182
|
+
def visit_ClassicalIf(self, classical_if: ClassicalIf) -> None:
|
|
183
|
+
self.visit(classical_if.then)
|
|
184
|
+
self.visit(classical_if.else_)
|
|
185
|
+
|
|
175
186
|
def _mark_as_non_permutation(self) -> None:
|
|
176
187
|
self._is_permutation = False
|
|
177
188
|
self._non_permutation_reasons.append(self._source_ref)
|
|
@@ -3,6 +3,7 @@ from .amplitude_amplification import (
|
|
|
3
3
|
exact_amplitude_amplification,
|
|
4
4
|
)
|
|
5
5
|
from .amplitude_estimation import *
|
|
6
|
+
from .amplitude_loading import assign_amplitude_table
|
|
6
7
|
from .discrete_sine_cosine_transform import *
|
|
7
8
|
from .discrete_sine_cosine_transform import _qct_d_operator, _qct_pi_operator
|
|
8
9
|
from .grover import *
|
|
@@ -11,7 +12,6 @@ from .hea import *
|
|
|
11
12
|
from .lcu import *
|
|
12
13
|
from .linear_pauli_rotation import *
|
|
13
14
|
from .linear_pauli_rotation import _single_pauli
|
|
14
|
-
from .lookup_table import span_lookup_table
|
|
15
15
|
from .modular_exponentiation import *
|
|
16
16
|
from .modular_exponentiation import _check_msb
|
|
17
17
|
from .qaoa_penalty import *
|
|
@@ -92,6 +92,7 @@ __all__ = [
|
|
|
92
92
|
"amplitude_amplification",
|
|
93
93
|
"amplitude_estimation",
|
|
94
94
|
"apply_to_all",
|
|
95
|
+
"assign_amplitude_table",
|
|
95
96
|
"c_modular_multiply",
|
|
96
97
|
"cc_modular_add",
|
|
97
98
|
"encode_in_angle",
|
|
@@ -126,6 +127,7 @@ __all__ = [
|
|
|
126
127
|
"prepare_ghz_state",
|
|
127
128
|
"prepare_int",
|
|
128
129
|
"prepare_linear_amplitudes",
|
|
130
|
+
"prepare_select",
|
|
129
131
|
"prepare_sparse_amplitudes",
|
|
130
132
|
"prepare_uniform_interval_state",
|
|
131
133
|
"prepare_uniform_trimmed_state",
|
|
@@ -151,7 +153,6 @@ __all__ = [
|
|
|
151
153
|
"qsvt_lcu_step",
|
|
152
154
|
"qsvt_step",
|
|
153
155
|
"reflect_about_zero",
|
|
154
|
-
"span_lookup_table",
|
|
155
156
|
"suzuki_trotter",
|
|
156
157
|
"swap_test",
|
|
157
158
|
"switch",
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
from typing import cast
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from sympy import fwht
|
|
5
|
+
|
|
6
|
+
from classiq.interface.exceptions import ClassiqValueError
|
|
7
|
+
|
|
8
|
+
from classiq.qmod.builtins.functions import CX, RY
|
|
9
|
+
from classiq.qmod.builtins.operations import (
|
|
10
|
+
bind,
|
|
11
|
+
skip_control,
|
|
12
|
+
)
|
|
13
|
+
from classiq.qmod.qfunc import qfunc
|
|
14
|
+
from classiq.qmod.qmod_variable import QArray, QBit, QNum
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _get_graycode(size: int, i: int) -> int:
|
|
18
|
+
if i == 2**size:
|
|
19
|
+
return _get_graycode(size, 0)
|
|
20
|
+
return i ^ (i >> 1)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _get_graycode_angles_wh(size: int, angles: list[float]) -> list[float]:
|
|
24
|
+
transformed_angles = fwht(np.array(angles) / 2**size)
|
|
25
|
+
return [transformed_angles[_get_graycode(size, j)] for j in range(2**size)]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _get_graycode_ctrls(size: int) -> list[int]:
|
|
29
|
+
return [
|
|
30
|
+
(_get_graycode(size, i) ^ _get_graycode(size, i + 1)).bit_length() - 1
|
|
31
|
+
for i in range(2**size)
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@qfunc
|
|
36
|
+
def assign_amplitude_table(
|
|
37
|
+
amplitudes: list[float], index: QNum, indicator: QBit
|
|
38
|
+
) -> None:
|
|
39
|
+
"""
|
|
40
|
+
[Qmod Classiq-library function]
|
|
41
|
+
|
|
42
|
+
Load a specified list of real amplitudes into a quantum variable using an extra indicator qubit:
|
|
43
|
+
\\( |i\\rangle|0\\rangle \\rightarrow a(i),\\ |i\\rangle|1\\rangle + \\sqrt{1 - a(i)^2},\\ |x\\rangle|0\\rangle \\).
|
|
44
|
+
Here, \\(a(i)\\) is the i-th amplitude, determined by the QNum when the index is in state \\(i\\).
|
|
45
|
+
A list extracted from a given classical function \\(f(x)\\), with indexing according to a given QNum, can be obtained via the utility SDK function `lookup_table`.
|
|
46
|
+
This function expects the indicator qubit to be initialized to \\(|0\\rangle\\).
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
amplitudes: Real values for the amplitudes
|
|
50
|
+
index: The quantum variable used for amplitude indexing
|
|
51
|
+
indicator: The quantum indicator qubit
|
|
52
|
+
|
|
53
|
+
Example:
|
|
54
|
+
```python
|
|
55
|
+
from classiq import *
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@qfunc
|
|
59
|
+
def main(x: Output[QNum[5, UNSIGNED, 5]], ind: Output[QBit]) -> None:
|
|
60
|
+
allocate(x)
|
|
61
|
+
hadamard_transform(x)
|
|
62
|
+
allocate(ind)
|
|
63
|
+
|
|
64
|
+
assign_amplitude_table(lookup_table(lambda x: x**2, x), x, ind)
|
|
65
|
+
```
|
|
66
|
+
"""
|
|
67
|
+
if len(amplitudes) != 2**index.size:
|
|
68
|
+
raise ClassiqValueError(
|
|
69
|
+
f"The number of amplitudes must be 2**index.size={2 ** index.size}, got "
|
|
70
|
+
f"{len(amplitudes)}"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
angles_to_load = cast(list[float], 2 * np.arcsin(amplitudes))
|
|
74
|
+
size = cast(int, index.size)
|
|
75
|
+
transformed_angles = _get_graycode_angles_wh(size, angles_to_load)
|
|
76
|
+
controllers = _get_graycode_ctrls(size)
|
|
77
|
+
|
|
78
|
+
qba: QArray = QArray()
|
|
79
|
+
bind(index, qba)
|
|
80
|
+
for k in range(2**size):
|
|
81
|
+
RY(transformed_angles[k], indicator)
|
|
82
|
+
skip_control(
|
|
83
|
+
lambda k=k: CX(qba[controllers[k]], indicator) # type:ignore[misc]
|
|
84
|
+
)
|
|
85
|
+
bind(qba, index)
|