Skip to content

Commit

Permalink
Merge branch 'main' into fix/fem-filter
Browse files Browse the repository at this point in the history
  • Loading branch information
nulinspiratie authored Nov 19, 2024
2 parents d360f34 + 0f231d4 commit d619f12
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 21 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
### Fixed
- Change location of port feedforward and feedback filters in config


## [0.3.7]
### 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
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,20 @@ from qm import qua
# Create a root-level QuAM instance
machine = BasicQuAM()

# Add an OPX output channel
channel = SingleChannel(opx_output=("con1", 1))
machine.channels["output"] = channel
# Add a qubit connected to an OPX output channel
qubit = SingleChannel(opx_output=("con1", 1))
machine.channels["qubit"] = qubit

# Add a Gaussian pulse to the channel
channel.operations["gaussian"] = pulses.GaussianPulse(
qubit.operations["gaussian"] = pulses.GaussianPulse(
length=100, # Pulse length in ns
amplitude=0.5, # Peak amplitude of Gaussian pulse
sigma=20, # Standard deviation of Guassian pulse
)

# Play the Gaussian pulse on the channel within a QUA program
with qua.program() as prog:
channel.play("gaussian")
qubit.play("gaussian")

# Generate the QUA configuration from QuAM
qua_configuration = machine.generate_config()
Expand Down
3 changes: 2 additions & 1 deletion docs/components/custom-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ First create the following folder structure
```
my-quam
├── my_quam
│ └── __init__.py
│ └── components
│ └── __init__.py
└── pyproject.toml
```
The file `__init__.py` should be empty, and `pyproject.toml` should have the following contents:
The `__init__.py` files should be empty, and `pyproject.toml` should have the following contents:

/// details | pyproject.toml
```toml
Expand Down
4 changes: 2 additions & 2 deletions docs/demonstration.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ for idx in range(num_qubits):
# Add resonator channel
transmon.resonator = InOutIQChannel(
id=idx,
opx_output_I=("con1", 3 * idx + 1),
opx_output_Q=("con1", 3 * idx + 2),
opx_output_I=("con1", 1),
opx_output_Q=("con1", 2),
opx_input_I=("con1", 1),
opx_input_Q=("con1", 2,),
frequency_converter_up=FrequencyConverter(
Expand Down
21 changes: 10 additions & 11 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,29 @@ QuAM is not just a tool but a gateway to streamlined and efficient quantum compu
- **State Management:** Effortlessly save and load your QuAM state, enabling consistent results and reproducibility in experiments.

```python
from quam.components import *
from quam.components import BasicQuAM, SingleChannel, pulses
from qm import qua

# Create a root-level QuAM instance
machine = BasicQuAM()

# Add an OPX output channel
channel = SingleChannel(opx_output=("con1", 1))
machine.channels["output"] = channel
# Add a qubit connected to an OPX output channel
qubit = SingleChannel(opx_output=("con1", 1))
machine.channels["qubit"] = qubit

# Add a Gaussian pulse to the channel
channel.operations["gaussian"] = pulses.GaussianPulse(
length=100, amplitude=0.5, sigma=20
qubit.operations["gaussian"] = pulses.GaussianPulse(
length=100, # Pulse length in ns
amplitude=0.5, # Peak amplitude of Gaussian pulse
sigma=20, # Standard deviation of Guassian pulse
)

# Play the Gaussian pulse within a QUA program
# Play the Gaussian pulse on the channel within a QUA program
with qua.program() as prog:
channel.play("gaussian")
qubit.play("gaussian")

# Generate the QUA configuration from QuAM
qua_configuration = machine.generate_config()

# Save QuAM to a JSON file
machine.save("state.json")
```
</div>

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "quam"
version = "0.3.6"
version = "0.3.7"
#dynamic = ["version"]
description = "Quantum Abstract Machine (QuAM) facilitates development of abstraction layers in experiments."
authors = [
Expand Down
48 changes: 47 additions & 1 deletion quam/components/pulses.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from abc import ABC, abstractmethod
from collections.abc import Iterable
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 All @@ -12,6 +13,7 @@
"Pulse",
"BaseReadoutPulse",
"ReadoutPulse",
"WaveformPulse",
"DragGaussianPulse",
"DragCosinePulse",
"DragPulse",
Expand Down Expand Up @@ -398,6 +400,50 @@ 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
if not isinstance(self.waveform_I, Iterable):
return None
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
47 changes: 47 additions & 0 deletions tests/components/pulses/test_waveform_pulse.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from collections.abc import Iterable
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])
)
assert pulse.length


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],
}


def test_waveform_pulse_length_error():
with pytest.raises(AttributeError):
pulse = WaveformPulse(waveform_I=[1, 2, 3], length=11)

pulse = WaveformPulse(waveform_I=[1, 2, 3])
with pytest.raises(AttributeError):
pulse.length = 11

0 comments on commit d619f12

Please sign in to comment.