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

Fully QPY compatible pulse builder for V2 and runtime backend #8949

Merged
merged 20 commits into from
Nov 23, 2022

Conversation

nkanazawa1989
Copy link
Contributor

@nkanazawa1989 nkanazawa1989 commented Oct 19, 2022

Summary

At present, Qiskit Pulse supports both Schedule and ScheduleBlock representation, but QPY only supports ScheduleBlock representation. This causes an issue in V2 IBM provider and runtime provider, since they assume QPY payload, and the pulse builder still accepts Schedule. For example,

x_gate_sched = backend.target["x"][(0, )].calibration

with builder() as schedule:
    call(x_gate_sched)

backend.run(schedule)

this execution will fail with V2 instance because x_gate_sched is still provided in legacy schedule format (because pulse defaults data is still PulseQobj format in IBM provider, regardless of backend version). Same issue happens in RZX calibration builder passes, which is in high demand for pulse level optimization of application circuits.

This PR allows V2 provider and runtime provider to execute such a program, and circuit optimized with RZX calibration builder passes.

Details and comments

This PR consists of several commits.

805a5b2

This commit removes usage of Schedule and Call from the pulse builder. Previously Schedule was wrapped by Call instruction and added to the schedule block as an instruction. Note that Call instruction is not QPY compatible because it internally keeps Schedule as-is (just a wrapper).
With this commit, schedule is internally converted into schedule block representation to guarantee the QPY compatibility. Because schedule block is not aware of absolute time interval of instruction, a helper compiler directive AreaBarrier is introduced to support instruction scheduling. This instruction must be removed before execution because this is not necessary supported by the hardware.

a02bcb5

This commit update QPY to support AreaBarrier instruction. Now builder-generated program is fully QPY compatible.

f2bc607

This commit updates the RZX calibration builder passes to use the updated pulse builder to construct stretched pulse schedules. Thus QPY compatibility is guaranteed.

(note)
AreaBarrier is renamed to TimeBlocked according to the review comment.

Alternative

Alternatively, we could add Schedule support to QPY to fix this problem. However, I hesitate this approach because QPY supports indicates we cannot drop this representation from the code stack permanently, due to the QPY backward compatibility. Schedule tends to be legacy, because we cannot support frame properly on top of it (because of its poor time slot representation).

Fix #8933

@qiskit-bot
Copy link
Collaborator

Thank you for opening a new pull request.

Before your PR can be merged it will first need to pass continuous integration tests and be reviewed. Sometimes the review process can be slow, so please be patient.

While you're waiting, please feel free to review other open PRs. While only a subset of people are authorized to approve pull requests for merging, everyone is encouraged to review open pull requests. Doing reviews helps reduce the burden on the core team and helps make the project's code better for everyone.

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

Copy link
Contributor

@eggerdj eggerdj left a comment

Choose a reason for hiding this comment

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

Thanks a lot. Overall, this looks fine. I have some questions that need clarification. See the comments.

qiskit/pulse/builder.py Outdated Show resolved Hide resolved
qiskit/pulse/builder.py Outdated Show resolved Hide resolved
qiskit/pulse/builder.py Outdated Show resolved Hide resolved
qiskit/pulse/builder.py Show resolved Hide resolved
Comment on lines 81 to 83
block = ScheduleBlock()
block.append(AreaBarrier(120, DriveChannel(0)))
block.append(Play(Constant(10, 0.1), DriveChannel(0)))
Copy link
Contributor

Choose a reason for hiding this comment

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

This feels like we are mixing representations. How does this work with parametric duration? What about

            block = ScheduleBlock()
            block.append(AreaBarrier(120, DriveChannel(0)))
            block.append(Play(Constant(Parameter("duration"), 0.1), DriveChannel(0)))

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As I type hinted, AreaBarrier doesn't accept parameter. This is internally used to convert schedule into block, in which schedule doesn't take parameterized duration.

qiskit/transpiler/passes/calibration/rzx_builder.py Outdated Show resolved Hide resolved
qiskit/transpiler/passes/calibration/rzx_builder.py Outdated Show resolved Hide resolved
nkanazawa1989 and others added 2 commits October 20, 2022 02:14
Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com>
@coveralls
Copy link

coveralls commented Oct 19, 2022

Pull Request Test Coverage Report for Build 3528859271

  • 151 of 163 (92.64%) changed or added relevant lines in 6 files are covered.
  • 1 unchanged line in 1 file lost coverage.
  • Overall coverage increased (+0.02%) to 84.22%

Changes Missing Coverage Covered Lines Changed/Added Lines %
qiskit/pulse/instructions/directives.py 15 16 93.75%
qiskit/pulse/transforms/canonicalization.py 19 20 95.0%
qiskit/transpiler/passes/calibration/rzx_builder.py 58 60 96.67%
qiskit/pulse/builder.py 53 61 86.89%
Files with Coverage Reduction New Missed Lines %
qiskit/pulse/builder.py 1 90.84%
Totals Coverage Status
Change from base Build 3527305551: 0.02%
Covered Lines: 62400
Relevant Lines: 74092

💛 - Coveralls

Copy link
Contributor

@eggerdj eggerdj left a comment

Choose a reason for hiding this comment

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

A few more comments and a suggestion for the directive name.

