-
Notifications
You must be signed in to change notification settings - Fork 33
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
✨ added inverse gate for iSWAP #460
Conversation
Important note: In other words: if the gate doesn't exist per se, technically I am not allowed to add it in the test file |
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.
Also here: many thanks for this addition! This is looking really good already and I believe you managed to cover almost all the places.
I left some comments inline.
Other than that, it's really only the C++ coverage check that is complaining. You can directly inspect the coverage report by clicking on the respective link in the comment posted by codecov. I believe there should be further existing tests that you can add this new gate to in order to cover the missing lines.
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## main #460 +/- ##
=====================================
Coverage 90.1% 90.1%
=====================================
Files 111 111
Lines 11845 11866 +21
Branches 2065 2066 +1
=====================================
+ Hits 10677 10702 +25
+ Misses 1168 1164 -4
|
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.
Looking good! many thanks!
Now that the new gate is in, a natural follow-up would be to consolidate the following methods:
mqt-core/src/operations/StandardOperation.cpp
Lines 471 to 559 in 95541a6
void StandardOperation::invert() { switch (type) { // self-inverting gates case I: case X: case Y: case Z: case H: case SWAP: case ECR: case Barrier: break; // gates where we just update parameters case GPhase: case P: case RX: case RY: case RZ: case RXX: case RYY: case RZZ: case RZX: parameter[0] = -parameter[0]; break; case U2: std::swap(parameter[0], parameter[1]); parameter[0] = -parameter[0] + PI; parameter[1] = -parameter[1] - PI; break; case U: parameter[0] = -parameter[0]; parameter[1] = -parameter[1]; parameter[2] = -parameter[2]; std::swap(parameter[1], parameter[2]); break; case XXminusYY: case XXplusYY: parameter[0] = -parameter[0]; break; case DCX: std::swap(targets[0], targets[1]); break; // gates where we have specialized inverted operation types case S: type = Sdg; break; case Sdg: type = S; break; case T: type = Tdg; break; case Tdg: type = T; break; case V: type = Vdg; break; case Vdg: type = V; break; case SX: type = SXdg; break; case SXdg: type = SX; break; case Peres: type = Peresdg; break; case Peresdg: type = Peres; break; // Tracking issue for iSwap: https://github.com/cda-tum/mqt-core/issues/423 case iSWAP: case None: case Compound: case Measure: case Reset: case Teleportation: case ClassicControlled: case ATrue: case AFalse: case MultiATrue: case MultiAFalse: case OpCount: throw QFRException("Inverting gate" + toString(type) + " is not supported."); } mqt-core/include/dd/Operations.hpp
Lines 27 to 93 in 95541a6
switch (type) { case qc::I: gm = I_MAT; break; case qc::H: gm = H_MAT; break; case qc::X: gm = X_MAT; break; case qc::Y: gm = Y_MAT; break; case qc::Z: gm = Z_MAT; break; case qc::S: gm = inverse ? SDG_MAT : S_MAT; break; case qc::Sdg: gm = inverse ? S_MAT : SDG_MAT; break; case qc::T: gm = inverse ? TDG_MAT : T_MAT; break; case qc::Tdg: gm = inverse ? T_MAT : TDG_MAT; break; case qc::V: gm = inverse ? VDG_MAT : V_MAT; break; case qc::Vdg: gm = inverse ? V_MAT : VDG_MAT; break; case qc::U: gm = inverse ? uMat(-parameter[1U], -parameter[2U], -parameter[0U]) : uMat(parameter[2U], parameter[1U], parameter[0U]); break; case qc::U2: gm = inverse ? u2Mat(-parameter[0U] + PI, -parameter[1U] - PI) : u2Mat(parameter[1U], parameter[0U]); break; case qc::P: gm = inverse ? pMat(-parameter[0U]) : pMat(parameter[0U]); break; case qc::SX: gm = inverse ? SXDG_MAT : SX_MAT; break; case qc::SXdg: gm = inverse ? SX_MAT : SXDG_MAT; break; case qc::RX: gm = inverse ? rxMat(-parameter[0U]) : rxMat(parameter[0U]); break; case qc::RY: gm = inverse ? ryMat(-parameter[0U]) : ryMat(parameter[0U]); break; case qc::RZ: gm = inverse ? rzMat(-parameter[0U]) : rzMat(parameter[0U]); break; default: std::ostringstream oss{}; oss << "DD for gate" << op->getName() << " not available!"; throw qc::QFRException(oss.str()); } return dd->makeGateDD(gm, nqubits, controls, target, startQubit); } mqt-core/include/dd/Operations.hpp
Lines 117 to 237 in 95541a6
switch (type) { case qc::SWAP: gm = SWAP_MAT; break; case qc::iSWAP: gm = inverse ? ISWAPDG_MAT : ISWAP_MAT; break; case qc::DCX: gm = DCX_MAT; break; case qc::ECR: gm = ECR_MAT; break; case qc::RXX: gm = inverse ? rxxMat(-parameter[0U]) : rxxMat(parameter[0U]); break; case qc::RYY: gm = inverse ? ryyMat(-parameter[0U]) : ryyMat(parameter[0U]); break; case qc::RZZ: gm = inverse ? rzzMat(-parameter[0U]) : rzzMat(parameter[0U]); break; case qc::RZX: gm = inverse ? rzxMat(-parameter[0U]) : rzxMat(parameter[0U]); break; case qc::XXminusYY: gm = inverse ? xxMinusYYMat(-parameter[0U], parameter[1U]) : xxMinusYYMat(parameter[0U], parameter[1U]); break; case qc::XXplusYY: gm = inverse ? xxPlusYYMat(-parameter[0U], parameter[1U]) : xxPlusYYMat(parameter[0U], parameter[1U]); break; default: definitionFound = false; } if (definitionFound) { return dd->makeTwoQubitGateDD(gm, nqubits, target0, target1, startQubit); } } switch (type) { case qc::SWAP: // SWAP is self-inverse return dd->makeSWAPDD(nqubits, controls, target0, target1, startQubit); case qc::iSWAP: if (inverse) { return dd->makeiSWAPinvDD(nqubits, controls, target0, target1, startQubit); } return dd->makeiSWAPDD(nqubits, controls, target0, target1, startQubit); case qc::Peres: if (inverse) { return dd->makePeresdagDD(nqubits, controls, target0, target1, startQubit); } return dd->makePeresDD(nqubits, controls, target0, target1, startQubit); case qc::Peresdg: if (inverse) { return dd->makePeresDD(nqubits, controls, target0, target1, startQubit); } return dd->makePeresdagDD(nqubits, controls, target0, target1, startQubit); case qc::DCX: return dd->makeDCXDD(nqubits, controls, target0, target1, startQubit); case qc::ECR: // ECR is self-inverse return dd->makeECRDD(nqubits, controls, target0, target1, startQubit); case qc::RXX: { if (inverse) { return dd->makeRXXDD(nqubits, controls, target0, target1, -parameter[0U], startQubit); } return dd->makeRXXDD(nqubits, controls, target0, target1, parameter[0U], startQubit); } case qc::RYY: { if (inverse) { return dd->makeRYYDD(nqubits, controls, target0, target1, -parameter[0U], startQubit); } return dd->makeRYYDD(nqubits, controls, target0, target1, parameter[0U], startQubit); } case qc::RZZ: { if (inverse) { return dd->makeRZZDD(nqubits, controls, target0, target1, -parameter[0U], startQubit); } return dd->makeRZZDD(nqubits, controls, target0, target1, parameter[0U], startQubit); } case qc::RZX: { if (inverse) { return dd->makeRZXDD(nqubits, controls, target0, target1, -parameter[0U], startQubit); } return dd->makeRZXDD(nqubits, controls, target0, target1, parameter[0U], startQubit); } case qc::XXminusYY: { if (inverse) { return dd->makeXXMinusYYDD(nqubits, controls, target0, target1, -parameter[0U], parameter[1U], startQubit); } return dd->makeXXMinusYYDD(nqubits, controls, target0, target1, parameter[0U], parameter[1U], startQubit); } case qc::XXplusYY: { if (inverse) { return dd->makeXXPlusYYDD(nqubits, controls, target0, target1, -parameter[0U], parameter[1U], startQubit); } return dd->makeXXPlusYYDD(nqubits, controls, target0, target1, parameter[0U], parameter[1U], startQubit); } default: std::ostringstream oss{}; oss << "DD for gate" << op->getName() << " not available!"; throw qc::QFRException(oss.str()); } }
There is lots of redundancy in the handling of the inverse
throughout the code base.
I do not have a perfect solution for resolving that in mind. It would be pretty convenient though if any operation could return its gate matrix. So essentially to refactor https://github.com/cda-tum/mqt-core/blob/main/include/dd/GateMatrixDefinitions.hpp so that the appropriate matrix can be queried from an Operation
.
To this end, it could be nice to adopt a strategy similar to Qiskit where there are individual classes for gates that all derive from StandardOperation
(and potentially SymbolicOperation
for parametrized gates). As an example:
- there would be an
XGate
class that provides a method to get the single-qubitGateMatrix
, the type of the gate (already covered by StandardOperation), the inverse type, the inverse GateMatrix and potentially other useful methods. - The challenge will be to get the interface right so that there is a standardized way to call the newly introduced methods. In that regard, further compile-time/
constexpr
convenience functions likeisSingleTargetGate
,isTwoTargetGate
, andisThreeOrMoreTargetGate
would probably make sense. - Naturally, this creates quite some new classes, but I believe it makes the library structure a little simpler. Needs a little experimenting on the refactor though.
Description
The DD Package was missing the
iSWAPDag
(iSWAP dagger) gate. However, the DD Package already had in some place aISWAPDG_MAT
which implemented the desired gate in a matrix form.Status
Currently, the
iSWAPdagDumpIsValid
test produces the following error:After reading through https://qiskit.org/documentation/apidoc/circuit_library.html, my assumption is that OpenQASM does not support at the moment the iSWAP dagger gate natively, so, therefore, it doesn't have implementation for it.
(I know that the previous assumption might not be the reason why, but I tried to point out a tiny direction why this might not work. Anyway, I will try to look further into this issue in the near future)
Fixes #423
Checklist: