Commit fd5e3a49 authored by Mccaskey, Alex's avatar Mccaskey, Alex
Browse files

working initial version of llvm functionpass that optimizes flat qasm kernel...


working initial version of llvm functionpass that optimizes flat qasm kernel by delegating to xacc ir transformations. adding llvm compiler implementation that provides translation impl to map xacc ir to llvm ir.

Signed-off-by: Mccaskey, Alex's avatarAlex McCaskey <mccaskeyaj@ornl.gov>
parent 875ba714
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@ add_library(${LIBRARY_NAME}
            token_collector_util.cpp)

target_include_directories(${LIBRARY_NAME}
                           PUBLIC .
                           PUBLIC . ${CMAKE_SOURCE_DIR}/runtime/qrt
                                  ${CLANG_INCLUDE_DIRS}
                                  ${LLVM_INCLUDE_DIRS})

+1 −106
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@
#include <limits>
#include <qalloc>

#include "AllGateVisitor.hpp"
#include "qrt_mapper.hpp"

#include "clang/Basic/TokenKinds.h"
#include "clang/Lex/Token.h"
@@ -55,111 +55,6 @@ run_token_collector(clang::Preprocessor &PP, clang::CachedTokens &Toks,
  return std::make_pair(kernel_src, compiler_name);
}

using namespace xacc::quantum;

class qrt_mapper : public AllGateVisitor,
                   public xacc::InstructionVisitor<Circuit> {
protected:
  std::stringstream ss;
  // The kernel name of the CompositeInstruction 
  // that this mapper is visiting.
  std::string kernelName;
  void addOneQubitGate(const std::string name, xacc::Instruction &inst) {
    auto expr = inst.getBitExpression(0);
    ss << "quantum::" + name + "(" << inst.getBufferNames()[0] << "["
         << (expr.empty() ? std::to_string(inst.bits()[0]) : expr) << "]";
    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";
  }

  void addTwoQubitGate(const std::string name, xacc::Instruction &inst) {
    auto expr_src = inst.getBitExpression(0);
    auto expr_tgt = inst.getBitExpression(1);
    ss << "quantum::" + name + "(" << inst.getBufferNames()[0] << "["
       << (expr_src.empty() ? std::to_string(inst.bits()[0]) : expr_src)
       << "], " << inst.getBufferNames()[1] << "["
       << (expr_tgt.empty() ? std::to_string(inst.bits()[1]) : expr_tgt) << "]";
    // Handle parameterized gate:
    if (inst.isParameterized()) {
      ss << ", " << inst.getParameter(0).toString();
      for (int i = 1; i < inst.nParameters(); i++) {
        ss << ", " << inst.getParameter(i).toString();
      }
    }
    ss << ");\n";   
  }

public:
  // Ctor: cache the kernel name of the CompositeInstruction
  qrt_mapper(const std::string& top_level_kernel_name):
    kernelName(top_level_kernel_name)
  {}

  auto get_new_src() { return ss.str(); }
  // One-qubit gates
  void visit(Hadamard &h) override { addOneQubitGate("h", h); }
  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(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); }
  
  // Two-qubit gates
  void visit(CNOT &cnot) override { addTwoQubitGate("cnot", cnot); }
  void visit(CY &cy) override { addTwoQubitGate("cy", cy); }
  void visit(CZ &cz) override { addTwoQubitGate("cz", cz); }
  void visit(Swap &s) override { addTwoQubitGate("swap", s); }
  void visit(CRZ &crz) override { addTwoQubitGate("crz", crz); }
  void visit(CH &ch) override { addTwoQubitGate("ch", ch); }
  void visit(CPhase &cphase) override { addTwoQubitGate("cphase", cphase); }
    
  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(Circuit &circ) override {
    if (circ.name() == kernelName) {
      return;
    }
    if (circ.name() == "exp_i_theta") {
      ss << "quantum::exp(" << circ.getBufferNames()[0] << ", "
         << circ.getArguments()[0]->name << ", " << circ.getArguments()[1]->name
         << ");\n";
    }
    else {
      // Call a previously-defined QCOR kernel:
      // In this context, we disable __execute flag around this hence
      // this sub-kernel will not be submitted.
      // i.e. only the outer-most kernel will be submitted.
      // Open a new scope since we use a local var '__cached_execute_flag'
      ss << "{\n";
      // Cache the state of the __execute flag
      ss << "const auto __cached_execute_flag = __execute;\n";
      // Reset the flag:
      ss << "__execute = false;\n";
      // Add the circuit invocation.
      ss << circ.name() << "(" << circ.getBufferNames()[0];
      for (const auto& arg : circ.getArguments()){
        ss << ", " << arg->name;
      }
      ss << ")" << ";\n";
      // Reinstate the __execute flag
      ss << "__execute = __cached_execute_flag;\n";
      ss << "}\n";
    }
  }
  void visit(IfStmt &ifStmt) override {}
};

