classiq 0.46.0__py3-none-any.whl → 0.47.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 +45 -8
- classiq/execution/execution_session.py +96 -39
- classiq/execution/jobs.py +108 -1
- classiq/interface/_version.py +1 -1
- classiq/interface/backend/quantum_backend_providers.py +0 -1
- classiq/interface/debug_info/debug_info.py +23 -1
- classiq/interface/execution/primitives.py +9 -0
- classiq/interface/generator/arith/arithmetic_operations.py +5 -2
- classiq/interface/generator/arith/binary_ops.py +21 -14
- classiq/interface/generator/arith/extremum_operations.py +9 -1
- classiq/interface/generator/arith/number_utils.py +6 -0
- classiq/interface/generator/arith/register_user_input.py +30 -21
- classiq/interface/generator/arith/unary_ops.py +13 -1
- classiq/interface/generator/generated_circuit_data.py +47 -2
- classiq/interface/generator/quantum_program.py +10 -2
- classiq/interface/ide/visual_model.py +7 -1
- classiq/interface/interface_version.py +1 -1
- classiq/interface/model/phase_operation.py +11 -0
- classiq/interface/model/statement_block.py +3 -0
- classiq/interface/server/routes.py +0 -3
- classiq/model_expansions/interpreter.py +6 -0
- classiq/model_expansions/quantum_operations/emitter.py +8 -2
- classiq/model_expansions/quantum_operations/phase.py +177 -0
- classiq/qmod/builtins/functions/amplitude_estimation.py +2 -1
- classiq/qmod/builtins/functions/arithmetic.py +2 -2
- classiq/qmod/builtins/functions/discrete_sine_cosine_transform.py +16 -16
- classiq/qmod/builtins/functions/exponentiation.py +12 -11
- classiq/qmod/builtins/functions/grover.py +28 -29
- classiq/qmod/builtins/functions/hea.py +1 -1
- classiq/qmod/builtins/functions/linear_pauli_rotation.py +8 -8
- classiq/qmod/builtins/functions/modular_exponentiation.py +9 -9
- classiq/qmod/builtins/functions/qaoa_penalty.py +10 -10
- classiq/qmod/builtins/functions/qft.py +1 -1
- classiq/qmod/builtins/functions/qpe.py +2 -2
- classiq/qmod/builtins/functions/qsvt.py +15 -15
- classiq/qmod/builtins/functions/standard_gates.py +13 -13
- classiq/qmod/builtins/functions/state_preparation.py +18 -17
- classiq/qmod/builtins/functions/swap_test.py +3 -3
- classiq/qmod/builtins/operations.py +14 -0
- classiq/qmod/native/pretty_printer.py +6 -0
- classiq/qmod/pretty_print/pretty_printer.py +6 -0
- {classiq-0.46.0.dist-info → classiq-0.47.0.dist-info}/METADATA +7 -4
- {classiq-0.46.0.dist-info → classiq-0.47.0.dist-info}/RECORD +44 -41
- {classiq-0.46.0.dist-info → classiq-0.47.0.dist-info}/WHEEL +0 -0
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import Any, Dict, Optional
|
1
|
+
from typing import Any, Dict, Optional, Tuple
|
2
2
|
|
3
3
|
import pydantic
|
4
4
|
|
@@ -14,6 +14,7 @@ class RegisterArithmeticInfo(HashablePydanticBaseModel):
|
|
14
14
|
size: pydantic.PositiveInt
|
15
15
|
is_signed: bool = pydantic.Field(default=False)
|
16
16
|
fraction_places: pydantic.NonNegativeInt = pydantic.Field(default=0)
|
17
|
+
bypass_bounds_validation: bool = pydantic.Field(default=False)
|
17
18
|
bounds: PydanticFloatTuple = pydantic.Field(default=None)
|
18
19
|
|
19
20
|
@pydantic.root_validator(pre=True)
|
@@ -22,32 +23,39 @@ class RegisterArithmeticInfo(HashablePydanticBaseModel):
|
|
22
23
|
values.pop("name")
|
23
24
|
return values
|
24
25
|
|
26
|
+
@staticmethod
|
27
|
+
def get_maximal_bounds(
|
28
|
+
*, size: int, is_signed: bool, fraction_places: int
|
29
|
+
) -> Tuple[float, float]:
|
30
|
+
lb = 0 if not is_signed else -(2 ** (size - 1))
|
31
|
+
ub = 2**size - 1 if not is_signed else 2 ** (size - 1) - 1
|
32
|
+
fraction_factor = float(2**-fraction_places)
|
33
|
+
return (lb * fraction_factor, ub * fraction_factor)
|
34
|
+
|
25
35
|
@pydantic.validator("bounds", always=True)
|
26
36
|
def _validate_bounds(
|
27
37
|
cls, bounds: Optional[PydanticFloatTuple], values: Dict[str, Any]
|
28
38
|
) -> PydanticFloatTuple:
|
29
|
-
if bounds is not None:
|
30
|
-
if min(bounds) < 0:
|
31
|
-
assert values.get("is_signed")
|
32
|
-
fraction_places = values.get("fraction_places")
|
33
|
-
if not isinstance(fraction_places, int):
|
34
|
-
raise ClassiqValueError(
|
35
|
-
"RegisterUserInput must have an integer fraction_places"
|
36
|
-
)
|
37
|
-
return number_utils.limit_fraction_places(
|
38
|
-
min(bounds), machine_precision=fraction_places
|
39
|
-
), number_utils.limit_fraction_places(
|
40
|
-
max(bounds), machine_precision=fraction_places
|
41
|
-
)
|
42
|
-
|
43
39
|
size = values.get("size")
|
40
|
+
is_signed = values.get("is_signed", False)
|
41
|
+
fraction_places = values.get("fraction_places", 0)
|
44
42
|
if not isinstance(size, int):
|
45
|
-
raise ClassiqValueError("
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
43
|
+
raise ClassiqValueError("RegisterArithmeticInfo must have an integer size")
|
44
|
+
|
45
|
+
maximal_bounds = cls.get_maximal_bounds(
|
46
|
+
size=size, is_signed=is_signed, fraction_places=fraction_places
|
47
|
+
)
|
48
|
+
if bounds is None:
|
49
|
+
return maximal_bounds
|
50
|
+
|
51
|
+
trimmed_bounds = (
|
52
|
+
number_utils.limit_fraction_places(min(bounds), fraction_places),
|
53
|
+
number_utils.limit_fraction_places(max(bounds), fraction_places),
|
54
|
+
)
|
55
|
+
if not values.get("bypass_bounds_validation", False):
|
56
|
+
assert min(trimmed_bounds) >= min(maximal_bounds), "Illegal bound min"
|
57
|
+
assert max(trimmed_bounds) <= max(maximal_bounds), "Illegal bound max"
|
58
|
+
return trimmed_bounds
|
51
59
|
|
52
60
|
def limit_fraction_places(self, machine_precision: int) -> "RegisterArithmeticInfo":
|
53
61
|
truncated_bits: int = max(self.fraction_places - machine_precision, 0)
|
@@ -56,6 +64,7 @@ class RegisterArithmeticInfo(HashablePydanticBaseModel):
|
|
56
64
|
is_signed=self.is_signed,
|
57
65
|
fraction_places=self.fraction_places - truncated_bits,
|
58
66
|
bounds=self.bounds,
|
67
|
+
bypass_bounds_validation=self.bypass_bounds_validation,
|
59
68
|
)
|
60
69
|
|
61
70
|
@property
|
@@ -5,6 +5,7 @@ import pydantic
|
|
5
5
|
from classiq.interface.exceptions import ClassiqValueError
|
6
6
|
from classiq.interface.generator.arith import argument_utils, number_utils
|
7
7
|
from classiq.interface.generator.arith.arithmetic_operations import (
|
8
|
+
MODULO_WITH_FRACTION_PLACES_ERROR_MSG,
|
8
9
|
ArithmeticOperationParams,
|
9
10
|
)
|
10
11
|
from classiq.interface.generator.arith.register_user_input import RegisterArithmeticInfo
|
@@ -74,6 +75,9 @@ class BitwiseInvert(UnaryOpParams):
|
|
74
75
|
|
75
76
|
|
76
77
|
class Negation(UnaryOpParams):
|
78
|
+
bypass_bounds_validation: bool = pydantic.Field(
|
79
|
+
default=False
|
80
|
+
) # True for efficient subtraction
|
77
81
|
output_name = "negated"
|
78
82
|
|
79
83
|
@staticmethod
|
@@ -88,11 +92,19 @@ class Negation(UnaryOpParams):
|
|
88
92
|
eff_arg = self.arg.limit_fraction_places(self.machine_precision)
|
89
93
|
is_signed = max(eff_arg.bounds) > 0 and self._include_sign
|
90
94
|
bounds = (-max(eff_arg.bounds), -min(eff_arg.bounds))
|
95
|
+
if self.output_size and not self.bypass_bounds_validation:
|
96
|
+
if eff_arg.fraction_places:
|
97
|
+
raise ValueError(MODULO_WITH_FRACTION_PLACES_ERROR_MSG)
|
98
|
+
max_bounds = RegisterArithmeticInfo.get_maximal_bounds(
|
99
|
+
size=self.output_size, is_signed=False, fraction_places=0
|
100
|
+
)
|
101
|
+
bounds = number_utils.bounds_cut(bounds, max_bounds)
|
91
102
|
return RegisterArithmeticInfo(
|
92
103
|
size=self.output_size or self._expected_result_size(eff_arg),
|
93
104
|
fraction_places=eff_arg.fraction_places,
|
94
105
|
is_signed=is_signed,
|
95
|
-
|
106
|
+
bypass_bounds_validation=self.bypass_bounds_validation,
|
107
|
+
bounds=bounds,
|
96
108
|
)
|
97
109
|
|
98
110
|
def zero_input_for_extension(self) -> pydantic.NonNegativeInt:
|
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
import logging
|
2
|
+
from typing import Any, Dict, List, Literal, Optional, Tuple, Union
|
2
3
|
|
3
4
|
import pydantic
|
4
5
|
from typing_extensions import TypeAlias
|
@@ -10,10 +11,10 @@ from classiq.interface.generator.synthesis_metadata.synthesis_execution_data imp
|
|
10
11
|
)
|
11
12
|
from classiq.interface.ide.visual_model import OperationParameter
|
12
13
|
|
14
|
+
_logger = logging.getLogger(__name__)
|
13
15
|
ParameterName = str
|
14
16
|
IOQubitMapping: TypeAlias = Dict[str, Tuple[int, ...]]
|
15
17
|
|
16
|
-
|
17
18
|
CLASSIQ_HIERARCHY_SEPARATOR: Literal["."] = "."
|
18
19
|
|
19
20
|
VISUALIZATION_HIDE_LIST = [
|
@@ -102,6 +103,7 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
102
103
|
absolute_qubits: Optional[Tuple[int, ...]]
|
103
104
|
is_basis_gate: Optional[bool]
|
104
105
|
parameters: List[OperationParameter] = list()
|
106
|
+
port_to_passed_variable_map: Dict[str, str] = pydantic.Field(default_factory=dict)
|
105
107
|
|
106
108
|
@property
|
107
109
|
def registers(self) -> List[GeneratedRegister]:
|
@@ -120,3 +122,46 @@ class FunctionDebugInfoInterface(pydantic.BaseModel):
|
|
120
122
|
if self.generated_function is None:
|
121
123
|
return list()
|
122
124
|
return self.generated_function.control_states
|
125
|
+
|
126
|
+
@staticmethod
|
127
|
+
def create_parameters_from_dict(
|
128
|
+
parameters: Dict[str, str]
|
129
|
+
) -> List[OperationParameter]:
|
130
|
+
return [
|
131
|
+
OperationParameter(label=key, value=value)
|
132
|
+
for key, value in parameters.items()
|
133
|
+
]
|
134
|
+
|
135
|
+
def update(self, **kwargs: Any) -> None:
|
136
|
+
for key, value in kwargs.items():
|
137
|
+
setattr(self, key, value)
|
138
|
+
|
139
|
+
def propagate_absolute_qubits(self) -> None:
|
140
|
+
if self.absolute_qubits is None:
|
141
|
+
return
|
142
|
+
|
143
|
+
for register in self.registers:
|
144
|
+
register.qubit_indexes_absolute = list(
|
145
|
+
_get_absolute_from_relative(
|
146
|
+
self.absolute_qubits, tuple(register.qubit_indexes_relative)
|
147
|
+
)
|
148
|
+
)
|
149
|
+
|
150
|
+
for child in self.children:
|
151
|
+
child.absolute_qubits = _get_absolute_from_relative(
|
152
|
+
self.absolute_qubits, child.relative_qubits
|
153
|
+
)
|
154
|
+
child.propagate_absolute_qubits()
|
155
|
+
|
156
|
+
|
157
|
+
def _get_absolute_from_relative(
|
158
|
+
absolute_qubits: Tuple[int, ...], relative_qubits: Tuple[int, ...]
|
159
|
+
) -> Tuple[int, ...]:
|
160
|
+
if max(relative_qubits) >= len(absolute_qubits):
|
161
|
+
_logger.warning(
|
162
|
+
"Invalid qubit computation (relative qubits: %s, absolute qubits: %s)",
|
163
|
+
relative_qubits,
|
164
|
+
absolute_qubits,
|
165
|
+
)
|
166
|
+
return tuple()
|
167
|
+
return tuple(absolute_qubits[relative_qubit] for relative_qubit in relative_qubits)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import uuid
|
2
|
-
from datetime import datetime
|
2
|
+
from datetime import datetime, timezone
|
3
3
|
from pathlib import Path
|
4
4
|
from typing import Dict, List, Optional, Tuple, Union
|
5
5
|
|
@@ -10,6 +10,7 @@ from classiq.interface.exceptions import (
|
|
10
10
|
ClassiqMissingOutputFormatError,
|
11
11
|
ClassiqStateInitializationError,
|
12
12
|
)
|
13
|
+
from classiq.interface.execution.primitives import PrimitivesInput
|
13
14
|
from classiq.interface.executor import quantum_code
|
14
15
|
from classiq.interface.executor.quantum_instruction_set import QuantumInstructionSet
|
15
16
|
from classiq.interface.executor.register_initialization import RegisterInitialization
|
@@ -50,16 +51,23 @@ def get_uuid_as_str() -> str:
|
|
50
51
|
return str(uuid.uuid4())
|
51
52
|
|
52
53
|
|
54
|
+
def _get_formatted_utc_current_time() -> str:
|
55
|
+
# The purpose of this method is to replicate the behavior of
|
56
|
+
# datetime.utcnow().isoformat(), since `utcnow` is now deprecated
|
57
|
+
return datetime.now(timezone.utc).isoformat().split("+")[0]
|
58
|
+
|
59
|
+
|
53
60
|
class QuantumProgram(VersionedModel, CircuitCodeInterface):
|
54
61
|
hardware_data: SynthesisHardwareData
|
55
62
|
initial_values: Optional[InitialConditions]
|
56
63
|
data: GeneratedCircuitData
|
57
64
|
model: ExecutionModel
|
58
65
|
transpiled_circuit: Optional[TranspiledCircuitData]
|
59
|
-
creation_time: str = pydantic.Field(default_factory=
|
66
|
+
creation_time: str = pydantic.Field(default_factory=_get_formatted_utc_current_time)
|
60
67
|
synthesis_duration: Optional[SynthesisStepDurations]
|
61
68
|
debug_info: Optional[List[FunctionDebugInfoInterface]]
|
62
69
|
program_id: str = pydantic.Field(default_factory=get_uuid_as_str)
|
70
|
+
execution_primitives_input: Optional[PrimitivesInput] = pydantic.Field(default=None)
|
63
71
|
|
64
72
|
def _hardware_agnostic_program_code(self) -> CodeAndSyntax:
|
65
73
|
circuit_code = self.program_circuit.get_code_by_priority()
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import Dict, List, Optional, Tuple
|
1
|
+
from typing import Any, Dict, List, Optional, Tuple
|
2
2
|
|
3
3
|
import pydantic
|
4
4
|
|
@@ -38,6 +38,7 @@ class OperationParameter(pydantic.BaseModel):
|
|
38
38
|
|
39
39
|
class OperationLink(pydantic.BaseModel):
|
40
40
|
label: str
|
41
|
+
inner_label: Optional[str] = None
|
41
42
|
qubits: Tuple[int, ...]
|
42
43
|
type: str
|
43
44
|
|
@@ -47,6 +48,11 @@ class OperationLink(pydantic.BaseModel):
|
|
47
48
|
def __hash__(self) -> int:
|
48
49
|
return hash((type(self), self.label, self.qubits, self.type))
|
49
50
|
|
51
|
+
def __eq__(self, other: Any) -> bool:
|
52
|
+
if not isinstance(other, OperationLink):
|
53
|
+
return False
|
54
|
+
return hash(self) == hash(other)
|
55
|
+
|
50
56
|
|
51
57
|
class OperationLinks(pydantic.BaseModel):
|
52
58
|
inputs: List[OperationLink]
|
@@ -1 +1 @@
|
|
1
|
-
INTERFACE_VERSION = "
|
1
|
+
INTERFACE_VERSION = "3"
|
@@ -0,0 +1,11 @@
|
|
1
|
+
from typing import Literal
|
2
|
+
|
3
|
+
from classiq.interface.generator.expressions.expression import Expression
|
4
|
+
from classiq.interface.model.quantum_expressions.quantum_expression import (
|
5
|
+
QuantumExpressionOperation,
|
6
|
+
)
|
7
|
+
|
8
|
+
|
9
|
+
class PhaseOperation(QuantumExpressionOperation):
|
10
|
+
kind: Literal["PhaseOperation"]
|
11
|
+
theta: Expression
|
@@ -9,6 +9,7 @@ from classiq.interface.model.control import Control
|
|
9
9
|
from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperation
|
10
10
|
from classiq.interface.model.invert import Invert
|
11
11
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
12
|
+
from classiq.interface.model.phase_operation import PhaseOperation
|
12
13
|
from classiq.interface.model.power import Power
|
13
14
|
from classiq.interface.model.quantum_expressions.amplitude_loading_operation import (
|
14
15
|
AmplitudeLoadingOperation,
|
@@ -38,6 +39,7 @@ ConcreteQuantumStatement = Annotated[
|
|
38
39
|
ClassicalIf,
|
39
40
|
Control,
|
40
41
|
WithinApply,
|
42
|
+
PhaseOperation,
|
41
43
|
],
|
42
44
|
Field(..., discriminator="kind"),
|
43
45
|
]
|
@@ -52,3 +54,4 @@ Invert.update_forward_refs(StatementBlock=StatementBlock)
|
|
52
54
|
WithinApply.update_forward_refs(StatementBlock=StatementBlock)
|
53
55
|
ClassicalIf.update_forward_refs(StatementBlock=StatementBlock)
|
54
56
|
NativeFunctionDefinition.update_forward_refs(StatementBlock=StatementBlock)
|
57
|
+
PhaseOperation.update_forward_refs(StatementBlock=StatementBlock)
|
@@ -17,9 +17,6 @@ ANALYZER_HC_TABLE_GRAPH_FULL_PATH = ANALYZER_PREFIX + ANALYZER_HC_TABLE_GRAPH
|
|
17
17
|
|
18
18
|
ANALYZER_HC_GRAPH_NEW = "/graphs/hardware_connectivity/new"
|
19
19
|
|
20
|
-
ANALYZER_OPTIONAL_DEVICES = "/graphs/available_devices"
|
21
|
-
ANALYZER_OPTIONAL_DEVICES_FULL_PATH = ANALYZER_PREFIX + ANALYZER_OPTIONAL_DEVICES
|
22
|
-
|
23
20
|
TASKS_SUFFIX = "/tasks"
|
24
21
|
RB = "/rb"
|
25
22
|
ANALYZER_DATA_TASK = f"{TASKS_SUFFIX}/data"
|
@@ -24,6 +24,7 @@ from classiq.interface.model.inplace_binary_operation import InplaceBinaryOperat
|
|
24
24
|
from classiq.interface.model.invert import Invert
|
25
25
|
from classiq.interface.model.model import Model
|
26
26
|
from classiq.interface.model.native_function_definition import NativeFunctionDefinition
|
27
|
+
from classiq.interface.model.phase_operation import PhaseOperation
|
27
28
|
from classiq.interface.model.power import Power
|
28
29
|
from classiq.interface.model.quantum_expressions.quantum_expression import (
|
29
30
|
QuantumAssignmentOperation,
|
@@ -74,6 +75,7 @@ from classiq.model_expansions.quantum_operations import (
|
|
74
75
|
VariableDeclarationStatementEmitter,
|
75
76
|
WithinApplyEmitter,
|
76
77
|
)
|
78
|
+
from classiq.model_expansions.quantum_operations.phase import PhaseEmitter
|
77
79
|
from classiq.model_expansions.scope import Evaluated, QuantumSymbol, Scope
|
78
80
|
from classiq.model_expansions.scope_initialization import (
|
79
81
|
add_entry_point_params_to_scope,
|
@@ -307,6 +309,10 @@ class Interpreter:
|
|
307
309
|
def emit_power(self, power: Power) -> None:
|
308
310
|
PowerEmitter(self).emit(power)
|
309
311
|
|
312
|
+
@emit.register
|
313
|
+
def emit_phase(self, phase: PhaseOperation) -> None:
|
314
|
+
PhaseEmitter(self).emit(phase)
|
315
|
+
|
310
316
|
def _expand_block(self, block: Sequence[QuantumStatement], block_name: str) -> None:
|
311
317
|
with self._builder.block_context(block_name):
|
312
318
|
for statement in block:
|
@@ -131,16 +131,22 @@ class Emitter(Generic[QuantumStatementT]):
|
|
131
131
|
or new_call.func_name == free.func_decl.name
|
132
132
|
)
|
133
133
|
parameters = {
|
134
|
-
arg_decl.name: FunctionDebugInfo.param_controller(value=
|
135
|
-
for arg_decl,
|
134
|
+
arg_decl.name: FunctionDebugInfo.param_controller(value=evaluated_arg.value)
|
135
|
+
for arg_decl, evaluated_arg in zip(new_positional_arg_decls, evaluated_args)
|
136
136
|
if isinstance(arg_decl, ClassicalParameterDeclaration)
|
137
137
|
}
|
138
138
|
|
139
|
+
port_to_passed_variable_map = {
|
140
|
+
arg_decl.name: evaluated_arg.value.handle.name
|
141
|
+
for arg_decl, evaluated_arg in zip(new_positional_arg_decls, evaluated_args)
|
142
|
+
if isinstance(arg_decl, PortDeclaration)
|
143
|
+
}
|
139
144
|
self._interpreter._model.debug_info[new_call.uuid] = FunctionDebugInfo(
|
140
145
|
name=new_call.func_name,
|
141
146
|
level=OperationLevel.QMOD_FUNCTION_CALL,
|
142
147
|
parameters=parameters,
|
143
148
|
is_allocate_or_free=is_allocate_or_free,
|
149
|
+
port_to_passed_variable_map=port_to_passed_variable_map,
|
144
150
|
)
|
145
151
|
if is_atomic:
|
146
152
|
new_call.set_func_decl(new_declaration)
|
@@ -0,0 +1,177 @@
|
|
1
|
+
from typing import TYPE_CHECKING, Dict, List, Tuple
|
2
|
+
|
3
|
+
import sympy
|
4
|
+
from sympy import sympify
|
5
|
+
|
6
|
+
from classiq.interface.exceptions import ClassiqExpansionError
|
7
|
+
from classiq.interface.generator.expressions.expression import Expression
|
8
|
+
from classiq.interface.model.bind_operation import BindOperation
|
9
|
+
from classiq.interface.model.handle_binding import HANDLE_ID_SEPARATOR, HandleBinding
|
10
|
+
from classiq.interface.model.phase_operation import PhaseOperation
|
11
|
+
from classiq.interface.model.quantum_function_call import QuantumFunctionCall
|
12
|
+
from classiq.interface.model.quantum_type import (
|
13
|
+
QuantumBitvector,
|
14
|
+
QuantumNumeric,
|
15
|
+
QuantumScalar,
|
16
|
+
)
|
17
|
+
from classiq.interface.model.variable_declaration_statement import (
|
18
|
+
VariableDeclarationStatement,
|
19
|
+
)
|
20
|
+
from classiq.interface.model.within_apply_operation import WithinApply
|
21
|
+
|
22
|
+
from classiq.applications.combinatorial_helpers.transformations.ising_converter import (
|
23
|
+
_find_sub_list_items,
|
24
|
+
_get_coeff_from_expr,
|
25
|
+
_get_vars,
|
26
|
+
_refine_ising_expr,
|
27
|
+
_to_ising_symbolic_objective_function,
|
28
|
+
)
|
29
|
+
from classiq.model_expansions.quantum_operations.expression_operation import (
|
30
|
+
ExpressionOperationEmitter,
|
31
|
+
)
|
32
|
+
from classiq.qmod.builtins.functions.exponentiation import suzuki_trotter
|
33
|
+
from classiq.qmod.semantics.error_manager import ErrorManager
|
34
|
+
|
35
|
+
|
36
|
+
class PhaseEmitter(ExpressionOperationEmitter[PhaseOperation]):
|
37
|
+
def emit(self, phase_op: PhaseOperation, /) -> None:
|
38
|
+
phase_expression = self._evaluate_op_expression(phase_op)
|
39
|
+
phase_op = phase_op.copy(update=dict(expression=phase_expression))
|
40
|
+
arrays_with_subscript = self._get_symbols_to_split(phase_op.expression)
|
41
|
+
if len(arrays_with_subscript) > 0:
|
42
|
+
self._emit_with_split(phase_op, phase_op.expression, arrays_with_subscript)
|
43
|
+
return
|
44
|
+
|
45
|
+
phase_op = self._evaluate_types_in_expression(phase_op, phase_op.expression)
|
46
|
+
if len(phase_op.var_handles) == 0:
|
47
|
+
ErrorManager().add_error(
|
48
|
+
"Cannot perform phase operation on an expression with no quantum variables."
|
49
|
+
)
|
50
|
+
return
|
51
|
+
|
52
|
+
aux_name = self._counted_name_allocator.allocate("phase_aux")
|
53
|
+
if len(phase_op.var_handles) > 1:
|
54
|
+
split_join = True
|
55
|
+
evolution_variable = HandleBinding(name=aux_name)
|
56
|
+
else:
|
57
|
+
split_join = False
|
58
|
+
evolution_variable = phase_op.var_handles[0]
|
59
|
+
expression = self._evaluate_op_expression(phase_op)
|
60
|
+
expression_evolution_function = QuantumFunctionCall(
|
61
|
+
function=suzuki_trotter.func_decl.name,
|
62
|
+
positional_args=[
|
63
|
+
_convert_cost_expression_to_hamiltonian(
|
64
|
+
expression.expr,
|
65
|
+
{
|
66
|
+
var.name: self._current_scope[var.name].value.quantum_type
|
67
|
+
for var in phase_op.var_handles
|
68
|
+
},
|
69
|
+
),
|
70
|
+
phase_op.theta,
|
71
|
+
Expression(expr="1"),
|
72
|
+
Expression(expr="1"),
|
73
|
+
evolution_variable,
|
74
|
+
],
|
75
|
+
source_ref=phase_op.source_ref,
|
76
|
+
)
|
77
|
+
expression_evolution_function.set_func_decl(suzuki_trotter.func_decl)
|
78
|
+
|
79
|
+
if split_join:
|
80
|
+
self._interpreter.emit_statement(
|
81
|
+
VariableDeclarationStatement(
|
82
|
+
name=aux_name, quantum_type=QuantumBitvector()
|
83
|
+
)
|
84
|
+
)
|
85
|
+
self._interpreter.emit_statement(
|
86
|
+
WithinApply(
|
87
|
+
compute=[
|
88
|
+
BindOperation(
|
89
|
+
in_handles=phase_op.var_handles,
|
90
|
+
out_handles=[HandleBinding(name=aux_name)],
|
91
|
+
)
|
92
|
+
],
|
93
|
+
action=[expression_evolution_function],
|
94
|
+
source_ref=phase_op.source_ref,
|
95
|
+
)
|
96
|
+
)
|
97
|
+
else:
|
98
|
+
self._interpreter.emit_statement(
|
99
|
+
expression_evolution_function,
|
100
|
+
)
|
101
|
+
|
102
|
+
|
103
|
+
def _get_single_bit_vars_expression(
|
104
|
+
expr: sympy.Expr, vars_info: Dict[str, QuantumScalar]
|
105
|
+
) -> Tuple[sympy.Expr, List[sympy.Symbol]]:
|
106
|
+
bit_vars = []
|
107
|
+
for var_name, var_info in vars_info.items():
|
108
|
+
size = var_info.size_in_bits
|
109
|
+
var = sympy.Symbol(var_name)
|
110
|
+
if size == 1:
|
111
|
+
bits = [var]
|
112
|
+
is_signed = False
|
113
|
+
fraction_places = 0
|
114
|
+
else:
|
115
|
+
if TYPE_CHECKING:
|
116
|
+
assert isinstance(var_info, QuantumNumeric)
|
117
|
+
bits = [
|
118
|
+
sympy.Symbol(f"{var_name}{HANDLE_ID_SEPARATOR}{i}__split__")
|
119
|
+
for i in range(size)
|
120
|
+
]
|
121
|
+
is_signed = var_info.sign_value
|
122
|
+
fraction_places = var_info.fraction_digits_value
|
123
|
+
bit_vars.extend(bits)
|
124
|
+
split_var = 0
|
125
|
+
for i, bit in enumerate(bits):
|
126
|
+
if is_signed and i == size - 1: # sign bit (MSB)
|
127
|
+
split_var -= bit * 2 ** (size - 1 - fraction_places)
|
128
|
+
else:
|
129
|
+
split_var += bit * 2 ** (i - fraction_places)
|
130
|
+
expr = expr.subs(var, split_var)
|
131
|
+
return expr, bit_vars
|
132
|
+
|
133
|
+
|
134
|
+
def _convert_ising_sympy_to_pauli_terms(
|
135
|
+
ising_expr: sympy.Expr, ordered_sympy_vars: List[sympy.Symbol]
|
136
|
+
) -> str:
|
137
|
+
pauli_terms: List[str] = []
|
138
|
+
for expr_term in ising_expr.args:
|
139
|
+
expr_vars = _get_vars(expr_term)
|
140
|
+
z_vec = _find_sub_list_items(ordered_sympy_vars, expr_vars)
|
141
|
+
pauli_elements = ["I"] * len(z_vec)
|
142
|
+
for index, is_z_op in enumerate(z_vec):
|
143
|
+
if is_z_op:
|
144
|
+
pauli_elements[len(z_vec) - index - 1] = (
|
145
|
+
"Z" # reminder: Pauli reverses the order!
|
146
|
+
)
|
147
|
+
coeff = _get_coeff_from_expr(expr_term)
|
148
|
+
paulis = [f"Pauli.{pauli}" for pauli in pauli_elements]
|
149
|
+
pauli_terms.append(
|
150
|
+
# fmt: off
|
151
|
+
"struct_literal("
|
152
|
+
"PauliTerm,"
|
153
|
+
f"pauli=[{', '.join(paulis)}],"
|
154
|
+
f"coefficient={Expression(expr=str(coeff))},"
|
155
|
+
")"
|
156
|
+
# fmt: on,
|
157
|
+
)
|
158
|
+
return f"[{', '.join(pauli_terms)}]"
|
159
|
+
|
160
|
+
|
161
|
+
def _convert_cost_expression_to_hamiltonian(
|
162
|
+
expr: str,
|
163
|
+
vars: Dict[str, QuantumScalar],
|
164
|
+
) -> Expression:
|
165
|
+
sympy_expr = sympify(expr)
|
166
|
+
single_bit_vars_expression, single_bit_vars = _get_single_bit_vars_expression(
|
167
|
+
sympy_expr, vars
|
168
|
+
)
|
169
|
+
if not single_bit_vars_expression.is_polynomial():
|
170
|
+
raise ClassiqExpansionError(f"phased expression {expr!r} is not polynomial")
|
171
|
+
|
172
|
+
ising_expr = _to_ising_symbolic_objective_function(single_bit_vars_expression)
|
173
|
+
ising_expr = _refine_ising_expr(ising_expr)
|
174
|
+
|
175
|
+
return Expression(
|
176
|
+
expr=_convert_ising_sympy_to_pauli_terms(ising_expr, single_bit_vars)
|
177
|
+
)
|
@@ -12,7 +12,8 @@ def amplitude_estimation(
|
|
12
12
|
) -> None:
|
13
13
|
"""
|
14
14
|
[Qmod Classiq-library function]
|
15
|
-
|
15
|
+
|
16
|
+
Estimate the probability of a state being marked by the operand `oracle` as a "good state."
|
16
17
|
|
17
18
|
The algorithm prepares the state in the `packed_vars` register and estimates the probability of this state being marked by the oracle as a "good state."
|
18
19
|
This is done using the Quantum Phase Estimation (QPE) algorithm, where the unitary for QPE is the Grover operator, which is composed of the `oracle` and `space_transform` operators.
|
@@ -14,7 +14,7 @@ def unitary(
|
|
14
14
|
"""
|
15
15
|
[Qmod core-library function]
|
16
16
|
|
17
|
-
|
17
|
+
Applies a unitary matrix on a quantum state.
|
18
18
|
|
19
19
|
Args:
|
20
20
|
elements: A 2d array of complex numbers representing the unitary matrix. This matrix must be unitary.
|
@@ -51,7 +51,7 @@ def integer_xor(left: QArray[QBit], right: QArray[QBit]) -> None:
|
|
51
51
|
@qfunc(external=True)
|
52
52
|
def modular_increment(a: CInt, x: QNum) -> None:
|
53
53
|
"""
|
54
|
-
Qmod Classiq-library function
|
54
|
+
[Qmod Classiq-library function]
|
55
55
|
|
56
56
|
Adds $a$ to $x$ modulo the range of $x$, assumed that $x$ is a non-negative integer and $a$ is an integer.
|
57
57
|
Mathematically it is described as:
|
@@ -14,22 +14,22 @@ def _qct_pi_operator(x: QArray[QBit], q: QBit) -> None:
|
|
14
14
|
|
15
15
|
@qfunc(external=True)
|
16
16
|
def qct_qst_type1(x: QArray[QBit]) -> None:
|
17
|
-
|
18
|
-
Qmod Classiq-library function
|
17
|
+
"""
|
18
|
+
[Qmod Classiq-library function]
|
19
19
|
|
20
20
|
Applies the quantum discrete cosine (DCT) and sine (DST)
|
21
21
|
transform of type 1 to the qubit array `x`.
|
22
|
-
Corresponds to the matrix (with $n
|
22
|
+
Corresponds to the matrix (with $n\\equiv$`x.len`):
|
23
23
|
|
24
24
|
$$
|
25
|
-
|
25
|
+
\\left(
|
26
26
|
\begin{array}{ccc|c}
|
27
27
|
{} &{} &{} \\
|
28
28
|
{}&{\rm DCT}^{(1)}(2^{n-1}+1) & {}& 0\\
|
29
29
|
{} &{} &{} \\
|
30
|
-
|
30
|
+
\\hline
|
31
31
|
{} & 0 & {} & i{\rm DST}^{(1)}(2^{n-1}-1)
|
32
|
-
|
32
|
+
\\end{array}
|
33
33
|
\right)
|
34
34
|
$$
|
35
35
|
|
@@ -44,20 +44,20 @@ def qct_qst_type1(x: QArray[QBit]) -> None:
|
|
44
44
|
|
45
45
|
@qfunc(external=True)
|
46
46
|
def qct_qst_type2(x: QArray[QBit], q: QBit) -> None:
|
47
|
-
|
48
|
-
Qmod Classiq-library function
|
47
|
+
"""
|
48
|
+
[Qmod Classiq-library function]
|
49
49
|
|
50
50
|
Applies the quantum discrete cosine (DCT) and sine (DST)
|
51
51
|
transform of type 2 to the qubit array `x` concatenated with `q`, with `q` being the MSB.
|
52
|
-
Corresponds to the matrix (with $n
|
52
|
+
Corresponds to the matrix (with $n\\equiv$`x.len`+1):
|
53
53
|
|
54
54
|
$$
|
55
|
-
|
55
|
+
\\left(
|
56
56
|
\begin{array}{c|c}
|
57
57
|
{\rm DCT}^{(2)}(2^{n-1}) & 0\\
|
58
|
-
|
58
|
+
\\hline
|
59
59
|
0 & -{\rm DST}^{(2)}(2^{n-1})
|
60
|
-
|
60
|
+
\\end{array}
|
61
61
|
\right)
|
62
62
|
$$
|
63
63
|
|
@@ -73,8 +73,8 @@ def qct_qst_type2(x: QArray[QBit], q: QBit) -> None:
|
|
73
73
|
|
74
74
|
@qfunc(external=True)
|
75
75
|
def qct_type2(x: QArray[QBit]) -> None:
|
76
|
-
|
77
|
-
Qmod Classiq-library function
|
76
|
+
"""
|
77
|
+
[Qmod Classiq-library function]
|
78
78
|
|
79
79
|
Applies the quantum discrete cosine (DCT)
|
80
80
|
transform of type 2, ${\rm DCT}^{(2)}$, to the qubit array `x`.
|
@@ -90,8 +90,8 @@ def qct_type2(x: QArray[QBit]) -> None:
|
|
90
90
|
|
91
91
|
@qfunc(external=True)
|
92
92
|
def qst_type2(x: QArray[QBit]) -> None:
|
93
|
-
|
94
|
-
Qmod Classiq-library function
|
93
|
+
"""
|
94
|
+
[Qmod Classiq-library function]
|
95
95
|
|
96
96
|
Applies the quantum discrete sine (DST)
|
97
97
|
transform of type 2, ${\rm DST}^{(2)}$, to the qubit array `x`.
|