Commit c9ea638b authored by Nguyen, Thien Minh's avatar Nguyen, Thien Minh
Browse files

QIR runtime update + examples



- adding a QFT example for Q#

- add a missing array_slice_1d QIR function, delegate to the generic slice at dimension 0.

- Modify the NISQ import helper for Q# to handle kernels with only 1 qreg arg.

Signed-off-by: default avatarThien Nguyen <nguyentm@ornl.gov>
parent 10c1ef6d
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
#include <iostream> 
#include <vector>
#include "import_kernel_utils.hpp"

// Util pre-processor to wrap Q# operation 
// in a QCOR QuantumKernel.
qcor_import_qsharp_kernel(QCOR__Bell);

int main() {
  // Allocate 2 qubits
  auto q = qalloc(2);
  QCOR__Bell(q);
  q.print();
  return 0;
}
 No newline at end of file
+65 −4
Original line number Diff line number Diff line
@@ -3,9 +3,9 @@
// external Q# kernel (compiled to QIR)
#include "qir-types.hpp"

#define STRINGIZE(arg) STRINGIZE1(arg)
#define STRINGIZE1(arg) STRINGIZE2(arg)
#define STRINGIZE2(arg) #arg
#define _STRINGIZE(arg) _STRINGIZE1(arg)
#define _STRINGIZE1(arg) _STRINGIZE2(arg)
#define _STRINGIZE2(arg) #arg

#define CONCATENATE(arg1, arg2) CONCATENATE1(arg1, arg2)
#define CONCATENATE1(arg1, arg2) CONCATENATE2(arg1, arg2)
@@ -40,7 +40,13 @@
#define ARGS_LIST_FOR_FUNC_INVOKE(...)                                         \
  FOR_EACH(CONSTRUCT_VAR_NAME_LIST, __VA_ARGS__)

#define qcor_import_qsharp_kernel(OPERATION_NAME, ...)                         \
#define GET_MACRO(_0, _1, _2, NAME, ...) NAME
#define qcor_import_qsharp_kernel(...)                                         \
  GET_MACRO(_0, ##__VA_ARGS__, qcor_import_qsharp_kernel_var,                  \
            qcor_import_qsharp_kernel_no_var, unknown)                         \
  (__VA_ARGS__)

#define qcor_import_qsharp_kernel_var(OPERATION_NAME, ...)                     \
  extern "C" void OPERATION_NAME##__body(::Array *, __VA_ARGS__);              \
  class OPERATION_NAME                                                         \
      : public qcor::QuantumKernel<class OPERATION_NAME, qreg, __VA_ARGS__> {  \
@@ -97,6 +103,61 @@
    class OPERATION_NAME kernel(q ARGS_LIST_FOR_FUNC_INVOKE(__VA_ARGS__));     \
  }

#define qcor_import_qsharp_kernel_no_var(OPERATION_NAME)                       \
  extern "C" void OPERATION_NAME##__body(::Array *);                           \
  class OPERATION_NAME                                                         \
      : public qcor::QuantumKernel<class OPERATION_NAME, qreg> {               \
  private:                                                                     \
    ::Array *m_qubits = nullptr;                                               \
    friend class qcor::QuantumKernel<class OPERATION_NAME, qreg>;              \
                                                                               \
  protected:                                                                   \
    void operator()(qreg q) {                                                  \
      if (!parent_kernel) {                                                    \
        std::cout << "Create parent kernel\n"; \
        parent_kernel = qcor::__internal__::create_composite(kernel_name);     \
      }                                                                        \
      std::cout << "Parent:\n" << parent_kernel->toString() << "\n";           \
      quantum::set_current_program(parent_kernel);                             \
      if (runtime_env == QrtType::FTQC) {                                      \
        quantum::set_current_buffer(q.results());                              \
      }                                                                        \
      /* Convert the qreg to QIR Array of Qubits */                            \
      if (!m_qubits) {                                                         \
        m_qubits = new ::Array(q.size());                                      \
        for (int i = 0; i < q.size(); ++i) {                                   \
          auto qubit = Qubit::allocate();                                      \
          int8_t *arrayPtr = (*m_qubits)[i];                                   \
          auto qubitPtr = reinterpret_cast<Qubit **>(arrayPtr);                \
          *qubitPtr = qubit;                                                   \
        }                                                                      \
      }                                                                        \
      std::cout << "INVOKE:\n" << parent_kernel->toString() << "\n";           \
      OPERATION_NAME##__body(m_qubits); /* std::cout << "INVOKE:\n" <<         \
                            parent_kernel->toString(); */                      \
    }                                                                          \
                                                                               \
  public:                                                                      \
    inline static const std::string kernel_name = #OPERATION_NAME;             \
    OPERATION_NAME(qreg q) : QuantumKernel<OPERATION_NAME, qreg>(q) {}         \
    OPERATION_NAME(std::shared_ptr<qcor::CompositeInstruction> _parent,        \
                   qreg q)                                                     \
        : QuantumKernel<OPERATION_NAME, qreg>(_parent, q) {}                   \
    virtual ~OPERATION_NAME() {                                                \
      if (disable_destructor) {                                                \
        return;                                                                \
      }                                                                        \
      auto [q] = args_tuple;                                                   \
      std::cout << "here\n"; \
      operator()(q);                                                           \
      xacc::internal_compiler::execute_pass_manager();                         \
      if (is_callable) {                                                       \
        quantum::submit(q.results());                                          \
      }                                                                        \
    }                                                                          \
  };                                                                           \
  void OPERATION_NAME(qreg q) { class OPERATION_NAME kernel(q); }

