Commit 05c14acb authored by Mccaskey, Alex's avatar Mccaskey, Alex
Browse files

Adding new qcor mode that maps kernels to calls to a quantum runtime library...


Adding new qcor mode that maps kernels to calls to a quantum runtime library that can be lowered to LLVM IR for Pass execution. Previous mode should still be operational.

Signed-off-by: Mccaskey, Alex's avatarAlex McCaskey <mccaskeyaj@ornl.gov>
parent 0705f025
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
#include <qalloc>

// Define a multi-register kernel
__qpu__ void bell_multi(qreg q, qreg r) {
  H(q[0]);
  CX(q[0], q[1]);
  H(r[0]);
  CX(r[0], r[1]);
  for (int i = 0; i < q.size(); i++) {
    Measure(q[i]);
    Measure(r[i]);
  }
}

int main() {

  // Create two qubit registers, each size 2
  auto q = qalloc(2);
  auto r = qalloc(2);

  // Run the quantum kernel
  bell_multi(q, r);

  // dump the results
  q.print();
  r.print();
}
 No newline at end of file
+35 −0
Original line number Diff line number Diff line
#include <qalloc>

__qpu__ void test_xasm(qreg q) {
  using qcor::xasm;
  H(q[0]);
  CX(q[0], q[1]);
  for (int i = 0; i < 2; i++)
    X(q[i]);
  Measure(q[0]);
  Measure(q[1]);
}

__qpu__ void test_openqasm(qreg q) {
    using qcor::openqasm;

    creg result[2];

    h q[0];
    cx q[0], q[1];

    measure q[0] -> result[0];
    measure q[1] -> result[1];
}


int main() {
  auto q = qalloc(2);
  test_xasm(q);
  q.print();

  auto qq = qalloc(2);
  test_openqasm(qq);
  qq.print();

}
+11 −14
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@ public:
    const DeclaratorChunk::FunctionTypeInfo &FTI = D.getFunctionTypeInfo();
    std::string kernel_name = D.getName().Identifier->getName().str();
    if (!FTI.Params) {
      diagnostics.Report(D.getBeginLoc(), invalid_no_args);
    //   diagnostics.Report(D.getBeginLoc(), invalid_no_args);
    }

    function_prototype = "(";
