-
Notifications
You must be signed in to change notification settings - Fork 21
🎨 Update of IonQ and Rigetti Devices #570
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the first draft here.
I just rather quickly went over everything here and added a couple of inline comments.
This is going into the right direction but, as expected, will take a little more work to get right.
get_ionq_target("ionq_forte_1"), | ||
get_ionq_target("ionq_aria_1"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the naming of these should be adjusted so that the last number indicates the number of qubits similar to IBM and IQM. The 1
here does not add any value
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are the specific device names and there is also an Aria 2
. Therefore, I suggest to keep the name, but this is independent of the qubit number at the end - if you prefer that, I am happy to add it to all devices.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But aria 2 still uses the same layout and the same gateset if I am not mistaken.
Given how the device data is averaged anyway, I do not think there is too much value to this number.
If the gateset should really be different between these, then I would actually add the number to the gateset name and still append an underscore with the number of qubits for the device here.
Same holds for the Rigetti device.
Codecov ReportAttention: Patch coverage is 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for all the changes. This is definitely moving in the right direction. I hope the new comments help to push this even closer to the finish line.
src/mqt/bench/targets/devices/ibm.py
Outdated
@@ -18,7 +18,7 @@ | |||
|
|||
from qiskit.providers.fake_provider import GenericBackendV2 | |||
|
|||
from ..gatesets import get_ibm_eagle_gateset, get_ibm_falcon_gateset, get_ibm_heron_gateset | |||
from mqt.bench.targets.gatesets.ibm import get_ibm_eagle_gateset, get_ibm_falcon_gateset, get_ibm_heron_gateset |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change is unrelated to the PR and I actually would not make that change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adjusted it accordingly.
from qiskit.transpiler import InstructionProperties, Target | ||
|
||
from mqt.bench.targets.gatesets.ionq import GPI2Gate, GPIGate, MSGate, ZZGate, add_ionq_equivalences |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would have a rather strong preference for relative imports. Tends to work better with editable installs and IDEs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adjusted it accordingly.
return get_ionq_aria1_target() | ||
if device_name == "ionq_harmony": | ||
return get_ionq_harmony_target() | ||
add_ionq_equivalences() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not yet quite sure this is the best place to put this. As there are multiple IonQ devices, this will inevitably get called multiple times.
I could imagine that this literally duplicates all the rules, which doesn't sound good.
I think it would be good to ensure that this is only called once. And preferably, it would only be called when the target is used, not when it's constructed. We only want to add these right before the transpile
call when the target is used.
I'd also like to understand a little bit more about the lifecycle of the SessionEquivalenceLibrary. How long does it persist? Where does it enter the equation in the transpile call? Would moving to the StagedPassManager allow us to use a custom EL?
elif gate == "fixed_angle_rx_pi_by_2": | ||
target.add_instruction(RXPiOver2Gate()) | ||
elif gate == "fixed_angle_rx_pi_by_minus_2": | ||
target.add_instruction(RXMinusPiOver2Gate()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Slightly more consistent naming could be useful here:
elif gate == "fixed_angle_rx_pi_by_2": | |
target.add_instruction(RXPiOver2Gate()) | |
elif gate == "fixed_angle_rx_pi_by_minus_2": | |
target.add_instruction(RXMinusPiOver2Gate()) | |
elif gate == "rxpi2": | |
target.add_instruction(RXPI2Gate()) | |
elif gate == "rxpi2dg": | |
target.add_instruction(RXPI2DgGate()) |
Or something along those lines.
The PI and PI2 in "gpi" and "gpi2" stand for exactly that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I implemented that now.
@@ -13,33 +13,32 @@ | |||
import pytest | |||
from qiskit.transpiler import Target | |||
|
|||
from mqt.bench.targets.devices.ionq import get_ionq_target | |||
from mqt.bench.targets.devices.ionq import _build_ionq_target, get_ionq_target # noqa: PLC2701 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not quite sure whether I like that this is importing a private function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the test.
get_ionq_target("ionq_forte_1"), | ||
get_ionq_target("ionq_aria_1"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But aria 2 still uses the same layout and the same gateset if I am not mistaken.
Given how the device data is averaged anyway, I do not think there is too much value to this number.
If the gateset should really be different between these, then I would actually add the number to the gateset name and still append an underscore with the number of qubits for the device here.
Same holds for the Rigetti device.
[0], | ||
) | ||
u_gate.append(GPI2Gate(0.5 + phi_param / (2 * np.pi)), [0]) | ||
SessionEquivalenceLibrary.add_equivalence(UGate(theta_param, phi_param, lambda_param), u_gate) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it would be better if these methods would take an equivalence library as a parameter instead of unconditionally adding to the SEL.
Gives a bit more flexibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I followed your hint. Additionally, I add the library now only immediately before the transpile
method is called. I played around a bit with the EquivalenceLibrary
and you can get all available decompositions for a gate but would need to compare the existing ones with the one that might be added to check if it is already in there - which is also not ideal.
SessionEquivalenceLibrary.add_equivalence(UGate(theta, phi, lam), qc) | ||
|
||
|
||
def cx_gate_equivalence() -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it really true that a single CX gate takes two iSWAP gates? Seems counterintuitive to me.
Furthermore, the iSWAP gate is a Qiskit standard gate, so this equivalence should not really be needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thinks so, also considering this reference implementation (https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.iSwapGate). Although it is the other way round there. Anyways, I removed this equivalence because it is not necessary.
self.definition = qc | ||
|
||
|
||
def u_gate_equivalence() -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it could be worth to add a couple more equivalences here that take fewer gates.
Equivalences for the real (parametrized) RX gate come to mind.
Pretty sure there are others.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added one for that, although it still has 5 gates (but at least without parameters - not sure how much it improves though). Let me know if you have specific other ones in mind as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One final comment, then I believe this should be good to go here 🙌
def rx_gate_equivalence(sel: EquivalenceLibrary) -> None: | ||
"""Add RX(θ) gate equivalence using RX(±π/2) and RZ gates.""" | ||
theta = Parameter("θ") | ||
qr = QuantumRegister(1, "q") | ||
qc = QuantumCircuit(qr) | ||
|
||
qc.rz(-np.pi / 2, qr[0]) | ||
qc.append(RXPI2Gate(), [qr[0]]) | ||
qc.rz(theta, qr[0]) | ||
qc.append(RXPI2DgGate(), [qr[0]]) | ||
qc.rz(np.pi / 2, qr[0]) | ||
|
||
sel.add_equivalence(RXGate(theta), qc) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't really help much I suppose.
What I actually meant with my other comment was to add equivalences from
RXGate(pi/2)
to RXPI2Gate
and so on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this what you would like to see here?
def rx_gate_equivalences(sel: EquivalenceLibrary) -> None:
"""Add RX(θ) gate equivalence using RX(±π/2) and RZ gates."""
qr = QuantumRegister(1, "q")
qc = QuantumCircuit(qr)
qc.append(RXPI2Gate(), [qr[0]])
sel.add_equivalence(RXGate(np.pi / 2), qc)
qr = QuantumRegister(1, "q")
qc = QuantumCircuit(qr)
qc.append(RXPI2DgGate(), [qr[0]])
sel.add_equivalence(RXGate(-np.pi / 2), qc)
qr = QuantumRegister(1, "q")
qc = QuantumCircuit(qr)
qc.append(RXPIGate(), [qr[0]])
sel.add_equivalence(RXGate(np.pi), qc)
If yes: Apparently it is not possible to add these equivalences only for specific angles.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. Something like that is what I would have expected.
How's that not supported? Or what is the error you are seeing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It leads to an internal Qiskit Error in qiskit.transpiler.passes.basis.basis_translator.BasisTranslator.run
: AttributeError: 'float' object has no attribute '_uuid'
because for a parameterized gate, Qiskit apparently expects a parameter instead of just a specific angle.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm.. unfortunate..
That's something that I would consider opening up an issue on the Qiskit tracker for.
Such equivalences could be extremely helpful.
Any how.. I suppose this PR can be merged then 😌
This PR...
resolves #289