Skip to content
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

Qiskit Pulse - Introduce logical elements and frames #10694

Merged
merged 54 commits into from
Oct 24, 2023

Conversation

TsafrirA
Copy link
Collaborator

@TsafrirA TsafrirA commented Aug 23, 2023

Summary

This PR is the first step in implementing RFC0012. It introduces the new building blocks LogicalElement and Frame to the pulse stack, which will take over the legacy Channels.

Details and comments

For more details, see the RFC.

One element which is currently missing is a substitute for MemorySlot. The new class should play exactly the same role as MemorySlot but we want it in the new namespace, while also not duplicating the name.

Release notes will be added at a later stage for the combined changes.

@TsafrirA TsafrirA requested review from eggerdj, wshanks and a team as code owners August 23, 2023 08:27
@qiskit-bot
Copy link
Collaborator

One or more of the the following people are requested to review this:

  • @Qiskit/terra-core
  • @nkanazawa1989

@nkanazawa1989 nkanazawa1989 self-assigned this Aug 23, 2023
@nkanazawa1989 nkanazawa1989 added the mod: pulse Related to the Pulse module label Aug 23, 2023
@coveralls
Copy link

coveralls commented Aug 23, 2023

Pull Request Test Coverage Report for Build 6526630111

  • 120 of 122 (98.36%) changed or added relevant lines in 5 files are covered.
  • 5 unchanged lines in 3 files lost coverage.
  • Overall coverage increased (+0.05%) to 87.062%

Changes Missing Coverage Covered Lines Changed/Added Lines %
qiskit/pulse/model/logical_elements.py 45 46 97.83%
qiskit/pulse/model/mixed_frames.py 20 21 95.24%
Files with Coverage Reduction New Missed Lines %
crates/qasm2/src/expr.rs 1 93.76%
crates/qasm2/src/lex.rs 2 91.16%
qiskit/extensions/quantum_initializer/squ.py 2 84.15%
Totals Coverage Status
Change from base Build 6513196991: 0.05%
Covered Lines: 74467
Relevant Lines: 85533

💛 - Coveralls

Copy link
Contributor

@nkanazawa1989 nkanazawa1989 left a comment

Choose a reason for hiding this comment

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

Thanks @TsafrirA. This is good amount of code to carefully review. I'm glad to see finally we came to implementation phase. I feel logical element is one of the great inventions from our long discussion :)

I started to reconsider the handling of name property in Qiskit, after our internal discussion about mixed frame identifier mapping. I lean toward using only type information in Python layer, and later assign string identifier tailored for each backend system configuration when we create a communication payload. This requires slight design change in frame and logical elements, but I think this will remove complexity of mixed frame notation mapping between Qiskit and backend compiler.