@@ -83,27 +83,24 @@ public:

    // std::cout << "FPROTO: " << function_prototype << "\n";
    // If we failed to get the name, then we fail
    if (bufferNames.empty()) {
      diagnostics.Report(D.getBeginLoc(), invalid_qreg_name);
      exit(1);
    }
    // if (bufferNames.empty()) {
    //   diagnostics.Report(D.getBeginLoc(), invalid_qreg_name);
    //   exit(1);
    // }

    // Get Tokens as a string, rewrite code
    // with XACC api calls
    auto kernel_src_and_compiler =
        qcor::run_token_collector(PP, Toks, function_prototype);
    auto kernel_src = kernel_src_and_compiler.first;
    auto compiler_name = kernel_src_and_compiler.second;

    if (qrt) {

      // call to the util function to use xacc to
      // generate new code to the OS
      qcor::map_xacc_kernel_to_qrt_calls(kernel_src, qpu_name, compiler_name,
                                         kernel_name, bufferNames, OS,
                                         (shots > 0 ? shots : 0));
      qcor::run_token_collector_llvm_rt(PP, Toks, function_prototype,
                                        bufferNames, kernel_name, OS, qpu_name);

    } else {
      auto kernel_src_and_compiler =
          qcor::run_token_collector(PP, Toks, function_prototype);
      auto kernel_src = kernel_src_and_compiler.first;
      auto compiler_name = kernel_src_and_compiler.second;
      // std::cout << "HELLO:\n" << kernel_src << "\n";
      // Write new source code in place of the
      // provided quantum code tokens
+309 −45
Original line number Diff line number Diff line
@@ -2,9 +2,11 @@
#include "token_collector.hpp"
#include "xacc.hpp"
#include "xacc_service.hpp"
#include <qalloc>

#include "AllGateVisitor.hpp"

#include "clang/Basic/TokenKinds.h"
#include "clang/Lex/Token.h"
namespace qcor {

void set_verbose(bool verbose) { xacc::set_verbose(verbose); }
@@ -55,75 +57,337 @@ class qrt_mapper : public AllGateVisitor {
protected:
  std::stringstream ss;

  void addOneQubitGate(const std::string name, xacc::Instruction &inst) {
    auto expr = inst.getBitExpression(0);
    if (expr.empty()) {
      ss << "quantum::" + name + "(" << inst.getBufferNames()[0] << "["
         << inst.bits()[0] << "]";

    } else {
      ss << "quantum::" + name + "(" << inst.getBufferNames()[0] << "["
         << inst.getBitExpression(0) << "]";
    }
    if (inst.isParameterized() && inst.name() != "Measure") {
      ss << ", " << inst.getParameter(0).toString();
      for (int i = 1; i < inst.nParameters(); i++) {
        ss << ", " << inst.getParameter(i).toString() << "\n";
      }
    }
    ss << ");\n";
  }

public:
  auto get_new_src() { return ss.str(); }

  void visit(Hadamard &h) override {
    ss << "quantum::h(" << h.getBufferNames()[0] << "[" << h.bits()[0]
       << "]);\n";
  }
  void visit(Hadamard &h) override { addOneQubitGate("h", h); }
  void visit(CNOT &cnot) override {
    ss << "quantum::cnot(" << cnot.getBufferNames()[0] << "[" << cnot.bits()[0]
       << "], " << cnot.getBufferNames()[1] << "[" << cnot.bits()[1] << "]);\n";
  }

  void visit(Rz &rz) override {}
  void visit(Ry &ry) override {}
  void visit(Rx &rx) override {}
  void visit(X &x) override {}
  void visit(Y &y) override {}
  void visit(Z &z) override {}
  void visit(Rz &rz) override { addOneQubitGate("rz", rz); }
  void visit(Ry &ry) override { addOneQubitGate("ry", ry); }
  void visit(Rx &rx) override { addOneQubitGate("rx", rx); }
  void visit(X &x) override { addOneQubitGate("x", x); }
  void visit(Y &y) override { addOneQubitGate("y", y); }
  void visit(Z &z) override { addOneQubitGate("z", z); }
  void visit(CY &cy) override {}
  void visit(CZ &cz) override {}
  void visit(Swap &s) override {}
  void visit(CRZ &crz) override {}
  void visit(CH &ch) override {}
  void visit(S &s) override {}
  void visit(Sdg &sdg) override {}
  void visit(T &t) override {}
  void visit(Tdg &tdg) override {}
  void visit(S &s) override { addOneQubitGate("s", s); }
  void visit(Sdg &sdg) override { addOneQubitGate("sdg", sdg); }
  void visit(T &t) override { addOneQubitGate("t", t); }
  void visit(Tdg &tdg) override { addOneQubitGate("tdg", tdg); }
  void visit(CPhase &cphase) override {}
  void visit(Measure &measure) override {
    ss << "quantum::mz(" << measure.getBufferNames()[0] << "[" << measure.bits()[0]
       << "]);\n";
  }
  void visit(Identity &i) override {}
  void visit(U &u) override {}
  void visit(Measure &measure) override { addOneQubitGate("mz", measure); }
  void visit(Identity &i) override { addOneQubitGate("i", i); }
  void visit(U &u) override { addOneQubitGate("u", u); }
  void visit(IfStmt &ifStmt) override {}
};

void map_xacc_kernel_to_qrt_calls(const std::string &kernel_str, const std::string& qpu_name,
                                  const std::string &compiler_name,
                                  const std::string &kernel_name,
void run_token_collector_llvm_rt(clang::Preprocessor &PP,
                                 clang::CachedTokens &Toks,
                                 const std::string &function_prototype,
                                 std::vector<std::string> bufferNames,
                                  llvm::raw_string_ostream &OS, int shots) {
  auto compiler = xacc::getCompiler(compiler_name);
  auto kernel = compiler->compile(kernel_str)->getComposites()[0];
                                 const std::string &kernel_name,
                                 llvm::raw_string_ostream &OS,
                                 const std::string &qpu_name, int shots) {

  if (!xacc::isInitialized()) {
    xacc::Initialize();
  }

  auto add_spacing = [](const std::string language) {
    if (language == "xasm") {
      return "";
    } else {
      return " ";
    }
  };

  // Programmers can specify the language by saying
  // using qcor::openqasm or something like that, default is xasm
  auto process_inst_stmt = [&](int &i, std::shared_ptr<xacc::Compiler> compiler,
                               clang::Token &current_token,
                               std::string &terminating_char,
                               std::string extra_preamble = "") {
    std::stringstream ss;
    auto current_token_str = PP.getSpelling(current_token);

    ss << extra_preamble;

    while (current_token_str != terminating_char) {
      ss << current_token_str << add_spacing(compiler->name());
      std::cout << "HELLO INST STMT " << current_token_str << "\n";
      i++;
      current_token = Toks[i];
      current_token_str = PP.getSpelling(current_token);
    }
    ss << terminating_char;

    std::cout << "COMPILING\n"
              << function_prototype + "{" + ss.str() + "}"
              << "\n";

    // FIXME, check canParse, and if not, then just write ss.str() to qrt_code
    // somehow

    return compiler
        ->compile("__qpu__ " + function_prototype + "{" + ss.str() + "}")
        ->getComposites()[0]
        ->getInstruction(0);
  };

  auto compiler = xacc::getCompiler("xasm");
  auto terminating_char = compiler->get_statement_terminator();

  std::stringstream qrt_code;
  std::string extra_preamble = "";

  for (int i = 0; i < Toks.size(); i++) {
    auto current_token = Toks[i];
    auto current_token_str = PP.getSpelling(current_token);

    if (current_token.is(clang::tok::kw_using)) {
      // Found using
      // i+3 bc we skip using, qcor and ::;
      auto language = PP.getSpelling(Toks[i + 3]);
      if (language == "openqasm") {
        // use staq
        language = xacc::hasCompiler("staq") ? "staq" : "openqasm";

        std::stringstream sss;
        for (auto &b : bufferNames) {
          // sss << "qreg " << b << "[100];\n";
          auto q = qalloc(100);
          q.setNameAndStore(b.c_str());
        }
        extra_preamble += sss.str();
      }

      compiler = xacc::getCompiler(language);
      terminating_char = compiler->get_statement_terminator();
      // +4 to skip ';' too
      i = i + 4;
      continue;
    }

    // if (current_token_str == "qreg") {

    //   // allocate called within kernel, likely with openqasm
    //   // get the size and allocated it, but dont add to kernel string

    //   // skip qreg
    //   i++;
    //   current_token = Toks[i];

    //   // get qreg var name
    //   auto variable_name = PP.getSpelling(current_token);

    //   // skip [
    //   i += 2;
    //   current_token = Toks[i];

    //   std::cout << variable_name
    //             << ", CURRENT: " << PP.getSpelling(current_token) << "\n";
    //   auto size = std::stoi(PP.getSpelling(current_token));

    //   // skip ] and ;
    //   i += 2;
    //   std::cout << "NOW WE ARE " << PP.getSpelling(Toks[i]) << "\n";

    //   auto q = qalloc(size);
    //   q.setNameAndStore(variable_name.c_str());

    //   // Update function_prototype FIXME
    //   continue;
    // }

    if (current_token_str == "creg") {

      std::stringstream sss;
      while (current_token.isNot(clang::tok::semi)) {
        sss << current_token_str << " ";
        i++;
        current_token = Toks[i];
        current_token_str = PP.getSpelling(current_token);
      }

      //   std::cout << "saving creg; " << sss.str() << "\n";

      extra_preamble += sss.str() + ";\n";

      continue;
    }

    if (current_token.is(clang::tok::kw_for)) {
      //   std::cout << "Found for statment\n";

      // slurp up the for
      std::stringstream for_ss;

      // eat up the l_paren
      for_ss << "for (";
      int seen_l_paren = 1;
      i += 2;
      current_token = Toks[i];

      while (seen_l_paren > 0) {
        if (current_token.is(clang::tok::l_paren))
          seen_l_paren++;
        if (current_token.is(clang::tok::r_paren))
          seen_l_paren--;
        for_ss << PP.getSpelling(current_token) << " ";
        i++;
        current_token = Toks[i];
      }
      //   while (current_token.isNot(clang::tok::r_paren) || seen_l_paren > 0)
      //   {
      //     if (current_token.is(clang::tok::l_paren)) seen_l_paren++;
      //     if (current_token.is(clang::tok::r_paren)) seen_l_paren--;

      //     for_ss << PP.getSpelling(current_token) << " ";
      //     i++;
      //     current_token = Toks[i];
      //   }
      //   for_ss << ") ";
      qrt_code << for_ss.str();

      std::cout << "FOR: " << for_ss.str() << "\n";
      // slurp up the )
      //   i++;
      //   current_token = Toks[i];

      // we could have for stmt with l_brace or without for a single inst
      if (current_token.is(clang::tok::l_brace)) {
        qrt_code << " {\n";

        // eat up the {
        i++;
        current_token = Toks[i];

        // Now loop through the for loop body
        int l_brace_count = 1;
        while (l_brace_count != 0) {
          // In here we have statements separated by compiler terminator
          // (default ';')
          auto inst = process_inst_stmt(i, compiler, current_token,
                                        terminating_char, extra_preamble);
          {
            auto visitor = std::make_shared<qrt_mapper>();
            inst->accept(visitor);
            qrt_code << visitor->get_new_src();
          }
          // missing ';', eat it up too
          i++;
          current_token = Toks[i];
          std::cout << "IN HERE\n" << PP.getSpelling(current_token) << "\n";

          if (current_token.is(clang::tok::l_brace)) {
            l_brace_count++;
          }

          if (current_token.is(clang::tok::r_brace)) {
            l_brace_count--;
          }
          std::cout << "LBRACE: " << l_brace_count << "\n";
        }
        std::cout << "HERE:\n" << qrt_code.str() << "\n";

        // now eat the r_brace
        i++;
        // current_token = Toks[i];
        qrt_code << "}\n";

        continue;
      } else {
        // Here we don't have a l_brace, so we just have the one
        // quantum instruction

        qrt_code << "\n   ";

        auto inst = process_inst_stmt(i, compiler, current_token,
                                      terminating_char, extra_preamble);
        {
          auto visitor = std::make_shared<qrt_mapper>();
          inst->accept(visitor);
          qrt_code << visitor->get_new_src();
        }

  xacc::InstructionIterator iter(kernel);
  while (iter.hasNext()) {
    auto next = iter.next();
    if (!next->isComposite()) {
      next->accept(visitor);
        // missing ';', eat it up too
        i++;
        current_token = Toks[i];
      }
    }

  auto code = visitor->get_new_src();
    if (current_token.is(clang::tok::kw_if)) {
    }

    // this is a quantum statement + terminating char
    // slurp up to the terminating char
    auto inst = process_inst_stmt(i, compiler, current_token, terminating_char,
                                  extra_preamble);
    {
      auto visitor = std::make_shared<qrt_mapper>();
      inst->accept(visitor);
      qrt_code << visitor->get_new_src();
    }
  }

  std::cout << "HELLO: " << code << "\n";
  //   std::cout << "QRT CODE:\n" << qrt_code.str() << "\n";

  OS << "quantum::initialize(\""<< qpu_name << "\", \"" << kernel_name << "\");\n";
  OS << "quantum::initialize(\"" << qpu_name << "\", \"" << kernel_name
     << "\");\n";
  for (auto &buf : bufferNames) {
    OS << buf << ".setNameAndStore(\"" + buf + "\");\n";
  }
  if (shots > 0) {
    OS << "quantum::set_shots(" << shots << ");\n";
  }
  OS << code;
  OS << "quantum::submit(" << bufferNames[0] << ".results()";
  OS << qrt_code.str();
  OS << "if (__execute) {\n";

  if (bufferNames.size() > 1) {
    OS << "xacc::AcceleratorBuffer * buffers[" << bufferNames.size() << "] = {";
    OS << bufferNames[0] << ".results()";
    for (unsigned int k = 1; k < bufferNames.size(); k++) {
      OS << ", " << bufferNames[k] << ".results()";
    }
    OS << "};\n";
    OS << "quantum::submit(buffers," << bufferNames.size();
  } else {
    OS << "quantum::submit(" << bufferNames[0] << ".results()";
  }

//   OS << "quantum::submit(" << bufferNames[0] << ".results()";
//   for (unsigned int k = 1; k < bufferNames.size(); k++) {
//     OS << ", " << bufferNames[k] << ".results()";
//   }

  OS << ");\n";
  OS << "}";
}

} // namespace qcor
 No newline at end of file
+7 −5
Original line number Diff line number Diff line
@@ -13,11 +13,13 @@ std::pair<std::string, std::string>
run_token_collector(clang::Preprocessor &PP, clang::CachedTokens &Toks,
                    const std::string &function_prototype);

void map_xacc_kernel_to_qrt_calls(const std::string &kernel_str, const std::string& qpu_name,
                                  const std::string &compiler_name,
                                  const std::string &kernel_name,
void run_token_collector_llvm_rt(clang::Preprocessor &PP,
                                 clang::CachedTokens &Toks,
                                 const std::string &function_prototype,
                                 std::vector<std::string> bufferNames,
                                  llvm::raw_string_ostream &OS, int shots = 0);
                                 const std::string &kernel_name,
                                 llvm::raw_string_ostream &OS,
                                 const std::string &qpu_name, int shots = 0);

void set_verbose(bool verbose);
void info(const std::string &s);
Loading