Skip to content

Conversation

@mnfarooqi
Copy link
Collaborator

Description

Introduce opaque pointers (QDMI_Pulse_Parameter, QDMI_Pulse_Waveform and QDMI_Pulse_Implementation), as well as the corresponding enums and query functions, to represent and query pulses.

Fixes #173

Checklist:

  • The pull request only contains commits that are related to it.
  • I have added appropriate tests and documentation.
  • I have made sure all CI jobs on GitHub pass.
  • The pull request introduces no new warnings and follows the project's style guidelines.

@github-actions
Copy link
Contributor

PR Preview Action v1.6.1

🚀 View preview at
https://Munich-Quantum-Software-Stack.github.io/QDMI/pr-preview/pr-182/

Built to branch gh-pages at 2025-06-30 09:21 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@codecov
Copy link

codecov bot commented Jun 30, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Impacted file tree graph

@@            Coverage Diff            @@
##           develop    #182     +/-   ##
=========================================
- Coverage     90.8%   90.6%   -0.2%     
=========================================
  Files            7       7             
  Lines         1072    1071      -1     
  Branches       222     221      -1     
=========================================
- Hits           974     971      -3     
- Misses          98     100      +2     
Flag Coverage Δ
cpp 90.6% <ø> (-0.2%) ⬇️

see 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@mnfarooqi mnfarooqi marked this pull request as ready for review July 3, 2025 22:40
@mnfarooqi mnfarooqi self-assigned this Jul 3, 2025
@mnfarooqi mnfarooqi added the feature New feature or feature request label Jul 3, 2025
@mnfarooqi mnfarooqi added this to the v1.2.0 milestone Jul 3, 2025
@mnfarooqi mnfarooqi requested review from burgholzer and ystade July 3, 2025 22:41
@mnfarooqi
Copy link
Collaborator Author

Hi @donsano33 and @amitQC,
Could you please check and confirm whether this is what we need to represent a pulse?

Copy link
Collaborator

@ystade ystade left a comment

Choose a reason for hiding this comment

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

Thanks @mnfarooqi for this first proposal for the pulse level support. Your implementation is already very clean and follows the look and feel of QDMI.

I went though it skipping the FoMaC implementation for now because I think, we should first dicuss some high level topics first. In a later iteration, we can also consolidate FoMaC and the testing.

As indicated in one of the comments, I have one major point of concern: With QDMI_Pulse_Waveform we are defining our own standard for representing pulses. Honestly, I am not an expert in pulse level quantum computing. However, I strongly argue that introducing a new calibration grammar should be well-considered. This concern accumulates in the following two points:

  1. The current proposal allows to query the waveform in the OpenPulse format. Why do I then even need to have another representation as OpenPulse seems to be supported. I suggest that we decide to use one representation and not multiple to keep the interface minimalistic and do not impose additional implemenation efforts on device implementers.
  2. The current (QDMI) waveform specification relies on an unspecified string. First of all, I would argue that a string to represent a term is not the best choice. However, what I find more important to fix the semantics of the pulse representation. Right now, a compiler cannot expect anything from the returned string. For example, consider, the OpenPulse Grammar. If we do not want to rely on open pulse (for whatever reason), we need a semantic for our pulse representation as well.

@mnfarooqi sorry, questioning the pulse representation. This has nothing to do with the actual code. Your code is perfectly well-strucutred, well-documented, and clean. Nevertheless, we should consolidate what we actually want here.

Comment on lines +26 to +27
#include "c_qdmi/types.h"

Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
#include "c_qdmi/types.h"

Iirc, this should not be necessary since the header above already re-exposes the types header.

Comment on lines +853 to +854
struct C_QDMI_Pulse_Waveform_impl_t {};
struct C_QDMI_Pulse_Implementation_impl_t {};
Copy link
Collaborator

Choose a reason for hiding this comment

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

The types, i.e., the structure names ending with _t should not needed to be redefined by the device, actually, this should lead to a compiler error because of redefinition. Am I overseeing something?

C_QDMI_Device_Session session, C_QDMI_Pulse_Parameter param,
const QDMI_Pulse_Parameter_Property prop, const size_t size, void *value,
size_t *size_ret) {

Copy link
Collaborator

Choose a reason for hiding this comment

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

Even if no property is supported, the function is expected to validate its arguments and return QDMI_ERROR_INVALIDARGUMENT if necessary. That has higher priority than QDMI_ERROR_NOTSUPPORTED.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This comment also applies to the two subsequent functions.

Comment on lines +27 to +28
#include "cxx_qdmi/types.h"
#include "qdmi/constants.h"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Those should all be re-exported by the device header and not needed here. In particular, including headers from the directory with the prefixed headers and from the main include directory does not seem clean to me.

Suggested change
#include "cxx_qdmi/types.h"
#include "qdmi/constants.h"

};
} // namespace

const CXX_QDMI_Pulse_Parameter_impl_d AMPLITUDE{"A"};
Copy link
Collaborator

Choose a reason for hiding this comment

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

