Skip to content

Commit

Permalink
Feat: Add WaveformPulse
Browse files Browse the repository at this point in the history
  • Loading branch information
nulinspiratie committed Oct 25, 2024
1 parent 5f90665 commit da81c48
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 1 deletion.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## [Unreleased]
### Added
- Added `WaveformPulse` to allow for pre-defined waveforms.


## [0.3.6]
### Changed
- Modified `MWChannel` to also have `RF_frequency` and `LO_frequency` to match the signature of `IQChannel`.
Expand Down
44 changes: 43 additions & 1 deletion quam/components/pulses.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from abc import ABC, abstractmethod
import numbers
import warnings
from typing import Any, ClassVar, Dict, List, Union, Tuple
from typing import Any, ClassVar, Dict, List, Optional, Union, Tuple
import numpy as np

from quam.core import QuamComponent, quam_dataclass
Expand Down Expand Up @@ -398,6 +398,48 @@ def integration_weights_function(self) -> List[Tuple[Union[complex, float], int]
}


@quam_dataclass
class WaveformPulse(Pulse):
"""Pulse that uses a pre-defined waveform, as opposed to a function.
For a single channel, only `waveform_I` is required.
For an IQ channel, both `waveform_I` and `waveform_Q` are required.
The length of the pulse is derived from the length of `waveform_I`.
Args:
waveform_I (list[float]): The in-phase waveform.
waveform_Q (list[float], optional): The quadrature waveform.
"""

waveform_I: List[float] # pyright: ignore
waveform_Q: Optional[List[float]] = None
# Length is derived from the waveform_I length, but still needs to be declared
# to satisfy the dataclass, but we'll override its behavior
length: Optional[int] = None # pyright: ignore

@property
def length(self): # noqa: 811
return len(self.waveform_I)

@length.setter
def length(self, length: Optional[int]):
if length is not None and not isinstance(length, property):
raise AttributeError(f"length is not writable with value {length}")

def waveform_function(self):
if self.waveform_Q is None:
return np.array(self.waveform_I)
return np.array(self.waveform_I) + 1.0j * np.array(self.waveform_Q)

def to_dict(
self, follow_references: bool = False, include_defaults: bool = False
) -> Dict[str, Any]:
d = super().to_dict(follow_references, include_defaults)
d.pop("length")
return d


@quam_dataclass
class DragGaussianPulse(Pulse):
"""Gaussian-based DRAG pulse that compensate for the leakage and AC stark shift.
Expand Down
36 changes: 36 additions & 0 deletions tests/components/pulses/test_waveform_pulse.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import numpy as np
import pytest
from quam.components.pulses import WaveformPulse


def test_waveform_pulse_length():
pulse = WaveformPulse(waveform_I=[1, 2, 3])
assert pulse.length == 3

pulse.waveform_I = [1, 2, 3, 4]

with pytest.raises(AttributeError):
pulse.length = 5

assert pulse.length == 4


def test_waveform_pulse_IQ():
pulse = WaveformPulse(waveform_I=[1, 2, 3], waveform_Q=[4, 5, 6])
assert np.all(
pulse.waveform_function() == np.array([1, 2, 3]) + 1.0j * np.array([4, 5, 6])
)


def test_waveform_pulse_IQ_mismatch():
pulse = WaveformPulse(waveform_I=[1, 2, 3], waveform_Q=[4, 5])
with pytest.raises(ValueError):
pulse.waveform_function()


def test_waveform_pulse_to_dict():
pulse = WaveformPulse(waveform_I=[1, 2, 3], waveform_Q=[4, 5, 6])
assert pulse.to_dict() == {
"waveform_I": [1, 2, 3],
"waveform_Q": [4, 5, 6],
}

2 comments on commit da81c48

@HiroQM
Copy link

@HiroQM HiroQM commented on da81c48 Oct 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nulinspiratie
At line. 23, maybe need to add "WaveformPulse",?
from quam.components.pulses import WaveformPulse didn't find the added class on my env. Maybe it is editor dependent.

@nulinspiratie
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, thanks!

Please sign in to comment.