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

Merge branch 'master' into tnguyen/qasm3-as-qsharp-callables



Signed-off-by: default avatarThien Nguyen <nguyentm@ornl.gov>

# Conflicts:
#	mlir/parsers/qasm3/visitor_handlers/subroutine_handler.cpp
#	tools/driver/qcor.in
parents e40819c4 747bea78
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
OPENQASM 3;

// QCOR can scan for this and make 
// sure MLIRGen does not add main()
// This is useful for QASM3 files that are 
// purely library functions
#pragma no_entrypoint;

// Inverse QFT subroutine on n_counting qubits
def inverse_qft(int[64]:nc) qubit[DYNAMIC]:qq {
    for i in [0:nc/2] {
        swap qq[i], qq[nc-i-1];
    }
    for i in [0:nc-1] {
        h qq[i];
        int j = i + 1;
        int y = i;
        while (y >= 0) {
            double theta = -pi / (2^(j-y));
            cphase(theta) qq[j], qq[y];
            y -= 1;
        }
    }
    h qq[nc-1];
}
 No newline at end of file
+46 −0
Original line number Diff line number Diff line
#include "qir_nisq_kernel_utils.hpp"

// Compile:
// qcor qft.qasm qpe.cpp -shots 1024 
using QPEOracleSignature = KernelSignature<qubit>;

// External QASM3 function with signature void(qreg, int)
// All imported kernels assumed to take qreg as first arg
qcor_import_qasm3_kernel(inverse_qft, int);

__qpu__ void qpe(qreg q, QPEOracleSignature oracle) {
  // Extract the counting qubits and the state qubit
  auto counting_qubits = q.extract_range({0,3});
  auto state_qubit = q[3];
  // Put it in |1> eigenstate
  X(state_qubit);
  // Create uniform superposition on all 3 qubits
  H(counting_qubits);

  // run ctr-oracle operations
  for (auto i : range(counting_qubits.size())) {
    const int nbCalls = 1 << i;
    for (auto j : range(nbCalls)) {
      oracle.ctrl(counting_qubits[i], state_qubit);
    }
  }

  // Run Inverse QFT on counting qubits
  // Using the Q# Kernel (wrapped as a QCOR kernel)
  // Signature is void(qreg, int) as per above import stmt
  inverse_qft(counting_qubits, counting_qubits.size());

  // Measure the counting qubits
  Measure(counting_qubits);
}

// Oracle to consider
__qpu__ void oracle(qubit q) { T(q); }

int main(int argc, char **argv) {
  auto q = qalloc(4);
  qpe::print_kernel(q, oracle);
  // Run
  qpe(q, oracle);
  q.print();
}
+1 −3
Original line number Diff line number Diff line
#include <iostream> 
#include <vector>
#include "qir_nisq_kernel_utils.hpp"

// Compile:
// qcor -qdk-version 0.17.2106148041-alpha qft.qs qft_driver.cpp -shots 1024 -print-final-submission
// qcor -qdk-version 0.17.2106148041-alpha qft.qs qpe.cpp -shots 1024 -print-final-submission
using QPEOracleSignature = KernelSignature<qubit>;
qcor_import_qsharp_kernel(QCOR__IQFT);

+45 −4
Original line number Diff line number Diff line
@@ -86,10 +86,14 @@ antlrcpp::Any qasm3_visitor::visitSubroutineDefinition(
      } else {
        argument_types.push_back(array_type);
        auto designator = quantum_arg->designator()->getText();
        if (designator == "[DYNAMIC]") {
          arg_attributes.push_back({""});
        } else {
          auto qreg_size =
              symbol_table.evaluate_constant_integer_expression(designator);
          arg_attributes.push_back({std::to_string(qreg_size)});
        }
      }

      arg_names.push_back(quantum_arg->association()->Identifier()->getText());
    }
