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

merging syntax_handler branch

parents 1c03cf0f 36faf7b1
Loading
Loading
Loading
Loading
Loading
+10 −5
Original line number Diff line number Diff line
@@ -3,6 +3,10 @@ if(${CMAKE_VERSION} VERSION_LESS 3.12)
    cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
endif()

if (NOT XACC_DIR)
  set(XACC_DIR "$ENV{HOME}/.xacc")
endif()

set(CMAKE_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_DISABLE_IN_SOURCE_BUILDS ON)
@@ -10,20 +14,21 @@ set(CMAKE_DISABLE_SOURCE_CHANGES ON)
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/Modules)
set(CLANG_COMPILER /usr/bin/clang++-9)

project(qcor LANGUAGES CXX)

option(QCOR_BUILD_TESTS "Build qcor tests" OFF)

find_package(Clang 9.0.0 REQUIRED)
find_package(Clang 10.0.0 REQUIRED)
find_package(XACC REQUIRED)

# Store the location of the clang executable
set (CLANG_EXECUTABLE "${LLVM_INSTALL_PREFIX}/bin/clang++")

configure_file(${CMAKE_SOURCE_DIR}/scripts/qcor.in
               ${CMAKE_BINARY_DIR}/qcor)
include_directories(/home/cades/dev/llvm-project/clang/include)

add_subdirectory(handlers)
add_subdirectory(runtime)
add_subdirectory(compiler)
add_subdirectory(ir)

install(PROGRAMS ${CMAKE_BINARY_DIR}/qcor DESTINATION bin)
+55 −51
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@
# QCOR

QCOR is a C++ language extension and associated compiler implementation
for variational quantum computation on near-term, noisy devices.
for hybrid quantum-classical programming.


