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

Updating with improved Info reporting through Utils.hpp and spdlog. fixed bug...

Updating with improved Info reporting through Utils.hpp and spdlog. fixed bug in ScaffoldASTConsumer
parent 5fa65496
......@@ -35,7 +35,6 @@
#include <complex>
#include "Tensor.hpp"
#include <bitset>
#include "spdlog/spdlog.h"
namespace xacc {
......@@ -169,20 +168,22 @@ public:
}
virtual void print() {
auto console = spdlog::get("console");
if (size() < TotalNumberOfQubits) {
for (int i = 0; i < bufferState.dimension(0); i++) {
console->info(std::bitset<TotalNumberOfQubits>(i).to_string().substr(
TotalNumberOfQubits - size(), TotalNumberOfQubits) + " -> "
for (int i = 0; i < bufferState.dimension(0); i++) {
XACCInfo(
std::bitset<TotalNumberOfQubits>(i).to_string().substr(
TotalNumberOfQubits - size(),
TotalNumberOfQubits) + " -> "
+ std::to_string(std::real(bufferState(i))));
}
} else {
for (int i = 0; i < bufferState.dimension(0); i++) {
XACCInfo(
std::bitset<TotalNumberOfQubits>(i).to_string() + " -> "
+ std::to_string(std::real(bufferState(i))));
}
} else {
for (int i = 0; i < bufferState.dimension(0); i++) {
console->info(std::bitset<TotalNumberOfQubits>(i).to_string()
+ " -> " + std::to_string(std::real(bufferState(i))));
}
}
}
}
}
virtual ~SimulatedQubits() {}
......
......@@ -55,39 +55,22 @@ bool FireTensorAccelerator::isValidBufferSize(const int NBits) {
return NBits <= 10;
}
/**
* The constructor, create tensor gates
*/
FireTensorAccelerator::FireTensorAccelerator() {
fire::Tensor<2, fire::EigenProvider, std::complex<double>> h(2, 2), cnot(4,
4), I(2, 2), x(2, 2), p0(2, 2), p1(2, 2), z(2, 2);
h.setValues(
{ { 1.0 / sqrt2, 1.0 / sqrt2 }, { 1.0 / sqrt2, -1.0 / sqrt2 } });
cnot.setValues( { { 1, 0, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 }, { 0, 0, 1,
0 } });
x.setValues( { { 0, 1 }, { 1, 0 } });
I.setValues( { { 1, 0 }, { 0, 1 } });
p0.setValues( { { 1, 0 }, { 0, 0 } });
p1.setValues( { { 0, 0 }, { 0, 1 } });
z.setValues( { { 1, 0 }, { 0, -1 } });
gates.insert(std::make_pair("h", h));
gates.insert(std::make_pair("cnot", cnot));
gates.insert(std::make_pair("x", x));
gates.insert(std::make_pair("I", I));
gates.insert(std::make_pair("p0", p0));
gates.insert(std::make_pair("p1", p1));
gates.insert(std::make_pair("z", z));
}
void FireTensorAccelerator::execute(std::shared_ptr<AcceleratorBuffer> buffer,
const std::shared_ptr<xacc::Function> kernel) {
using ProductList = std::vector<fire::Tensor<2, fire::EigenProvider, std::complex<double>>>;
using ComplexTensor = fire::Tensor<2, fire::EigenProvider, std::complex<double>>;
// Cast to what we know should be SimulatedQubits
auto qubits = std::static_pointer_cast<SimulatedQubits<10>>(buffer);
if (!qubits) {
XACCError("Invalid derived AcceleratorBuffer passed to "
"FireTensorAccelerator. Must be of type SimulatedQubits<10>.");
}
// Create a lambda for each type of gate we may encounter,
// each lambda must perform a gate-specific unitary
// on the qubits accelerator buffer.
auto hadamard =
[&] (Hadamard& hGate) mutable {
[&] (Hadamard& hGate) {
ComplexTensor h(2,2), I(2,2);
I.setValues( { {1, 0}, {0, 1}});
h.setValues(
......@@ -136,7 +119,7 @@ void FireTensorAccelerator::execute(std::shared_ptr<AcceleratorBuffer> buffer,
qubits->applyUnitary(localU);
};
auto x = [&] (X& xGate) mutable {
auto x = [&] (X& xGate) {
ComplexTensor x(2,2), I(2,2);
I.setValues( { { 1, 0 }, { 0, 1 } });
x.setValues( { { 0, 1 }, { 1, 0 } });
......@@ -156,7 +139,7 @@ void FireTensorAccelerator::execute(std::shared_ptr<AcceleratorBuffer> buffer,
qubits->applyUnitary(localU);
};
auto z = [&] (Z& zGate) mutable {
auto z = [&] (Z& zGate) {
ComplexTensor z(2,2), I(2,2);
I.setValues( { { 1, 0 }, { 0, 1 } });
z.setValues( { { 1, 0 }, { 0, -1 } });
......@@ -178,8 +161,10 @@ void FireTensorAccelerator::execute(std::shared_ptr<AcceleratorBuffer> buffer,
};
auto measure = [&](Measure& mGate) {
ComplexTensor I(2,2);
ComplexTensor I(2,2), p0(2,2), p1(2,2);
I.setValues( { {1, 0}, {0, 1}});
p0.setValues( { { 1, 0 }, { 0, 0 } });
p1.setValues( { { 0, 0 }, { 0, 1 } });
auto actingQubits = mGate.bits();
ProductList productList;
for (int j = 0; j < qubits->size(); j++) {
......@@ -188,7 +173,7 @@ void FireTensorAccelerator::execute(std::shared_ptr<AcceleratorBuffer> buffer,
auto rho = qubits->getState() * qubits->getState();
productList.at(actingQubits[0]) = gates.at("p0");
productList.at(actingQubits[0]) = p0;
auto Pi0 = productList.at(0);
for (int i = 1; i < productList.size(); i++) {
Pi0 = Pi0.kronProd(productList.at(i));
......@@ -212,7 +197,7 @@ void FireTensorAccelerator::execute(std::shared_ptr<AcceleratorBuffer> buffer,
qubits->normalize();
} else {
result = 1;
productList.at(actingQubits[0]) = gates.at("p1");
productList.at(actingQubits[0]) = p1;
auto Pi1 = productList.at(0);
for (int i = 1; i < productList.size(); i++) {
Pi1 = Pi1.kronProd(productList.at(i));
......@@ -221,222 +206,39 @@ void FireTensorAccelerator::execute(std::shared_ptr<AcceleratorBuffer> buffer,
qubits->normalize();
}
std::cout << "Qubit " << actingQubits[0] << " measured to be a " << result << "\n";
qubits->updateBit(actingQubits[0], result);
};
auto cond = [&](ConditionalFunction& c) {
auto qubit = c.getConditionalQubit();
auto bufResult = qubits->getAcceleratorBitState(qubit);
c.evaluate(bufResult == AcceleratorBitState::ONE ? 1 : 0);
std::cout << "Measurement on " << qubit << " was a " <<
(bufResult == AcceleratorBitState::ONE ? 1 : 0) << "\n";
if (bufResult == AcceleratorBitState::UNKNOWN) {
XACCError("Conditional Node is conditional on unmeasured qubit.");
}
auto bufResultAsInt = bufResult == AcceleratorBitState::ONE ? 1 : 0;
c.evaluate(bufResultAsInt);
XACCInfo("Measurement on " + std::to_string(qubit) + " was a " +
std::to_string(bufResultAsInt));
};
// Create a Visitor that will execute our lambdas when
// we encounter one
auto visitor = std::make_shared<GateInstructionVisitor>(hadamard, cnot, x,
measure, z, cond);
// Our QIR is really a tree structure
// so create a pre-order tree traversal
// InstructionIterator to walk it
InstructionIterator it(kernel);
while (it.hasNext()) {
// Get the next node in the tree
auto nextInst = it.next();
// If enabled, invoke the accept
// method which kicks off the visitor
// to execute the appropriate lambda.
if (nextInst->isEnabled()) {
std::cout << "NEXTINST: " << nextInst->getName() << "\n";
nextInst->accept(visitor);
qubits->print(std::cout);
}
}
// Appy the unitary and update th state
std::cout << "\n\n";
qubits->print(std::cout);
}
/**
* Execute the simulation. Requires both a valid SimulatedQubits buffer and
* Graph IR instance modeling the quantum circuit.
*
* @param ir
*/
void FireTensorAccelerator::execute(std::shared_ptr<AcceleratorBuffer> buffer,
const std::shared_ptr<xacc::IR> ir) {
auto qubits = std::static_pointer_cast<SimulatedQubits<10>>(buffer);
// Set the size
int nQubits = qubits->size();
// Cast to a GraphIR, if we can...
auto graphir = std::dynamic_pointer_cast<QuantumGraphIR>(ir);
if (!graphir) {
XACCError(
"Invalid IR - this Accelerator only accepts GraphIR<Graph<CircuitNode>>.");
}
// Get the Graph and related info
std::vector<CircuitNode> gateOperations;
std::map<int, int> qubitIdToMeasuredResult;
auto graph = graphir->getGraph();
int nNodes = graph.order(), layer = 1;
int finalLayer = graph.getVertexProperty<1>(nNodes - 1);
// Get a vector of all gates
for (int i = 0; i < nNodes; i++) {
CircuitNode n;
n.properties = graph.getVertexProperties(i);
gateOperations.emplace_back(n);
}
// Loop over all gates in the circuit
for (auto gate : gateOperations) {
// Skip disabled gates...
if (!std::get<4>(gate.properties)) {
continue;
}
// Create a list of nQubits Identity gates
std::vector<fire::Tensor<2, fire::EigenProvider, std::complex<double>>>productList;
for (int i = 0; i < nQubits; i++) {
productList.push_back(gates.at("I"));
}
// Create a local U gate, initialized to identity
fire::Tensor<2, fire::EigenProvider, std::complex<double>> localU =
gates.at("I");
// Get the current gate anme
auto gateName = std::get<0>(gate.properties);
// Get the qubits we're acting on...
auto actingQubits = std::get<3>(gate.properties);
if (gateName == "conditional") {
// If we hit a measured result then we
// need to figure out the measured qubit it
// depends on, then if its a 1, enable the disabled
// gates from this node to the next FinalState node
auto qubitWeNeed = std::get<3>(gate.properties)[0];
auto qubitFound = qubitIdToMeasuredResult.find(qubitWeNeed);
if (qubitFound == qubitIdToMeasuredResult.end()) {
XACCError(
"Invalid conditional node - this qubit has not been measured.");
}
auto result = qubitIdToMeasuredResult[qubitWeNeed];
auto currentNodeId = std::get<2>(gate.properties);
if (result == 1) {
// Walk over the next nodes until we hit a FinalState node
// set their enabled state to true
for (int i = currentNodeId + 1; i < nNodes; i++) {
// Once we hit the next final state node, then break out
if (std::get<0>(gateOperations[i].properties)
== "FinalState") {
break;
}
std::get<4>(gateOperations[i].properties) = true;
}
}
} else if (gateName == "measure") {
// get rho - outer product of state with itself
auto rho = qubits->getState() * qubits->getState();
// Create a total unitary for this layer of the circuit
productList.at(actingQubits[0]) = gates.at("p0");
auto Pi0 = productList.at(0);
for (int i = 1; i < productList.size(); i++) {
Pi0 = Pi0.kronProd(productList.at(i));
}
// Get probability qubit is a 0
double probZero = 0.0;
std::array<IndexPair, 1> contractionIndices;
contractionIndices[0] = std::make_pair(1, 0);
auto Prob0 = Pi0.contract(rho, contractionIndices);
for (int i = 0; i < Prob0.dimension(0); i++)
probZero += std::real(Prob0(i, i));
// Make the measurement random...
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_real_distribution<double> dist(0, 1.0);
int result;
auto val = dist(mt);
if (val < std::real(probZero)) {
result = 0;
qubits->applyUnitary(Pi0);
qubits->normalize();
} else {
result = 1;
productList.at(actingQubits[0]) = gates.at("p1");
// Create a total unitary for this layer of the circuit
auto Pi1 = productList.at(0);
for (int i = 1; i < productList.size(); i++) {
Pi1 = Pi1.kronProd(productList.at(i));
}
qubits->applyUnitary(Pi1);
qubits->normalize();
}
// Record the measurement result
qubitIdToMeasuredResult.insert(
std::make_pair(actingQubits[0], result));
} else {
if (gateName != "FinalState" && gateName != "InitialState") {
// Regular Gate operations...
if (actingQubits.size() == 1) {
// If this is a one qubit gate, just replace
// the currect I in the list with the gate
productList.at(actingQubits[0]) = gates.at(gateName);
// Create a total unitary for this layer of the circuit
localU = productList.at(0);
for (int i = 1; i < productList.size(); i++) {
localU = localU.kronProd(productList.at(i));
}
} else if (actingQubits.size() == 2) {
// If this is a 2 qubit gate, then we need t
// to construct Kron(P0, I, ..., I) + Kron(P1, I, ..., Gate, ..., I)
productList.at(actingQubits[0]) = gates.at("p0");
localU = productList.at(0);
for (int i = 1; i < productList.size(); i++) {
localU = localU.kronProd(productList.at(i));
}
// Now create the second term in the sum
productList.at(actingQubits[0]) = gates.at("p1");
productList.at(actingQubits[1]) = gates.at(
gateName == "cnot" ? "x" : "I");
auto temp = productList.at(0);
for (int i = 1; i < productList.size(); i++) {
temp = temp.kronProd(productList.at(i));
}
// Sum them up
localU = localU + temp;
} else {
XACCError("Can only simulate one and two qubit gates.");
}
// Make sure that localU is the correct size
assert(
localU.dimension(0) == std::pow(2, nQubits)
&& localU.dimension(1) == std::pow(2, nQubits));
// Appy the unitary and update th state
qubits->applyUnitary(localU);
}
}
}
}
......
......@@ -43,8 +43,9 @@ namespace xacc {
namespace quantum {
double sqrt2 = std::sqrt(2.0);
using QuantumGraphIR = xacc::GraphIR<QuantumCircuit>;
using TensorMap = std::map<std::string, fire::Tensor<2, fire::EigenProvider, std::complex<double>>>;
using ProductList = std::vector<fire::Tensor<2, fire::EigenProvider, std::complex<double>>>;
using ComplexTensor = fire::Tensor<2, fire::EigenProvider, std::complex<double>>;
/**
* The FireTensorAccelerator is an XACC Accelerator that simulates
* gate based quantum computing circuits. It models the QPUGate Accelerator
......@@ -55,17 +56,36 @@ using TensorMap = std::map<std::string, fire::Tensor<2, fire::EigenProvider, st
class FireTensorAccelerator : virtual public QPUGate {
public:
/**
* Create, store, and return an AcceleratorBuffer with the given
* variable id string. This string serves as a unique identifier
* for future lookups and reuse of the AcceleratorBuffer.
*
* @param varId
* @return
*/
std::shared_ptr<AcceleratorBuffer> createBuffer(const std::string& varId);
/**
* 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);
virtual bool isValidBufferSize(const int NBits);
/**
* The constructor, create tensor gates
* Return true if this Accelerator can allocated
* NBits number of bits.
* @param NBits
* @return
*/
FireTensorAccelerator();
virtual bool isValidBufferSize(const int NBits);
/**
* Execute the simulation. Requires both a valid SimulatedQubits buffer and
......@@ -74,19 +94,12 @@ public:
* @param ir
*/
virtual void execute(std::shared_ptr<AcceleratorBuffer> buffer, const std::shared_ptr<xacc::Function> kernel);
virtual void execute(std::shared_ptr<AcceleratorBuffer> buffer, const std::shared_ptr<xacc::IR> kernel);
/**
* The destructor
*/
virtual ~FireTensorAccelerator() {}
protected:
/**
* Mapping of gate names to actual gate matrices.
*/
TensorMap gates;
};
......
......@@ -34,7 +34,6 @@
#include <memory>
#include <boost/test/included/unit_test.hpp>
#include "FireTensorAccelerator.hpp"
#include "GraphIR.hpp"
using namespace xacc::quantum;
......
......@@ -32,7 +32,7 @@
#define QUANTUM_IMPROVEDSCAFFOLDCOMPILER_HPP_
#include "Compiler.hpp"
#include "XACCError.hpp"
#include "Utils.hpp"
#include <boost/algorithm/string.hpp>
#include "../scaffold/ScaffoldASTConsumer.hpp"
......
......@@ -3,6 +3,9 @@
#include <iostream>
#include <boost/algorithm/string.hpp>
#include "ParameterizedGateInstruction.hpp"
#include "Utils.hpp"
using namespace xacc;
namespace scaffold {
......@@ -20,10 +23,10 @@ bool ScaffoldASTConsumer::VisitDecl(Decl *d) {
auto varType = varDecl->getType().getAsString();
if (boost::contains(varType, "cbit")) {
cbitVarName = varDecl->getDeclName().getAsString();
std::cout << "Found " << cbitVarName << "\n";
// std::cout << "Found " << cbitVarName << "\n";
} else if (boost::contains(varType, "qbit")) {
qbitVarName = varDecl->getDeclName().getAsString();
std::cout << "Found " << qbitVarName << "\n";
// std::cout << "Found " << qbitVarName << "\n";
}
} else if (isa<FunctionDecl>(d)) {
auto c = cast<FunctionDecl>(d);
......@@ -42,7 +45,7 @@ bool ScaffoldASTConsumer::VisitStmt(Stmt *s) {
std::string ifStr;
llvm::raw_string_ostream ifS(ifStr);
ifStmt->printPretty(ifS, nullptr, policy);
std::cout << "HELLO IF:\n" << ifS.str() << "\n";
// std::cout << "HELLO IF:\n" << ifS.str() << "\n";
if (const auto binOp = llvm::dyn_cast<BinaryOperator>(
ifStmt->getCond())) {
......@@ -52,21 +55,21 @@ bool ScaffoldASTConsumer::VisitStmt(Stmt *s) {
std::string str;
llvm::raw_string_ostream s(str);
LHS->printPretty(s, nullptr, policy);
std::cout << "LHS IF: " << s.str() << "\n";
// std::cout << "LHS IF: " << s.str() << "\n";
if (boost::contains(s.str(), cbitVarName)) {
auto RHS = binOp->getRHS();
std::string rhsstr;
llvm::raw_string_ostream rhss(rhsstr);
RHS->printPretty(rhss, nullptr, policy);
std::cout << "RHS IF: " << rhss.str() << "\n";
// std::cout << "RHS IF: " << rhss.str() << "\n";
auto thenCode = ifStmt->getThen();
std::string thenStr;
llvm::raw_string_ostream thenS(thenStr);
thenCode->printPretty(thenS, nullptr, policy);
auto then = thenS.str();
std::cout << "ThenStmt:\n" << then << "\n";
// std::cout << "ThenStmt:\n" << then << "\n";
then.erase(std::remove(then.begin(), then.end(), '\t'),
then.end());
boost::replace_all(then, "{\n", "");
......@@ -75,12 +78,14 @@ bool ScaffoldASTConsumer::VisitStmt(Stmt *s) {
boost::trim(then);
int conditionalQubit = cbitRegToMeasuredQubit[s.str()];
currentConditional = std::make_shared<xacc::quantum::ConditionalFunction>(conditionalQubit);
currentConditional = std::make_shared<
xacc::quantum::ConditionalFunction>(
conditionalQubit);
std::vector<std::string> vec;
boost::split(vec, then, boost::is_any_of("\n"));
nCallExprToSkip = vec.size();
std::cout << "NCALLEXPRTOSKIP = " << nCallExprToSkip << "\n";
// std::cout << "NCALLEXPRTOSKIP = " << nCallExprToSkip << "\n";
}
}
}
......@@ -97,7 +102,6 @@ bool ScaffoldASTConsumer::VisitCallExpr(CallExpr* c) {
if (t != NULL) {
bool isParameterizedInst = false;
auto fd = c->getDirectCallee();
std::cout << "HOWDY: " << fd->getNameInfo().getAsString() << "\n";
auto gateName = fd->getNameInfo().getAsString();
std::vector<int> qubits;
std::vector<double> params;
......@@ -106,7 +110,7 @@ bool ScaffoldASTConsumer::VisitCallExpr(CallExpr* c) {
llvm::raw_string_ostream argstream(arg);
i->printPretty(argstream, nullptr, policy);
auto argStr = argstream.str();
std::cout << "Arg: " << argstream.str() << "\n";
// std::cout << "Arg: " << argstream.str() << "\n";
if (boost::contains(argStr, qbitVarName)) {
boost::replace_all(argStr, qbitVarName, "");
......@@ -134,13 +138,12 @@ bool ScaffoldASTConsumer::VisitCallExpr(CallExpr* c) {
XACCError(
"Can only handle 1 and 2 parameter gates... and only doubles... for now.");
}
std::cout << "CREATED A " << gateName << " parameterized gate\n";
// std::cout << "CREATED A " << gateName << " parameterized gate\n";
} else if (gateName != "MeasZ") {
inst = xacc::quantum::GateInstructionRegistry::instance()->create(
gateName, qubits);
std::cout << "CREATED A " << gateName << " gate\n";
}
if (gateName != "MeasZ") {
......@@ -148,7 +151,7 @@ bool ScaffoldASTConsumer::VisitCallExpr(CallExpr* c) {
if (nCallExprToSkip == 0) {
function->addInstruction(inst);