@@ -159,7 +163,7 @@ antlrcpp::Any qasm3_visitor::visitSubroutineDefinition(

  auto main_block = builder.saveInsertionPoint();

  mlir::FuncOp function;
  mlir::FuncOp function, interop_function;
  if (has_return) {
    auto func_type = builder.getFunctionType(argument_types, return_type);
    auto proto = mlir::FuncOp::create(builder.getUnknownLoc(), subroutine_name,
@@ -215,6 +219,43 @@ antlrcpp::Any qasm3_visitor::visitSubroutineDefinition(

  symbol_table.set_last_created_block(nullptr);

  // Add __interop__ function here so we can invoke from C++.
  mlir::FunctionType interop_func_type;
  // QASM3 subroutines have classical args followed by qubit args
  // for qcor interop, we need qubit/qreg arg first.
  // FIXME Only do this for subroutines with a single qubit[] array.
  std::reverse(argument_types.begin(), argument_types.end());
  if (has_return) {
    interop_func_type = builder.getFunctionType(argument_types, return_type);
  } else {
    interop_func_type = builder.getFunctionType(argument_types, llvm::None);
  }

  auto interop =
      mlir::FuncOp::create(builder.getUnknownLoc(),
                           subroutine_name + "__interop__", interop_func_type);
  auto& interop_entryBlock = *interop.addEntryBlock();
  builder.setInsertionPointToStart(&interop_entryBlock);

  std::vector<mlir::BlockArgument> vec_to_reverse;
  for (int i = 0; i < arguments.size(); i++) {
    vec_to_reverse.push_back(interop_entryBlock.getArgument(i));
  }
  std::reverse(vec_to_reverse.begin(), vec_to_reverse.end());

  auto call_op_interop = builder.create<mlir::CallOp>(
      builder.getUnknownLoc(), function, llvm::makeArrayRef(vec_to_reverse));
  if (has_return) {
    builder.create<mlir::ReturnOp>(builder.getUnknownLoc(),
                                   call_op_interop.getResults());
  } else {
    builder.create<mlir::ReturnOp>(builder.getUnknownLoc());
  }

  builder.restoreInsertionPoint(main_block);

  m_module.push_back(interop);
  
  // TODO: add a compile switch to enable/disable this export: 
  add_body_wrapper(builder, subroutine_name, m_module, function);
  return 0;
+39 −1
Original line number Diff line number Diff line
@@ -89,3 +89,41 @@
// - Argument translation is not currently supported: i.e. can only handle
// simple types: int, double; not vectors.
// - Max number of arguments: 8 (add more macros if needed)


#define qcor_import_qasm3_kernel(...)                                         \
  GET_MACRO(_0, ##__VA_ARGS__, qcor_import_qasm3_kernel_var,                  \
            qcor_import_qasm3_kernel_no_var, unknown)                         \
  (__VA_ARGS__)

// Note: By convention, the Q# LLVM func name has the __body suffix:
// Strategy: define a __qpu__ function which call the LLVM func.
#define qcor_import_qasm3_kernel_var(OPERATION_NAME, ...)                     \
  extern "C" void OPERATION_NAME##__interop__(::Array *, __VA_ARGS__);              \
  __qpu__ void OPERATION_NAME(                                                 \
      qreg q ARGS_LIST_FOR_FUNC_SIGNATURE(__VA_ARGS__)) {                      \
    auto m_qubits = new ::Array(q.size());                                     \
    for (int i = 0; i < q.size(); ++i) {                                       \
      auto qubit = Qubit::create(q[i].second);                                 \
      int8_t *arrayPtr = (*m_qubits)[i];                                       \
      auto qubitPtr = reinterpret_cast<Qubit **>(arrayPtr);                    \
      (*qubitPtr) = qubit;                                                     \
    }                                                                          \
    OPERATION_NAME##__interop__(m_qubits ARGS_LIST_FOR_FUNC_INVOKE(__VA_ARGS__));   \
    delete m_qubits;                                                           \
  }

#define qcor_import_qasm3_kernel_no_var(OPERATION_NAME)                       \
  extern "C" void OPERATION_NAME##__interop__(::Array *);                           \
  __qpu__ void OPERATION_NAME(qreg q) {                                        \
    auto m_qubits = new ::Array(q.size());                                     \
    for (int i = 0; i < q.size(); ++i) {                                       \
      auto qubit = Qubit::create(q[i].second);                                 \
      int8_t *arrayPtr = (*m_qubits)[i];                                       \
      auto qubitPtr = reinterpret_cast<Qubit **>(arrayPtr);                    \
      (*qubitPtr) = qubit;                                                     \
    }                                                                          \
    OPERATION_NAME##__interop__(m_qubits);                                          \
    delete m_qubits;                                                           \
  }
  
 No newline at end of file
Loading