void run_token_collector_llvm_rt(clang::Preprocessor &PP,
                                 clang::CachedTokens &Toks,
                                 const std::string &function_prototype,
+49 −52
Original line number Diff line number Diff line
@@ -10,9 +10,9 @@
#include "Observable.hpp"
#include "Optimizer.hpp"

#include "PauliOperator.hpp"
#include "qalloc"
#include "xacc_internal_compiler.hpp"
#include "PauliOperator.hpp"

#include "qrt.hpp"

@@ -25,17 +25,11 @@ using Optimizer = xacc::Optimizer;
using CompositeInstruction = xacc::CompositeInstruction;
using PauliOperator = xacc::quantum::PauliOperator;

PauliOperator X(int idx){
  return PauliOperator({{idx, "X"}});
}
PauliOperator X(int idx) { return PauliOperator({{idx, "X"}}); }

PauliOperator Y(int idx){
  return PauliOperator({{idx, "Y"}});
}
PauliOperator Y(int idx) { return PauliOperator({{idx, "Y"}}); }

PauliOperator Z(int idx){
  return PauliOperator({{idx, "Z"}});
}
PauliOperator Z(int idx) { return PauliOperator({{idx, "Z"}}); }

PauliOperator allZs(const int nQubits) {
  auto ret = Z(0);
@@ -45,22 +39,18 @@ PauliOperator allZs(const int nQubits) {
  return ret;
}

template<typename T>
PauliOperator operator+(T coeff, PauliOperator &op){
template <typename T> PauliOperator operator+(T coeff, PauliOperator &op) {
  return PauliOperator(coeff) + op;
}
template<typename T>
PauliOperator operator+(PauliOperator &op, T coeff){
template <typename T> PauliOperator operator+(PauliOperator &op, T coeff) {
  return PauliOperator(coeff) + op;
}

template<typename T>
PauliOperator operator-(T coeff, PauliOperator &op){
template <typename T> PauliOperator operator-(T coeff, PauliOperator &op) {
  return -1.0 * coeff + op;
}

template<typename T>
PauliOperator operator-(PauliOperator &op, T coeff){
template <typename T> PauliOperator operator-(PauliOperator &op, T coeff) {
  return -1.0 * coeff + op;
}

@@ -119,6 +109,9 @@ template <typename Function, typename Tuple> auto call(Function f, Tuple t) {
template <typename QuantumKernel, typename... Args>
std::shared_ptr<CompositeInstruction>
kernel_as_composite_instruction(QuantumKernel &k, Args... args) {
#ifdef QCOR_USE_QRT
  quantum::clearProgram();
#endif
  // turn off execution
  xacc::internal_compiler::__execute = false;
  // Execute to compile, this will store and we can get it
@@ -147,6 +140,12 @@ std::shared_ptr<ObjectiveFunction> get_objective(const std::string & type);

} // namespace __internal__

template <typename QuantumKernel, typename... Args>
void print_kernel(std::ostream &os, QuantumKernel &kernel, Args... args) {
  os << __internal__::kernel_as_composite_instruction(kernel, args...)
            ->toString();
}

// The ObjectiveFunction represents a functor-like data structure that
// models a general parameterized scalar function. It is initialized with a
// problem-specific Observable and Quantum Kernel, and exposes a method for
@@ -277,11 +276,9 @@ auto observe(QuantumKernel &kernel, std::shared_ptr<Observable> obs,
}

template <typename QuantumKernel, typename... Args>
auto observe(QuantumKernel &kernel, Observable &obs,
             Args... args) {
auto observe(QuantumKernel &kernel, Observable &obs, Args... args) {
  auto program = __internal__::kernel_as_composite_instruction(kernel, args...);
  return [program, &obs](Args... args) {

    // Get the first argument, which should be a qreg
    auto q = std::get<0>(std::forward_as_tuple(args...));
    // std::cout << "\n" << program->toString() << "\n";
+1 −0
Original line number Diff line number Diff line
@@ -243,4 +243,5 @@ void submit(xacc::AcceleratorBuffer **buffers, const int nBuffers) {
  xacc::internal_compiler::execute(buffers, nBuffers, program);
}
std::shared_ptr<xacc::CompositeInstruction> getProgram() { return program; }
void clearProgram() {program->clear();}
} // namespace quantum
 No newline at end of file
+1 −0
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ void submit(xacc::AcceleratorBuffer *buffer);
void submit(xacc::AcceleratorBuffer **buffers, const int nBuffers);

std::shared_ptr<xacc::CompositeInstruction> getProgram();
void clearProgram();

} // namespace quantum

Loading