// Usage:
// qcor_import_qsharp_kernel(MyQsharpKernel, double, int);
// This will inject a QCOR kernel of signature:
+46 −0
Original line number Diff line number Diff line

namespace QCOR {
    open QCOR.Intrinsic;
    operation SWAP(q1 : Qubit, q2: Qubit) : Unit {
        CNOT(q1, q2);
        CNOT(q2, q1);
        CNOT(q1, q2);
    }
    
    // 1-qubit QFT
    operation OneQubitQFT (q : Qubit) : Unit {
        H(q);
    }
    // Rotation gate
    // Applies a rotation about the |1⟩ state by an angle 
    // specified as a dyadic fraction.
    operation Rotation (q : Qubit, k : Int) : Unit is Adj+Ctl {
        let angle = 2.0 * PI() / IntAsDouble(1 <<< k);
        Rz(angle, q);
    }
    // Prepare binary fraction exponent in place (quantum input)
    operation BinaryFractionQuantumInPlace (register : Qubit[]) : Unit {
        OneQubitQFT(register[0]);
        for ind in 1 .. Length(register) - 1 {
            Controlled Rotation([register[ind]], (register[0], ind + 1));
        }
    }
    // Reverse the order of qubits
    operation ReverseRegister (register : Qubit[]) : Unit {
        let N = Length(register);
        for ind in 0 .. N / 2 - 1 {
            SWAP(register[ind], register[N - 1 - ind]);
        }
    }
    // Quantum Fourier transform
    // Input: A register of qubits in state |j₁j₂...⟩
    // Goal: Apply quantum Fourier transform to the input register
    operation QuantumFourierTransform(register : Qubit[]) : Unit {
        let n = Length(register);
        for i in 0 .. n - 1 {
            BinaryFractionQuantumInPlace(register[i ...]);
        }
        ReverseRegister(register);
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -104,6 +104,9 @@ Array *__quantum__rt__array_slice(Array *array, int32_t dim,
// this local func.
Array *quantum__rt__array_slice(Array *array, int32_t dim, Range range);

// 1D-Array slice
Array *__quantum__rt__array_slice_1d(Array *array, int64_t range_start,
                                     int64_t range_step, int64_t range_end);
// Ref. counting
void __quantum__rt__array_update_alias_count(Array *array, int32_t increment);
void __quantum__rt__array_update_reference_count(Array *aux, int32_t count);
+6 −0
Original line number Diff line number Diff line
@@ -22,6 +22,12 @@ Array *__quantum__rt__array_slice(Array *array, int32_t dim,
                                  {range_start, range_step, range_end});
}

Array *__quantum__rt__array_slice_1d(Array *array, int64_t range_start,
                                     int64_t range_step, int64_t range_end) {
  return __quantum__rt__array_slice(array, 0, range_start, range_step,
                                    range_end);
}

Array *quantum__rt__array_slice(Array *array, int32_t dim, Range range) {
  if (verbose)
    std::cout << "[qir-qrt] Extract array slice (dim = " << dim
Loading