## Dependencies
@@ -14,24 +14,30 @@ for variational quantum computation on near-term, noisy devices.
Compiler (C++14): GNU 6.1+, Clang 3.4+
CMake 3.9+ (for build)
XACC: see https://xacc.readthedocs.io/en/latest/install.html#building-xacc
clang-dev version 8+
LLVM/Clang [Syntax Handler Fork](https://github.com/hfinkel/llvm-project-csp).
```

## Linux Build Instructions
Easiest way to install CMake - do not use the package manager,
instead use `pip`, and ensure that `/usr/local/bin` is in your PATH:
```bash
$ python -m pip install --upgrade cmake
$ python3 -m pip install --upgrade cmake
$ export PATH=$PATH:/usr/local/bin
```

On Ubuntu 16.04 (for others, replace xenial with Ubuntu release name),
install latest clang and llvm libraries and headers (you may need sudo)
For now we require our users build a specific fork of LLVM/Clang that 
provides Syntax Handler plugin support. We expect this fork to be upstreamed 
in a future release of LLVM and Clang, and at that point users will only 
need to download the appropriate LLVM/Clang binaries (via `apt-get` for instance).

To build this fork of LLVM/Clang (be aware this step takes up a good amount of RAM):
```bash
$ wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
$ echo "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main" > /etc/apt/sources.list.d/llvm.list
$ apt-get update && apt-get install -y libclang-9-dev llvm-9-dev clangd-9
$ ln -s /usr/bin/llvm-config-9 /usr/bin/llvm-config
$ (for theia ide) ln -s /usr/bin/clangd-9 /usr/bin/clangd
$ apt-get install ninja-build [if you dont have ninja]
$ git clone https://github.com/hfinkel/llvm-project-csp llvm
$ cd llvm && mkdir build && cd build
$ cmake -G Ninja ../llvm -DCMAKE_INSTALL_PREFIX=$HOME/.llvm -DBUILD_SHARED_LIBS=TRUE -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD="X86" -DLLVM_ENABLE_PROJECTS=clang
$ cmake --build . --target install
$ sudo ln -s $HOME/.llvm/bin/llvm-config /usr/bin
```

Note that, for now, developers must clone QCOR manually:
@@ -39,9 +45,9 @@ Note that, for now, developers must clone QCOR manually:
$ git clone https://github.com/ornl-qci/qcor
$ cd qcor
$ mkdir build && cd build
$ cmake .. -DXACC_DIR=~/.xacc (or wherever you installed XACC)
$ [with tests] cmake .. -DXACC_DIR=~/.xacc -DQCOR_BUILD_TESTS=TRUE
$ make install
$ cmake .. 
$ [with tests] cmake .. -DQCOR_BUILD_TESTS=TRUE
$ make -j$(nproc) install
```
Update your PATH to ensure that the ```qcor``` compiler is available.
```bash
@@ -56,56 +62,54 @@ the following file
```cpp
#include "qcor.hpp"

int main(int argc, char **argv) {

  // Initialize QCOR
  qcor::Initialize(argc, argv);

  // Define your quantum kernel, here as a
  // standard C++ lambda containing quantum code
  auto ansatz = [&](qbit q, std::vector<double> t) {
// QCOR kernel requirements:
// C-like function, unique function name
// takes qreg as first argument, can take any 
// arguments after that which are necessary for function 
// evaluation (like gate rotation parameters). 
// Function body is written in a 'supported language' (i.e. 
// we have a xacc::Compiler parser for it, here XASM (which is default))
// Must be annotated with the __qpu__ attribute, which expands 
// to [[clang::syntax(qcor)]], thereby invoking our custom Clang SyntaxHandler.

__qpu__ void ansatz(qreg q, double t) {
  X(q[0]);
    Ry(q[1], t[0]);
    CNOT(q[1], q[0]);
  };
  Ry(q[1], t);
  CX(q[1], q[0]);
}

  // Get a valid Optimizer
  auto optimizer =
      qcor::getOptimizer("nlopt", {std::make_pair("nlopt-optimizer", "cobyla"),
                                   std::make_pair("nlopt-maxeval", 20)});
int main(int argc, char **argv) {

  // Define the Observable
  auto observable =
      qcor::getObservable("pauli", std::string("5.907 - 2.1433 X0X1 "
                                               "- 2.1433 Y0Y1"
                                               "+ .21829 Z0 - 6.125 Z1"));
 // Allocate 2 qubits
  auto q = qalloc(2);

  // Call qcor::taskInitiate to kick off asynchronous execution of
  // VQE with given ansatz, optimizer, and observable, and initial params 0.0
  auto handle = qcor::taskInitiate(ansatz, "vqe", optimizer, observable,
                                   std::vector<double>{0.0});
  // Create the Deuteron Hamiltonian (Observable)
  auto H = qcor::getObservable(
      "5.907 - 2.1433 X0X1 - 2.1433 Y0Y1 + .21829 Z0 - 6.125 Z1");

  // Go do other work, task is running asynchronously
  // Create the ObjectiveFunction, here we want to run VQE
  // need to provide ansatz and the Observable
  // Must also provide initial params for ansatz (under the hood, uses 
  // variadic template)
  auto objective = qcor::createObjectiveFunction("vqe", ansatz, H, q, 0.0);

  // Now request the results, this will wait
  // until the task finishes.
  auto results = qcor::sync(handle);
  // Evaluate the ObjectiveFunction at a specified set of parameters
  auto energy = (*objective)(q, .59);

  // Get the results...
  std::cout << results->getInformation("opt-val").as<double>() << "\n";
  // Print the result
  printf("vqe energy = %f\n", energy);
  q.print();

  // Finalize the framework.
  qcor::Finalize();
}

```
To compile this with QCOR targeting a Rigetti QCS QPU, run the following

```bash
$ qcor -o deuteron -a qcs:Aspen-4-4Q-A deuteron.cpp
$ [run on QPU] qcor -o deuteron_qcs -qpu qcs:Aspen-4-4Q-A deuteron.cpp
$ [run on Simulator] qcor -o deuteron_tnqvm -qpu tnqvm deuteron.cpp
```
This will create the ```deuteron``` quantum-classical binary executable.
Now just run
This will create the ```deuteron_tnqvm``` and ```deuteron_qcs``` quantum-classical binary executables, 
each compiled for the specified backend. Now just run one of them
```bash
$ ./deuteron
$ ./deuteron_tnqvm
```

compiler/fuzzy_parsing.hpp

deleted100644 → 0
+0 −43
Original line number Diff line number Diff line
#ifndef COMPILER_FUZZYPARSINGEXTERNALSEMASOURCE_HPP_
#define COMPILER_FUZZYPARSINGEXTERNALSEMASOURCE_HPP_

#include "clang/AST/ASTContext.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Sema/ExternalSemaSource.h"
#include "clang/Sema/Lookup.h"

using namespace clang;

namespace qcor {
namespace compiler {
class FuzzyParsingExternalSemaSource : public ExternalSemaSource {
private:
  std::vector<std::string> validInstructions;
  CompilerInstance &ci;
  ParmVarDecl *qbit;
  ParmVarDecl *hMapRValue;
  ParmVarDecl *stdVector;
  std::unique_ptr<ASTUnit> hast;
  std::vector<std::string> compositeInstructions;
//   std::vector<bool> compositeRequiresStdVector;

  // Keep a vector of ASTs for each FunctionDecl
  // representation of our quantum instructions.
  // This ExternalSemaSource should exist throughout
  // the tooling lifetime, so we should be good with
  // regards to these nodes being deleted
  std::vector<std::unique_ptr<ASTUnit>> quantumInstructionASTs;
  std::map<std::string, std::string> quantumInstruction2src;

public:
  FuzzyParsingExternalSemaSource(CompilerInstance &c) : ci(c) {}
  void initialize(std::vector<std::string> args = {});
  //   void setASTContext(ASTContext *context) { m_Context = context; }
  //   void setFileManager(FileManager *m) { manager = m; }

  bool LookupUnqualified(clang::LookupResult &R, clang::Scope *S) override;
};
} // namespace compiler
} // namespace qcor
#endif

compiler/fuzzy_parsing.in.cpp

deleted100644 → 0
+0 −149
Original line number Diff line number Diff line
#include "fuzzy_parsing.hpp"

#include "IRProvider.hpp"
#include "xacc.hpp"
#include "xacc_service.hpp"

#include "Utils.hpp"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTImporter.h"
#include "clang/ASTMatchers/ASTMatchers.h"

#include "qcor_clang_utils.hpp"

using namespace clang;
using namespace clang::ast_matchers;

namespace qcor {
namespace compiler {

void FuzzyParsingExternalSemaSource::initialize(std::vector<std::string> args) {
  auto provider = xacc::getService<xacc::IRProvider>("quantum");
  validInstructions = provider->getInstructions();
  validInstructions.push_back("CX");

  std::vector<std::shared_ptr<xacc::Instruction>> composites;
  std::string totalTempSource = "";
  for (auto &instructionName : validInstructions) {
    std::string tmpSource = "void " + instructionName + "(";
    auto tmpInst = provider->createInstruction(
        instructionName == "CX" ? "CNOT" : instructionName, {});
    if (!tmpInst->isComposite()) {
      int nRequiredBits = tmpInst->nRequiredBits();
      tmpSource += "int q0";
      for (int i = 1; i < nRequiredBits; i++) {
        tmpSource += ", int q" + std::to_string(i);
      }
      if (tmpInst->isParameterized() && instructionName != "Measure") {
        int nRequiredParams = tmpInst->nParameters();
        tmpSource += ", double p0";
        for (int i = 1; i < nRequiredParams; i++) {
          tmpSource += ", double p" + std::to_string(i);
        }
      }
      tmpSource += "){return;}";
      quantumInstruction2src.insert(
          {instructionName + "__qcor_instruction", tmpSource});
    } else {
      compositeInstructions.push_back(instructionName + "__qcor_instruction");
    }
  }

  hast = tooling::buildASTFromCodeWithArgs(
      "#include \"heterogeneous.hpp\"\nvoid f(xacc::HeterogeneousMap&& "
      "m, std::vector<double>& x){return;}", args);
      //{"-std=c++14", "-I@CMAKE_INSTALL_PREFIX@/include/xacc","-I/usr/lib/gcc/x86_64-linux-gnu/8/include", "-v"});
  hMapRValue = FirstDeclMatcher<ParmVarDecl>().match(
      hast->getASTContext().getTranslationUnitDecl(), namedDecl(hasName("m")));
  stdVector = FirstDeclMatcher<ParmVarDecl>().match(
      hast->getASTContext().getTranslationUnitDecl(), namedDecl(hasName("x")));
}

bool FuzzyParsingExternalSemaSource::LookupUnqualified(clang::LookupResult &R,
                                                       clang::Scope *S) {
  std::string unknownName = R.getLookupName().getAsString();

  // If this is a valid quantum instruction, tell Clang not to error
  if (quantumInstruction2src.count(unknownName + "__qcor_instruction") &&
      S->getFlags() != 128 && S->getBlockParent() != nullptr) {

    auto Matcher = namedDecl(hasName(unknownName));

    auto ast = tooling::buildASTFromCodeWithArgs(
        quantumInstruction2src[unknownName + "__qcor_instruction"],
        {"-std=c++11"});
    FunctionDecl *D0 = FirstDeclMatcher<FunctionDecl>().match(
        ast->getASTContext().getTranslationUnitDecl(), Matcher);

    quantumInstructionASTs.push_back(std::move(ast));

    R.addDecl(D0);
    // D0->dump();
  } else if (std::find(compositeInstructions.begin(),
                       compositeInstructions.end(),
                       unknownName + "__qcor_instruction") !=
             std::end(compositeInstructions) &&
      S->getFlags() != 128 && S->getBlockParent() != nullptr) {

    if (!qbit) {
      // Save pointers to xacc::qbit, xacc::HeterogeneousMap&& ParmVarDecl
      qbit = FirstDeclMatcher<ParmVarDecl>().match(
          ci.getASTContext().getTranslationUnitDecl(),
          parmVarDecl(hasType(recordDecl(matchesName("xacc::qbit")))));
    }

    // This is a Circuit Generator CompositeInstruction. We assume
    // (for now) it has must have prototype
    // f(qbit q, std::vector<double>& x, HeterogeneousMap&&)
    // or f(qbit q, HeterogeneousMap&&)

    // Create a new ParmVarDecl exactly like that one
    auto qb_copy = ParmVarDecl::Create(
        ci.getASTContext(), ci.getSema().getFunctionLevelDeclContext(),
        SourceLocation(), SourceLocation(), qbit->getIdentifier(),
        qbit->getType(), 0, SC_None, nullptr);
   auto v_copy = ParmVarDecl::Create(
        ci.getASTContext(), ci.getSema().getFunctionLevelDeclContext(),
        SourceLocation(), SourceLocation(), stdVector->getIdentifier(),
        stdVector->getType(), 0, SC_None, nullptr);
    auto h_copy = ParmVarDecl::Create(
        ci.getASTContext(), ci.getSema().getFunctionLevelDeclContext(),
        SourceLocation(), SourceLocation(), hMapRValue->getIdentifier(),
        hMapRValue->getType(), 0, SC_None, nullptr);

    // Use astContext.getFunctionType (RETURNTYPE, ARGSPARMVARS, fpi)
    // to create a new Function QualType
    std::vector<QualType> ParamTypes;
    ParamTypes.push_back(qb_copy->getType());
    ParamTypes.push_back(v_copy->getType());
    ParamTypes.push_back(h_copy->getType());
    FunctionProtoType::ExtProtoInfo fpi;
    fpi.Variadic = false;
    llvm::ArrayRef<QualType> Args(ParamTypes);
    QualType newFT = ci.getASTContext().getFunctionType(
        ci.getASTContext().VoidTy, Args, fpi);

    // Then use FunctionDecl::Create() to create a new functiondecl
    auto fdecl = FunctionDecl::Create(ci.getASTContext(),
                                      R.getSema().getFunctionLevelDeclContext(),
                                      SourceLocation(), SourceLocation(),
                                      R.getLookupName(), newFT, 0, SC_None);
    std::vector<ParmVarDecl *> params{qb_copy, v_copy, h_copy};
    llvm::ArrayRef<ParmVarDecl *> parms(params);
    fdecl->setParams(parms);
    std::vector<Stmt *> svec;
    auto rtrn = ReturnStmt::CreateEmpty(ci.getASTContext(), false);
    svec.push_back(rtrn);
    llvm::ArrayRef<Stmt *> stmts(svec);
    auto cmp = CompoundStmt::Create(ci.getASTContext(), stmts, SourceLocation(),
                                    SourceLocation());
    fdecl->setBody(cmp);
    // fdecl->dump();

    R.addDecl(fdecl);
    return true;
  }
  return false;
}
} // namespace compiler
} // namespace qcor

compiler/qcor-driver.in.cpp

deleted100644 → 0
+0 −55
Original line number Diff line number Diff line
#include "qcor_frontend_action.hpp"

int main(int argc, char **argv) {

  xacc::Initialize();

  // Get filename
  // FIXME, we assume it is the last arg...
  std::string fileName(argv[argc - 1]);
  if (!xacc::fileExists(fileName)) {
    xacc::error("File " + fileName + " does not exist.");
  }

  std::ifstream t(fileName);
  std::string src((std::istreambuf_iterator<char>(t)),
                  std::istreambuf_iterator<char>());

  // Initialize rewriter
  Rewriter Rewrite;

  std::vector<std::string> args{"-Wno-dangling", "-std=c++14", 
                                "-I@CMAKE_INSTALL_PREFIX@/include/qcor",
                                "-I@CMAKE_INSTALL_PREFIX@/include/xacc"};

  // Do a little bit of argument analysis
  // We need to know any new include paths
  // and the accelerator backend
  std::string accName = "";
  std::vector<std::string> arguments(argv + 1, argv + argc);
  for (int i = 0; i < arguments.size(); i++) {
    if (arguments[i] == "-I") {
      if (arguments[i + 1] != "@CMAKE_INSTALL_PREFIX@/include/qcor" &&
          arguments[i + 1] != "@CMAKE_INSTALL_PREFIX@/include/xacc") {
        args.push_back(arguments[i] + arguments[i + 1]);
      }
    } else if (arguments[i].find("-I") != std::string::npos) {
      args.push_back(arguments[i]);
    } else if (arguments[i] == "--accelerator") {
      accName = arguments[i + 1];
    } else if (arguments[i] == "-a") {
      accName = arguments[i + 1];
    }
  }

  auto action = new qcor::compiler::QCORFrontendAction(Rewrite, fileName, args);

  if (!accName.empty()) {
    xacc::setAccelerator(accName);
  }
  if (!tooling::runToolOnCodeWithArgs(action, src, args)) {
    xacc::error("Error running qcor compiler.");
  }

  return 0;
}
Loading