If I understand that correctly, than only "A" is transferred as the name of the parameter through QDMI. Is that standardized anywhere? If not, I propose to use a more comprehensive name, i.e., "amplitude". Similar reasoning applies to the next one.

Comment on lines +489 to +493
* @brief Enum of the pulse parameter properties that can be queried via @ref
* QDMI_device_session_query_pulse_parameter_property as part of the @ref
* device_interface "device interface" and via @ref
* QDMI_device_query_pulse_parameter_property as part of the @ref
* client_interface "client interface".
Copy link
Collaborator

Choose a reason for hiding this comment

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

I hope we did not vialoate this in the rest of QDMI, but a @brief description is a one-liner. You can move additional information into @details.

Comment on lines +517 to +523
/**
* @brief `bool` Whether the pulse parameter is mutable.
* @details If this property is set to true, the pulse parameter can be
* modified by the user. If it is set to false, the pulse parameter is fixed
* and cannot be changed.
*/
QDMI_PULSE_PARAMETER_PROPERTY_MUTABLE = 3,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I do not understand the purpose of immutable parameters, those are constants. I suggest that constants can already occur as constants in the formula.

Comment on lines +551 to +555
* @brief Enum of the pulse waveform properties that can be queried via @ref
* QDMI_device_session_query_pulse_waveform_property as part of the @ref
* device_interface "device interface" and via @ref
* QDMI_device_query_pulse_waveform_property as part of the @ref
* client_interface "client interface".
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same as above, should be a one-liner.

Comment on lines +564 to +568
QDMI_PULSE_WAVEFORM_PROPERTY_NAME = 0,
/**
* @brief `char*` (string) The formula used to generate the pulse waveform.
*/
QDMI_PULSE_WAVEFORM_PROPERTY_FORMULA = 1,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ok, this will be a bigger comment. I'll put that in my general comment such that we can discuss and consolidate this in the main thread of this PR.

QDMI_PULSE_IMPLEMENTATION_PROPERTY_OPENPULSE = 0,
/**
* @brief `QDMI_Pulse_Waveform` (@ref QDMI_Pulse_Waveform) The pulse
* implementation as pulse waveform.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
* implementation as pulse waveform.
* implementation as a pulse waveform.

?

@mnfarooqi
Copy link
Collaborator Author

Thank you @ystade for the detailed review and for moving the discussion forward. The current implementation is based on our initial discussion and is not final, but is intended to facilitate the discussion. I will address the inline comments once we have agreed on the basics. I am also not an expert on pulses, which is why I tagged @donsano33 and @amitQC. Hopefully, they will add their review soon.

The pulse representation here is supposed to serve two purposes: one is to query the generic pulse shapes supported by the device, along with the constraints and pulses for specific operations. The second purpose is to set or overwrite existing pulses for a specific operation.

  1. The current proposal allows to query the waveform in the OpenPulse format. Why do I then even need to have another representation as OpenPulse seems to be supported. I suggest that we decide to use one representation and not multiple to keep the interface minimalistic and do not impose additional implemenation efforts on device implementers.

The OpenPulse Grammar can be used to query and set pulses specific to an operation, but as far as I know, it cannot be used to query generic pulse waveforms, i.e. the pulse formula and the limits of the parameters in that formula.

  1. The current (QDMI) waveform specification relies on an unspecified string. First of all, I would argue that a string to represent a term is not the best choice. However, what I find more important to fix the semantics of the pulse representation. Right now, a compiler cannot expect anything from the returned string. For example, consider, the OpenPulse Grammar. If we do not want to rely on open pulse (for whatever reason), we need a semantic for our pulse representation as well.

As far as I understand, the queried generic pulse waveform is to be consumed manually by a human, not by a compiler. The only automation tool I can think of at the moment would be one that consumes this information and generates human-readable documentation on the pulses supported by a device, along with the parameters and the limits on those parameters. I searched for a standard notation that could be specified in the QDMI specification, but I could not find one. I am thinking of proposing infix notation, which could be used by a tool such as str2sym from MATLAB. If you have any other ideas, please share them.

@amitQC
Copy link

amitQC commented Jul 7, 2025

Thanks @mnfarooqi and @ystade for starting the conversation. The idea of waveform, or in other words, envelope, is to query the existing waveforms, e.g., Gaussian, sine, etc. (see here: https://openqasm.com/language/openpulse.html#waveforms). These are generally an array of complex values. We also wanted to provide a mathematical description of these waveforms or envelopes, where a user can change the parameter values of an envelope to generate a pulse. For example, a sine envelope is of the form: $\Sigma_{n} a_{n} sin(n *pi *t/T)$, where one just needs to define values of $a_{n}$ to define a pulse.

From the compiler's perspective, in my opinion, the only thing that matters is the corresponding operation to a pulse. For example, if a NOT gate has a sine waveform, you can take the performance of this pulse into account in FoMaC, but not the underlying pulse itself.

@ystade
Copy link
Collaborator

ystade commented Jul 7, 2025

Would you mind explaining the following point in your answer a bit further? I do not think that I understand this correctly. What information exactly matters to a compiler? How do I get from a random (I only write random because right now the string is not further specified and could in principle be anything) string to the performance?

you can take the performance of this pulse into account in FoMaC, but not the underlying pulse itself.

@amitQC
Copy link

amitQC commented Jul 9, 2025

Would you mind explaining the following point in your answer a bit further? I do not think that I understand this correctly. What information exactly matters to a compiler? How do I get from a random (I only write random because right now the string is not further specified and could in principle be anything) string to the performance?

you can take the performance of this pulse into account in FoMaC, but not the underlying pulse itself.

Sure! Depending on the level of compilation, for a top-level, the underlying pulse implementing a gate U does not matter; however, one can consider pulse performance, such as fidelity, time, etc., into consideration. I think that there should be a performance metric corresponding to each gate (i.e., underlying pulse) that can be queried by the compiler. For a very low-level compiler, one can also consider compiling only at the level of pulses, i.e., convert every gate U object into pulses and then try compilation. This might help to further compress the resulting circuit (however, there are no general guarantees; this is hardware-specific). Does this answer your question?

@mnfarooqi
Copy link
Collaborator Author

Would you mind explaining the following point in your answer a bit further? I do not think that I understand this correctly. What information exactly matters to a compiler? How do I get from a random (I only write random because right now the string is not further specified and could in principle be anything) string to the performance?

you can take the performance of this pulse into account in FoMaC, but not the underlying pulse itself.

Sure! Depending on the level of compilation, for a top-level, the underlying pulse implementing a gate U does not matter; however, one can consider pulse performance, such as fidelity, time, etc., into consideration. I think that there should be a performance metric corresponding to each gate (i.e., underlying pulse) that can be queried by the compiler. For a very low-level compiler, one can also consider compiling only at the level of pulses, i.e., convert every gate U object into pulses and then try compilation. This might help to further compress the resulting circuit (however, there are no general guarantees; this is hardware-specific). Does this answer your question?

Does that mean that the compiler will not use the pulse shape formula (i.e. the mathematical description of these waveforms or envelopes) directly? If not, where/who can use it?

@amitQC
Copy link

amitQC commented Jul 10, 2025

Would you mind explaining the following point in your answer a bit further? I do not think that I understand this correctly. What information exactly matters to a compiler? How do I get from a random (I only write random because right now the string is not further specified and could in principle be anything) string to the performance?

you can take the performance of this pulse into account in FoMaC, but not the underlying pulse itself.

Sure! Depending on the level of compilation, for a top-level, the underlying pulse implementing a gate U does not matter; however, one can consider pulse performance, such as fidelity, time, etc., into consideration. I think that there should be a performance metric corresponding to each gate (i.e., underlying pulse) that can be queried by the compiler. For a very low-level compiler, one can also consider compiling only at the level of pulses, i.e., convert every gate U object into pulses and then try compilation. This might help to further compress the resulting circuit (however, there are no general guarantees; this is hardware-specific). Does this answer your question?

Does that mean that the compiler will not use the pulse shape formula (i.e. the mathematical description of these waveforms or envelopes) directly? If not, where/who can use it?

If compilers use the mathematical formula, it might be a very complex process (as the number of gates and qubits increases), because you have to deal with some complex parameters, compared to an object U. These formulas can be used by the user to define the pulse corresponding to a gate U. For example, see waveforms defined here: https://docs.meetiqm.com/iqm-pulse/api/iqm.pulse.playlist.waveforms.html.

In my opinion (and as we also briefly discussed during our meetings), one can design pulses corresponding to beyond what the hardware offers, i.e., native gate sets using the formulas mentioned above or by giving a full complex array of amplitudes. If these are different pulse implementations corresponding to a gate U (say, P1, P2, P3, etc.), and each pulse P has a different property, i.e., duration, fidelity, noise-robustness, etc. These various implementations of U can be used by the compiler. For example, if the circuit is deep, the compiler should choose the pulse with the shortest duration.

@mnfarooqi
Copy link
Collaborator Author

Moving this thread forward, do either of you have any major comments or suggestions before I address the detailed comments on the code, @burgholzer and @ystade?

@burgholzer
Copy link
Contributor

Moving this thread forward, do either of you have any major comments or suggestions before I address the detailed comments on the code, @burgholzer and @ystade?

I think the most important high level comment is still the one from Yannick (#182 (review))
As long as you keep that in mind while working on the code, that should work.
Note that Yannick is still on vacation the next week. I'll try to answer any questions that come up.

@burgholzer burgholzer modified the milestones: v1.2.0, v1.3.0 Nov 19, 2025
@burgholzer
Copy link
Contributor

I am moving this to the 1.3.0 milestone for now as there still seems to be ongoing discussions that need to be resolved and the changes on main have been pretty stable for a while.

@burgholzer burgholzer removed their request for review December 13, 2025 14:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or feature request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

✨ Data types for pulse representation

5 participants