Releases: qua-platform/quam
Release list
v0.6.0
Highlights
Python 3.13 and 3.14 support
QUAM now supports Python 3.13 and 3.14, and the CI matrix runs against Python 3.10 through 3.14. Support for Python 3.9 has been dropped — the minimum supported version is now 3.10.
qualang-tools is no longer a dependency
qualang-tools has been removed as a runtime dependency. The waveform and integration-weight helper functions QUAM relied on are now vendored directly in quam/components/_waveform_tools.py, and scipy is now a direct
dependency. This trims QUAM's dependency footprint with no change in behavior.
Shaped pulses are moving to quam-builder
The standard shaped-pulse classes — GaussianPulse, DragGaussianPulse, DragCosinePulse, FlatTopGaussianPulse, FlatTopBlackmanPulse, BlackmanIntegralPulse, FlatTopCosinePulse, and FlatTopTanhPulse — are now
deprecated in quam. Their implementations have moved to quam-builder. They still work for now but will be removed in v1.0, so update your imports to the quam-builder versions when you get a chance.
lo_mode on MW-FEM input ports
MWFEMAnalogInputPort gains an lo_mode field ("auto" or "always_on", defaulting to None). Leaving it as None omits the field from the generated QUA config, so the QUA default LO mode applies; setting it explicitly
overrides that default in the QUA config.
Package versions recorded in saved configs
Saved QUAM JSON configs now include a __package_versions__ entry recording the installed versions of all packages used to build the config. This makes a saved state easier to reproduce or debug later, since you know exactly what
produced it.
Fix: saving without ~/.qualibrate
Saving or loading a QUAM state no longer raises an error when the ~/.qualibrate directory doesn't exist.
Full Changelog
Added
- Added
__package_versions__to saved JSON configs, recording the installed versions of all packages used to build the config. - Added
lo_modefield toMWFEMAnalogInputPort(Optional[Literal["auto", "always_on"]], defaults toNone). WhenNone, the field is omitted from the QUA config so the QUA default applies; when set, the value is passed
through to the QUA config, allowing users to override the QOP default. - Added support for Python 3.13 and 3.14.
Changed
- Removed
qualang-toolsas a runtime dependency. Waveform and integration-weight functions are now vendored inquam/components/_waveform_tools.py. Addedscipyas a direct dependency.
Deprecated
DragGaussianPulse,DragCosinePulse,GaussianPulse,FlatTopGaussianPulse,FlatTopBlackmanPulse,BlackmanIntegralPulse,FlatTopCosinePulse,FlatTopTanhPulse— implementations moved to quam-builder. Will be removed
in v1.0.
Removed
- Removed support for Python 3.9.
Fixed
- Saving / loading no longer raises an error if
~/.qualibratedoesn't exist.
GitHub Pull Requests
- Fix saving/loading when ~/.qualibrate does not exist by @natangqm in #209
- Deprecate shaped pulses and remove qualang-tools dependency by @natangqm in #210
- Add Python 3.13 support and drop Python 3.9 by @natangqm in #214
- Add lo_mode to MWFEMAnalogInputPort by @natangqm in #211
- Record package versions in saved configs by @natangqm in #218
- Add Python 3.14 support by @natangqm in #219
Full Changelog: v0.5.0...v0.6.0
v0.5.0
Highlights
2 GS/s waveform support for OPX1000
Pulses now read the sampling rate directly from the channel's output port when generating waveforms, defaulting to 1 GHz for OPX+ and picking up 2 GS/s automatically for LF-FEM and MW-FEM. All the standard shaped pulses (GaussianPulse, DragGaussianPulse, DragCosinePulse, FlatTopGaussianPulse, FlatTopBlackmanPulse, BlackmanIntegralPulse, FlatTopCosinePulse, FlatTopTanhPulse) now generate the right number of samples for the hardware they're attached to. No code changes needed.
Port containers and new base classes
FEMPortsContainer and OPXPlusPortsContainer give you a single place to access all ports on a controller. Two matching base classes, BasicFEMQuam and BasicOPXPlusQuam, come with these containers already wired up so you don't have to set that up yourself. Error messages for bad port references in these containers are also improved.
Channels as quantum components
You can now use multiple inheritance to make a class that is both a Channel and a Qubit (or another quantum component). For example, class HybridChannel(SingleChannel, Qubit) is valid. This lets you attach macros and gate operations directly to a channel without needing a separate qubit wrapper, which is useful when your channels and qubits map one-to-one.
Voltage ramp methods
Channel.ramp(slope, duration) and Channel.ramp_to_zero(duration) are now available on all channels. They wrap the corresponding QUA commands.
Serialization now includes defaults
QUAM state files now include fields at their default values rather than omitting them. The saved JSON is more explicit and easier to read or hand-edit. You can control this per-project with quam.serialization.include_defaults. The config format has moved to v3, with an automatic migration on first use.
This is a breaking change. If your tooling expects the previous compact format, pass include_defaults=False to save() or set it in ~/.qualibrate/config.toml.
inferred_id on all QUAM components
QuamBase.inferred_id returns the component's explicit id field if set, or the attribute name or dict key it lives under in its parent. It's equivalent to self.parent.get_attr_name(self) but without having to remember that method exists.
skip_save field metadata
You can now mark a dataclass field with field(metadata={"skip_save": True}) to leave it out of JSON serialization while keeping it available at runtime. Useful for transient or derived fields that don't belong in the saved state.
Better error messages
Loading errors are a lot more useful now. If you have a typo in an attribute name, the error lists the valid options and suggests the closest match. Missing required attributes show what type was expected. Class-not-found errors from __class__ entries tell you which module and class failed to import. Every loading error from QuamRoot.load() also includes the path to the file being loaded.
Frequency inference errors on IQChannel and MWChannel now say which specific field is None or unresolved, rather than leaving you to figure it out.
Config version mismatches now produce one of two distinct errors: InvalidQuamConfigVersionError if your config is older than the package (run the migration), or GreaterThanSupportedQuamConfigVersionError if your package is older than the config (upgrade the package, or downgrade the config at ~/.qualibrate/config.toml). Previously these were hard to tell apart.
OctaveDownConverter calibration fix
OctaveDownConverter.apply_to_config now always writes IF_outputs to the QUA config when a channel is connected. Before this fix, omitting LO_frequency would produce a config missing the physical port wiring, and qm.calibrate_element() would fail silently with no useful error.
set_at_reference default changed
QuamBase.set_at_reference() now defaults to allow_non_reference=True. The old default (False) required an explicit keyword argument in almost every real use case. If you need the strict behavior, pass allow_non_reference=False explicitly.
This is a breaking change.
Channel-level port property deprecations
Several properties that exposed port configuration directly on channels (SingleChannel.opx_output_offset, filter_fir_taps, filter_iir_taps, DigitalOutputChannel.shareable, DigitalOutputChannel.inverted, and the I/Q offset variants) are deprecated. Set these on the Port object instead. Each deprecation warning now includes a code example. These properties will be removed in v0.6.0.
Full Changelog
Added
QuamBase.inferred_idproperty: returns the explicitidfield value if set, otherwise returns the attribute name or key under which the component is stored in its parent. Equivalent toself.parent.get_attr_name(self). Resolves #53.- Added
Channel.sampling_rateproperty that reads the sampling rate from the channel's output port, defaulting to 1 GHz. Overridden inSingleChannel,IQChannel, andMWChannelto read from their respective ports. - Added
Pulse._get_sampling_rate()method for pulses to retrieve the sampling rate of their attached channel. - Updated
GaussianPulse,DragGaussianPulse,DragCosinePulse,FlatTopGaussianPulse,FlatTopBlackmanPulse,BlackmanIntegralPulse,FlatTopCosinePulse, andFlatTopTanhPulseto use the channel's sampling rate when generating waveforms. This enables correct waveform generation at 2 GS/s (e.g., on OPX1000 with LF-FEM or MW-FEM). - Added
OPXPlusAnalogOutputPort.sampling_rateclass variable set to 1e9 Hz. - Added
Channel.ramp(slope, duration)method for playing linear voltage ramps. - Added
Channel.ramp_to_zero(duration)method for ramping channel output to zero. - Added
FEMPortsContainerandOPXPlusPortsContainerfor centralized port management. - Added
BasicFEMQuamandBasicOPXPlusQuamclasses with integrated port containers. - Added
quam.serialization.include_defaultsconfig field to control whether default values are included in serialized JSON (defaults toTrue). - Added v2→v3 config migration with automatic upgrade support for the new serialization settings.
- Added support for channels as quantum components via multiple inheritance, enabling channel-level macros and operations (e.g.,
class HybridChannel(SingleChannel, Qubit)). - Added
skip_savefield metadata support to exclude specific dataclass fields from serialization while keeping them accessible at runtime. Usefield(metadata={"skip_save": True})to mark fields that should not be saved to JSON.
Changed
- Improved error messages during QUAM state loading. Unexpected attributes now list all valid required and optional attributes and suggest close matches for typos. Missing required attributes now show the expected type and what was actually provided, with typo suggestions. Class-not-found errors from
__class__entries now report the failing module/class name and suggest close matches. All loading errors fromQuamRoot.load()now include the source file path. - Improved error messages in
OPXPlusPortsContainerandFEMPortsContainer: invalid port types now raiseValueErrorimmediately, and malformed port reference strings raiseValueErrorwith a descriptive message instead of a cryptic unpacking error. - Breaking Change:
set_at_reference()method now defaults toallow_non_reference=Trueinstead ofFalse. Users requiring error-by-default can explicitly passallow_non_reference=False. - Breaking Change: Default serialization behavior changed from excluding defaults to including them for more explicit state representation.
- Config version bumped from v2 to v3.
- Restructured config to use nested
serializationsubcategory underquamconfig section. - Priority chain for resolving
include_defaultssetting: (1) explicit parameter tosave()orto_dict(), (2)JSONSerialiserinstance value, (3) config settingquam.serialization.include_defaults, (4) fallback toTrue. - Enhanced deprecation warnings for channel-level port properties to include migration examples with code snippets.
- Expanded documentation for channel-ports.md with comprehensive examples of port container usage.
- Removed
versionfield from qua config template.
Deprecated
SingleChannel.opx_output_offset— usePort.offsetinstead. Will be removed in v0.6.0.SingleChannel.filter_fir_taps— usePort.feedforward_filterinstead. Will be removed in v0.6.0.SingleChannel.filter_iir_taps— usePort.feedback_filterinstead. Will be removed in v0.6.0.DigitalOutputChannel.shareable— usePort.shareableinstead. Will be removed in v0.6.0.DigitalOutputChannel.inverted— usePort.invertedinstead. Will be removed in v0.6.0.- All channel-level port offset properties (
opx_output_offset_I/Q,opx_input_offset, etc.) — usePort.offsetinstead. Will be removed in v0.6.0.
All deprecated properties now show migration guidance with code examples. See Port documentation for migration details.
Fixed
- Added
exponential_dc_gainandhigh_pass_filterfields toLFFEMAnalogOutputPortfor QOP 3.5+ filter support; fixed validation so the two fields can coexist andexponential_dc_gainalone conflicts withfeedback_filter. - Clarified in documentation how kwargs and attributes differ for method macros: kwargs are per-call overrides, attributes are persistent calibrated values that are saved with the QUAM state.
- Improved error messages for inferred frequency properties (
inferred_RF_frequency,inferred_intermediate_frequency,inferred_LO_frequency) inIQChannelandMWChannel: errors now clearly identify the specific field and whether it isNoneor an unresolved reference. - Fixed conf...
v0.5.0a3
What's Changed
- fix: mock get_quam_config in tests to prevent loading system config by @nulinspiratie in #171
- feat: Add port containers and deprecate channel-level port properties by @nulinspiratie in #172
- Add include_defaults_in_serialization config option by @nulinspiratie in #168
- Change: JSONSerialiser to skip hidden directories during loading by @nulinspiratie in #154
- Fix: set_at_reference for list index references by @nulinspiratie in #163
- Change allow_non_reference default to True by @nulinspiratie in #175
- Fix config version mismatch error handling by @nulinspiratie in #179
- Fix project-specific config not being recognized in QUAM by @nulinspiratie in #178
- Fix: Remove "version" from config to align with latest QUA by @nulinspiratie in #180
- Fix include_defaults inconsistency by @nulinspiratie in #181
- Add field metadata skip_save for serialization exclusion by @nulinspiratie in #182
- Fix Method Macros documentation example by @nulinspiratie in #186
- Refactor/pulses with smooth time by @paulQM in #173
Full Changelog: v0.5.0a2...v0.5.0a3
v0.5.0a2
What's Changed
- fix(#166): pass kwargs to super().to_dict() in QuamDict by @nulinspiratie in #169
Full Changelog: v0.5.0a1...v0.5.0a2
v0.5.0a1
This is a prerelease version for the upcoming v0.5.0.
Currently there are no breaking changes, though minor breaking changes are planned related to serialization
What's Changed
- Fix: Serialisation of QUA Config failing with digital marker by @nulinspiratie in #159
- add documentation on method macros by @nulinspiratie in #161
- Update outputPulseParameters to timeTaggingParameters by @matteo-at-ionq in #144
- Adding 4 types of pulses: FlatTopBlackmanPulse, BlackmanIntegralPulse, FlatTopTanhPulse, FlatTopCosinePulse by @giladqm in #158
- add cosine bipolar pulse by @paulQM in #165
New Contributors
- @matteo-at-ionq made their first contribution in #144
- @giladqm made their first contribution in #158
- @paulQM made their first contribution in #165
Full Changelog: v0.4.2...v0.5.0a1
v0.4.2
This release introduces several new fixes and small additions. The main motivation for releasing this now is that there is some incompatibility with the newly released QUA v1.2.3, and this has been resolved in this release of QUAM.
Added
- Added
durationandfidelityas optional parameters toQuamMacro
Fixed
- Fixed
QuamBase.get_attr_name()failing when an attribute was a reference. - Fixed arbitrary waveforms on IQ/MW channels not converting Q component to list consistently
- Changed default
time_of_flightfrom 24 ns to 140 ns to fix error on newer qm-qua versions- 140 ns seems like a reasonable default for most channels
- Fixed type annotation issue in
QuamRoot.load()method causing linting errors for subclasses likeBasicQuam - Fixed
Pulse.waveform_functionreturn type being wrong - Compatibility with qm-qua 1.2.3: Updated channel measure command to explicitly pass
adc_stream - Removed all private imports from
qm.qua - Fixed float-to-int coercion during component instantiation
Github Pull requests
- Fix: Consistent conversion of pulse waveforms to list by @nulinspiratie in #138
- Feat: Add duration and fidelity properties to QuamMacro by @nulinspiratie in #139
- Fix type annotation in QuamRoot.load() method by @nulinspiratie in #146
- Fix: time_of_flight default from 24 ns to 140 ns(LF) and 280 ns (MW) by @nulinspiratie in #145
- Recast duration as a property of QuamMacro instead of optional field by @arthurostrauss in #147
- Update min qm-qua version ≥ 1.22 and add prerelease allow by @nulinspiratie in #150
- Fix: Broken MWChannel.Upconverter_frequency by @nulinspiratie in #148
- Update measure calls for QM-QUA 1.2.2 compatibility by @nulinspiratie in #151
- Fix: Change waveform function return type by @nulinspiratie in #149
- Fix/qua types pulse amp for qm-qua 1.2.3 by @nulinspiratie in #152
- Docs/quam capitalization by @nulinspiratie in #153
- Ticket #14: Complete _PulseAmp removal and type hint updates by @nulinspiratie in #155
- Fit: Float-to-int coercion during component instantiation by @nulinspiratie in #156
- Fix: typo in qubit error by @nulinspiratie in #157
New Contributors
- @arthurostrauss made their first contribution in #147
Full Changelog: v0.4.1...v0.4.2
v0.4.1
What's Changed
- Fix: get_attr_name fails with references by @nulinspiratie in #134
Full Changelog: v0.4.0...v0.4.1
v0.4.0
This release introduces several major new features and changes
QuantumComponents (qubits and qubit pairs) and gate-level operations
This PR introduces the QuantumComponent class, which is the base class for qubits and qubit pairs.
The subclasses Qubit and QubitPair are used to represent qubits and qubit pairs, respectively.
These classes can contain implementations for gate-level operations, which we call macros.
This PR additionally introduces several components for gate-level operations, see the documentation for more details.
QUAM Config
An optional configuration file has been added to QUAM.
This file can be created by calling quam config --state-path {state_path}, where {state_path} is the path to the quam state file.
This path will be used as the default path when calling quam.save() without an explicit path.
The config file will be saved to ~/.qualibrate/config.toml, to be shared with QUAlibrate.
Improved serialisation
The JSONSerialiser has been upgraded in several ways:
- Several parameters
content_mapping,include_defaults, andstate_pathhave been added to the JSONSerialiser.
This allows these parameters to be set beforehand - The config state path is used as a default path.
- The content_mapping structure has been changed. Instead of the keys being the file names, the keys are the component names and the values are the filenames.
- Old method:
content_mapping={"wiring.json": ["wiring", "network"]} - Equivalent new method:
content_mapping={"wiring": "wiring.json", "network": "wiring.json"} - The old method is still valid, though raises a deprecation warning
- Old method:
Change all instances of QuAM to QUAM
Given the slightly confusing abbreviation QuAM, we decided to change it to QUAM.
This was done in both the code and the documentation.
Note that the class BasicQuAM was renamed to BasicQUAM, with a deprecation warning if the old name is used.
Full list of changes
Added
- Add support for qubit-level components and gate implementations
- Add
QuantumComponentand subclassesQubitandQubitPair.
These introduce qubit-level objects to interfacce with circuit-level languages. - Add qubit-level macros (
BaseMacro,QuamMacro,QubitMacro,QubitPairMacro,PulseMacro) that implementations for gate operations. - Add
OperationsRegistryto register gate-level operations. - See documentation for details.
- Add
- Add
Channel.reset_if_phase()which matches the QUA commandreset_if_phase(element) - Add the QUAM config, which can be called from the terminal command
quam config.
This enables adding a default QUAM state path - Add config entry
raise_error_missing_referenceto raise error if a reference is missing, rather than a warning - Add the following properties to the
JSONSerialiser:content_mapping,include_defaults, andstate_path. - Add
quam.__version__
Changed
JSONSerialiser.content_mappingstructure has been changed from dict{filename.json: [list of components]}to{component: filname.json}. The previous format is still supported although this raises a warning- Remove implicit support for content mapping structure `{filename.json: component} (note the lack of a list). This used to be supported even though it was never mentioned.
- The default state path for a
QuamRootis now retrieved from the environment variableQUAM_STATE_PATH, and if this doesn't exist, from the quam config. - Add
__class__to serialisation (to_dictmethod), even if the target class matches the expected type - If a specific class can't be imported during instantiation, a warning will be raised and it will load the default class (specified by the type hint) instead
- QuAM has been renamed to QUAM
- Some root classes have been renamed to CamelCase (e.g.
BasicQuAM->BasicQuam)
- Some root classes have been renamed to CamelCase (e.g.
- Deprecate Quam component method
get_unreferenced_valuein favor of namingget_raw_value
GitHub merged pull requests
- QuAM config by @maxim-v4s in #101
- feat: Add gates to QuAM by @nulinspiratie in #43
- Feat/upgraded json serialiser by @nulinspiratie in #123
- Feat: Add Channel.reset_if_phase() by @nulinspiratie in #125
- Change/serialise default classes by @nulinspiratie in #124
- Hotfix: Ensure new content_mapping is not mistaken for old by @nulinspiratie in #126
- Change all instance QuAM -> QUAM by @nulinspiratie in #128
- Change: Deprecate
get_unreferenced_valuein favor ofget_raw_valueby @nulinspiratie in #130 - fix the octave component to work with the latest QOP versions by @TheoLaudatQM in #129
- Feat: Add version by @nulinspiratie in #132
- Feat/Add config entry raise_error_missing_reference by @maxim-v4s in #131
New Contributors
- @TheoLaudatQM made their first contribution in #129
Full Changelog: v0.3.10...v0.4.0
v0.3.10
The primary change is to add support for qm-qua 1.2.2
Changed
QuamBase.generate_config()now returns aDictQuaConfiginstead of aDict[str, Any]
This provides type hints for the generated config.- Add
MWFEMAnalogInputPort.gain_dbto allow for setting the gain of the analog input port - Removed warning when multiple QuamRoot objects are instantiated
- Added
SingleChannel.exponential_filterin compliance with QOP 3.3. - Deprecated
SingleChannel.filter_iir_tapsin favor ofSingleChannel.exponential_filterfor the LF-FEM
Fixed
- Fixed
QuamBase.iterate_components()argskip_elemshaving the wrongtype - Deprecated
threadargument in favor ofcoreinChannelwhen qm >= 1.2.2 - Fixed
MWFEMAnalogOutputPort.upconvertersnot being converted to a dict in the config - Fixed QUA type import errors due to qm-qua 1.2.2 changing type locations
GitHub merged pull requests
- Fix: Serialization issue in MWFEMAnalogOutputPort.upconverters by @nulinspiratie in #109
- Fix: Replace thread with core for qua >= 1.2.2 by @nulinspiratie in #111
- Fix: QuamBase.iterate_components skip_elements wrong type by @nulinspiratie in #114
- Fix wrong link in docs by @shumpohl in #116
- Feat: Use DictQuaCofnig for QuamBase.generate_config by @nulinspiratie in #115
- Feat: Add MWFEMAnalogInputPort.gain_db by @nulinspiratie in #117
- Change: Remove warning when multiple QuamRoot initialized by @nulinspiratie in #119
- Change: new filters that comply with QOP 3.3 by @nulinspiratie in #120
- Fix: Use new QUA variable types by @nulinspiratie in #112
- CI: update publish pypi action by @maxim-v4s in #122
New Contributors
Full Changelog: v0.3.9...v0.3.10
v0.3.9
Added
- Added
QuamBase.set_at_referenceto set a value at a reference - Added
string_reference.get_parent_referenceto get the parent reference of a string reference - Added
FrequencyConverter.LO_frequencysetter which updates the local oscillator frequency - Added optional
relative_pathto methodQuamBase.get_reference() - Added support for multiple QuamRoot objects
- Added
QuamBase.get_root()to get the QuamRoot object of a component
Changed
Pulse.integration_weightsnow defaults to#./default_integration_weights, which returns [(1, pulse.length)]
Fixed
- Fixed issues with parameters being references in a QuamRoot object
- Fixed
MWFEMAnalogOutputPort.upconvertersnot having the correct type - A warning is raised if a new
QuamRootinstance is created while a previous one exists. - Fixed
MWFEMAnalogOutputPort.upconvertersnot being converted to a dict in the config - Fixed: Improve error message when instantiating: list or dict expected but a different type is provided
MWChannel.upconverter_frequencyandMWChannel.LO_frequencynow correctly return the upconverter frequency from theopx_outputport, supporting bothupconverter_frequencyandupconvertersspecifications.
GitHub Merged Pull Requests
- Feat: Add default integration weights by @nulinspiratie in #87
- Feat/Add
QuamBase.set_at_referenceby @nulinspiratie in #88 - Fix: Referencing issues in Quam Root by @nulinspiratie in #93
- Fix: MWFEMAnalogOutputPort.upconverters type by @nulinspiratie in #94
- Add setter to LO_frequency property in FrequencyConverter. by @deanpoulos in #95
- Feat: Add
relative_pathtoQuamBase.get_referencesby @nulinspiratie in #100 - Fix: Add error message when multiple QuamRoot objects are instantiated by @nulinspiratie in #103
- Fix: Update typing for channel output/input ports by @nulinspiratie in #102
- Feat: Support multiple QuamRoot objects by @nulinspiratie in #104
- Fix: Convert MWFEMAnalogOutputPort.upconverters to dict when generati… by @nulinspiratie in #106
- Fix: Improve error message when wrongly instantiating a list or dict by @nulinspiratie in #108
- Fix: Improve upconverter frequency retrieval in MWChannel by @nulinspiratie in #107
Full Changelog: v0.3.8...v0.3.9