Unverified Commit 20546261 authored by Mccaskey, Alex's avatar Mccaskey, Alex Committed by GitHub
Browse files

Merge pull request #35 from tnguyen-ornl/tnguyen/qrt_impls

Merge FTQC work to upstream dev branch
parents 294fdb8b 68210c68
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -11,3 +11,6 @@ add_test(NAME qrt_kernel_include COMMAND ${CMAKE_BINARY_DIR}/qcor -c -I${CMAKE_C
add_test(NAME qrt_period_finding COMMAND ${CMAKE_BINARY_DIR}/qcor -c -I${CMAKE_CURRENT_SOURCE_DIR}/shared ${CMAKE_CURRENT_SOURCE_DIR}/simple/period_finding.cpp)
add_test(NAME qrt_grover COMMAND ${CMAKE_BINARY_DIR}/qcor -c ${CMAKE_CURRENT_SOURCE_DIR}/grover/grover.cpp)
add_test(NAME qrt_adapt COMMAND ${CMAKE_BINARY_DIR}/qcor -c ${CMAKE_CURRENT_SOURCE_DIR}/hybrid/adapt_h2.cpp)
add_test(NAME qrt_ftqc_simple COMMAND ${CMAKE_BINARY_DIR}/qcor -c -qrt ftqc ${CMAKE_CURRENT_SOURCE_DIR}/ftqc_qrt/simple-demo.cpp)
add_test(NAME qrt_ftqc_rus COMMAND ${CMAKE_BINARY_DIR}/qcor -c -qrt ftqc ${CMAKE_CURRENT_SOURCE_DIR}/ftqc_qrt/repeat-until-success.cpp)
add_test(NAME qrt_ftqc_qec COMMAND ${CMAKE_BINARY_DIR}/qcor -c -qrt ftqc ${CMAKE_CURRENT_SOURCE_DIR}/ftqc_qrt/bit-flip-code.cpp)
 No newline at end of file
+132 −0
Original line number Diff line number Diff line
#include <qalloc>

// Example demonstrates a simple 3-qubit bit-flip code.
// Compile:
// qcor -qpu aer[noise-model:<noise.json>] -qrt ftqc bit-flip-code.cpp 
// ./a.out

bool getLogicalVal(qreg q, int logicalIdx) {
  if (q.creg[logicalIdx*3] == q.creg[logicalIdx*3]) {
    // First two bits matched.
    return q.creg[logicalIdx*3];
  }
  // The last bit is the tie-breaker.
  return q.creg[logicalIdx*3 + 2];
}

// Encode qubits into logical qubits:
// Assume q[0], q[3], q[6] are initial physical qubits
// that will be mapped to logical qubits q[0-2], q[3-5], etc.
__qpu__ void encodeQubits(qreg q) {
  int nbLogicalQubits = q.size() / 3;
  for (int i = 0; i < nbLogicalQubits; ++i) {
    int physicalQubitIdx = 3 * i;
    CX(q[physicalQubitIdx], q[physicalQubitIdx + 1]);
    CX(q[physicalQubitIdx], q[physicalQubitIdx + 2]);
  }
}

// Logical CNOT b/w two logical qubits
__qpu__ void cnotLogical(qreg q) {
  for (int i = 0; i < 3; ++i) {
    CX(q[i], q[3 + i]);
  }
}

__qpu__ void measureLogical(qreg q, int logicalIdx) {
  int physicalIdx = logicalIdx * 3;
  for (int i = physicalIdx; i < physicalIdx + 3; ++i) {
    Measure(q[i]);
  }
}

__qpu__ void correctLogicalQubit(qreg q, int logicalIdx, int ancIdx) {
  int physicalIdx = logicalIdx * 3;
  // Assume that we only has 1 ancilla qubit
  CX(q[physicalIdx], q[ancIdx]);
  CX(q[physicalIdx + 1], q[ancIdx]);
  const bool parity01 = Measure(q[ancIdx]);
  if (parity01) {
    // Reset anc qubit for reuse
    X(q[ancIdx]);
  }

  CX(q[physicalIdx + 1], q[ancIdx]);
  CX(q[physicalIdx + 2], q[ancIdx]);
  const bool parity12 = Measure(q[ancIdx]);
  if (parity12) {
    // Reset anc qubit
    X(q[ancIdx]);
  }

  // Correct error based on parity results
  if (parity01 && !parity12) {
    X(q[physicalIdx]);
  }  
  
  if (parity01 && parity12) {
    X(q[physicalIdx + 1]);
  }  

  if (!parity01 && parity12) {
    X(q[physicalIdx + 2]);
  }
}

__qpu__ void runQecCycle(qreg q) {
  int nbLogicalQubits = q.size() / 3;
  int ancBitIdx = q.size() - 1;
  for (int i = 0; i < nbLogicalQubits; ++i) {
    correctLogicalQubit(q, i, ancBitIdx);
  }
}

__qpu__ void resetAll(qreg q) {
  for (int i = 0; i < q.size(); ++i) {
    // Reset qubits by measure + correct.
    if (Measure(q[i])) {
      X(q[i]);
    }
  }
}

// Error corrected Bell example:
// Note: the 3-q bit-flip code can only protect against X errors.
__qpu__ void bellQEC(qreg q, int nbRuns) {
  using qcor::xasm;
  int ancQbId = 6;
  for (int i = 0; i < nbRuns; ++i) {
    // Apply H before encoding.
    H(q[0]);
    // Encode the qubits into logical qubits.
    encodeQubits(q);
    // Run a QEC cycle
    runQecCycle(q);
    // Apply *logical* CNOT
    cnotLogical(q);
    // Run a QEC cycle
    runQecCycle(q);
    
    // Measure *logical* qubits
    measureLogical(q, 0);
    measureLogical(q, 1);

    // Get *logical* results
    const bool logicalReq0 = getLogicalVal(q, 0);
    const bool logicalReq1 = getLogicalVal(q, 1);
    
    if (logicalReq0 == logicalReq1) {
      std::cout << "Iter " << i << ": Matched!\n";
    } else {
      std::cout << "Iter " << i << ": NOT Matched!\n";
    }
    resetAll(q);
  }
}

int main() {
  // Note: we need 3 physical qubits for each logical qubit +
  // an ancilla qubit for syndrom measurement.
  auto q = qalloc(7);
  bellQEC(q, 100);
}
 No newline at end of file
+43 −0
Original line number Diff line number Diff line
#include <qalloc>
// Compile with: qcor -qpu qpp -qrt ftqc repeat-until-success.cpp
// Execute: ./a.out
// We should get the print out conditioned by the measurement.
// If not using the "ftqc" QRT, this will cause errors since the Measure results
// are not available yet.

// Using Repeat-Until-Success pattern to prepare a quantum state.
// https://docs.microsoft.com/en-us/quantum/user-guide/using-qsharp/control-flow#rus-to-prepare-a-quantum-state
__qpu__ void PrepareStateUsingRUS(qreg q, int maxIter) {
  using qcor::xasm;
  // Note: target = q[0], aux = q[1]
  H(q[1]);
  // We limit the max number of RUS iterations.
  for (int i = 0; i < maxIter; ++i) {
    std::cout << "Iter: " << i << "\n";
    Tdg(q[1]);
    CNOT(q[0], q[1]);
    T(q[1]);

    // In order to measure in the PauliX basis, changes the basis.
    H(q[1]);
    if (!Measure(q[1])) {
      // Success (until (outcome == Zero))
      std::cout << "Success after " << i + 1 << " iterations.\n";
      break;
    } 
    else {
      // Measure 1: |1> state
      // Fix up: Bring the auxiliary and target qubits back to |+> state.
      X(q[1]);
      H(q[1]);
      X(q[0]);
      H(q[0]);
    }
  }
}

int main() {
  // qcor::set_verbose(true);
  auto q = qalloc(2);
  PrepareStateUsingRUS(q, 100);
}
+33 −0
Original line number Diff line number Diff line
#include <qalloc>
// Compile with: qcor -qpu qpp -qrt ftqc simple-demo.cpp
// or with noise: qcor -qpu aer[noise-model:noise_model.json] -qrt ftqc simple-demo.cpp
// Execute: ./a.out
// We should get the print out conditioned by the measurement.
// If not using the "ftqc" QRT, this will cause errors since the Measure results
// are not available yet.
__qpu__ void bell(qreg q, int nbRuns) {
  using qcor::xasm;
  for (int i = 0; i < nbRuns; ++i) {
    H(q[0]);
    CX(q[0], q[1]);
    const bool q0Result = Measure(q[0]);
    const bool q1Result = Measure(q[1]);
    if (q0Result == q1Result) {
      std::cout << "Iter " << i << ": Matched!\n";
    } else {
      std::cout << "Iter " << i << ": NOT Matched!\n";
    }
    // Reset qubits
    if (q0Result) {
      X(q[0]);
    }
    if (q1Result) {
      X(q[1]);
    }
  }
}

int main() {
  auto q = qalloc(2);
  bell(q, 100);
}
+10 −0
Original line number Diff line number Diff line
@@ -152,6 +152,12 @@ void QCORSyntaxHandler::GetReplacement(
        "qcor::__internal__::create_composite(kernel_name);\n";
  OS << "}\n";
  OS << "quantum::set_current_program(parent_kernel);\n";
  // Set the buffer in FTQC mode so that following QRT calls (in new_src) are
  // executed on that buffer.
  OS << "if (runtime_env == QrtType::FTQC) {\n";
  // We only support one buffer in FTQC mode atm.
  OS << "quantum::set_current_buffer(" << bufferNames[0] << ".results());\n";
  OS << "}\n";
  OS << new_src << "\n";
  OS << "}\n";

@@ -207,6 +213,10 @@ void QCORSyntaxHandler::GetReplacement(
    OS << ", " << program_parameters[i];
  }
  OS << ");\n";
  // If this is a FTQC kernel, skip runtime optimization passes and submit.
  OS << "if (runtime_env == QrtType::FTQC) {\n";
  OS << "return;\n";
  OS << "}\n";

  OS << "xacc::internal_compiler::execute_pass_manager();\n";
  OS << "if (optimize_only) {\n";
Loading