Skip to content

Unitary Layer RB #1561

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

Merged
merged 29 commits into from
Jun 18, 2025
Merged

Unitary Layer RB #1561

merged 29 commits into from
Jun 18, 2025

Conversation

dcmckayibm
Copy link
Collaborator

@dcmckayibm dcmckayibm commented May 14, 2025

This PR adds a new class to do layer style RB with an arbitrary set of 2Q unitary circuits for the entangling layer. Example usage which will perform RB with 10 different RZZ angles.

rzz_gate_lst = []

for i in range(10):

    qc = QuantumCircuit(2)
    qc.rzz(np.pi/2/10*(i+1),0,1)
    print(np.pi/2/10*(i+1))

    rzz_gate_lst.append(qc.to_instruction())


rb_obj = LayerFidelityUnitary(physical_qubits = [0,1,2,3], 
                              two_qubit_layers = [[[0,1],[2,3]]], 
                              two_qubit_gates=rzz_gate_lst, 
                              lengths = [2,10,100], backend=backend, num_samples=1, seed=1, layer_barrier=False)

Copy link
Collaborator

@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.

Looks pretty good!

@dcmckayibm dcmckayibm changed the title [DRAFT] Unitary Layer RB Unitary Layer RB Jun 8, 2025
@dcmckayibm
Copy link
Collaborator Author

@wshanks two_qubit_gates: Sequence[Instruction | Gate] seems to fail one of the tests

image

@dcmckayibm
Copy link
Collaborator Author

Looks like that's only for python 3.10 and we still run 3.9 tests

@wshanks
Copy link
Collaborator

wshanks commented Jun 10, 2025

Ah, okay, using Union is fine. The other option is to use from __future__ import annotations.

# Add the fold rzz angle pass
# pylint: disable=no-member
if "rzz" in basis_gates:
pass_manager_2q.translation.append([FoldRzzAngle()])
Copy link
Collaborator

@wshanks wshanks Jun 10, 2025

Choose a reason for hiding this comment

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

Okay, one last suggestion for avoiding doing this -- if we pass a backend to generate_preset_pass_manager any custom transpiler passes should get picked up through the plugin system. So we could pass self.backend here. I think a reason we do not do that now is that generate_preset_pass_manager warns not to pass both a backend and basis_gates/coupling_map. I believe when you pass basis_gates and coupling_map to the transpiler it just generates a target internally any way. So we could instead just create the target and pass that, which does not generate a warning, like this:

target = Target.from_configuration(basis_gates=basis_gates, coupling_map=CouplingMap(((0, 1),)))
pass_manager = generate_preset_pass_manager(backend=self.backend, target=target)

I don't think we need separate 1q and 2q pass managers in this case.

@mtreinish Could you confirm if this approach is idiomatic Qiskit?

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 guess I was also thinking this might weigh the transpiler down more? but I didn't test it

Copy link
Collaborator

Choose a reason for hiding this comment

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

Passing the backend and target? I don't think so because the transpiler will still build a target internally. If the cost of building the transpiler were a concern, we could build it once at a higher level instead of building it here for each circuit. It is probably not a bottle neck though.

Looking at the generate_preset_pass_manager code, it seems to do what we want in terms of taking the gates and coupling map from the target and the other things like dt and durations from the backend. I think we don't care about those things any way but I am not sure if it is better in general to copy them from the backend into the new target or not.

Copy link
Contributor

Choose a reason for hiding this comment

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

Passing both the backend and the target will work. If you do that the target argument takes precedence over the backend.target which will be ignored. But the default pipeline method from the backend should still get picked up. If you're looking to run the custom passes from a backend that would work. I don't think we have test coverage of that particular usage but quickly scanning the code it matches the documented behavior.

As an alternative if you wanted to use a bit more explicit syntax you could do:

pass_manager = generate_preset_pass_manager(
    target=target,
    translation_method=backend.get_translation_stage_plugin(),
    scheduling_method=backend.get_scheduling_stage_plugin(),
)

but either is valid.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

So I tried one pass manager for both 1Q and 2Q but then in this line

qc_tmp = pass_manager.run(qc_tmp)
circ._append(qc_tmp.to_instruction(), (circ.qubits[q],), ())

it was actually turning that into a 2Q instruction and this was causing issues... so I went back to 1Q and 2Q pass manager (as pushed)

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 used the version where we pass the target and backend. @mtreinish 's example would require me to check if those calls exist in the backend first and so seemed a bit more messy

Copy link
Collaborator

@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.

The code looks good. Just one question about the release notes.

@dcmckayibm dcmckayibm added this pull request to the merge queue Jun 18, 2025
Merged via the queue into main with commit ff4828a Jun 18, 2025
12 checks passed
@wshanks wshanks deleted the rzz_layer branch June 19, 2025 03:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants