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

Merge branch 'master' into tnguyen/qsharp-demo

parents e0a80178 871299ac
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
#include "qcor.hpp"
using namespace qcor;

int main() {
  qpu_lambda<> ansatz_X0X1(
      [](qreg q, double x) {
        qpu_lambda_body({
          X(q[0]);
          Ry(q[1], x);
          CX(q[1], q[0]);
          H(q);
          Measure(q);
        })
      },
      qpu_lambda_variables({"q", "x"}, {}));

  OptFunction obj(
      [&](const std::vector<double> &x, std::vector<double> &) {
        print("running ", x[0]);
        auto q = qalloc(2);
        ansatz_X0X1(q, x[0]);
        auto exp = q.exp_val_z();
        print(x[0], exp);
        return exp;
      },
      1);

  auto optimizer = createOptimizer(
      "nlopt",
      {{"initial-parameters", std::vector<double>{1.2}}, {"maxeval", 10}});
  auto [opt_val, opt_params] = optimizer->optimize(obj);
  print("opt_val = ", opt_val);
}
 No newline at end of file
+27 −0
Original line number Diff line number Diff line
#include "qcor.hpp"

int main(int argc, char** argv) {
  int n = argc;
  double m = 22;  
  
  using namespace qcor;
 
  qpu_lambda<int, double> superposition(
      [](qreg q) {          // Provide the kernel lambda
        qpu_lambda_body({  // wrap function body in this macro
          print("n = ", n);
          print("m = ", m);
          for (int i = 0; i < n; i++) {
            H(q[0]);
          }
          Measure(q[0]);
        })
      },
      qpu_lambda_variables({"q"},
                           {"n", "m"}),  // Must provide variable names in order
      n, m);                             // Must provide the captured variables

  auto q = qalloc(1);
  superposition(q);
  q.print();
}
 No newline at end of file
+1 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@ add_library(${LIBRARY_NAME} SHARED ${SRC})
target_include_directories(${LIBRARY_NAME} PUBLIC . qrt 
                            observable 
                            optimizer 
                            jit
                            kernel 
                            objectives 
                            execution 
+2 −3
Original line number Diff line number Diff line
@@ -235,9 +235,9 @@ const std::pair<std::string, std::string> QJIT::run_syntax_handler(
  auto args_split = split_args_signature(args_signature);
  for (auto &arg : args_split) {
    auto arg_var = split(arg, ' ');
    if (arg_var[0] == "qreg") {
    if (arg_var[0] == "qreg" || arg_var[0] == "xacc::internal_compiler::qreg") {
      bufferNames.push_back(arg_var[1]);
    } else if (arg_var[0] == "qubit") {
    } else if (arg_var[0] == "qubit" || arg_var[0] == "xacc::internal_compiler::qubit") {
      bufferNames.push_back(arg_var[1]);
    }
    arg_types.push_back(arg_var[0]);
@@ -250,7 +250,6 @@ const std::pair<std::string, std::string> QJIT::run_syntax_handler(
      kernel_src.find_first_of("{") + 1,
      kernel_src.find_last_of("}") - kernel_src.find_first_of("{") - 1);

  // std::cout << "QJIT FBODY:\n" << function_body << "\n";
  LexerHelper helper;
  auto [tokens, PP] = helper.Lex(function_body);
  clang::CachedTokens cached;
+133 −3
Original line number Diff line number Diff line
#pragma once

#include "qcor_jit.hpp"
#include "qcor_observable.hpp"
#include "qcor_utils.hpp"
#include "qrt.hpp"
@@ -470,4 +471,133 @@ ONE_QUBIT_KERNEL_CTRL_ENABLER(Tdg, tdg)
ONE_QUBIT_KERNEL_CTRL_ENABLER(S, s)
ONE_QUBIT_KERNEL_CTRL_ENABLER(Sdg, sdg)

// The following is a first pass at enabling qcor
// quantum lambdas. The goal is to mimic lambda functionality
// via our QJIT infrastructure. The lambda class takes
// as input a lambda of desired kernel signature calling
// a specific macro which expands to return the function body
// expression as a string, which we use with QJIT jit_compile.
// The lambda class is templated on the types of any capture variables
// the programmer would like to specify, and takes a second constructor
// argument indicating the variable names of all kernel arguments and
// capture variables. Finally, all capture variables must be passed to the
// trailing variadic argument for the lambda class constructor. Once
// instantiated lambda invocation looks just like kernel invocation.

template <typename... Args>
using GenerateKernelBodyPtr = std::string (*)(Args...);

// This class is used to simplify the syntax of 
// passing kernel and capture variable names to the qpu_lambda
class qpu_lambda_variables {
 protected:
  std::vector<std::string> kernel_args;
  std::vector<std::string> capture_args;

 public:
  qpu_lambda_variables(std::initializer_list<std::string> k)
      : kernel_args(std::vector<std::string>(k)) {}
  qpu_lambda_variables(std::initializer_list<std::string> k,
                       std::initializer_list<std::string> c)
      : kernel_args(std::vector<std::string>(k)),
        capture_args(std::vector<std::string>(c)) {}
  std::vector<std::string> get_kernel_args() { return kernel_args; }
  std::vector<std::string> get_capture_args() { return capture_args; }
};

template <typename... Args>
class qpu_lambda {
 private:
  class TupleToTypeArgString {
   protected:
    std::string &tmp;
    std::vector<std::string> var_names;
    int counter = 0;

    template <class T>
    std::string type_name() {
      typedef typename std::remove_reference<T>::type TR;
      std::unique_ptr<char, void (*)(void *)> own(
          abi::__cxa_demangle(typeid(TR).name(), nullptr, nullptr, nullptr),
          std::free);
      std::string r = own != nullptr ? own.get() : typeid(TR).name();
      return r;
    }

   public:
    TupleToTypeArgString(std::string &t) : tmp(t) {}
    TupleToTypeArgString(std::string &t, std::vector<std::string> &_var_names)
        : tmp(t), var_names(_var_names) {}
    template <typename T>
    void operator()(T &t) {
      tmp += type_name<decltype(t)>() + " " +
             (var_names.empty() ? "arg_" + std::to_string(counter)
                                : var_names[counter]) +
             ",";
      counter++;
    }
  };

 protected:
  void *f;
  std::tuple<Args...> capture_vars;
  qpu_lambda_variables variable_names_map;

 public:
  template <typename LambdaType>
  qpu_lambda(LambdaType &&ff, qpu_lambda_variables &&variable_names,
             Args... _capture_vars)
      : variable_names_map(std::move(variable_names)),
        capture_vars(std::forward_as_tuple(_capture_vars...)) {
    f = reinterpret_cast<void *>(+ff);
  }

  template <typename... FunctionArgs>
  void operator()(FunctionArgs... args) {
    auto casted = reinterpret_cast<GenerateKernelBodyPtr<FunctionArgs...>>(f);
    std::stringstream ss;
    QJIT qjit;
    // Map the args to a tuple
    auto kernel_args_tuple = std::make_tuple(args...);

    // Get the kernel body as a string
    auto s = casted(args...);

    // Extract the kernel and capture variable names
    auto kernel_var_names = variable_names_map.get_kernel_args();
    auto capture_var_names = variable_names_map.get_capture_args();

    // Build up the function argument signature string
    std::string args_string = "";
    TupleToTypeArgString to(args_string, kernel_var_names);
    __internal__::tuple_for_each(kernel_args_tuple, to);
    TupleToTypeArgString co(args_string);
    __internal__::tuple_for_each(capture_vars, co);
    args_string = args_string.substr(0, args_string.length() - 1);

    std::string capture_preamble = "";
    for (auto [i, capture_name] : qcor::enumerate(capture_var_names)) {
      capture_preamble +=
          "auto " + capture_name + " = arg_" + std::to_string(i) + ";\n";
    }

    // Insert the capture preamble code
    s.insert(1, "\n" + capture_preamble);

    // Create the kernel string for QJIT
    ss << "__qpu__ void foo(" << args_string << ")\n" << s;

    // Compile
    qjit.jit_compile(ss.str());

    // Merge the kernel args and the capture vars and execute
    auto final_args_tuple = std::tuple_cat(kernel_args_tuple, capture_vars);
    std::apply([&](auto &&...args) { qjit.invoke("foo", args...); },
               final_args_tuple);
  }
};

#define STRINGIZE(A) #A
#define qpu_lambda_body(EXPR) return std::string(STRINGIZE(EXPR));

}  // namespace qcor