Commit 9a75220c authored by Mccaskey, Alex's avatar Mccaskey, Alex
Browse files

Setup AcceleratorBuffer infrastructure, made it possible to pass kernel arguments

parent 57c3efc5
......@@ -33,7 +33,7 @@
// Quantum Kernel executing teleportation of
// qubit state to another.
const std::string src("__qpu__ teleport () {\n"
const std::string src("__qpu__ teleport (qbit qreg) {\n"
" cbit creg[2];\n"
" // Init qubit 0 to 1\n"
" X(qreg[0]);\n"
......@@ -57,22 +57,24 @@ int main (int argc, char** argv) {
auto qpu = std::make_shared<Simple6QubitAcc>();
// Allocate some qubits, give them a unique identifier...
auto qreg = qpu->allocate("qreg");
auto qubitReg = qpu->createBuffer("qreg", 3);
using QubitRegisterType = decltype(qubitReg);
// Construct a new Program
xacc::Program quantumProgram(qpu, src);
// Build the program
quantumProgram.build("--compiler scaffold --writeIR teleport.xir");
quantumProgram.build("--compiler scaffold "
"--writeIR teleport.xir");
// Retrieve the constructed kernel
auto teleport = quantumProgram.getKernel("teleport");
auto teleport = quantumProgram.getKernel<QubitRegisterType>("teleport");
// Execute the kernel!
teleport();
teleport(qubitReg);
// Get the execution result
qreg->printState(std::cout);
qubitReg->printBufferState(std::cout);
return 0;
}
......
......@@ -32,7 +32,6 @@
#define QUANTUM_GATE_ACCELERATORS_QPU_HPP_
#include "Accelerator.hpp"
#include "Qubits.hpp"
namespace xacc {
namespace quantum {
......@@ -40,8 +39,8 @@ namespace quantum {
/**
*
*/
template<const int NQubits>
class QPUGate: virtual public Accelerator<Qubits<NQubits>> {
template<typename BitsType>
class QPUGate: virtual public Accelerator<BitsType> {
public:
/**
......@@ -63,19 +62,6 @@ public:
}
virtual ~QPUGate() {}
protected:
/**
* This Accelerator can allocate any number of
* qubits (at a great computational cost...)
*
* @param N
* @return
*/
bool canAllocate(const int N) {
return N <= NQubits;
}
};
}
......
#ifndef QUANTUM_GATE_QUBITS_HPP_
#define QUANTUM_GATE_QUBITS_HPP_
#include "Accelerator.hpp"
#include <complex>
#include <Eigen/Dense>
namespace xacc {
namespace quantum {
using QubitState = Eigen::VectorXcd;
/**
*
*/
template<const int NumberOfQubits>
class Qubits: public AcceleratorBits<NumberOfQubits> {
protected:
QubitState state;
QubitState subState;
bool usingSubstate;
public:
Qubits() :
state((int) std::pow(2, NumberOfQubits)), usingSubstate(false) {
// Initialize to |000...000> state
state.setZero();
state(0) = 1.0;
}
template<typename ... ActiveBits>
Qubits(QubitState& currentState, ActiveBits ... activeBits) :
state(currentState), subState((int) std::pow(2, sizeof...(ActiveBits))),
usingSubstate(true) {
}
template<typename... SubBits>
auto allocateSubset(
SubBits... subset) -> decltype(std::shared_ptr<Qubits<sizeof...(SubBits)>>()) {
auto qubits = std::make_shared<Qubits<sizeof...(SubBits)>>(state, subset...);
return qubits;
}
void applyUnitary(Eigen::MatrixXcd& U) {
state = U * state;
}
QubitState& getState() {
return state;
}
void setState(QubitState& st) {
state = st;
}
void printState(std::ostream& stream) {
for (int i = 0; i < state.rows(); i++) {
stream << std::bitset<NumberOfQubits>(i).to_string() << " -> " << state(i) << "\n";
}
}
};
}
}
#endif
#ifndef QUANTUM_GATE_SIMULATEDQUBITS_HPP_
#define QUANTUM_GATE_SIMULATEDQUBITS_HPP_
#include "Accelerator.hpp"
#include <complex>
#include <Eigen/Dense>
namespace xacc {
namespace quantum {
using QubitState = Eigen::VectorXcd;
/**
*
*/
template<const int TotalNumberOfQubits>
class SimulatedQubits: public AcceleratorBuffer {
protected:
QubitState bufferState;
public:
SimulatedQubits(const std::string& str) :
AcceleratorBuffer(str), bufferState((int) std::pow(2, TotalNumberOfQubits)) {
// Initialize to |000...000> state
bufferState.setZero();
bufferState(0) = 1.0;
}
SimulatedQubits(const std::string& str, const int N) :
AcceleratorBuffer(str, N), bufferState((int) std::pow(2, N)) {
bufferState.setZero();
bufferState(0) = 1.0;
}
template<typename ... Indices>
SimulatedQubits(const std::string& str, int firstIndex, Indices ... indices) :
AcceleratorBuffer(str, firstIndex, indices...), bufferState(
(int) std::pow(2, sizeof...(indices) + 1)) {
}
void applyUnitary(Eigen::MatrixXcd& U) {
bufferState = U * bufferState;
}
QubitState& getState() {
return bufferState;
}
void setState(QubitState& st) {
bufferState = st;
}
void printBufferState(std::ostream& stream) {
for (int i = 0; i < bufferState.rows(); i++) {
stream
<< std::bitset<TotalNumberOfQubits>(i).to_string().substr(
size(), TotalNumberOfQubits) << " -> "
<< bufferState(i) << "\n";
}
}
void mapBufferStateToSystemState() {
}
};
}
}
#endif
......@@ -34,6 +34,7 @@
#include "QPUGate.hpp"
#include "QasmToGraph.hpp"
#include "GraphIR.hpp"
#include "SimulatedQubits.hpp"
#include <unsupported/Eigen/KroneckerProduct>
#include <random>
......@@ -48,7 +49,7 @@ using QuantumGraphIR = xacc::GraphIR<GraphType>;
*
*/
template<const int NQubits>
class EigenAccelerator : virtual public QPUGate<NQubits> {
class EigenAccelerator : virtual public QPUGate<SimulatedQubits<NQubits>> {
public:
/**
......@@ -76,9 +77,14 @@ public:
*
* @param ir
*/
virtual void execute(const std::shared_ptr<xacc::IR> ir) {
virtual void execute(const std::string& bufferId, const std::shared_ptr<xacc::IR> ir) {
auto qubits = std::dynamic_pointer_cast<Qubits<NQubits>>(this->bits);
auto qubits = this->allocatedBuffers[bufferId];
if (!qubits) {
QCIError("Invalid buffer id. Could not get qubit buffer.");
}
int nQubits = qubits->size();
// Cast to a GraphIR, if we can...
auto graphir = std::dynamic_pointer_cast<QuantumGraphIR>(ir);
......@@ -111,8 +117,8 @@ public:
}
// Create a list of nQubits Identity gates
std::vector<Eigen::MatrixXcd> productList(NQubits);
for (int i = 0; i < NQubits; i++) {
std::vector<Eigen::MatrixXcd> productList(nQubits);
for (int i = 0; i < nQubits; i++) {
productList[i] = gates["I"];
}
......@@ -238,7 +244,7 @@ public:
}
// Make sure that localU is the correct size
assert(localU.rows() == std::pow(2, NQubits) && localU.cols() == std::pow(2,NQubits));
assert(localU.rows() == std::pow(2, nQubits) && localU.cols() == std::pow(2,nQubits));
qubits->applyUnitary(localU);
......@@ -248,6 +254,9 @@ public:
}
}
}
// Map buffer state to accelerator system state
}
/**
......
......@@ -41,7 +41,7 @@ using namespace xacc::quantum;
BOOST_AUTO_TEST_CASE(checkConstruction) {
EigenAccelerator<3> acc;
auto qreg = acc.allocate("qreg");
// auto qreg = acc.allocate("qreg");
// Create a graph IR modeling a
......
......@@ -32,15 +32,11 @@
#define BOOST_TEST_MODULE QubitsTester
#include <boost/test/included/unit_test.hpp>
#include "Qubits.hpp"
#include "SimulatedQubits.hpp"
using namespace qci::common;
using namespace xacc::quantum;
BOOST_AUTO_TEST_CASE(checkConstruction) {
Qubits<3> qubits;
BOOST_VERIFY(qubits.N == 3);
std::bitset<3> bits;
BOOST_VERIFY(bits == qubits.measure());
}
......@@ -49,15 +49,20 @@ void ScaffoldCompiler::modifySource() {
kernelSource.erase(kernelSource.find("__qpu__"), 7);
kernelSource = std::string("module ") + kernelSource;
std::string qubitAllocationLine, cbitAllocationLine, cbitVarName;
std::string qubitAllocationLine, cbitAllocationLine, cbitVarName, qbitVarName;
std::map<int, int> cbitToQubit;
std::regex qbitName("qbit\\s.*");
qubitAllocationLine = (*std::sregex_iterator(kernelSource.begin(),
kernelSource.end(), qbitName)).str() + "\n";
std::vector<std::string> splitQbit;
boost::split(splitQbit, qubitAllocationLine, boost::is_any_of(" "));
auto qbitVarName = splitQbit[1].substr(0, splitQbit[1].find_first_of("["));
if (typeToVarKernelArgs.find("qbit") != typeToVarKernelArgs.end()) {
qubitAllocationLine = "qbit " + typeToVarKernelArgs["qbit"] + ";\n";
qbitVarName = typeToVarKernelArgs["qbit"].substr(0, typeToVarKernelArgs["qbit"].find_first_of("["));
} else {
std::regex qbitName("qbit\\s.*");
qubitAllocationLine = (*std::sregex_iterator(kernelSource.begin(),
kernelSource.end(), qbitName)).str() + "\n";
std::vector<std::string> splitQbit;
boost::split(splitQbit, qubitAllocationLine, boost::is_any_of(" "));
qbitVarName = splitQbit[1].substr(0, splitQbit[1].find_first_of("["));
}
// Create Cbit to Qbit mapping
std::regex cbitName("cbit\\s.*");
......@@ -150,8 +155,27 @@ void ScaffoldCompiler::modifySource() {
functionName);
std::string fName = (*begin).str();
std::string qbitAllocation = "", fargs;
if (this->typeToVarKernelArgs.find("qbit") != this->typeToVarKernelArgs.end()) {
auto varName = this->typeToVarKernelArgs["qbit"];
qbitAllocation = "qbit " + varName + ";\n ";
}
for (auto i = typeToVarKernelArgs.begin(); i != typeToVarKernelArgs.end(); ++i) {
if ("qbit" == i->first) {
fargs += i->second.substr(0, i->second.find_first_of("[")) + ",";
} else {
fargs += i->second + ",";
}
}
if (!fargs.empty()) {
fargs = fargs.substr(0, fargs.size()-1);
boost::replace_first(fName, "(", "(" + fargs);
}
// Now wrap in a main function for ScaffCC
kernelSource = kernelSource + std::string("\nint main() {\n ") + fName
kernelSource = kernelSource + std::string("\nint main() {\n ") + qbitAllocation + fName
+ std::string(");\n}");
// std::cout << "\n" << kernelSource << "\n";
......
......@@ -66,39 +66,64 @@ enum AcceleratorType { qpu_gate, qpu_aqc, npu };
*
* @author Alex McCaskey
*/
template<const int Number>
class AcceleratorBits {
class AcceleratorBuffer {
public:
/**
* Reference to the number of bits
*/
static constexpr int N = Number;
/**
* Return the current state of the bits
* @return
*/
virtual std::bitset<(size_t) Number> measure() {
return bits;
AcceleratorBuffer(const std::string& str) :
bufferId(str) {
}
template<typename... SubBits>
auto allocateSubset(
SubBits ... subset) ->
decltype(std::shared_ptr<AcceleratorBits<sizeof...(SubBits)>>()) {
AcceleratorBuffer(const std::string& str, const int N) :
bufferId(str), bufferSize(N) {
}
template<typename ... Indices>
AcceleratorBuffer(const std::string& str, int firstIndex,
Indices ... indices) :
bufferId(str), bufferSize(1 + sizeof...(indices)) {
}
int size() {
return bufferSize;
}
std::string name() {
return bufferId;
}
virtual ~AcceleratorBits() {}
protected:
/**
* The bits themselves
*/
std::bitset<(size_t)Number> bits;
std::vector<int> activeBits;
int bufferSize = 0;
std::string bufferId;
};
//template<const int Number>
//class AcceleratorBits {
//public:
// /**
// * Reference to the number of bits
// */
// static constexpr int N = Number;
//
// /**
// * Return the current state of the bits
// * @return
// */
// virtual std::bitset<(size_t) Number> measure() {
// return bits;
// }
//
// template<typename... SubBits>
// auto allocateSubset(
// SubBits ... subset) ->
// decltype(std::shared_ptr<AcceleratorBits<sizeof...(SubBits)>>()) {
// }
// virtual ~AcceleratorBits() {}
//
//protected:
//
// /**
// * The bits themselves
// */
// std::bitset<(size_t)Number> bits;
//
// std::vector<int> activeBits;
//
//};
class IAccelerator : public qci::common::QCIObject {
public:
......@@ -121,7 +146,7 @@ public:
*
* @param ir
*/
virtual void execute(const std::shared_ptr<IR> ir) = 0;
virtual void execute(const std::string& bufferId, const std::shared_ptr<IR> ir) = 0;
/**
* Return the number of bits that the user most recently
......@@ -129,15 +154,14 @@ public:
*
* @return nBits The number of requested bits
*/
virtual int getAllocationSize() = 0;
virtual int getBufferSize(const std::string& id) = 0;
virtual int getBufferSize() = 0;
protected:
virtual bool isValidBufferSize(const int NBits) {return true;}
/**
* Return the variable name provided upon bit allocation
* (for example - qreg for gate model quantum bits in (qbit qreg[2];))
*
* @return varName The name of the bits allocated.
*/
virtual const std::string getVariableName() = 0;
};
/**
......@@ -153,49 +177,57 @@ public:
* instances that transform XACC IR to be amenable to execution
* on the hardware.
*/
template<typename TotalBits>
template<typename BitsType>
class Accelerator : public IAccelerator {
static_assert(is_valid_bitstype<TotalBits>::value, "Derived BitsType parameter must contain N int member for number of bits.");
static_assert(std::is_base_of<AcceleratorBits<TotalBits::N>, TotalBits>::value, "");
static_assert(std::is_base_of<AcceleratorBuffer, BitsType>::value, "");
using BitsTypePtr = std::shared_ptr<BitsType>;
public:
/**
* Allocate bit resources (if needed).
*
* @return bits The AcceleratorBits derived type
*/
std::shared_ptr<TotalBits> allocate(const std::string& variableNameId) {
if (!canAllocate(TotalBits::N)) {
QCIError("Error in allocated requested bits");
}
bits = std::make_shared<TotalBits>();
NBitsAllocated = TotalBits::N;
bitVarId = variableNameId;
return bits;
Accelerator() {
totalSystemBuffer = std::make_shared<BitsType>("default");
allocatedBuffers.insert(std::make_pair("default", totalSystemBuffer));
}
/**
* Allocate some subset of Accelerator bit resources.
*
* @param variableNameId
* @param bitList
* @return
*/
template<typename... SubBits>
auto allocate(const std::string& variableNameId,
SubBits... bitList) -> decltype(std::declval<TotalBits>().allocateSubset(bitList...)) {
// FIXME CHECK THAT THEY PASSED IN LIST OF INTS
if (!canAllocate(sizeof...(SubBits))) {
QCIError("Error in allocated requested bits");
BitsTypePtr createBuffer(const std::string& varId) {
if (isValidBufferVarId(varId)) {
auto buffer = std::make_shared<BitsType>(varId);
allocatedBuffers.insert(std::make_pair(varId, buffer));
return buffer;
} else {
QCIError("Invalid buffer variable name.");
}
}
auto subsetBits = bits->allocateSubset(bitList...);
NBitsAllocated = (int) sizeof...(SubBits);
bitVarId = variableNameId;
BitsTypePtr createBuffer(const std::string& varId, const int size) {
if (!isValidBufferVarId(varId)) {
QCIError("Invalid buffer variable name.");
}
if (!isValidBufferSize(size)) {
QCIError("Invalid buffer size.");
}
auto buffer = std::make_shared<BitsType>(varId, size);
allocatedBuffers.insert(std::make_pair(varId, buffer));
return buffer;
}
return subsetBits;
template<typename... Indices>
BitsTypePtr createBuffer(const std::string& varId, int firstIndex,
Indices ... indices) {
if (!isValidBufferVarId(varId)) {
QCIError("Invalid buffer variable name.");
}
if (!isValidBufferSize(sizeof...(indices) + 1)) {
QCIError("Invalid buffer size.");
}
if (!validIndices(indices...)) {
QCIError("Invalid buffer indices.");
}
auto buffer = std::make_shared<BitsType>(varId, firstIndex, indices...);
allocatedBuffers.insert(std::make_pair(varId, buffer));
return buffer;
}
/**
......@@ -204,20 +236,19 @@ public:
*
* @return nBits The number of requested bits
*/
virtual int getAllocationSize() {
return NBitsAllocated;
virtual int getBufferSize(const std::string& id) {
return allocatedBuffers[id]->size();
}
/**
* Return the variable name provided upon bit allocation
* (for example - qreg for gate model quantum bits in (qbit qreg[2];))
*
* @return varName The name of the bits allocated.
*/
virtual const std::string getVariableName() {
return bitVarId;
virtual int getBufferSize() {
return allocatedBuffers["default"]->size();
}
BitsTypePtr getExistingBuffer(const std::string& varId) {
return allocatedBuffers[varId];
}
/**
* Destructor