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

Merge pull request #17 from tnguyen-ornl/tnguyen/qpe

Update quantum runtime 
parents bde17052 00d3bea3
Loading
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -9,3 +9,5 @@ add_test(NAME qrt_deuteron_exp_inst COMMAND ${CMAKE_BINARY_DIR}/qcor -qrt -c ${C
add_test(NAME qrt_deuteron_task_initiate COMMAND ${CMAKE_BINARY_DIR}/qcor -qrt -c ${CMAKE_CURRENT_SOURCE_DIR}/deuteron_task_initiate.cpp)
add_test(NAME qrt_qaoa_example COMMAND ${CMAKE_BINARY_DIR}/qcor -qrt -c ${CMAKE_CURRENT_SOURCE_DIR}/qaoa_example.cpp)
add_test(NAME qrt_simple-objective-function COMMAND ${CMAKE_BINARY_DIR}/qcor -qrt -c ${CMAKE_CURRENT_SOURCE_DIR}/simple-objective-function.cpp)
add_test(NAME qrt_qpe_example COMMAND ${CMAKE_BINARY_DIR}/qcor -qrt -c ${CMAKE_CURRENT_SOURCE_DIR}/qpe_example_qrt.cpp)
add_test(NAME qrt_kernel_include COMMAND ${CMAKE_BINARY_DIR}/qcor -qrt -c ${CMAKE_CURRENT_SOURCE_DIR}/multiple_kernels.cpp)
+34 −0
Original line number Diff line number Diff line
// The header file which contains QFT kernel def
#include "qft.hpp"

// Entry point kernel
__qpu__ void f(qreg q) {
  const auto nQubits = q.size();
  // Add some gates
  X(q[1]);

  // Call qft kernel (defined in a separate header file)
  qft(q, nQubits);  
  
  // Inverse QFT:
  iqft(q, nQubits);
  
  // Measure all qubits
  for (int qIdx = 0; qIdx < nQubits; ++qIdx) {
    Measure(q[qIdx]);
  }  
}

// Compile:
// qcor -o multiple_kernels -qpu qpp -shots 1024 -qrt multiple_kernels.cpp
// Execute:
// ./multiple_kernels
// Expected: "010" state (all shots) since we do X(q[1]) the QFT->IQFT (identity)
int main(int argc, char **argv) {
  // Allocate 3 qubits
  auto q = qalloc(3);
  // Call entry-point kernel
  f(q);
  // dump the results
  q.print();
}
+47 −0
Original line number Diff line number Diff line
#include "qalloc.hpp"

// Demonstrating Bell Test using multiple kernels

__qpu__ void measureAllQubits(qreg q) {
  for (int qIdx = 0; qIdx < q.size(); ++qIdx) {
    Measure(q[qIdx]);
  }  
}

// Entangle all qubit in a qubit register with a master qubit
__qpu__ void entangleAll(qreg q, int masterBitIdx) {
  for (int qIdx = 0; qIdx < masterBitIdx; ++qIdx) {
    CNOT(q[masterBitIdx], q[qIdx]);
  }  
  
  for (int qIdx = masterBitIdx + 1; qIdx < q.size(); ++qIdx) {
    CNOT(q[masterBitIdx], q[qIdx]);
  }  
}

// Entry point kernel
__qpu__ void bellTest(qreg qBits) {
  // Add some gates
  // Entangle the *middle* qubit with all other qubits
  int masterBit = qBits.size() / 2;
  // Hadamard
  H(qBits[masterBit]);
  // Entangle
  entangleAll(qBits, masterBit);
  
  // Measure all qubits
  measureAllQubits(qBits);
}

// Compile:
// qcor -o multiple_kernels -qpu qpp -shots 1024 -qrt multiple_kernels_Bell_test.cpp
int main(int argc, char **argv) {
  // Allocate 7 qubits: 
  // i.e. Hadamard on q[3] and entangle q[3] with other qubits.
  auto q = qalloc(7);
  // Call entry-point kernel
  bellTest(q);
  // dump the results
  // Expect: ~50-50 for "0000000" and "1111111"
  q.print();
}

examples/qrt/qft.hpp

0 → 100644
+48 −0
Original line number Diff line number Diff line
// Example code structure whereby quantum kernels are defined
// in separate header files which can be included into a cpp file
// which is compiled by QCOR.
#pragma once

#include "qalloc.hpp"

// QFT kernel:
// Input: Qubit register and the max qubit index for the QFT,
// i.e. allow us to do QFT on a subset of the register [0, maxBitIdx)
__qpu__ void qft(qreg q, int maxBitIdx) {
  // Local Declarations
  const auto nQubits = maxBitIdx;

  for (int qIdx = 0; qIdx < nQubits; ++qIdx) {
    auto bitIdx = nQubits - qIdx - 1;
    H(q[bitIdx]);
    for (int j = 0; j < bitIdx; ++j) {
      const double theta = M_PI/std::pow(2.0, bitIdx - j);
      CPhase(q[j], q[bitIdx], theta);
    }
  }

  // Swap qubits
  for (int qIdx = 0; qIdx < nQubits / 2; ++qIdx) {
    Swap(q[qIdx], q[nQubits - qIdx - 1]);
  }
}

// Inverse QFT
__qpu__ void iqft(qreg q, int maxBitIdx) {
  // Local Declarations
  const auto nQubits = maxBitIdx;
  // Swap qubits
  for (int qIdx = 0; qIdx < nQubits / 2; ++qIdx) {
    Swap(q[qIdx], q[nQubits - qIdx - 1]);
  }

  for (int qIdx = 0; qIdx < nQubits - 1; ++qIdx) {
    H(q[qIdx]);
    for (int j = 0; j < qIdx + 1; ++j) {
      const double theta = -M_PI/std::pow(2.0, qIdx + 1 - j);
      CPhase(q[j], q[qIdx + 1], theta);
    }
  }

  H(q[nQubits - 1]);
}
 No newline at end of file
+63 −0
Original line number Diff line number Diff line
#include "qcor.hpp"
// Use the pre-defined IQFT kernel
#include "qft.hpp"

using namespace qcor;

// The Oracle: T gate
__qpu__ void compositeOp(qreg q) {
  // T gate on the last qubit
  int bitIdx = q.size() - 1;
  T(q[bitIdx]);
}

// Example of Quantum Phase Estimation circuit using QCOR runtime.
// Compile with:
// qcor -o qpe -qpu qpp -shots 1024 -qrt qpe_example_qrt.cpp
// ----------------------------------------------------------------
// In this example, we demonstrate a simple QPE algorithm, i.e.
// i.e. Oracle(|State>) = exp(i*Phase)*|State>
// and we need to estimate that Phase value.
// The Oracle in this case is a T gate and the eigenstate is |1>
// i.e. T|1> = exp(i*pi/4)|1>
// We use 3 counting bits => totally 4 qubits.
__qpu__ void QuantumPhaseEstimation(qreg q) {
  const auto nQubits = q.size();
  // Last qubit is the eigenstate of the unitary operator 
  // hence, prepare it in |1> state
  X(q[nQubits - 1]);

  // Apply Hadamard gates to the counting qubits:
  for (int qIdx = 0; qIdx < nQubits - 1; ++qIdx) {
    H(q[qIdx]);
  }

  // Apply Controlled-Oracle: in this example, Oracle is T gate;
  // i.e. Ctrl(T) = CPhase(pi/4)
  const auto bitPrecision = nQubits - 1;
  for (int32_t i = 0; i < bitPrecision; ++i) {
    const int nbCalls = 1 << i;
    for (int j = 0; j < nbCalls; ++j) {
      int ctlBit = i;
      // Controlled-Oracle
      Controlled::Apply(ctlBit, compositeOp, q);
    }
  }

  // Inverse QFT on the counting qubits:
  inverseQuantumFourierTransform(q, bitPrecision);

  // Measure counting qubits
  for (int qIdx = 0; qIdx < bitPrecision; ++qIdx) {
    Measure(q[qIdx]);
  }
}

int main(int argc, char **argv) {
  // Allocate 4 qubits, i.e. 3-bit precision
  auto q = qalloc(4);
  QuantumPhaseEstimation(q);
  // dump the results
  // EXPECTED: only "100" bitstring
  q.print();
}
Loading