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

♻️ Refactor handling of gate matrices and inverses #484

Open
burgholzer opened this issue Nov 18, 2023 · 0 comments · May be fixed by #752
Open

♻️ Refactor handling of gate matrices and inverses #484

burgholzer opened this issue Nov 18, 2023 · 0 comments · May be fixed by #752
Labels
c++ Anything related to C++ code Core Anything related to the Core library and IR DD Anything related to the DD package enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed refactor Anything related to code refactoring
Milestone

Comments

@burgholzer
Copy link
Member

After #460, all gates in MQT Core have a proper inverse now.
A natural follow-up would be to consolidate the following methods:

  • 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.");
    }
  • 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);
    }
  • 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-qubit GateMatrix, 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 like isSingleTargetGate, isTwoTargetGate, and isThreeOrMoreTargetGate 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.
  • Maybe the above actually makes some of the operations (such as iSWAPdg redundant again.

Originally posted by @burgholzer in #460 (review)

@burgholzer burgholzer added enhancement New feature or request refactor Anything related to code refactoring DD Anything related to the DD package Core Anything related to the Core library and IR c++ Anything related to C++ code labels Nov 18, 2023
@burgholzer burgholzer added this to MQT Core and MQT Nov 18, 2023
@burgholzer burgholzer moved this to In Progress in MQT Nov 18, 2023
@burgholzer burgholzer moved this to In Progress in MQT Core Nov 18, 2023
@burgholzer burgholzer added the help wanted Extra attention is needed label Nov 18, 2023
@burgholzer burgholzer added this to the MQT Core milestone Nov 18, 2023
@burgholzer burgholzer moved this from In Progress to Todo in MQT Core Apr 19, 2024
@burgholzer burgholzer added the good first issue Good for newcomers label Jun 10, 2024
@burgholzer burgholzer moved this from In Progress to Todo in MQT Jun 10, 2024
@Joshy-R Joshy-R linked a pull request Nov 14, 2024 that will close this issue
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Anything related to C++ code Core Anything related to the Core library and IR DD Anything related to the DD package enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed refactor Anything related to code refactoring
Projects
Status: Todo
Status: Todo
1 participant