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

Continuing work on D-Wave Accelerator

parent a8c72e95
......@@ -29,29 +29,34 @@
#
#**********************************************************************************/
include_directories(${CMAKE_SOURCE_DIR}/quantum/aqc/ir)
include_directories(${CMAKE_SOURCE_DIR}/quantum/aqc/compiler)
find_package(OpenSSL)
include_directories(${CMAKE_SOURCE_DIR}/tpls/rapidjson/include)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
if (OPENSSL_FOUND)
include_directories(${CMAKE_SOURCE_DIR}/quantum/aqc/ir)
include_directories(${CMAKE_SOURCE_DIR}/quantum/aqc/compiler)
set (PACKAGE_NAME "XACC D-Wave Compiler and Accelerator Support")
set (PACKAGE_DESCIPTION "Extensions to XACC to work with the D-Wave QPU")
set (LIBRARY_NAME xacc-dwave)
include_directories(${CMAKE_SOURCE_DIR}/tpls/rapidjson/include)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${CMAKE_SOURCE_DIR}/tpls/fire/util)
include_directories(${CMAKE_SOURCE_DIR}/tpls/fire/tpls/simpleweb)
file (GLOB_RECURSE HEADERS *.hpp)
file (GLOB SRC *.cpp)
set (PACKAGE_NAME "XACC D-Wave Compiler and Accelerator Support")
set (PACKAGE_DESCIPTION "Extensions to XACC to work with the D-Wave QPU")
set (LIBRARY_NAME xacc-dwave)
add_library(${LIBRARY_NAME} SHARED ${SRC})
file (GLOB_RECURSE HEADERS *.hpp)
file (GLOB SRC *.cpp)
target_link_libraries(${LIBRARY_NAME} ${Boost_LIBRARIES})
add_library(${LIBRARY_NAME} SHARED ${SRC})
install(TARGETS ${LIBRARY_NAME} DESTINATION lib)
target_link_libraries(${LIBRARY_NAME} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES})
# Gather tests
file (GLOB test_files tests/*.cpp)
add_tests("${test_files}" "${CMAKE_CURRENT_SOURCE_DIR}" "xacc-dwave;dl;pthread")
install(TARGETS ${LIBRARY_NAME} DESTINATION lib)
# Gather tests
file (GLOB test_files tests/*.cpp)
add_tests("${test_files}" "${CMAKE_CURRENT_SOURCE_DIR}" "xacc-dwave;dl;pthread")
endif()
#add_subdirectory(examples)
/***********************************************************************************
* Copyright (c) 2017, UT-Battelle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the xacc nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Contributors:
* Initial API and implementation - Alex McCaskey
*
**********************************************************************************/
#include <boost/filesystem.hpp>
#include <fstream>
#include <memory>
#include "DWAccelerator.hpp"
namespace xacc {
namespace quantum {
template<typename T>
bool IsInBounds(const T& value, const T& low, const T& high) {
return !(value < low) && !(high < value);
}
std::shared_ptr<AcceleratorBuffer> DWAccelerator::createBuffer(
const std::string& varId, const int size) {
if (!isValidBufferSize(size)) {
XACCError("Invalid buffer size.");
}
auto buffer = std::make_shared<AcceleratorBuffer>(varId, size);
storeBuffer(varId, buffer);
return buffer;
}
bool DWAccelerator::isValidBufferSize(const int NBits) {
return NBits > 0;
}
void DWAccelerator::execute(std::shared_ptr<AcceleratorBuffer> buffer,
const std::shared_ptr<xacc::Function> kernel) {
auto dwKernel = std::dynamic_pointer_cast<DWKernel>(kernel);
if (!dwKernel) {
XACCError("Invalid kernel.");
}
std::vector<std::string> splitLines;
boost::split(splitLines, dwKernel->toString(""), boost::is_any_of("\n"));
auto nQMILines = splitLines.size();
auto options = RuntimeOptions::instance();
std::string jsonStr = "",
solverName = "DW_2000Q_VFYC", solveType = "ising", trials = "100";
if (options->exists("dwave-solver")) {
solverName = (*options)["dwave-solver"];
}
if (!availableSolvers.count(solverName)) {
XACCError(solverName + " is not available.");
}
auto solver = availableSolvers[solverName];
// Normalize the QMI data
auto allWeightValues = dwKernel->getAllCouplers();
auto minWeight = *std::min_element(allWeightValues.begin(),allWeightValues.end());
auto maxWeight = *std::max_element(allWeightValues.begin(),allWeightValues.end());
// Check if we should normalize Bias values
if (minWeight < solver.jRangeMin || maxWeight > solver.jRangeMax) {
for (auto inst : dwKernel->getInstructions()) {
auto divisor =
(std::fabs(minWeight) > std::fabs(maxWeight)) ?
std::fabs(minWeight) : std::fabs(maxWeight);
auto weight = boost::get<double>(inst->getParameter(0));
auto newWeight = weight / divisor;
InstructionParameter p(newWeight);
inst->setParameter(0, p);
}
}
jsonStr += "[{ \"solver\" : \"" + solverName + "\", \"type\" : \""
+ solveType + "\", \"data\" : \"" + std::to_string(solver.nQubits)
+ " " + std::to_string(nQMILines-1) + "\\n"
+ dwKernel->toString("") + "\", \"params\": { \"num_reads\" : "
+ trials + "} }]";
boost::replace_all(jsonStr, "\n", "\\n");
std::cout << "HELLO:\n" << jsonStr << "\n\n\n";
auto newclient = fire::util::AsioNetworkingTool<SimpleWeb::HTTPS>("qubist.dwavesys.com", false);
auto postResponse = newclient.post("/sapi/problems/", jsonStr, headers);
if (!postResponse.successful) {
XACCError("HTTP Post was not successful");
}
std::stringstream ss2;
ss2 << postResponse.content.rdbuf();
auto message = ss2.str();
std::cout << "REPSONSE:\n" << message << "\n";
Document document;
document.Parse(message.c_str());
auto jobId = std::string(document[0]["id"].GetString());
std::cout << "JOB ID IS " << jobId << "\n";
bool finished = false;
std::cout << "\nDWAccelerator Awaiting Job Results";
std::cout.flush();
int count = 1;
while(!finished) {
auto c = fire::util::AsioNetworkingTool<SimpleWeb::HTTPS>("qubist.dwavesys.com", false);
auto r = c.get("/sapi/problems/"+jobId, headers);
std::stringstream ss3;
ss3 << r.content.rdbuf();
message = ss3.str();
if (boost::contains(message, "COMPLETED")) {
finished = true;
}
std::cout << ".";
std::cout.flush();
if (!(count % 3)) {
std::cout << "\b\b\b \b\b\b";
}
count++;
document.Parse(message.c_str());
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
std::cout << "\n";
std::cout << "COMPLETEDMESSAGE:\n" << message << "\n";
}
void DWAccelerator::searchAPIKey(std::string& key, std::string& url) {
// Search for the API Key in $HOME/.dwave_config,
// $DWAVE_CONFIG, or in the command line argument --dwave-api-key
auto options = RuntimeOptions::instance();
boost::filesystem::path dwaveConfig(
std::string(getenv("HOME")) + "/.dwave_config");
if (boost::filesystem::exists(dwaveConfig)) {
findApiKeyInFile(key, url, dwaveConfig);
} else if (const char * nonStandardPath = getenv("DWAVE_CONFIG")) {
boost::filesystem::path nonStandardDwaveConfig(
nonStandardPath);
findApiKeyInFile(key, url, nonStandardDwaveConfig);
} else {
// Ensure that the user has provided an api-key
if (!options->exists("dwave-api-key")) {
XACCError("Cannot execute kernel on DW chip without API Key.");
}
// Set the API Key
key = (*options)["dwave-api-key"];
if (options->exists("dwave-api-url")) {
url = (*options)["dwave-api-url"];
}
}
// If its still empty, then we have a problem
if (key.empty()) {
XACCError("Error. The API Key is empty. Please place it "
"in your $HOME/.dwave_config file, $DWAVE_CONFIG env var, "
"or provide --dwave-api-key argument.");
}
}
void DWAccelerator::findApiKeyInFile(std::string& apiKey, std::string& url,
boost::filesystem::path &p) {
std::ifstream stream(p.string());
std::string contents(
(std::istreambuf_iterator<char>(stream)),
std::istreambuf_iterator<char>());
std::vector<std::string> lines;
boost::split(lines, contents, boost::is_any_of("\n"));
for (auto l : lines) {
if (boost::contains(l, "key")) {
std::vector<std::string> split;
boost::split(split, l, boost::is_any_of(":"));
auto key = split[1];
boost::trim(key);
apiKey = key;
} else if (boost::contains(l, "url")) {
std::vector<std::string> split;
boost::split(split, l, boost::is_any_of(":"));
auto key = split[1] + ":" + split[2];
boost::trim(key);
url = key;
}
}
}
}
}
/***********************************************************************************
* Copyright (c) 2017, UT-Battelle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the xacc nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Contributors:
* Initial API and implementation - Alex McCaskey
*
**********************************************************************************/
#ifndef QUANTUM_GATE_ACCELERATORS_DWACCELERATOR_HPP_
#define QUANTUM_GATE_ACCELERATORS_DWACCELERATOR_HPP_
#include "Accelerator.hpp"
#include "AsioNetworkingTool.hpp"
#include "RuntimeOptions.hpp"
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include "DWKernel.hpp"
#include "DWQMI.hpp"
#define RAPIDJSON_HAS_STDSTRING 1
#include "rapidjson/prettywriter.h"
#include "rapidjson/document.h"
using namespace rapidjson;
using namespace xacc;
namespace xacc {
namespace quantum {
struct DWSolver {
std::string name;
std::string description;
double jRangeMin;
double jRangeMax;
double hRangeMin;
double hRangeMax;
int nQubits;
};
/**
*
*/
class DWAccelerator : virtual public Accelerator {
public:
/**
* Create, store, and return an AcceleratorBuffer with the given
* variable id string and of the given number of bits.
* The string id serves as a unique identifier
* for future lookups and reuse of the AcceleratorBuffer.
*
* @param varId
* @param size
* @return
*/
std::shared_ptr<AcceleratorBuffer> createBuffer(const std::string& varId,
const int size);
/**
* Return true if this Accelerator can allocated
* NBits number of bits.
* @param NBits
* @return
*/
virtual bool isValidBufferSize(const int NBits);
/**
* Execute the kernel on the provided AcceleratorBuffer through a
* HTTP Post of Quil instructions to the Rigetti QPU at api.rigetti.com/qvm
*
* @param ir
*/
virtual void execute(std::shared_ptr<AcceleratorBuffer> buffer,
const std::shared_ptr<xacc::Function> kernel);
/**
* This Accelerator models QPU Gate accelerators.
* @return
*/
virtual AcceleratorType getType() {
return AcceleratorType::qpu_aqc;
}
/**
* We have no need to transform the IR for this Accelerator,
* so return an empty list, for now.
* @return
*/
virtual std::vector<xacc::IRTransformation> getIRTransformations() {
std::vector<xacc::IRTransformation> v;
return v;
}
/**
* Return all relevant RigettiAccelerator runtime options.
* Users can set the api-key, execution type, and number of triels
* from the command line with these options.
*/
virtual std::shared_ptr<options_description> getOptions() {
auto desc = std::make_shared<options_description>(
"D-Wave Accelerator Options");
desc->add_options()("dwave-api-key", value<std::string>(),
"Provide the D-Wave API key. This is used if "
"$HOME/.dwave_config is not found")("dwave-api-url",
value<std::string>(), "The D-Wave SAPI URL, "
"https://qubist.dwavesys.com/sapi "
"used by default.")("dwave-solver",
value<std::string>(), "The name of the solver to run on.");
return desc;
}
/**
* Register this Accelerator with the framework.
*/
static void registerAccelerator() {
xacc::RegisterAccelerator<xacc::quantum::DWAccelerator> DWTEMP(
"d-wave");
}
DWAccelerator() {
auto options = RuntimeOptions::instance();
searchAPIKey(apiKey, url);
auto tempURL = url;
boost::replace_all(tempURL, "https://", "");
boost::replace_all(tempURL, "/sapi", "");
// Set up the extra HTTP headers we are going to need
headers.insert(std::make_pair("X-Auth-Token", apiKey));
headers.insert(std::make_pair("Content-type", "application/x-www-form-urlencoded"));
headers.insert(std::make_pair("Accept", "*/*"));
// Get the Remote URL Solver data...
auto getSolverClient = fire::util::AsioNetworkingTool<SimpleWeb::HTTPS>(tempURL, false);
auto r = getSolverClient.get("/sapi/solvers/remote", headers);
std::stringstream ss;
ss << r.content.rdbuf();
auto message = ss.str();
Document document;
document.Parse(message.c_str());
if (document.IsArray()) {
for (auto i = 0; i < document.Size(); i++) {
DWSolver solver;
solver.name = document[i]["id"].GetString();
solver.description = document[i]["description"].GetString();
if (document[i]["properties"].FindMember("j_range") != document[i]["properties"].MemberEnd()) {
solver.jRangeMin = document[i]["properties"]["j_range"][0].GetDouble();
solver.jRangeMax = document[i]["properties"]["j_range"][1].GetDouble();
solver.hRangeMin = document[i]["properties"]["h_range"][0].GetDouble();
solver.hRangeMax = document[i]["properties"]["h_range"][1].GetDouble();
}
solver.nQubits = document[i]["properties"]["num_qubits"].GetInt();
availableSolvers.insert(std::make_pair(solver.name, solver));
}
}
}
/**
* The destructor
*/
virtual ~DWAccelerator() {}
protected:
std::string apiKey;
std::string url;
std::map<std::string, std::string> headers;
std::map<std::string, DWSolver> availableSolvers;
private:
/**
* Private utility to search for the D-Wave
* API key in $HOME/.dwave_config, $DWAVE_CONFIG,
* or --dwave-api-key command line arg
*/
void searchAPIKey(std::string& key, std::string& url);
/**
* Private utility to search for key in the config
* file.
*/
void findApiKeyInFile(std::string& key, std::string& url, boost::filesystem::path &p);
};
// Create an alias to search for.
}
}
#endif
/***********************************************************************************
* Copyright (c) 2017, UT-Battelle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the xacc nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Contributors:
* Initial API and implementation - Alex McCaskey
*
**********************************************************************************/
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE DWAcceleratorTester
#include <memory>
#include <boost/test/included/unit_test.hpp>
#include "DWAccelerator.hpp"
using namespace xacc::quantum;
class FakeHttpClient: public fire::util::INetworkingTool {
public:
bool postOccured = false;
virtual fire::util::HttpResponse get(const std::string& relativePath,
const std::map<std::string, std::string>& header = std::map<
std::string, std::string>()) {
std::stringstream ss;
ss << "HELLO\n";
fire::util::HttpResponse r(ss);
r.successful = true;
return r;
}
/**
* Issue an HTTP Post command at the given relative path with
* the provided message. Clients can provide a map of header key values to modify the
* POST request.
*
* @param relativePath The path relative to the hostname/port provided to this NetworkingTool
* @param message The message to post
* @param header The map of additional HTTP POST header information
* @return success Boolean indicating if post was successful
*
*/
virtual fire::util::HttpResponse post(const std::string& relativePath,
const std::string& message,
const std::map<std::string, std::string>& header = std::map<
std::string, std::string>()) {
postOccured = true;
std::stringstream ss;
ss << "HELLO\n";
fire::util::HttpResponse r(ss);
r.successful = true;
return r;
}
};
BOOST_AUTO_TEST_CASE(checkKernelExecution) {
auto options = RuntimeOptions::instance();
// DWAccelerator acc;
auto f = std::make_shared<DWKernel>("shor15");
f->addInstruction(std::make_shared<DWQMI>(0, 0, 20));
f->addInstruction(std::make_shared<DWQMI>(1, 1, 50));
f->addInstruction(std::make_shared<DWQMI>(2, 2, 60));
f->addInstruction(std::make_shared<DWQMI>(4, 4, 50));
f->addInstruction(std::make_shared<DWQMI>(5, 5, 60));
f->addInstruction(std::make_shared<DWQMI>(6, 6, -160));
f->addInstruction(std::make_shared<DWQMI>(1, 4, -1000));
f->addInstruction(std::make_shared<DWQMI>(2, 5, -1000));
f->addInstruction(std::make_shared<DWQMI>(0, 4, -14));
f->addInstruction(std::make_shared<DWQMI>(0, 5, -12));
f->addInstruction(std::make_shared<DWQMI>(0, 6, 32));
f->addInstruction(std::make_shared<DWQMI>(1, 5, 68));
f->addInstruction(std::make_shared<DWQMI>(1, 6, -128));
f->addInstruction(std::make_shared<DWQMI>(2, 6, -128));
// acc.execute(acc.createBuffer("qubits", 1), f);
// BOOST_VERIFY(client->postOccured);
}