qiskit/pulse/logical_elements_frames.py Outdated Show resolved Hide resolved
qiskit/pulse/logical_elements_frames.py Outdated Show resolved Hide resolved
qiskit/pulse/logical_elements_frames.py Outdated Show resolved Hide resolved
qiskit/pulse/logical_elements_frames.py Outdated Show resolved Hide resolved
qiskit/pulse/logical_elements_frames.py Outdated Show resolved Hide resolved
qiskit/pulse/logical_elements_frames.py Outdated Show resolved Hide resolved
qiskit/pulse/logical_elements_frames.py Outdated Show resolved Hide resolved
that qubits are coupled.
"""

def __init__(self, logical_element: LogicalElement, frame: Frame):
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you turn logical_element into optional?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't think that's a good option. A MixedFrame with no LogicalElement is simply a Frame, and it will be less confusing to just use the Frame. This will convey in a clearer way the meaning of the two. For example, on the instruction level, a user can choose to apply a shift phase instruction to a Frame instead of a MixedFrame, but that would clearly indicate that the instruction will be broadcasted across MixedFrame's.

qiskit/pulse/logical_elements_frames.py Outdated Show resolved Hide resolved
qiskit/pulse/logical_elements_frames.py Outdated Show resolved Hide resolved
@nkanazawa1989 nkanazawa1989 mentioned this pull request Sep 1, 2023
9 tasks
@nkanazawa1989 nkanazawa1989 linked an issue Sep 1, 2023 that may be closed by this pull request
9 tasks
@TsafrirA
Copy link
Collaborator Author

TsafrirA commented Sep 5, 2023

@nkanazawa1989
I've updated the PR according to your review. Please note some unresolved conversations from the previous review where I left comments.

Copy link
Contributor

@nkanazawa1989 nkanazawa1989 left a comment

Choose a reason for hiding this comment

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

Thanks Tsafrir for your great work! This is almost good for merge. My review is bit nitpicky this time. Since this is introduction of new module, I'm trying to minimize a risk of future deprecation.

Another point I would consider is the use of slots. This optimizes memory footprint. I remember one experienced researcher worried about performance bottleneck due to heavy usage of python objects. Proper use of slots will alleviates such performance overhead.

qiskit/pulse/model/__init__.py Outdated Show resolved Hide resolved
qiskit/pulse/model/__init__.py Outdated Show resolved Hide resolved
qiskit/pulse/model/__init__.py Outdated Show resolved Hide resolved
qiskit/pulse/model/frames.py Outdated Show resolved Hide resolved
qiskit/pulse/model/frames.py Outdated Show resolved Hide resolved
qiskit/pulse/model/logical_elements.py Outdated Show resolved Hide resolved
qiskit/pulse/model/logical_elements.py Outdated Show resolved Hide resolved
qiskit/pulse/model/frames.py Outdated Show resolved Hide resolved
qiskit/pulse/model/logical_elements.py Outdated Show resolved Hide resolved
"""
self._logical_element = logical_element
self._frame = frame
self._hash = hash((self._logical_element, self._frame))
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the reason of having this _hash attribute? Just a curiosity.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was following the logic of the Channel module. I think it makes sense to have this here as well, since we'll probably want to key things on the MixedFrame, and we can thus hash everything and do it only once. I can remove that if you prefer.

Copy link
Contributor

@wshanks wshanks left a comment

Choose a reason for hiding this comment

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

This looks good to me. I added some suggestions for the docstrings.

The classes could just about be dataclasses though maybe it's not worth the extra features they bring along.

qiskit/pulse/model/__init__.py Outdated Show resolved Hide resolved
qiskit/pulse/model/__init__.py Outdated Show resolved Hide resolved
qiskit/pulse/model/__init__.py Outdated Show resolved Hide resolved

MixedFrame
=============
The combination of a :class:`LogicalElement` and :class:`Frame` is dubbed a :class:`MixedFrame`.
Copy link
Contributor

Choose a reason for hiding this comment

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

Could some intuition for this name be given? Does "mixed" refer to the output of a microwave mixer or the combination of two kinds of frame?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's a great question I can't answer. @nkanazawa1989 any ideas?

Copy link
Contributor

Choose a reason for hiding this comment

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

This is conventional name and I don't know how this was introduced. Probably we can introduce another name?

Copy link

Choose a reason for hiding this comment

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

I think here "mixed" doesn't refer to the microwave mixer. 'MixedFrame' refers to a frame that is associated with a LogicalElement vs Frame which is not associated

qiskit/pulse/model/frames.py Outdated Show resolved Hide resolved
qiskit/pulse/model/mixed_frames.py Outdated Show resolved Hide resolved
qiskit/pulse/model/mixed_frames.py Outdated Show resolved Hide resolved
better convey the meaning of the code, and change the compilation process. One example
are shift/set frequency/phase instructions which are not broadcasted to other
:class:`MixedFrame`s if applied on a specific :class:`MixedFrame` (unlike the behavior
of :class:`Frame`). User can also use a subclass of :class:`MixedFrame` for a particular
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do set/shift instructions work differently for MixedFrame compared to Frame? Is there a specific use case motivating that behavior, over applying the instruction to all MixedFrames sharing the Frame?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The logic here is to leave all options open for the user. If you want to target a specific MixedFrame - you can (the same as you would target a specific Channel before). But, if you want to target all MixedFrames associated with a Frame you can do that too by targeting the Frame.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, right, the old Channels are effectively instances of MixedFrames (and might become subclasses concretely?), so this preserves backwards compatibility.

What I was thinking about when asking about this is that I was thinking it made the implementation more complex since both frames and mixed frames need to be tracked in PulseIR (or other any representation). I think the way it has to work is that a frame is an alias for all of its mixed frames. So one way to think about it is that all shift/set frequency/phase instructions on a frame are replaced with those instructions on all mixed frames in a first pulse compiler pass. There is not really a standalone frame that has a frequency and phase over time, though you can think of there being one if you never apply instructions directly to mixed frames that make them different from the underlying frame.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think the endgame is to deprecate the Channels.

