Commit 7f1e2a69 authored by Mccaskey, Alex's avatar Mccaskey, Alex
Browse files

Adding Rigetti example that runs Rabi experiment on physical QPU - AJM

parent 0486497f
......@@ -52,7 +52,8 @@ endif()
find_package(MPI REQUIRED)
# We require Boost
find_package(Boost COMPONENTS system program_options filesystem REQUIRED)
find_package(PythonLibs REQUIRED)
find_package(Boost COMPONENTS system program_options filesystem python REQUIRED)
# Look for OpenMP, add it we find it
find_package(OpenMP)
......
......@@ -33,6 +33,14 @@ find_package(OpenSSL)
if (OPENSSL_FOUND)
include_directories(${CMAKE_SOURCE_DIR}/tpls/rapidjson/include)
include_directories(${PYTHON_INCLUDE_DIR})
include_directories(${CMAKE_SOURCE_DIR}/tpls/fire/tensors)
include_directories(${CMAKE_SOURCE_DIR}/tpls/fire/tensors/impl)
include_directories(${CMAKE_SOURCE_DIR}/tpls/fire/tpls/eigen)
include_directories(${CMAKE_SOURCE_DIR}/quantum/gate/ir)
include_directories(${CMAKE_SOURCE_DIR}/quantum/gate/utils)
include_directories(${CMAKE_SOURCE_DIR}/quantum/gate/ir/instructions)
......@@ -50,7 +58,7 @@ if (OPENSSL_FOUND)
add_library(${LIBRARY_NAME} SHARED ${SRC})
target_link_libraries(${LIBRARY_NAME} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} xacc-gate-ir)
target_link_libraries(${LIBRARY_NAME} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} xacc-gate-ir ${PYTHON_LIBRARY})
#install(FILES ${HEADERS} DESTINATION include)
install(TARGETS ${LIBRARY_NAME} DESTINATION lib)
......
......@@ -29,16 +29,37 @@
*
**********************************************************************************/
#include "RigettiAccelerator.hpp"
#include "SimulatedQubits.hpp"
#include <boost/filesystem.hpp>
#include <fstream>
#include <iomanip>
#include <codecvt>
#include <memory>
#include <boost/python.hpp>
#define RAPIDJSON_HAS_STDSTRING 1
#include "rapidjson/prettywriter.h"
#include "rapidjson/document.h"
using namespace rapidjson;
using namespace boost::python;
namespace xacc {
namespace quantum {
std::shared_ptr<AcceleratorBuffer> RigettiAccelerator::createBuffer(
const std::string& varId) {
auto buffer = std::make_shared<AcceleratorBuffer>(varId);
std::shared_ptr<AcceleratorBuffer> buffer;
if ((*RuntimeOptions::instance())["rigetti-type"] == "wavefunction") {
buffer = std::make_shared<SimulatedQubits<10>>(varId);
} else {
buffer = std::make_shared<AcceleratorBuffer>(varId);
}
storeBuffer(varId, buffer);
return buffer;
}
......@@ -48,7 +69,14 @@ std::shared_ptr<AcceleratorBuffer> RigettiAccelerator::createBuffer(
if (!isValidBufferSize(size)) {
XACCError("Invalid buffer size.");
}
auto buffer = std::make_shared<AcceleratorBuffer>(varId, size);
std::shared_ptr<AcceleratorBuffer> buffer;
if ((*RuntimeOptions::instance())["rigetti-type"] == "wavefunction") {
buffer = std::make_shared<SimulatedQubits<10>>(varId, size);
} else {
buffer = std::make_shared<AcceleratorBuffer>(varId, size);
}
storeBuffer(varId, buffer);
return buffer;
}
......@@ -64,7 +92,7 @@ void RigettiAccelerator::execute(std::shared_ptr<AcceleratorBuffer> buffer,
// some basic variables we are going to need
auto options = RuntimeOptions::instance();
std::string type = "multishot";
std::string jsonStr = "", apiKey = "";
std::string jsonStr = "", apiKey = "", userId = "";
std::string trials = "10";
std::map<std::string, std::string> headers;
......@@ -83,7 +111,7 @@ void RigettiAccelerator::execute(std::shared_ptr<AcceleratorBuffer> buffer,
}
// Search for the API key
searchAPIKey(apiKey);
searchAPIKey(apiKey, userId);
// Set the execution type if the user provided it
if (options->exists("rigetti-type")) {
......@@ -95,11 +123,20 @@ void RigettiAccelerator::execute(std::shared_ptr<AcceleratorBuffer> buffer,
trials = (*options)["rigetti-trials"];
}
if (type.empty()) {
type = "multishot";
}
// Construct the JSON payload string
if (type == "ping") {
jsonStr += "{ \"type\" : \"ping\" }";
} else if (type == "version") {
jsonStr += "{ \"type\" : \"version\" }";
} else if (type == "pyquillow") {
// Gotta do a post, then a get for this one
jsonStr = "{\"machine\": \"QPU\", \"program\": {\"qcid\": 5, \"stop\": 1.0, \"start\": 0.0, \"step\": 0.04, \"experiment\": \"rabi\", \"time\": 100.0, \"type\": \"pyquillow\", \"device_id\": \"Z12-13-C4a2\"}, \"userId\": \""+userId+"\", \"results\": \"\", \"jobId\": \"\"}";
} else {
// Get the quil instructions
......@@ -119,11 +156,13 @@ void RigettiAccelerator::execute(std::shared_ptr<AcceleratorBuffer> buffer,
headers.insert(std::make_pair("Content-type", "application/json"));
headers.insert(std::make_pair("Accept", "application/octet-stream"));
headers.insert(std::make_pair("x-api-key", apiKey));
headers.insert(std::make_pair("x-user-id", "a8827b3f-459f-471f-911b-721e1444d62b"));
XACCInfo("Rigetti Json Payload = " + jsonStr);
// Execute the HTTP Post!
auto postResponse = httpClient->post("/qvm", jsonStr, headers);
auto relativePath = type == "pyquillow" ? "/beta/job" : "/qvm";
auto postResponse = httpClient->post(relativePath, jsonStr, headers);
// Check that it succeeded
if (!postResponse.successful) {
......@@ -135,144 +174,170 @@ void RigettiAccelerator::execute(std::shared_ptr<AcceleratorBuffer> buffer,
// Process results... to come
if (type == "wavefunction") {
auto qubits = std::dynamic_pointer_cast<SimulatedQubits<10>>(buffer);
std::istream& str = postResponse.content;
char buf[postResponse.contentLength];
str.read(buf, postResponse.contentLength);
// unsigned char buf[] = {
// 0x05, // classical measurement octets - 0x05 is 5 which is 1 0 1 // make sense for teleport
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Re component 0
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Im component 0
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Re 0x3f = 63 = 1 1 1 1 1 1, 0xf0 = 15 = 1 1 1 1
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x0a // new line
// };
// What to do with this???
auto roundToNextMultiple = [](int n, int m) -> int {
return ((n + m - 1) / m) * m;
};
auto getBits = [](auto o) -> std::bitset<8> {
if (!(0 <= o <= 255)) XACCError("invalid byte");
std::bitset<8> bits;
for (int i = 0; i < 8; i++) {
if (1 == o & 1) {
bits[i] = 1;
}
o = o >> 1;
}
return bits;
};
int numOctets = postResponse.contentLength;
int numAddresses = visitor->getNumberOfAddresses();
int numMemoryOctets = roundToNextMultiple(numAddresses, 8) / 8;
int numWFOctets = numOctets - numMemoryOctets;
assert(numWFOctets / 16 == std::pow(2, buffer->size()));
std::cout << "NUM MEM OCT: " << numMemoryOctets << ", " << numAddresses << ", " << numWFOctets << "\n";
std::vector<int> temp;
for (int i = 0; i < numMemoryOctets; i++) {
std::stringstream ss;
ss << buf[i];
unsigned long long n;
ss >> n;
std::bitset<8> bits(n);
// auto bits = getBits(buf[i]);
for (int j = 0; j < 8; j++) {
temp.push_back(bits[j]);
Py_Initialize();
// Retrieve the main module.
object main = import("__main__");
object main_namespace = main.attr("__dict__");
main_namespace["coef_string"] = std::string(buf, postResponse.contentLength);
main_namespace["num_addresses"] = visitor->getNumberOfAddresses();
const std::string code =
"import base64\n"
"import json\n"
"import requests\n"
"import struct\n"
"import numpy as np\n"
"from six import integer_types\n"
"\n"
"def _round_to_next_multiple(n, m):\n"
" return n if n % m == 0 else n + m - n % m\n"
"\n"
"def _octet_bits(o):\n"
" if not isinstance(o, integer_types):\n"
" raise TypeError(\"o should be an int\")\n"
" if not (0 <= o <= 255):\n"
" raise ValueError(\"o should be between 0 and 255 inclusive\")\n"
" bits = [0] * 8\n"
" for i in range(8):\n"
" if 1 == o & 1:\n"
" bits[i] = 1\n"
" o = o >> 1\n"
" return bits\n"
"num_octets = len(coef_string)\n"
"num_addresses = 3\n"
"num_memory_octets = _round_to_next_multiple(num_addresses, 8) // 8\n"
"num_wavefunction_octets = num_octets - num_memory_octets\n"
"mem = []\n"
"measStr = ''\n"
"for i in range(num_memory_octets):\n"
" octet = struct.unpack('B', coef_string[i:i+1])[0]\n"
" bs = _octet_bits(octet)\n"
" measStr += str(bs) + '\\n'\n"
" mem.extend(bs)\n"
"\n"
"mem = mem[0:num_addresses]\n"
"wf = np.zeros(num_wavefunction_octets // 16, dtype=np.cfloat)\n"
"extractStr = ''\n"
"for i, p in enumerate(range(num_memory_octets, num_octets, 16)):\n"
" re_be = coef_string[p: p + 8]\n"
" im_be = coef_string[p + 8: p + 16]\n"
" re = struct.unpack('>d', re_be)[0]\n"
" im = struct.unpack('>d', im_be)[0]\n"
" wf[i] = complex(re, im)\n"
" extractStr += str(re) + ', ' + str(im) + '\\n'";
try {
exec(code.c_str(), main_namespace);
} catch (boost::python::error_already_set& e) {
PyErr_Print();
}
std::string res = extract<std::string>(main_namespace["extractStr"]);
std::string measStr = extract<std::string>(main_namespace["measStr"]);
Py_Finalize();
int i = 0;
std::vector<std::string> splitOnNewLines;
boost::split(splitOnNewLines, res, boost::is_any_of("\n"));
for (auto s : splitOnNewLines) {
if (!s.empty() && boost::contains(s, ",")) {
std::vector<std::string> splitOnComma;
boost::split(splitOnComma, s, boost::is_any_of(","));
auto re = splitOnComma[0];
auto im = splitOnComma[1];
boost::trim(re);
boost::trim(im);
std::complex<double> d(std::stod(re), std::stod(im));
qubits->getState()(i) = d;
i++;
}
}
std::vector<int> mem(temp.begin(), temp.begin() + numAddresses);
std::cout << "Classical Measurements: ";
for (auto i : mem) {
std::cout << i << " ";
}
std::cout << "\n";
} else if (type == "pyquillow") {
std::stringstream ss;
ss << postResponse.content.rdbuf();
std::string message = ss.str();
Document document;
document.Parse(message.c_str());
auto jobId = std::string(document["jobId"].GetString());
bool finished = false;
std::cout << "\nRigettiAccelerator Awaiting Job Results";
std::cout.flush();
int count = 1;
while(!finished) {
auto r = httpClient->get("/beta/job/"+jobId, headers);
std::stringstream ss;
ss << r.content.rdbuf();
message = ss.str();
if (boost::contains(message, "result")) {
finished = true;
}
union Converter { uint64_t i; double d; };
std::cout << ".";
std::cout.flush();
auto getDouble = [&] (std::bitset<64> const& bs) -> double{
Converter c;
c.i = bs.to_ullong();
return c.d;
};
if (!(count % 3)) {
std::cout << "\b\b\b \b\b\b";
}
count++;
document.Parse(message.c_str());
int p = numMemoryOctets;
while (p < numOctets) {
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
std::cout << "\n";
// Get subset of the binary buffer
char re_be[8], im_be[8];
std::memcpy(re_be, &buf[p], sizeof(double));
std::memcpy(im_be, &buf[p+8], sizeof(double));
auto results = std::string(document["result"].GetString());
double re = *reinterpret_cast<double*>(re_be);
double im = *reinterpret_cast<double*>(im_be);
auto first = results.find_first_of("]");
auto xAxisStr = results.substr(1, first);
auto yAxisStr = results.substr(first+2, results.length());
std::cout << p << ": " << "(" << re << ", " << im << ")\n";
boost::replace_all(xAxisStr, "]", "");
boost::replace_all(xAxisStr, "[", "");
boost::replace_all(yAxisStr, "]", "");
boost::replace_all(yAxisStr, "[", "");
p+=16;
std::vector<std::string> splitX, splitY;
boost::split(splitX, xAxisStr, boost::is_any_of(","));
boost::split(splitY, yAxisStr, boost::is_any_of(","));
std::ofstream csvFile("rabiOscillation.csv");
csvFile << "# ResponseAmplitude, PulseAmplitude\n";
for (int i = 0; i < splitX.size(); i++) {
boost::trim(splitX[i]);
boost::trim(splitY[i]);
csvFile << std::stod(splitX[i]) << ", " << std::stod(splitY[i]) << "\n";
}
/*
* def recover_complexes(coef_string):
num_octets = len(coef_string)
num_addresses = len(classical_addresses)
num_memory_octets = _round_to_next_multiple(num_addresses, 8) // 8
num_wavefunction_octets = num_octets - num_memory_octets
# Parse the classical memory
mem = []
for i in range(num_memory_octets):
# Python 3 oddity: indexing coef_string with a single
# index returns an int. If you request a slice it keeps
# it as a bytestring.
octet = struct.unpack('B', coef_string[i:i+1])[0]
mem.extend(_octet_bits(octet))
mem = mem[0:num_addresses]
# Parse the wavefunction
wf = np.zeros(num_wavefunction_octets // OCTETS_PER_COMPLEX_DOUBLE, dtype=np.cfloat)
for i, p in enumerate(range(num_memory_octets, num_octets, OCTETS_PER_COMPLEX_DOUBLE)):
re_be = coef_string[p: p + OCTETS_PER_DOUBLE_FLOAT]
im_be = coef_string[p + OCTETS_PER_DOUBLE_FLOAT: p + OCTETS_PER_COMPLEX_DOUBLE]
re = struct.unpack('>d', re_be)[0]
im = struct.unpack('>d', im_be)[0]
wf[i] = complex(re, im)
*/
csvFile.close();
} else if (type == "multishot" || type == "ping" || type == "version") {
std::stringstream ss;
ss << postResponse.content.rdbuf();
XACCInfo("Rigetti QVM Response:\n\t" + ss.str());
} else {
// do nothing
}
}
void RigettiAccelerator::searchAPIKey(std::string& key) {
void RigettiAccelerator::searchAPIKey(std::string& key, std::string& userid) {
// Search for the API Key in $HOME/.pyquil_config,
// $PYQUIL_CONFIG, or in the command line argument --api-key
......@@ -280,11 +345,11 @@ void RigettiAccelerator::searchAPIKey(std::string& key) {
boost::filesystem::path pyquilConfig(
std::string(getenv("HOME")) + "/.pyquil_config");
if (boost::filesystem::exists(pyquilConfig)) {
findApiKeyInFile(key, pyquilConfig);
findApiKeyInFile(key, userid, pyquilConfig);
} else if (const char * nonStandardPath = getenv("PYQUIL_CONFIG")) {
boost::filesystem::path nonStandardPyquilConfig(
nonStandardPath);
findApiKeyInFile(key, nonStandardPyquilConfig);
findApiKeyInFile(key, userid, nonStandardPyquilConfig);
} else {
// Ensure that the user has provided an api-key
......@@ -294,6 +359,10 @@ void RigettiAccelerator::searchAPIKey(std::string& key) {
// Set the API Key
key = (*options)["rigetti-api-key"];
if (options->exists("rigetti-user-id")) {
userid = (*options)["rigetti-user-id"];
}
}
// If its still empty, then we have a problem
......@@ -304,7 +373,7 @@ void RigettiAccelerator::searchAPIKey(std::string& key) {
}
}
void RigettiAccelerator::findApiKeyInFile(std::string& apiKey,
void RigettiAccelerator::findApiKeyInFile(std::string& apiKey, std::string& id,
boost::filesystem::path &p) {
std::ifstream stream(p.string());
std::string contents(
......@@ -320,9 +389,109 @@ void RigettiAccelerator::findApiKeyInFile(std::string& apiKey,
auto key = split[1];
boost::trim(key);
apiKey = key;
} else if (boost::contains(l, "user_id")) {
std::vector<std::string> split;
boost::split(split, l, boost::is_any_of(":"));
auto userId = split[1];
boost::trim(userId);
id = userId;
}
}
}
}
}
//
//
//// What to do with this???
//auto roundToNextMultiple = [](int n, int m) -> int {
// return ((n + m - 1) / m) * m;
//};
//
//auto getBits = [](auto o) -> std::bitset<8> {
// if (!(0 <= o <= 255)) XACCError("invalid byte");
// std::bitset<8> bits;
// for (int i = 0; i < 8; i++) {
// if (1 == o & 1) {
// bits[i] = 1;
// }
// o = o >> 1;
// }
// return bits;
//};
//
//int numOctets = postResponse.contentLength;
//int numAddresses = visitor->getNumberOfAddresses();
//int numMemoryOctets = roundToNextMultiple(numAddresses, 8) / 8;
//int numWFOctets = numOctets - numMemoryOctets;
//
//assert(numWFOctets / 16 == std::pow(2, buffer->size()));
//
//std::cout << "NUM MEM OCT: " << numMemoryOctets << ", " << numAddresses << ", " << numWFOctets << "\n";
//std::vector<int> temp;
//for (int i = 0; i < numMemoryOctets; i++) {
// auto bits = getBits(buf[i]);
// for (int j = 0; j < 8; j++) {
// temp.push_back(bits[j]);
// }
//}
//
//std::vector<int> mem(temp.begin(), temp.begin() + numAddresses);
//
//std::cout << "Classical Measurements: ";
//for (auto i : mem) {
// std::cout << i << " ";
//}
//std::cout << "\n";
//
//union Converter { uint64_t i; double d; };
//
//auto getDouble = [&] (std::bitset<64> const& bs) -> double{
// Converter c;
// c.i = bs.to_ullong();
// return c.d;
//};
//
//int p = numMemoryOctets;
//while (p < numOctets) {
//
// // Get subset of the binary buffer
// char re_be[8], im_be[8];
// std::memcpy(re_be, &buf[p], sizeof(double));
// std::memcpy(im_be, &buf[p+8], sizeof(double));
//
// auto re = *reinterpret_cast<double*>(re_be);
// auto im = *reinterpret_cast<double*>(im_be);
//
// std::cout << p << ": " << "(" << re << ", " << im << ")\n";
//
// p+=16;
//
//}
/*
* def recover_complexes(coef_string):
num_octets = len(coef_string)
num_addresses = len(classical_addresses)
num_memory_octets = _round_to_next_multiple(num_addresses, 8) // 8
num_wavefunction_octets = num_octets - num_memory_octets
# Parse the classical memory
mem = []
for i in range(num_memory_octets):
# Python 3 oddity: indexing coef_string with a single
# index returns an int. If you request a slice it keeps
# it as a bytestring.
octet = struct.unpack('B', coef_string[i:i+1])[0]
mem.extend(_octet_bits(octet))
mem = mem[0:num_addresses]
# Parse the wavefunction
wf = np.zeros(num_wavefunction_octets // OCTETS_PER_COMPLEX_DOUBLE, dtype=np.cfloat)
for i, p in enumerate(range(num_memory_octets, num_octets, OCTETS_PER_COMPLEX_DOUBLE)):
re_be = coef_string[p: p + OCTETS_PER_DOUBLE_FLOAT]
im_be = coef_string[p + OCTETS_PER_DOUBLE_FLOAT: p + OCTETS_PER_COMPLEX_DOUBLE]
re = struct.unpack('>d', re_be)[0]
im = struct.unpack('>d', im_be)[0]
wf[i] = complex(re, im)
*/
......@@ -138,11 +138,15 @@ public:
"rigetti");
}
RigettiAccelerator() :
httpClient(
std::make_shared<
fire::util::AsioNetworkingTool<SimpleWeb::HTTPS>>(
"api.rigetti.com", false)) {
RigettiAccelerator() : httpClient(std::make_shared<
fire::util::AsioNetworkingTool<SimpleWeb::HTTPS>>(
"api.rigetti.com", false)) {
auto runtimeOptions = RuntimeOptions::instance();
if (runtimeOptions->exists("rigetti-type") && (*runtimeOptions)["rigetti-type"] == "pyquillow") {
httpClient = std::make_shared<
fire::util::AsioNetworkingTool<SimpleWeb::HTTPS>>(
"job.rigetti.com", false);
}
}
RigettiAccelerator(std::shared_ptr<fire::util::INetworkingTool> http) :
......@@ -165,13 +169,13 @@ private:
* API key in $HOME/.pyquil_config, $PYQUIL_CONFIG,
* or --api-key command line arg
*/
void searchAPIKey(std::string& key);
void searchAPIKey(std::string& key, std::string& id);
/**
* Private utility to search for key in the config
* file.
*/
void findApiKeyInFile(std::string& key, boost::filesystem::path &p);
void findApiKeyInFile(std::string& key, std::string& id, boost::filesystem::path &p);
};
......
......@@ -33,4 +33,9 @@ if (TARGET xacc-scaffold)
target_link_libraries(teleport_scaffold_rigetti ${Boost_LIBRARIES} dl)
install(TARGETS teleport_scaffold_rigetti DESTINATION examples)