Unverified Commit 52934dbd authored by Mccaskey, Alex's avatar Mccaskey, Alex Committed by GitHub

Merge pull request #273 from tnguyen-ornl/tnguyen/fix-if

Support If statement in QObj generation
parents 87016df3 ccb676fa
Pipeline #118346 passed with stage
in 80 minutes and 20 seconds
import xacc
qpu = xacc.getAccelerator('qpp', {'shots':100})
qpu = xacc.getAccelerator('aer', {'shots':1024})
xacc.qasm('''
.compiler xasm
......
......@@ -17,5 +17,21 @@ bool IfStmt::expand(const HeterogeneousMap &runtimeOptions) {
}
return true;
}
const std::string IfStmt::toString() {
std::stringstream retStr;
retStr << "if (" << bufferName << "[" << bitIdx << "]) {\n";
for (auto i : instructions) {
if (i->isComposite() &&
!std::dynamic_pointer_cast<CompositeInstruction>(i)->hasChildren()) {
retStr << " " << i->name() << "()\n";
} else {
retStr << " " << i->toString() << "\n";
}
}
retStr << "}\n";
return retStr.str();
}
} // namespace quantum
} // namespace xacc
\ No newline at end of file
......@@ -93,6 +93,7 @@ public:
}
}
const std::string toString() override;
DEFINE_CLONE(IfStmt)
DEFINE_VISITABLE()
};
......
......@@ -31,6 +31,7 @@ protected:
std::string experimentName;
int nTotalQubits = 0;
std::vector<int> usedMemorySlots;
std::optional<int64_t> conditionalRegId;
public:
int maxMemorySlots = 0;
......@@ -49,6 +50,7 @@ public:
qubit2MemorySlot.insert({b, counter});
counter++;
}
xacc::ibm::RegisterAllocator::getInstance()->reset();
}
const std::string toString() override {
......@@ -63,7 +65,7 @@ public:
// std::to_string(experiment.get_instructions().size()));
if (experiment.get_instructions().empty()) {
// maxMemorySlots++;
// maxMemorySlots++;
// construct config and header, add
ExperimentHeader header;
......@@ -101,12 +103,18 @@ public:
return experiment;
}
void setConditional(xacc::ibm::Instruction &io_inst) {
if (conditionalRegId.has_value()) {
io_inst.set_condition_reg_id(conditionalRegId.value());
}
}
void visit(Hadamard &h) override {
xacc::ibm::Instruction inst;
inst.get_mutable_qubits().push_back(h.bits()[0]);
inst.get_mutable_name() = "u2";
inst.set_params({0.0, pi});
setConditional(inst);
instructions.push_back(inst);
}
......@@ -179,7 +187,7 @@ public:
inst.get_mutable_qubits().push_back(cn.bits()[0]);
inst.get_mutable_qubits().push_back(cn.bits()[1]);
inst.get_mutable_name() = "cx";
setConditional(inst);
instructions.push_back(inst);
}
......@@ -192,6 +200,7 @@ public:
inst.get_mutable_name() = "u3";
inst.set_params({pi, 0.0, pi});
setConditional(inst);
instructions.push_back(inst);
}
......@@ -202,6 +211,7 @@ public:
inst.get_mutable_name() = "u3";
inst.set_params({pi, pi / 2.0, pi});
setConditional(inst);
instructions.push_back(inst);
}
......@@ -212,6 +222,7 @@ public:
inst.get_mutable_name() = "u1";
inst.set_params({pi});
setConditional(inst);
instructions.push_back(inst);
}
......@@ -223,7 +234,7 @@ public:
inst.set_params({u.getParameter(0).as<double>(),
u.getParameter(1).as<double>(),
u.getParameter(2).as<double>()});
setConditional(inst);
instructions.push_back(inst);
}
......@@ -239,22 +250,20 @@ public:
inst.get_mutable_qubits().push_back(m.bits()[0]);
inst.get_mutable_name() = "measure";
inst.set_memory({maxMemorySlots});
instructions.push_back(inst);
// if (classicalBit > maxMemorySlots) {
maxMemorySlots++;// = qubit2MemorySlot[m.bits()[0]];
maxMemorySlots++; // = qubit2MemorySlot[m.bits()[0]];
// }
}
void visit(Rx &rx) override {
xacc::ibm::Instruction inst;
inst.get_mutable_qubits().push_back(rx.bits()[0]);
inst.get_mutable_name() = "u3";
inst.set_params(
{rx.getParameter(0).as<double>(), -1.0 * pi / 2.0, pi / 2.0});
setConditional(inst);
instructions.push_back(inst);
}
......@@ -263,7 +272,7 @@ public:
inst.get_mutable_qubits().push_back(ry.bits()[0]);
inst.get_mutable_name() = "u3";
inst.set_params({ry.getParameter(0).as<double>(), 0.0, 0.0});
setConditional(inst);
instructions.push_back(inst);
}
......@@ -272,7 +281,7 @@ public:
inst.get_mutable_qubits().push_back(rz.bits()[0]);
inst.get_mutable_name() = "u1";
inst.set_params({rz.getParameter(0).as<double>()});
setConditional(inst);
instructions.push_back(inst);
}
......@@ -291,6 +300,20 @@ public:
visit(u1_3);
}
void visit(IfStmt &ifStmt) override {
const auto cregId = ifStmt.bits()[0];
auto bfuncInst = xacc::ibm::Instruction::createConditionalInst(cregId);
const auto regId = bfuncInst.get_bFunc()->registerId;
instructions.push_back(bfuncInst);
// All the instructions that are scoped inside this block must have a
// `condition` field points to this register Id.
conditionalRegId = regId;
for (auto &i : ifStmt.getInstructions()) {
i->accept(this);
}
conditionalRegId.reset();
}
/**
* The destructor
*/
......
......@@ -289,16 +289,88 @@ public:
}
};
struct Bfunc {
Bfunc(int64_t in_regId, const std::string &in_hexMask,
const std::string &in_relation = "==",
const std::string &in_val = "")
: registerId(in_regId), hex_mask(in_hexMask), relation(in_relation),
hex_val(in_val.empty() ? in_hexMask : in_val) {}
int64_t registerId;
std::string hex_mask;
std::string relation;
std::string hex_val;
};
class RegisterAllocator {
static inline RegisterAllocator *instance = nullptr;
std::unordered_map<int64_t, int64_t> memoryToRegister;
int64_t registerId;
RegisterAllocator() { registerId = 0; }
public:
static RegisterAllocator *getInstance() {
if (!instance) {
instance = new RegisterAllocator;
}
return instance;
}
std::optional<int64_t> getRegisterId(int64_t in_memoryId) {
if (memoryToRegister.find(in_memoryId) != memoryToRegister.end()) {
return memoryToRegister[in_memoryId];
}
return std::nullopt;
}
int64_t mapMemory(int64_t in_memoryId) {
memoryToRegister[in_memoryId] = registerId;
registerId++;
return memoryToRegister[in_memoryId];
}
int64_t getNextRegister() {
const auto result = registerId;
++registerId;
return result;
}
void reset() {
memoryToRegister.clear();
registerId = 0;
}
};
class Instruction {
public:
Instruction() = default;
virtual ~Instruction() = default;
// Construct a binary function instruction
// Returns the Bfunc instruction and the register Id to condition the
// sub-circuit.
static Instruction createConditionalInst(int64_t memoryId) {
// Map the memory Id to a register:
const auto measRegisterId =
RegisterAllocator::getInstance()->mapMemory(memoryId);
const int64_t maskVal = 1ULL << measRegisterId;
const std::string hexMaskStr = "0x" + std::to_string(maskVal);
const int64_t resultRegisterId =
RegisterAllocator::getInstance()->getNextRegister();
Bfunc bFuncObj(resultRegisterId, hexMaskStr);
Instruction newInst;
newInst.bfunc = bFuncObj;
return newInst;
}
private:
std::vector<int64_t> qubits;
std::string name;
std::vector<double> params;
std::vector<int64_t> memory;
// Conditional on a register value
std::optional<int64_t> conditional;
// If this is a Bfunc to compute a register value
std::optional<Bfunc> bfunc;
public:
const std::vector<int64_t> &get_qubits() const { return qubits; }
......@@ -314,6 +386,13 @@ public:
std::vector<int64_t> get_memory() const { return memory; }
void set_memory(std::vector<int64_t> value) { this->memory = value; }
std::optional<Bfunc> get_bFunc() const { return bfunc; }
void set_bFunc(Bfunc value) { this->bfunc = value; }
bool isBfuc() const { return get_bFunc().has_value(); }
std::optional<int64_t> get_condition_reg_id() const { return conditional; }
void set_condition_reg_id(int64_t value) { this->conditional = value; }
};
class Experiment {
......@@ -698,6 +777,15 @@ inline void from_json(const json &j, xacc::ibm::Instruction &x) {
inline void to_json(json &j, const xacc::ibm::Instruction &x) {
j = json::object();
if (x.isBfuc()) {
j["name"] = "bfunc";
j["register"] = x.get_bFunc()->registerId;
j["mask"] = x.get_bFunc()->hex_mask;
j["relation"] = x.get_bFunc()->relation;
j["val"] = x.get_bFunc()->hex_val;
return;
}
j["qubits"] = x.get_qubits();
j["name"] = x.get_name();
if (!x.get_params().empty()) {
......@@ -705,6 +793,20 @@ inline void to_json(json &j, const xacc::ibm::Instruction &x) {
}
if (!x.get_memory().empty()) {
j["memory"] = x.get_memory();
// Technically, we only use one memory slot in each Measure Op:
if (x.get_memory().size() == 1) {
const auto memoryId = x.get_memory()[0];
const auto measRegisterId =
xacc::ibm::RegisterAllocator::getInstance()->getRegisterId(memoryId);
if (measRegisterId.has_value()) {
std::vector<int64_t> registerIds;
registerIds.emplace_back(measRegisterId.value());
j["register"] = registerIds;
}
}
}
if (x.get_condition_reg_id().has_value()) {
j["conditional"] = x.get_condition_reg_id().value();
}
}
......
......@@ -147,7 +147,7 @@ void AerAccelerator::execute(
nlohmann::json j = nlohmann::json::parse(qobj_str)["qObject"];
j["config"]["shots"] = m_shots;
j["config"]["noise_model"] = noise_model;
xacc::info("Qobj:\n" + j.dump(2));
auto results_json = nlohmann::json::parse(
AER::controller_execute_json<AER::Simulator::QasmController>(j.dump()));
......
......@@ -298,6 +298,164 @@ TEST(AerAcceleratorTester, checkApply) {
EXPECT_EQ(resultQ0, resultQ1);
}
TEST(AerAcceleratorTester, checkConditional) {
auto accelerator = xacc::getAccelerator("aer");
xacc::set_verbose(true);
auto xasmCompiler = xacc::getCompiler("xasm");
{
auto ir = xasmCompiler->compile(R"(__qpu__ void conditionalCirc(qbit q) {
X(q[0]);
Measure(q[0]);
Measure(q[1]);
// Should apply
if (q[0]) {
X(q[2]);
}
// Not apply
if (q[1]) {
X(q[3]);
}
// Apply
X(q[4]);
Measure(q[2]);
Measure(q[3]);
Measure(q[4]);
})",
accelerator);
auto program = ir->getComposite("conditionalCirc");
auto buffer = xacc::qalloc(5);
accelerator->execute(buffer, program);
buffer->print();
// Expected: q0 = 1, q1 = 0, q2 = 1, q3 = 0, q4 = 1
EXPECT_EQ(buffer->computeMeasurementProbability("10101"), 1.0);
}
{
// Check Teleport
auto ir = xasmCompiler->compile(R"(__qpu__ void teleportOneState(qbit q) {
// State to be transported
X(q[0]);
// Bell channel setup
H(q[1]);
CX(q[1], q[2]);
// Alice Bell measurement
CX(q[0], q[1]);
H(q[0]);
Measure(q[0]);
Measure(q[1]);
// Correction
if (q[0])
{
Z(q[2]);
}
if (q[1])
{
X(q[2]);
}
// Measure teleported qubit
Measure(q[2]);
})",
accelerator);
auto program = ir->getComposite("teleportOneState");
auto buffer = xacc::qalloc(3);
accelerator->execute(buffer, program);
buffer->print();
for (const auto &bitStr : buffer->getMeasurements()) {
EXPECT_EQ(bitStr.length(), 3);
// q[2] (MSB) must be '1' (teleported)
EXPECT_EQ(bitStr[0], '1');
}
}
{
// Check Teleport
auto ir = xasmCompiler->compile(R"(__qpu__ void teleportZeroState(qbit q) {
// Bell channel setup
H(q[1]);
CX(q[1], q[2]);
// Alice Bell measurement
CX(q[0], q[1]);
H(q[0]);
Measure(q[0]);
Measure(q[1]);
// Correction
if (q[0])
{
Z(q[2]);
}
if (q[1])
{
X(q[2]);
}
// Measure teleported qubit
Measure(q[2]);
})",
accelerator);
auto program = ir->getComposite("teleportZeroState");
auto buffer = xacc::qalloc(3);
accelerator->execute(buffer, program);
buffer->print();
for (const auto &bitStr : buffer->getMeasurements()) {
EXPECT_EQ(bitStr.length(), 3);
// q[2] (MSB) must be '0' (teleported)
EXPECT_EQ(bitStr[0], '0');
}
}
{
// Check Teleport (50-50)
auto ir = xasmCompiler->compile(
R"(__qpu__ void teleportSuperpositionState(qbit q) {
// State to be transported
// superposition of 0 and 1
H(q[0]);
// Bell channel setup
H(q[1]);
CX(q[1], q[2]);
// Alice Bell measurement
CX(q[0], q[1]);
H(q[0]);
Measure(q[0]);
Measure(q[1]);
// Correction
if (q[0])
{
Z(q[2]);
}
if (q[1])
{
X(q[2]);
}
// Measure teleported qubit
Measure(q[2]);
})",
accelerator);
auto program = ir->getComposite("teleportSuperpositionState");
auto buffer = xacc::qalloc(3);
accelerator->execute(buffer, program);
buffer->print();
double zeroProb = 0.0;
double oneProb = 0.0;
for (const auto &bitStr : buffer->getMeasurements()) {
EXPECT_EQ(bitStr.length(), 3);
// q[2] (MSB) is the teleported qubit
if (bitStr[0] == '0') {
zeroProb += buffer->computeMeasurementProbability(bitStr);
} else {
oneProb += buffer->computeMeasurementProbability(bitStr);
}
}
EXPECT_NEAR(zeroProb, 0.5, 0.1);
EXPECT_NEAR(oneProb, 0.5, 0.1);
}
xacc::set_verbose(false);
}
int main(int argc, char **argv) {
xacc::Initialize();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment