-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Draft] Code prototypes #24
base: main
Are you sure you want to change the base?
Changes from all commits
0d11763
b4498ad
198c558
364aa5d
3b186bc
c114041
8026e9a
3fb1d3f
80caf9e
ce0bd97
02482fe
ab8b144
7b438ed
cf15e8f
2b5f0f9
1318da6
62cb5fc
1c95065
31ed823
19c154b
42c5957
4a7f9d5
53dbc9d
e9aef31
bd4d126
9f8fb2d
6abdf60
aaa7191
f0681e1
2544f90
d1b3793
cd814b8
d4a44d7
72c8762
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
|
||
from abc import ABC, abstractmethod | ||
from enum import Enum, auto | ||
from typing import Any, Callable, Generic, Optional, TypeVar | ||
from typing import Any, Generic, Iterable, TypeVar | ||
|
||
ArrayType = TypeVar("ArrayType") | ||
SequenceType = TypeVar("SequenceType") | ||
|
@@ -78,6 +78,16 @@ def sequence(self) -> SequenceType: | |
""" | ||
pass | ||
|
||
@abstractmethod | ||
def parameters(self) -> Iterable[Any]: | ||
""" | ||
Get the parameters from the backend as an iterable. | ||
|
||
Returns: | ||
Parameters as an iterable. | ||
""" | ||
pass | ||
|
||
@abstractmethod | ||
def set_parameters(self, params: dict[str, ParameterType]) -> None: | ||
""" | ||
|
@@ -90,9 +100,7 @@ def set_parameters(self, params: dict[str, ParameterType]) -> None: | |
@abstractmethod | ||
def run( | ||
self, | ||
*, | ||
values: dict[str, ArrayType] | None = None, | ||
callback: Optional[Callable] = None, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Docstring should reflect this change. |
||
**kwargs: Any, | ||
) -> RunResultType: | ||
""" | ||
|
@@ -111,10 +119,8 @@ def run( | |
@abstractmethod | ||
def sample( | ||
self, | ||
*, | ||
values: dict[str, ArrayType] | None = None, | ||
shots: int | None = None, | ||
callback: Optional[Callable] = None, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Docstring should reflect this change |
||
**kwargs: Any, | ||
) -> SampleResultType: | ||
""" | ||
|
@@ -134,10 +140,8 @@ def sample( | |
@abstractmethod | ||
def expectation( | ||
self, | ||
*, | ||
values: dict[str, ArrayType] | None = None, | ||
observable: Any | None = None, | ||
callback: Optional[Callable] = None, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Docstring to be updated. |
||
**kwargs: Any, | ||
) -> ExpectationResultType: | ||
""" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,17 @@ | ||
from __future__ import annotations | ||
|
||
from enum import Enum, auto | ||
from typing import Any | ||
from functools import reduce | ||
from typing import Any, Iterable, cast | ||
|
||
import numpy as np | ||
import qutip | ||
from pulser.parametrized.variable import VariableItem | ||
from pulser.sequence import Sequence | ||
from pulser.waveforms import ConstantWaveform | ||
|
||
from qadence2_platforms.backends.utils import InputType, Support | ||
|
||
DEFAULT_AMPLITUDE = 4 * np.pi | ||
DEFAULT_DETUNING = 10 * np.pi | ||
|
||
|
@@ -86,15 +90,17 @@ def x(sequence: Sequence, **_: Any) -> None: | |
|
||
def h( | ||
sequence: Sequence, | ||
duration: VariableItem | float = 1000.0, | ||
duration: VariableItem | float = np.pi, | ||
support_list: str = "global", | ||
**_: Any, | ||
) -> None: | ||
support_list = "GLOBAL" | ||
amplitude = sequence.device.channels["rydberg_global"].max_amp or DEFAULT_AMPLITUDE | ||
duration *= 1000 * 2 * np.pi / amplitude | ||
duration = int(duration) if duration > 16 else 16 | ||
detuning = np.pi | ||
|
||
sequence.enable_eom_mode("global", amp_on=amplitude, correct_phase_drift=True) | ||
sequence.enable_eom_mode( | ||
"global", amp_on=amplitude, correct_phase_drift=True, detuning_on=detuning | ||
) | ||
sequence.add_eom_pulse( | ||
"global", duration=int(duration), phase=np.pi / 2, post_phase_shift=np.pi | ||
) | ||
|
@@ -186,3 +192,186 @@ def local_pulse_core( | |
"dmm_0", | ||
"no-delay" if concurrent else "min-delay", | ||
) | ||
|
||
|
||
def parse_native_observables( | ||
num_qubits: int, observable: list[InputType] | InputType | ||
) -> list[qutip.Qobj]: | ||
return QuTiPObservablesParser.build(num_qubits, observable) | ||
|
||
|
||
class QuTiPObservablesParser: | ||
""" | ||
Convert InputType object to Qutip native quantum objects for simulation on QuTiP. | ||
|
||
It is intended to be used on expectation method of the Fresnel1 interface class. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. used on the expectation method |
||
InputType can be qadence2-expressions expression or any other module with the same | ||
methods. | ||
""" | ||
|
||
operators_mapping = { | ||
"I": qutip.qeye(2), | ||
"Z": qutip.sigmaz(), | ||
} | ||
|
||
@classmethod | ||
def _compl_tensor_op(cls, num_qubits: int, expr: InputType) -> qutip.Qobj: | ||
""" | ||
Use it for pure kron operations or for single input operator that needs to match | ||
a bigger Hilbert space, e.g. `expr = Z(0)` but the number of qubits is 3. | ||
|
||
Args: | ||
num_qubits (int): the number of qubits to create the qutip object to | ||
expr (InputType): the input expression. Any qadence2-expressions expression | ||
compatible object, with the same methods | ||
|
||
Returns: | ||
A QuTiP object with the Hilbert space compatible with `num_qubits` | ||
""" | ||
|
||
op: qutip.Qobj | ||
arg: InputType | ||
|
||
if expr.subspace: | ||
native_ops: list[qutip.Qobj] = [] | ||
support_set: set = expr.subspace.subspace | ||
|
||
for k in range(num_qubits): | ||
if k in support_set: | ||
sub_num_qubits: int = cast(Support, expr.args[1]).max_index | ||
arg = cast(InputType, expr.args[0]) | ||
op = cls._get_op(sub_num_qubits, arg) | ||
|
||
else: | ||
op = cls.operators_mapping["I"] | ||
|
||
native_ops.append(op) | ||
|
||
return qutip.tensor(*native_ops) | ||
|
||
arg = cast(InputType, expr.args[0]) | ||
op = cls._get_op(num_qubits, arg) | ||
return qutip.tensor(*([op] * num_qubits)) | ||
|
||
@classmethod | ||
def _arith_tensor_op(cls, num_qubits: int, expr: InputType) -> qutip.Qobj: | ||
""" | ||
Use it for the arithmetic operations addition and multiplication that need to | ||
have an extended Hilbert space compatible with `num_qubits`. | ||
|
||
Args: | ||
num_qubits (int): the number of qubits to create the qutip object to | ||
expr (InputType): the input expression. Any qadence2-expressions expression | ||
compatible object, with the same methods | ||
|
||
Returns: | ||
A QuTiP object with the Hilbert space compatible with `num_qubits` | ||
""" | ||
|
||
subspace: set = cast(Support, expr.subspace).subspace | ||
super_space: set = set(range(num_qubits)) | ||
|
||
if super_space.issuperset(subspace): | ||
native_ops: list[qutip.Qobj] = [] | ||
arg_subspace: set = cast(Support, expr.args[1]).subspace | ||
|
||
for k in range(num_qubits): | ||
if k in arg_subspace: | ||
sub_num_qubits: int = cast(int, expr.args[1]) | ||
arg: InputType = cast(InputType, expr.args[0]) | ||
op: qutip.Qobj = cls._get_op(sub_num_qubits, arg) | ||
native_ops.append(op) | ||
else: | ||
native_ops.append(cls.operators_mapping["I"]) | ||
|
||
return qutip.tensor(*native_ops) | ||
|
||
raise ValueError( | ||
f"subspace of the object ({subspace}) is bigger than the " | ||
f"sequence space ({super_space})" | ||
) | ||
|
||
@classmethod | ||
def _get_op(cls, num_qubits: int, op: InputType) -> qutip.Qobj | None: | ||
""" | ||
Convert an expression into a native QuTiP object. A simple symbol, | ||
a quantum operator, and an operation (addition, multiplication or | ||
kron tensor) are valid objects. | ||
|
||
Args: | ||
num_qubits (int): the number of qubits to create the qutip object to | ||
op (InputType): the input expression. Any qadence2-expressions expression | ||
compatible object, with the same methods | ||
|
||
Returns: | ||
A QuTiP object with the Hilbert space compatible with `num_qubits` | ||
""" | ||
|
||
if op.is_symbol is True: | ||
symbol: str = cast(str, op.args[0]) | ||
return cls.operators_mapping[symbol] | ||
|
||
op_arg: qutip.Qobj | ||
|
||
if op.is_quantum_operator is True: | ||
sub_num_qubits: int = cast(Support, op.args[1]).max_index + 1 | ||
|
||
if sub_num_qubits < num_qubits: | ||
op_arg = cls._compl_tensor_op(num_qubits, op) | ||
|
||
else: | ||
arg: InputType = cast(InputType, op.args[0]) | ||
op_arg = cls._get_op(num_qubits, arg) | ||
|
||
return op_arg | ||
|
||
ops: list[qutip.Qobj] = [] | ||
args: Iterable = cast(Iterable, op.args) | ||
|
||
if op.is_addition is True: | ||
|
||
for arg in args: | ||
ops.append(cls._arith_tensor_op(num_qubits, arg)) | ||
|
||
return reduce(lambda a, b: a + b, ops) | ||
|
||
if op.is_multiplication is True: | ||
|
||
for arg in args: | ||
ops.append(cls._arith_tensor_op(num_qubits, arg)) | ||
|
||
return reduce(lambda a, b: a * b, ops) | ||
|
||
if op.is_kronecker_product is True: | ||
return cls._compl_tensor_op(num_qubits, op) | ||
|
||
raise NotImplementedError( | ||
f"could not retrieve the expression {op} ({type(op)}) from the observables" | ||
) | ||
|
||
@classmethod | ||
def _iterate_over_obs(cls, n_qubits: int, op: Iterable | InputType) -> list[qutip.Qobj]: | ||
if isinstance(op, Iterable): | ||
return [cls._get_op(n_qubits, arg) for arg in op] | ||
|
||
args: Iterable = cast(Iterable, op.args) | ||
return [cls._get_op(n_qubits, cast(InputType, arg)) for arg in args] | ||
|
||
@classmethod | ||
def build(cls, num_qubits: int, observables: list[InputType] | InputType) -> list[qutip.Qobj]: | ||
""" | ||
Parses an input expression or list of expressions into a native QuTiP object. | ||
|
||
Args: | ||
num_qubits (int): the number of qubits to create the qutip object to | ||
observables (InputType): the input expression. Any qadence2-expressions | ||
expression compatible object, with the same methods | ||
|
||
Returns: | ||
A QuTiP object with the Hilbert space compatible with `num_qubits` | ||
|
||
Returns: | ||
""" | ||
if not isinstance(observables, list): | ||
return [cls._get_op(num_qubits, observables)] | ||
return cls._iterate_over_obs(num_qubits, observables) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this import + default type setting be pushed down into the pyqtorch subfolder? If you're using a different backend importing torch is not necessary.