Yes, one of the first passes done by the compiler would be to broadcast instructions across MixedFrames. In principle, you can also imagine broadcasting instructions across MixedFrames associated with a specific LogicalElement - I think a delay is the only reasonable example?

qiskit/pulse/model/mixed_frames.py Outdated Show resolved Hide resolved
to define where pulses instructions are applied, and what would be their carrier frequency and phase
(because typically AC pulses are used). Each :class:`LogicalElement` represents a separate component
in the quantum system on which instructions could be applied. On the other hand, each :class:`Frame`
represents a frequency and phase duo for the carrier of the pulse.
Copy link
Contributor

Choose a reason for hiding this comment

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

We might want to be more careful here. In the Frame class, you specify initial frequency and initial phase. The Frame also represents the object to reference to change the frequency and phase duo over time by applying frequency and phase instructions but that frequency and phase duo can only be determined by scheduling a pulse program.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I agree, this is a delicate topic.
My thinking was that we need to set the initial frequency and phase at time t=0 for Frames that don't have a default value taken from the backend. If you think of a pulse program (like the old Schedule) this is sufficient. Things might get trickier if we think of a program with several pulse gates. In principle the initial values are still enough, but you will have to track the phases across the entire program? (To be fair, this is not handled well even with the current stack)

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, this was something else I was thinking about how to ask about. One of the motivations for this work on pulse frames was to help with the qutrit and cross-resonance cases where there is a need to apply a shift and have it persist across pulse gates. Do we try to capture that here? Or is that left to the backend implementation? At some point, we need to introduce a way to change the IBM backend implementation so that a pulse gate can influence later pulse gates.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's a great question, which we haven't ironed out yet.

I think the user friendly approach would be to define the initial frequency and phase at t=0 where t=0 is not the beginning of the pulse gate, but rather the beginning of the circuit. This gives a concrete meaning to the frame which could then be tracked over time. The job of actually tracking such a frame would have to be assigned to either the backend or the frontend. However, this introduces a global dependency to the pulse gates which is not currently available in the stack. Perhaps this global dependency is needed anyway if we want to carry out phases and frequencies correctly across pulse gates? If that's the case, we can sort all of these issues via a compiler run which will schedule the circuit as a whole, and address the frames on a global basis.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think eventually the management of frame is delegated to the backend. Frame is a global static variable and shared among pulse gates, and local shift/set frequency instruction doesn't update frame value.

Do we try to capture that here? Or is that left to the backend implementation?

Qiskit doesn't define how things must be implemented on the physical layer, so it's just a syntax. Actual implementation must be done on the backend, still IBM provider can provider a plug-in pass that computes local frame shift in each pulse gate (provider pulg-in pass should know backend implementation). If you run the same program on Qiskit Dynamics, phase and frequency are already carried over because it schedules entire circuit before running simulation. This is why I prefer pass based architecture.

Copy link
Contributor

@nkanazawa1989 nkanazawa1989 left a comment

Choose a reason for hiding this comment

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

Thanks @TsafrirA added fix for minor docs error. I'll approve and merge after the fix is applied.

qiskit/circuit/quantumcircuit.py Outdated Show resolved Hide resolved
qiskit/pulse/model/__init__.py Outdated Show resolved Hide resolved
qiskit/pulse/model/__init__.py Outdated Show resolved Hide resolved
qiskit/pulse/model/mixed_frames.py Outdated Show resolved Hide resolved
qiskit/pulse/model/mixed_frames.py Outdated Show resolved Hide resolved
qiskit/pulse/model/mixed_frames.py Outdated Show resolved Hide resolved
Copy link
Contributor

@nkanazawa1989 nkanazawa1989 left a comment

Choose a reason for hiding this comment

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

Thanks. This looks good to me.

@nkanazawa1989 nkanazawa1989 added this pull request to the merge queue Oct 24, 2023
Merged via the queue into Qiskit:main with commit b2e9ebb Oct 24, 2023
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
mod: pulse Related to the Pulse module
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Pulse Compiler and IR
6 participants