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

Adding implementation of DWQMICompiler

parent 497af36f
......@@ -32,6 +32,7 @@
#include <boost/algorithm/string.hpp>
#include "DWQMICompiler.hpp"
#include "DWKernel.hpp"
#include "RuntimeOptions.hpp"
namespace xacc {
......@@ -43,15 +44,16 @@ DWQMICompiler::DWQMICompiler() {
std::shared_ptr<IR> DWQMICompiler::compile(const std::string& src,
std::shared_ptr<Accelerator> acc) {
auto runtimeOptions = RuntimeOptions::instance();
auto hardwareGraph = acc->getAcceleratorConnectivity();
auto dwKernel = std::make_shared<DWKernel>();
std::set<int> qubits;
std::vector<std::shared_ptr<DWQMI>> instructions;
int nHardwareVerts = hardwareGraph->order();
// Set the Kernel Source code
kernelSource = src;
return std::make_shared<DWIR>();
}
std::shared_ptr<IR> DWQMICompiler::compile(const std::string& src) {
kernelSource = src;
// Here we expect we have a kernel, only one kernel,
// and that it is just machine level instructions
......@@ -60,41 +62,131 @@ std::shared_ptr<IR> DWQMICompiler::compile(const std::string& src) {
// First off, split the string into lines
std::vector<std::string> lines;
boost::split(lines, src, boost::is_any_of("\n"));
auto firstCodeLine = lines.begin() + 1;
auto lastCodeLine = lines.end() - 1;
std::vector<std::string> qmiStrVec(firstCodeLine, lastCodeLine);
auto dwKernel = std::make_shared<DWKernel>();
// Loop over the lines to create DWQMI
for (auto qmi : qmiStrVec) {
std::cout << "LINE: " << qmi << "\n";
boost::trim(qmi);
if (!qmi.empty()) {
std::vector<std::string> splitOnSpaces;
boost::split(splitOnSpaces, qmi, boost::is_any_of(" "));
auto qbit1 = std::stoi(splitOnSpaces[0]);
auto qbit2 = std::stoi(splitOnSpaces[1]);
auto weight = std::stod(splitOnSpaces[2]);
qubits.insert(qbit1);
qubits.insert(qbit2);
instructions.emplace_back(std::make_shared<DWQMI>(qbit1, qbit2, weight));
}
}
auto dwqmi = std::make_shared<DWQMI>(qbit1, qbit2, weight);
dwKernel->addInstruction(dwqmi);
// Create a graph representation of the problem
auto problemGraph = std::make_shared<DWGraph>(qubits.size());
for (auto inst : instructions) {
auto qbit1 = inst->bits()[0];
auto qbit2 = inst->bits()[1];
double weightOrBias = boost::get<double>(inst->getParameter(0));
if (qbit1 == qbit2) {
problemGraph->setVertexProperties(qbit1, weightOrBias);
} else {
problemGraph->addEdge(qbit1, qbit2,
weightOrBias);
}
}
// Get an embedding algorithm to execute
if (!runtimeOptions->exists("dwave-embedding")) {
// For now, this is an error
XACCError("You must specify an embedding algorithm");
}
auto algoStr = (*runtimeOptions)["dwave-embedding"];
auto embeddingAlgorithm = EmbeddingAlgorithmRegistry::instance()->create(algoStr);
// Compute the minor graph embedding
auto embedding = embeddingAlgorithm->embed(problemGraph, hardwareGraph);
auto countEdgesBetweenSubTrees = [&](std::list<int> Ti, std::list<int> Tj) -> int {
int nEdges = 0;
for (auto i : Ti) {
for (auto j : Tj) {
if (hardwareGraph->edgeExists(i, j)) {
nEdges++;
}
}
}
return nEdges;
};
auto subTreeContains = [](std::list<int> tree, int i) -> bool {
return std::find(tree.begin(), tree.end(), i) != tree.end();
};
// Setup the hardware bias values
for (auto& embKv : embedding) {
auto probVert = embKv.first;
auto hardwareMapping = embKv.second;
auto newBias = std::get<0>(problemGraph->getVertexProperties(probVert)) / hardwareMapping.size();
for (auto h : hardwareMapping) {
auto embeddedInst = std::make_shared<DWQMI>(h, h, newBias);
dwKernel->addInstruction(embeddedInst);
}
}
auto ir = std::make_shared<DWIR>();
std::cout << "NHARDWARE: " << nHardwareVerts << ", " << problemGraph->order() << "\n";
for (int i = 0; i < nHardwareVerts; i++) {
for (int j = 0; j < nHardwareVerts; j++) {
if (hardwareGraph->edgeExists(i,j) && i < j && i != j) {
for (int pi = 0; pi < problemGraph->order(); pi++) {
for (int pj = 0; pj < problemGraph->order(); pj++) {
auto Ti = embedding[pi];
auto Tj = embedding[pj];
if (subTreeContains(Ti, i) && subTreeContains(Tj, j)) {
double newWeight = 0.0;
if (pi != pj) {
newWeight = problemGraph->getEdgeWeight(pi, pj)
/ countEdgesBetweenSubTrees(Ti, Tj);
} else {
// ferro-magnetic coupling parameter that ensures that physical
// qubits representing one logical qubit remain highly correlated.
for (auto neighbor : problemGraph->getNeighborList(pi)) {
newWeight += std::fabs(problemGraph->getEdgeWeight(pi, neighbor));
}
newWeight = std::get<0>(problemGraph->getVertexProperties(pi)) + newWeight - 1.0;
}
std::cout << "Adding a Edge Instruction.\n";
auto embeddedInst = std::make_shared<DWQMI>(i,
j, newWeight);
dwKernel->addInstruction(embeddedInst);
}
// else {
// std::cout << "SUBTREE DID NOT CONTAIN: " << i << " and " << j << "\n";
// }
}
}
} else {
std::cout << "Edge did not exist between " << i << ", " << j << "\n";
}
}
}
// ir->addKernel(ir);
auto ir = std::make_shared<DWIR>();
ir->addKernel(dwKernel);
return ir;
}
std::shared_ptr<IR> DWQMICompiler::compile(const std::string& src) {
XACCError("Cannot compile D-Wave program without "
"information about Accelerator connectivity.");
}
}
......
......@@ -34,6 +34,8 @@
#include "Compiler.hpp"
#include "Utils.hpp"
#include "DWIR.hpp"
#include "DWGraph.hpp"
#include "EmbeddingAlgorithm.hpp"
namespace xacc {
......@@ -66,6 +68,14 @@ public:
return "dwave-qmi";
}
virtual std::shared_ptr<options_description> getOptions() {
auto desc = std::make_shared<options_description>(
"D-Wave QMI Compiler Options");
desc->add_options()("dwave-embedding", value<std::string>(),
"Provide the name of the Embedding Algorithm to use during compilation.");
return desc;
}
/**
* Register this Compiler with the framework.
*/
......@@ -78,6 +88,7 @@ public:
std::shared_ptr<Function> function) {
XACCError("DWQMICompiler::translate - Method not implemented");
};
/**
* The destructor
*/
......
......@@ -51,22 +51,101 @@ struct F {
std::shared_ptr<xacc::Compiler> compiler;
};
class FakeDWAcc : public xacc::Accelerator {
public:
virtual std::shared_ptr<xacc::AcceleratorGraph> getAcceleratorConnectivity() {
return K44Bipartite().getAcceleratorGraph();
}
virtual xacc::AcceleratorType getType() {
return xacc::AcceleratorType::qpu_aqc;
}
virtual bool isValidBufferSize(const int nBits) {
return true;
}
virtual std::vector<xacc::IRTransformation> getIRTransformations() {
};
/**
* Execute the provided XACC IR Function on the provided AcceleratorBuffer.
*
* @param buffer The buffer of bits this Accelerator should operate on.
* @param function The kernel to execute.
*/
virtual void execute(std::shared_ptr<xacc::AcceleratorBuffer> buffer,
const std::shared_ptr<xacc::Function> function) {
}
/**
* 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 The variable name of the created buffer
* @param size The number of bits in the created buffer
* @return buffer The buffer instance created.
*/
virtual std::shared_ptr<xacc::AcceleratorBuffer> createBuffer(
const std::string& varId, const int size) {
}
};
class FakeEmbedding : public EmbeddingAlgorithm {
public:
virtual std::map<int, std::list<int>> embed(
std::shared_ptr<DWGraph> problem, std::shared_ptr<xacc::AcceleratorGraph> hardware,
std::map<std::string, std::string> params = std::map<std::string,
std::string>()) override {
std::map<int, std::list<int>> embedding;
embedding.insert(std::make_pair(0, std::list<int>{0, 4}));
embedding.insert(std::make_pair(1, std::list<int>{1}));
embedding.insert(std::make_pair(2, std::list<int>{5}));
return embedding;
}
/**
* Return the name of this Embedding Algorithm
* @return
*/
virtual std::string name() {
return "fake-embedding";
}
};
//____________________________________________________________________________//
BOOST_FIXTURE_TEST_SUITE( s, F )
BOOST_AUTO_TEST_CASE(checkSimpleCompile) {
EmbeddingAlgorithmRegistry::instance()->add(FakeEmbedding().name(), []() { return std::make_shared<FakeEmbedding>(); });
const std::string simpleQMI =
"__qpu__ dwaveKernel() {\n"
" 0 0 0.98\n"
" 1 1 .33\n"
" 2 2 .44\n"
" 0 1 .22\n"
" 0 2 .55\n"
" 1 2 .11\n"
"}";
auto ir = compiler->compile(simpleQMI);
auto options = xacc::RuntimeOptions::instance();
options->insert(std::make_pair("dwave-embedding", "fake-embedding"));
auto acc = std::make_shared<FakeDWAcc>();
auto ir = compiler->compile(simpleQMI, acc);
std::cout << "STR:\n" << ir->getKernel("dw-kernel")->toString("") << "\n";
}
......
......@@ -38,28 +38,11 @@
#include <string>
#include "Utils.hpp"
#include "Registry.hpp"
#include "Graph.hpp"
#include "DWGraph.hpp"
namespace xacc {
namespace quantum {
/**
* The DWaveVertex is a subclass of the XACCVertex that
* keeps track of one vertex parameter - the qubit
* bias parameter. XACCVertex already keeps track
* of edge weights.
*/
class DWaveVertex: public XACCVertex<double> {
public:
DWaveVertex() :
XACCVertex() {
propertyNames[0] = "bias";
}
};
// Alias for Graphs of DWave Vertices
using DWaveGraph = Graph<DWaveVertex>;
/**
* The EmbeddingAlgorithm class provides an interface
* for minor graph embedding algorithms.
......@@ -89,8 +72,8 @@ public:
* @param params Any key-value string parameters to influence the algorithm.
* @return embedding A mapping of problem vertex indices to the list of hardware vertices they map to
*/
virtual std::map<int, std::list<int>> embed(std::shared_ptr<DWaveGraph> problem,
std::shared_ptr<DWaveGraph> hardware,
virtual std::map<int, std::list<int>> embed(std::shared_ptr<DWGraph> problem,
std::shared_ptr<AcceleratorGraph> hardware,
std::map<std::string, std::string> params = std::map<std::string,
std::string>()) = 0;
......
/*
* DWGraph.hpp
*
* Created on: Jul 11, 2017
* Author: aqw
*/
#ifndef QUANTUM_AQC_IR_DWGRAPH_HPP_
#define QUANTUM_AQC_IR_DWGRAPH_HPP_
#include "Graph.hpp"
namespace xacc {
namespace quantum {
/**
* The DWVertex is a subclass of the XACCVertex that
* keeps track of one vertex parameter - the qubit
* bias parameter. XACCVertex already keeps track
* of edge weights.
*/
class DWVertex: public XACCVertex<double> {
public:
DWVertex() :
XACCVertex() {
propertyNames[0] = "bias";
}
};
// Alias for Graphs of DWave Vertices
class DWGraph : public Graph<DWVertex> {
public:
DWGraph(const int nVertices) : Graph(nVertices) {
}
std::shared_ptr<AcceleratorGraph> getAcceleratorGraph() {
auto retGraph = std::make_shared<AcceleratorGraph>(order());
for (int i = 0; i < order(); i++) {
for (int j = 0; j < order(); j++) {
if (i < j) {
addEdge(i, j);
}
}
}
return retGraph;
}
virtual ~DWGraph() {}
};
class K44Bipartite : public DWGraph {
public:
K44Bipartite() :
DWGraph(8) {
addEdge(0, 4);
addEdge(0, 5);
addEdge(0, 6);
addEdge(0, 7);
addEdge(1, 4);
addEdge(1, 5);
addEdge(1, 6);
addEdge(1, 7);
addEdge(2, 4);
addEdge(2, 5);
addEdge(2, 6);
addEdge(2, 7);
addEdge(3, 4);
addEdge(3, 5);
addEdge(3, 6);
addEdge(3, 7);
}
virtual ~K44Bipartite() {}
};
class CompleteGraph : public DWGraph {
public:
CompleteGraph(int nVertices) :
DWGraph(nVertices) {
for (int i = 0; i < nVertices; i++) {
for (int j = 0; j < nVertices; j++) {
if (i < j) {
addEdge(i, j);
}
}
}
}
virtual ~CompleteGraph() {}
};
class Chimera : public DWGraph {
public:
Chimera(int gridSize) : DWGraph(8 * gridSize * gridSize) {
// We are going to assign a tuple of ints to each vertex
std::map<std::tuple<int, int, int, int>, int> vertexTuples;
int i, j, k, l, v = 0;
// Loop through the list of vertices and give
// them the correct 4-tuple: (i,j) is unit cell in
// the chimera grid, (k,l) is vertex in unit cell, k = 0-3,
// l = 0,1 (left, right of bipartite graph)
for (i = 0; i < gridSize; i++) {
for (j = 0; j < gridSize; j++) {
for (l = 0; l < 2; l++) {
for (k = 0; k < 4; k++) {
vertexTuples.insert(
std::make_pair(std::make_tuple(i, j, k, l), v));
v++;
}
}
}
}
// Setup edges within each cell
for (i = 0; i < gridSize; i++) {
for (j = 0; j < gridSize; j++) {
for (k = 0; k < 4; k++) {
for (int k2 = 0; k2 < 4; k2++) {
int v1 = vertexTuples[std::make_tuple(i, j, k, 0)];
int v2 = vertexTuples[std::make_tuple(i, j, k2, 1)];
addEdge(v1, v2);
}
}
}
}
// Setup edges between cells
for (i = 0; i < gridSize - 1; i++) {
for (j = 0; j < gridSize; j++) {
for (k = 0; k < 4; k++) {
int v1 = vertexTuples[std::make_tuple(i, j, k, 0)];
int v2 = vertexTuples[std::make_tuple(i + 1, j, k, 0)];
addEdge(v1, v2);
}
}
}
for (i = 0; i < gridSize; i++) {
for (j = 0; j < gridSize - 1; j++) {
for (k = 0; k < 4; k++) {
int v1 = vertexTuples[std::make_tuple(i, j, k, 1)];
int v2 = vertexTuples[std::make_tuple(i, j + 1, k, 1)];
addEdge(v1, v2);
}
}
}
}
virtual ~Chimera() {}
};
}
}
#endif
......@@ -2,23 +2,11 @@
#define QUANTUM_AQC_IR_DWIR_HPP_
#include "IR.hpp"
#include "Graph.hpp"
#include "DWGraph.hpp"
namespace xacc {
namespace quantum {
using DWVertex = XACCVertex<double>;
class DWGraph :public Graph<DWVertex> {
public:
virtual void read(std::istream& stream) {
}
virtual ~DWGraph() {}
};
class DWIR : public virtual IR {
public:
......@@ -52,19 +40,34 @@ public:
}
virtual void addKernel(std::shared_ptr<Function> kernel) {
kernels.push_back(kernel);
}
virtual bool kernelExists(const std::string& name) {
return false;
return std::any_of(kernels.cbegin(), kernels.cend(),
[=](std::shared_ptr<Function> i) {return i->getName() == name;});
}
virtual std::shared_ptr<Function> getKernel(const std::string& name) {
for (auto f : kernels) {
if (f->getName() == name) {
return f;
}
}
XACCError("Invalid kernel name.");
}
virtual std::vector<std::shared_ptr<Function>> getKernels() {
return kernels;
}
protected:
/**
* Reference to this QIR's list of quantum functions
*/
std::vector<std::shared_ptr<Function>> kernels;
};
}
......
......@@ -40,6 +40,8 @@ namespace xacc {
namespace quantum {
/**
* The DWKernel is an XACC Function that contains
* DWQMI Instructions.
*/
class DWKernel: public virtual Function {
......
......@@ -39,21 +39,69 @@ namespace xacc {
namespace quantum {
/**
* The DWQMI (D=Wave Quantum Machine Instruction) class is an XACC
* Instruction that models a logical problem or hardware bias or
* connection for an optimization problem to be solved on
* the D-Wave QPU. It keeps track of 2 bits indices, if both
* are the same index then this DWQMI Instruction models
* a bias value, and if they are different indices,
* then this Instruction models a logical coupling.
*
* Note that this class can model both problem bias/couplings and
* hardware bias/couplings. The hardware bias/couplings result from
* a minor graph embedding computation.
*
*/
class DWQMI : public Instruction {
protected:
/**
* The qubits involved in this Instruction
*/
std::vector<int> qubits;