"""Pulse ``AreaBarrier`` directive.

This instruction is intended to be used internally within the pulse builder,
to naively convert :class:`.Schedule` into :class:`.ScheduleBlock`.
Copy link
Contributor

Choose a reason for hiding this comment

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

If this is used for a naive conversion then what would a non-naive conversion look like?

Copy link
Contributor Author

@nkanazawa1989 nkanazawa1989 Oct 24, 2022

Choose a reason for hiding this comment

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

In the current mechanism I "give up" to infer the original alignment context. For example,

with align_sequential():
    with align_left():
        play(cr45p, ControlChannel(1))
        play(cr45p_d0, DriveChannel(0))
   play(x, DriveChannel(1))
    with align_left():
        play(cr45m, ControlChannel(1))
        play(cr45m_d0, DriveChannel(0))
   play(x, DriveChannel(1))

this would be the context of the ECR schedule, but current mechanism will generate

with align_left():
    play(cr45p, ControlChannel(1))
    play(cr45p_d0, DriveChannel(0))
    time_block(xxx, DriveChannel(1))
    play(x, DriveChannel(1))
    ...

Note that we cannot modify any pulse duration (otherwise you must also modify time block duration), but this should be okey because we are consuming "already scheduled" sequence. To recover original alignment context, we may need to generate DAG to find parallel context. Such logic may become heavy and still has a rick of some edge case. So I just avoid doing over engineering and choose to say "this is naive version of alignment context recovery".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added bit more detail in 7ab2710

qiskit/pulse/instructions/directives.py Outdated Show resolved Hide resolved
qiskit/pulse/instructions/directives.py Outdated Show resolved Hide resolved
nkanazawa1989 and others added 4 commits October 24, 2022 11:38
eggerdj
eggerdj previously approved these changes Oct 25, 2022
Copy link
Contributor

@eggerdj eggerdj left a comment

Choose a reason for hiding this comment

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

LGTM.

eggerdj
eggerdj previously approved these changes Oct 30, 2022
Copy link
Contributor

@eggerdj eggerdj left a comment

Choose a reason for hiding this comment

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

LGTM

@mtreinish mtreinish added Changelog: New Feature Include in the "Added" section of the changelog Changelog: API Change Include in the "Changed" section of the changelog Changelog: Bugfix Include in the "Fixed" section of the changelog labels Oct 31, 2022
@mtreinish mtreinish added this to the 0.23.0 milestone Oct 31, 2022
@mtreinish mtreinish self-assigned this Oct 31, 2022
Copy link
Member

@mtreinish mtreinish left a comment

Choose a reason for hiding this comment

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

Overall this LGTM I just had a couple of small questions inline

@@ -731,17 +737,6 @@ def append_instruction(self, instruction: instructions.Instruction):
"""
self._context_stack[-1].append(instruction)

@_compile_lazy_circuit_before
def append_block(self, context_block: ScheduleBlock):
Copy link
Member

Choose a reason for hiding this comment

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

Are there any backwards compatibility implications with this removal. The documentation referred to this method previously so was it user facing or is this all internal?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

qiskit/pulse/instructions/__init__.py Show resolved Hide resolved
qiskit/qpy/type_keys.py Show resolved Hide resolved
- render class docs of the directive instructions
- update qpy format documentation
- add separate reno for time blockade
Copy link
Member

@mtreinish mtreinish left a comment

Choose a reason for hiding this comment

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

Overall I'm happy with the state of the code now, thanks for making the updates. I just have a few small inline comments. The only one I think is really required is adding a unit test for RZXCalibration* with QPY, other than that I'm happy to approve this.

test/python/transpiler/test_calibrationbuilder.py Outdated Show resolved Hide resolved
@@ -187,6 +194,20 @@ def test_nested_blocks(self):
builder.delay(200, DriveChannel(1))
self.assert_roundtrip_equal(test_sched)

def test_called_schedule(self):
Copy link
Member

Choose a reason for hiding this comment

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

One thing I think would be good is to maybe have a qpy test with the rzx calibration passes. Since a big part of this PR is fixing the QPY support with the pass output having a test to verify the pass output can be round tripped through qpy is simple way to ensure we don't regress on this moving forward.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks Matthew. I added new tests for QPY. I also refactored the RZX builder test in 8562d98 since the output schedule has been never really tested. This is why the alignment error has been existed before this PR (this PR unintentionally fixed it).

Copy link
Member

@mtreinish mtreinish left a comment

Choose a reason for hiding this comment

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

Thanks for the updates it lgtm now.

@mergify mergify bot merged commit e5ea411 into Qiskit:main Nov 23, 2022
@nkanazawa1989 nkanazawa1989 deleted the upgrade/add_schedule_block_like branch November 25, 2022 02:56
Cryoris pushed a commit to Cryoris/qiskit-terra that referenced this pull request Jan 12, 2023
…#8949)

* Eliminate use of Schedule in the builder context. Schedule is implicitly converted into ScheduleBlock with AreaBarrier instructions.

* Support AreaBarrier instruction in QPY

* Update RZX calibration builder to generate ScheduleBlock

* Add release note

* Support python3.7

* Fix hadamard schedule in the RZX builder. This should not use sequential context.

* Review comments

Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com>

* Unified append_block and append_subroutine

* Update instruction name AreaBarrier -> TimeBlockade

* Documentation updates

Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com>

* Remove old name

* lint fix

* add release note for bugfix

* review comments
- render class docs of the directive instructions
- update qpy format documentation
- add separate reno for time blockade

* more detailed upgrade notes

* add new unittests. rescale_cr_inst method is updated so that it becomes more robust to the rounding error.

Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: API Change Include in the "Changed" section of the changelog Changelog: Bugfix Include in the "Fixed" section of the changelog Changelog: New Feature Include in the "Added" section of the changelog
Projects
None yet
Development

Successfully merging this pull request may close these issues.

RZXCalibrationBuilder aligns pulses incorrectly CalibrationBuilders should not use Schedule
6 participants