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

Updates to cpr submodule to get unix-socket support for curl. adding quilc...


Updates to cpr submodule to get unix-socket support for curl. adding quilc compiler leveraging curl calls to docker engine
Signed-off-by: Mccaskey, Alex's avatarAlex McCaskey <mccaskeyaj@ornl.gov>
parent ac0a684e
Pipeline #101837 passed with stage
in 70 minutes and 53 seconds
......@@ -3,6 +3,55 @@ Extensions
Here we detail concrete implementations of various XACC interfaces as well as any
input parameters they expose.
Compilers
---------
xasm
++++
The XASM Compiler is the default compiler in XACC. It is the closest language to the underlying
XACC IR data model. The XASM compiler provides a quantum assembly like language with support
for custom Instructions and Composite Instruction generators as part of the language. Instructions
are provided to the language via the usual XACC service registry. Most common digital gates are provided
by default, and it is straightforward to add new Instructions.
quilc
++++++
XACC provides a Compiler implementation that delegates to the Rigetti-developed
quilc compiler. This is acheieved through the ``rigetti/quilc`` Docker image and
the internal XACC REST client API. The Quilc Compiler implementation makes direct
REST POSTs and GETs to the users local Docker Engine. Therefore,
in order to use this Compiler, you must pull down this image.
.. code:: bash
$ docker pull rigetti/quilc
With that image pulled, you can now use the Quilc compiler via the usual XACC API calls.
.. code:: cpp
auto compiler = xacc::getCompiler("quilc");
auto ir = compiler->compile(R"##(H 0
CNOT 0 1
)##");
std::cout << ir->getComposites()[0]->toString() << "\n";
or in Python
.. code:: python
compiler = xacc.getCompiler('quilc')
ir = compiler.compile('''__qpu__ void ansatz(qbit q, double x) {
X 0
CNOT 1 0
RY(x) 1
}''')
Note that you can structure your input to the compiler as a typical XACC kernel source string
or as a raw Quil string.
Optimizers
----------
XACC provides implementations for the ``Optimizer`` that delegate to NLOpt and MLPack. Here we demonstrate
......
......@@ -12,6 +12,7 @@
*******************************************************************************/
#include "Gate.hpp"
#include "xacc.hpp"
#include "xacc_service.hpp"
namespace xacc {
namespace quantum {
......@@ -27,8 +28,8 @@ Gate::Gate(std::string name, std::vector<std::size_t> qubts,
std::vector<InstructionParameter> params)
: gateName(name), qbits(qubts), parameters(params) {}
Gate::Gate(const Gate &inst)
: gateName(inst.gateName), qbits(inst.qbits), parameters(inst.parameters), arguments(inst.arguments),
enabled(inst.enabled) {}
: gateName(inst.gateName), qbits(inst.qbits), parameters(inst.parameters),
arguments(inst.arguments), enabled(inst.enabled) {}
const std::string Gate::name() const { return gateName; }
const std::string Gate::description() const {
......@@ -39,9 +40,10 @@ std::string Gate::getBufferName(const std::size_t bitIdx) {
return bitIdx < buffer_names.size() ? buffer_names[bitIdx] : "q";
}
void Gate::setBufferNames(const std::vector<std::string> bufferNamesPerIdx) {
void Gate::setBufferNames(const std::vector<std::string> bufferNamesPerIdx) {
if (bufferNamesPerIdx.size() != this->nRequiredBits()) {
xacc::error("Invalid number of buffer names for this instruction: " +name() + ", " + std::to_string(bufferNamesPerIdx.size()));
xacc::error("Invalid number of buffer names for this instruction: " +
name() + ", " + std::to_string(bufferNamesPerIdx.size()));
}
buffer_names = bufferNamesPerIdx;
}
......@@ -69,7 +71,8 @@ const std::string Gate::toString() {
int counter = 0;
for (auto q : bits()) {
str += (buffer_names.empty() ? "q" : buffer_names[counter]) + std::to_string(q) + ",";
str += (buffer_names.empty() ? "q" : buffer_names[counter]) +
std::to_string(q) + ",";
counter++;
}
......@@ -86,8 +89,8 @@ void Gate::enable() { enabled = true; }
const InstructionParameter Gate::getParameter(const std::size_t idx) const {
if (idx + 1 > parameters.size()) {
XACCLogger::instance()->error(
"Invalid Parameter requested from Parameterized Gate ("+name()+") Instruction: " +
std::to_string(idx) + ".");
"Invalid Parameter requested from Parameterized Gate (" + name() +
") Instruction: " + std::to_string(idx) + ".");
}
return parameters[idx];
......@@ -96,8 +99,8 @@ const InstructionParameter Gate::getParameter(const std::size_t idx) const {
void Gate::setParameter(const std::size_t idx, InstructionParameter &p) {
if (idx + 1 > parameters.size()) {
XACCLogger::instance()->error(
"Invalid Parameter being set in Parameterized Gate ("+name() +") Instruction: " +
std::to_string(idx) + ".");
"Invalid Parameter being set in Parameterized Gate (" + name() +
") Instruction: " + std::to_string(idx) + ".");
}
parameters[idx] = p;
......@@ -105,6 +108,36 @@ void Gate::setParameter(const std::size_t idx, InstructionParameter &p) {
std::vector<InstructionParameter> Gate::getParameters() { return parameters; }
void Gate::applyRuntimeArguments() {
if (!parsingUtil) {
parsingUtil = xacc::getService<ExpressionParsingUtil>("exprtk");
}
for (auto &kv : arguments) {
if (kv.second->type.find("std::vector<double>") != std::string::npos) {
parameters[kv.first] = kv.second->runtimeValue.get<std::vector<double>>(
INTERNAL_ARGUMENT_VALUE_KEY)[param_idx_to_vector_idx[kv.first]];
} else {
double val;
auto orig = parameters[kv.first].getOriginalExpression();
// std::cout << parameters[kv.first].toString() << ", "
// << parameters[kv.first].getOriginalExpression() << ", "
// << kv.second->name << ", " << kv.second->runtimeValue.get<double>(
// INTERNAL_ARGUMENT_VALUE_KEY) << "\n";
if (parsingUtil->evaluate(orig, {kv.second->name},
{kv.second->runtimeValue.get<double>(
INTERNAL_ARGUMENT_VALUE_KEY)},
val)) {
parameters[kv.first] = val;
parameters[kv.first].storeOriginalExpression(orig);
} else {
parameters[kv.first] =
kv.second->runtimeValue.get<double>(INTERNAL_ARGUMENT_VALUE_KEY);
}
}
}
}
bool Gate::isParameterized() { return nParameters() > 0; }
const int Gate::nParameters() { return parameters.size(); }
......
......@@ -16,6 +16,7 @@
#include "Instruction.hpp"
#include "Cloneable.hpp"
#include "Utils.hpp"
#include "expression_parsing_util.hpp"
namespace xacc {
namespace quantum {
......@@ -25,6 +26,8 @@ namespace quantum {
class Gate : public Instruction {
protected:
std::shared_ptr<ExpressionParsingUtil> parsingUtil;
std::string gateName;
std::vector<std::size_t> qbits;
bool enabled = true;
......@@ -66,18 +69,7 @@ public:
param_idx_to_vector_idx.insert({idx_1, idx_2});
}
void applyRuntimeArguments() override {
for (auto &kv : arguments) {
if (kv.second->type.find("std::vector<double>") != std::string::npos) {
parameters[kv.first] = kv.second->runtimeValue.get<std::vector<double>>(
INTERNAL_ARGUMENT_VALUE_KEY)[param_idx_to_vector_idx[kv.first]];
} else {
parameters[kv.first] =
kv.second->runtimeValue.get<double>(INTERNAL_ARGUMENT_VALUE_KEY);
}
}
}
void applyRuntimeArguments() override;
const std::vector<std::size_t> bits() override;
void setBits(const std::vector<std::size_t> bits) override { qbits = bits; }
std::string getBufferName(const std::size_t bitIdx) override;
......
......@@ -79,3 +79,5 @@ message(
STATUS "${BoldGreen}Building QCS Accelerator.${ColorReset}")
#add_subdirectory(qcs)
endif()
add_subdirectory(quilc)
\ No newline at end of file
......@@ -130,6 +130,7 @@ expression:
| <assoc = right> expression POWER expression # powerExp
| expression (TIMES | DIVIDE) expression # mulDivExp
| expression (PLUS | MINUS) expression # addSubExp
| expression LBRACKET expression RBRACKET # variablewithbracket
| function LPAREN expression RPAREN # functionExp
| segment # segmentExp
| number # numberExp
......
......@@ -59,7 +59,7 @@ bool QuilCompiler::canParse(const std::string &src) {
}
std::shared_ptr<IR> QuilCompiler::compile(const std::string &src,
std::shared_ptr<Accelerator> acc) {
std::cout << "SRC: " << src << "\n";
ANTLRInputStream input(src);
QuilLexer lexer(&input);
CommonTokenStream tokens(&lexer);
......
......@@ -91,20 +91,44 @@ void QuilToXACCListener::exitGate(quil::QuilParser::GateContext *ctx) {
xacc::debug("[XasmCompiler] Parameter added is " + std::to_string(value));
params.emplace_back(value);
} else {
xacc::debug("[XasmCompiler] Parameter added is " + ctx->param(i)->getText());
params.emplace_back(ctx->param(i)->getText());
xacc::debug("[XasmCompiler] Parameter added is " +
ctx->param(i)->getText());
InstructionParameter p(ctx->param(i)->getText());
p.storeOriginalExpression();
params.emplace_back(p);
// This depends on a function argument
auto arg = function->getArgument(ctx->param(i)->getText());
args.insert({i,arg});
if (!arg) {
// we may have a case where the parameter is an expression string,
// maybe something like 1.0 * theta[0]
for (auto &_arg : function->getArguments()) {
auto param_str = ctx->param(i)->getText();
param_str.erase(std::remove_if(param_str.begin(), param_str.end(),
[](char c) {
return !std::isalpha(c) ||
c == '[' || c == ']';
}),
param_str.end());
// std::cout << "PARAMSTR: " << param_str << "\n";
double val;
if (parsingUtil->validExpression(param_str, {_arg->name})) {
arg = _arg;
break;
}
}
}
args.insert({i, arg});
}
}
std::shared_ptr<xacc::Instruction> instruction =
gateRegistry->createInstruction(gateName, qubits, params);
for (auto& kv : args) instruction->addArgument(kv.second, kv.first);
for (auto &kv : args)
instruction->addArgument(kv.second, kv.first);
function->addInstruction(instruction);
}
......
......@@ -168,6 +168,9 @@ public:
virtual void enterAddSubExp(QuilParser::AddSubExpContext * /*ctx*/) override { }
virtual void exitAddSubExp(QuilParser::AddSubExpContext * /*ctx*/) override { }
virtual void enterVariablewithbracket(QuilParser::VariablewithbracketContext * /*ctx*/) override { }
virtual void exitVariablewithbracket(QuilParser::VariablewithbracketContext * /*ctx*/) override { }
virtual void enterFunctionExp(QuilParser::FunctionExpContext * /*ctx*/) override { }
virtual void exitFunctionExp(QuilParser::FunctionExpContext * /*ctx*/) override { }
......
......@@ -166,6 +166,9 @@ public:
virtual void enterAddSubExp(QuilParser::AddSubExpContext *ctx) = 0;
virtual void exitAddSubExp(QuilParser::AddSubExpContext *ctx) = 0;
virtual void enterVariablewithbracket(QuilParser::VariablewithbracketContext *ctx) = 0;
virtual void exitVariablewithbracket(QuilParser::VariablewithbracketContext *ctx) = 0;
virtual void enterFunctionExp(QuilParser::FunctionExpContext *ctx) = 0;
virtual void exitFunctionExp(QuilParser::FunctionExpContext *ctx) = 0;
......
......@@ -854,6 +854,18 @@ public:
virtual void exitRule(antlr4::tree::ParseTreeListener *listener) override;
};
class VariablewithbracketContext : public ExpressionContext {
public:
VariablewithbracketContext(ExpressionContext *ctx);
std::vector<ExpressionContext *> expression();
ExpressionContext* expression(size_t i);
antlr4::tree::TerminalNode *LBRACKET();
antlr4::tree::TerminalNode *RBRACKET();
virtual void enterRule(antlr4::tree::ParseTreeListener *listener) override;
virtual void exitRule(antlr4::tree::ParseTreeListener *listener) override;
};
class FunctionExpContext : public ExpressionContext {
public:
FunctionExpContext(ExpressionContext *ctx);
......
# *******************************************************************************
# Copyright (c) 2019 UT-Battelle, LLC.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# and Eclipse Distribution License v.10 which accompany this distribution.
# The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
# and the Eclipse Distribution License is available at
# https://eclipse.org/org/documents/edl-v10.php
#
# Contributors:
# Alexander J. McCaskey - initial API and implementation
# *******************************************************************************/
set(LIBRARY_NAME xacc-quilc)
file(GLOB SRC quilc.cpp quilc_activator.cpp)
usfunctiongetresourcesource(TARGET ${LIBRARY_NAME} OUT SRC)
usfunctiongeneratebundleinit(TARGET ${LIBRARY_NAME} OUT SRC)
add_library(${LIBRARY_NAME} SHARED ${SRC})
target_include_directories(
${LIBRARY_NAME}
PUBLIC .)
target_link_libraries(${LIBRARY_NAME} PUBLIC xacc PRIVATE cpr)
set(_bundle_name xacc_quilc)
set_target_properties(${LIBRARY_NAME}
PROPERTIES COMPILE_DEFINITIONS
US_BUNDLE_NAME=${_bundle_name}
US_BUNDLE_NAME
${_bundle_name})
usfunctionembedresources(TARGET
${LIBRARY_NAME}
WORKING_DIRECTORY
${CMAKE_CURRENT_SOURCE_DIR}
FILES
manifest.json)
if(APPLE)
set_target_properties(${LIBRARY_NAME}
PROPERTIES INSTALL_RPATH "@loader_path/../lib;${LLVM_INSTALL_PREFIX}/lib")
set_target_properties(${LIBRARY_NAME}
PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
else()
set_target_properties(${LIBRARY_NAME}
PROPERTIES INSTALL_RPATH "$ORIGIN/../lib:${LLVM_INSTALL_PREFIX}/lib")
set_target_properties(${LIBRARY_NAME} PROPERTIES LINK_FLAGS "-shared")
endif()
install(TARGETS ${LIBRARY_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/plugins)
if (XACC_BUILD_TESTS)
# uses docker, so turn this on manually to try it out
#add_subdirectory(tests)
endif()
{
"bundle.symbolic_name" : "xacc_quilc",
"bundle.activator" : true,
"bundle.name" : "XACC QuilC Docker Client",
"bundle.description" : "This bundle provides integration with QuilC via Docker."
}
\ No newline at end of file
/*******************************************************************************
* Copyright (c) 2019 UT-Battelle, LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompanies this
* distribution. The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html and the Eclipse Distribution
*License is available at https://eclipse.org/org/documents/edl-v10.php
*
* Contributors:
* Alexander J. McCaskey - initial API and implementation
*******************************************************************************/
#include "quilc.hpp"
#include "cpr/api.h"
#include "cpr/unix_socket.h"
#include "xacc.hpp"
#include "xacc_service.hpp"
#include <cpr/cpr.h>
#include "json.hpp"
#include <regex>
#include <string>
using json = nlohmann::json;
namespace xacc {
namespace quilc {
Quilc::Quilc() : Compiler() {}
bool Quilc::canParse(const std::string &src) {
// We define canParse as we have docker and rigetti/quilc
// and this source string is Quil-like
cpr::UnixSocket unix_socket{"/var/run/docker.sock"};
auto get_images = cpr::Get(cpr::Url("http:/v1.35/images/json"), unix_socket);
if (get_images.status_code != 200) {
return false;
}
auto get_images_json = json::parse(get_images.text);
bool found_quilc = false;
for (auto &image : get_images_json) {
auto repo_tag = image["RepoTags"][0].get<std::string>();
if (repo_tag.find("rigetti/quilc") != std::string::npos) {
found_quilc = true;
break;
}
}
if (!found_quilc) {
return false;
}
if (src.find("__qpu__") != std::string::npos) {
// valid xacc kernel
return xacc::getCompiler("quil")->canParse(src);
} else {
// invalid xacc kernel, add function prototype to it
return xacc::getCompiler("quil")->canParse(
"__qpu__ void tmp_quil(qbit q) {\n" + src + "}\n");
}
}
std::shared_ptr<IR> Quilc::compile(const std::string &src,
std::shared_ptr<Accelerator> acc) {
bool verbose = false;
cpr::UnixSocket unix_socket{"/var/run/docker.sock"};
cpr::Url createUrl{"http:/v1.35/containers/create"};
auto get_images = cpr::Get(cpr::Url("http:/v1.35/images/json"), unix_socket);
auto get_images_json = json::parse(get_images.text);
bool found_quilc = false;
for (auto &image : get_images_json) {
auto repo_tag = image["RepoTags"][0].get<std::string>();
if (repo_tag.find("rigetti/quilc") != std::string::npos) {
found_quilc = true;
break;
}
}
if (!found_quilc) {
xacc::error("Could not find rigetti/quilc docker image. Please pull the "
"image before using the quilc compiler.");
}
// preprocess src string, can be xacc-like or just standard quil
// if xacc-like, get all parameters
std::string _src = src;
if (src.find("__qpu__") != std::string::npos) {
// this is xacc-like, get all double / std::vector<double> parameters
auto prototype = src.substr(0, src.find_first_of("{"));
auto args_str =
prototype.substr(prototype.find_first_of("("), prototype.length());
args_str.erase(
std::remove_if(args_str.begin(), args_str.end(),
[](char ch) { return ch == '(' || ch == ')'; }),
args_str.end());
auto args = xacc::split(args_str, ',');
std::vector<std::string> paramLines;
for (auto &arg : args) {
xacc::trim(arg);
auto type_var = xacc::split(arg, ' ');
if (type_var[0] == "double") {
paramLines.push_back("DECLARE " + type_var[1] + " REAL[1]");
} else if (type_var[0] == "std::vector<double>") {
}
}
_src = src.substr(src.find_first_of("{") + 1, src.length());
_src.erase(std::remove_if(_src.begin(), _src.end(),
[](char ch) { return ch == '}'; }),
_src.end());
std::string tmp = "";
for (auto p : paramLines) {
tmp += p + "\n";
}
_src = tmp + _src;
}
cpr::Header cprHeaders;
cprHeaders.insert({"Content-Type", "application/json"});
json j;
j["Image"] = "rigetti/quilc";
j["AttachStdin"] = false;
j["OpenStdin"] = true;
j["Tty"] = false;
j["StdinOnce"] = false;
j["AttachStdout"] = false;
j["AttachStderr"] = false;
j["NetworkDisabled"] = false;
auto post_str = j.dump();
auto r = cpr::Post(createUrl, unix_socket, cpr::Body(post_str), cprHeaders,
cpr::Verbose(verbose));
// std::cout << "r.status_code :" << r.status_code << std::endl;
// std::cout << "r.text :" << r.text << std::endl;
auto jj = json::parse(r.text);
std::string container_id = jj["Id"].get<std::string>();
// START CONTAINER
// 5516 curl --unix-socket /var/run/docker.sock
// http:/v1.35/containers/f7b908f2d8b83ac3344b57c0cb4579a37f4416705a68284b4cd32aeeec6d3227/start
// -X POST
cpr::Url startUrl{"http:/v1.35/containers/" + container_id + "/start"};
auto start_response = cpr::Post(startUrl, unix_socket, cpr::Verbose(verbose));
// std::cout << "start_response.status_code :" << start_response.status_code
// << std::endl;
// std::cout << "start_response.text :" << start_response.text << std::endl;
// EXEC
// 5519 curl --unix-socket /var/run/docker.sock
// http:/v1.35/containers/f7b908f2d8b83ac3344b57c0cb4579a37f4416705a68284b4cd32aeeec6d3227/exec
// -X POST -H "Content-Type: application/json" -d '{"Container":
// "f7b908f2d8b83ac3344b57c0cb4579a37f4416705a68284b4cd32aeeec6d3227",
// "User": "", "Privileged": false, "Tty": false,
// "AttachStdin": false, "AttachStdout": true, "AttachStderr": true, "Cmd":
// ["/bin/bash", "-c", "echo H 0 | quilc"]}'
cpr::Url execUrl{"http:/v1.35/containers/" + container_id + "/exec"};
json execJson;
execJson["Container"] = container_id;
execJson["User"] = "";
execJson["Priviliged"] = false;
execJson["AttachStdin"] = false;
execJson["AttachStdout"] = true;
execJson["AttachStderr"] = true;
execJson["Tty"] = false;
execJson["Cmd"] = std::vector<std::string>{"/bin/bash", "-c",
"printf \"" + _src + "\" | quilc"};
auto execR = cpr::Post(execUrl, unix_socket, cpr::Body(execJson.dump()),
cprHeaders, cpr::Verbose(verbose));
// std::cout << "\n\nexecR.status_code :" << execR.status_code << std::endl;
// std::cout << "execR.text :" << execR.text << std::endl;
json jjj = json::parse(execR.text);
// start the exec
// 5520 curl --unix-socket /var/run/docker.sock
// http:/v1.35/exec/0c4b0ea67096feec14e9fda831774ef1126923e9d2aea904f1c065ee3c441653/start
// -X POST -H "Content-Type: application/json" -d '{"Tty": false, "Detach":
// false}'
json startExec;
startExec["Detach"] = false;
startExec["Tty"] = false;
cpr::Url startExecUrl{"http:/v1.35/exec/" + jjj["Id"].get<std::string>() +
"/start"};
auto execStart =
cpr::Post(startExecUrl, unix_socket, cpr::Body(startExec.dump()),
cprHeaders, cpr::Verbose(verbose));
// std::cout << "\n\nexecStartR.status_code :" << execStart.status_code
// << std::endl;
// std::cout << "execStartR.text :\n" << execStart.text << std::endl;
auto result_quil = execStart.text.substr(8, execStart.text.length());
// std::cout << "RESULTQUIL:\n" << result_quil << "\n";
// stop the container, --rm will also delete it
cpr::Url stopUrl{"http:/v1.35/containers/" + container_id + "/stop"};
// Stop and remove the container, like we were never even here...
auto stopResponse =
cpr::Post(stopUrl, unix_socket, cprHeaders, cpr::Verbose(verbose));
auto deleted = cpr::Delete(cpr::Url("http:/v1.35/containers/" + container_id),
unix_socket);