Skip to content

Commit

Permalink
Merge pull request #481 from tnguyen-ornl/tnguyen/ifstmt-to-extended-…
Browse files Browse the repository at this point in the history
…qasm2

Support IfStmt to OpenQASM2 generation
  • Loading branch information
Alex McCaskey authored Aug 20, 2021
2 parents b1cf4a2 + 044bff7 commit 50bee5d
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 13 deletions.
3 changes: 3 additions & 0 deletions quantum/gate/ir/CommonGates.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ class IfStmt : public Circuit {
std::vector<InstructionParameter> getParameters() override {
return {bufferName};
}

std::vector<std::string> getBufferNames() override { return {bufferName}; }

const int nParameters() override { return 1; }

const int nRequiredBits() const override { return 1; }
Expand Down
8 changes: 8 additions & 0 deletions quantum/plugins/ibm/aer/accelerator/aer_accelerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,14 @@ IbmqNoiseModel::averageTwoQubitGateFidelity() const {

return result;
}

std::string
AerAccelerator::getNativeCode(std::shared_ptr<CompositeInstruction> program,
const HeterogeneousMap &config) {
auto qobj_str = xacc_to_qobj->translate(program);
nlohmann::json j = nlohmann::json::parse(qobj_str)["qObject"];
return j.dump(2);
}
} // namespace quantum
} // namespace xacc

Expand Down
3 changes: 2 additions & 1 deletion quantum/plugins/ibm/aer/accelerator/aer_accelerator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ class AerAccelerator : public Accelerator {
void apply(std::shared_ptr<AcceleratorBuffer> buffer,
std::shared_ptr<Instruction> inst) override;
bool isInitialized() const { return initialized; }

std::string getNativeCode(std::shared_ptr<CompositeInstruction> program,
const HeterogeneousMap &config = {}) override;
private:
static double calcExpectationValueZ(
const std::vector<std::pair<double, double>> &in_stateVec,
Expand Down
15 changes: 15 additions & 0 deletions quantum/plugins/optimizers/simple/CircuitOptimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,19 @@ bool CircuitOptimizer::tryRotationMergingUsingPhasePolynomials(std::shared_ptr<C
}
}
else if (instruction->bits().size() == 2) {
// If this gate not involved the qubits that we are checking
if (!container::contains(qubits, instruction->bits()[0]) &&
!container::contains(qubits, instruction->bits()[1])) {
// Skip
continue;
}

// This 2-q gate **involves** at least one qubit:
if (instruction->name() != "CNOT") {
// Not a CNOT: we need to terminate, hence prune the subcircuit.
break;
}

assert(instruction->name() == "CNOT");
// If the control is *outside* the boundary, we need to terminate, hence prune the subcircuit.
const auto controlIdx = instruction->bits()[0];
Expand Down Expand Up @@ -735,6 +748,8 @@ bool CircuitOptimizer::tryRotationMergingUsingPhasePolynomials(std::shared_ptr<C
}
}
}
// Move the instruction index
++i;
}
else {
// Not a CNOT gate, continue
Expand Down
47 changes: 47 additions & 0 deletions quantum/plugins/optimizers/simple/tests/CircuitOptimizerTester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,53 @@ measure q[3] -> c[3];
EXPECT_EQ(truthTableBefore, truthTableAfter);
}

TEST(CircuitOptimizerTester, checkCZ) {
xacc::set_verbose(true);
auto compiler = xacc::getService<xacc::Compiler>("xasm");
auto program = compiler
->compile(R"(__qpu__ void testCz(qbit q) {
CNOT(q[0], q[1]);
CZ(q[0], q[1]);
Measure(q[0]);
Measure(q[1]);
})")
->getComposites()[0];

const auto before_xasm_str = program->toString();
auto optimizer = xacc::getService<IRTransformation>("circuit-optimizer");
optimizer->apply(program, nullptr);
std::cout << "FINAL CIRCUIT:\n" << program->toString() << "\n";
const auto after_xasm_str = program->toString();
EXPECT_EQ(before_xasm_str, after_xasm_str);
}

// Check circuit with random gates
TEST(CircuitOptimizerTester, checkComplexCircuit) {
xacc::set_verbose(true);
auto compiler = xacc::getService<xacc::Compiler>("xasm");
auto program = compiler
->compile(R"(__qpu__ void testRandom(qbit q) {
X(q[0]);
H(q[1]);
CZ(q[0], q[1]);
H(q[1]);
CNOT(q[1], q[2]);
T(q[1]);
CZ(q[1], q[0]);
Measure(q[0]);
Measure(q[1]);
Measure(q[2]);
})")
->getComposites()[0];

const auto before_xasm_str = program->toString();
auto optimizer = xacc::getService<IRTransformation>("circuit-optimizer");
optimizer->apply(program, nullptr);
std::cout << "FINAL CIRCUIT:\n" << program->toString() << "\n";
const auto after_xasm_str = program->toString();
EXPECT_EQ(before_xasm_str, after_xasm_str);
}

int main(int argc, char **argv) {
xacc::Initialize(argc, argv);
::testing::InitGoogleTest(&argc, argv);
Expand Down
47 changes: 38 additions & 9 deletions quantum/plugins/staq/compiler/staq_compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,27 +428,56 @@ std::shared_ptr<IR> StaqCompiler::compile(const std::string &src) {
const std::string
StaqCompiler::translate(std::shared_ptr<xacc::CompositeInstruction> function) {
std::map<std::string, int> bufNamesToSize;
std::map<std::string, int> cRegNamesToSize;
InstructionIterator iter(function);
// First search buffer names and see if we have
while (iter.hasNext()) {
auto &next = *iter.next();
if (next.isEnabled()) {
for (int i = 0; i < next.nRequiredBits(); i++) {
auto bufName = next.getBufferName(i);
int size = next.bits()[i] + 1;
if (bufNamesToSize.count(bufName)) {
if (bufNamesToSize[bufName] < size) {
bufNamesToSize[bufName] = size;
if (!next.isComposite() && next.isEnabled()) {
if (next.name() == "Measure") {
xacc::quantum::Measure &m = (xacc::quantum::Measure &)next;
if (m.hasClassicalRegAssignment()) {
auto cregName = next.getBufferName(1);
int size = m.getClassicalBitIndex() + 1;
if (cRegNamesToSize.count(cregName)) {
if (cRegNamesToSize[cregName] < size) {
cRegNamesToSize[cregName] = size;
}
} else {
cRegNamesToSize.insert({cregName, size});
}
} else {
bufNamesToSize.insert({bufName, size});
auto bufName = next.getBufferName(0);
int size = next.bits()[0] + 1;
if (bufNamesToSize.count(bufName)) {
if (bufNamesToSize[bufName] < size) {
bufNamesToSize[bufName] = size;
}
} else {
bufNamesToSize.insert({bufName, size});
}
}
} else {
for (int i = 0; i < next.nRequiredBits(); i++) {
auto bufName = next.getBufferName(i);
int size = next.bits()[i] + 1;
if (bufNamesToSize.count(bufName)) {
if (bufNamesToSize[bufName] < size) {
bufNamesToSize[bufName] = size;
}
} else {
bufNamesToSize.insert({bufName, size});
}
}
}
}
}

auto translate =
std::make_shared<internal_staq::XACCToStaqOpenQasm>(bufNamesToSize);
cRegNamesToSize.empty()
? std::make_shared<internal_staq::XACCToStaqOpenQasm>(bufNamesToSize)
: std::make_shared<internal_staq::XACCToStaqOpenQasm>(
bufNamesToSize, cRegNamesToSize);
InstructionIterator iter2(function);
while (iter2.hasNext()) {
auto &next = *iter2.next();
Expand Down
25 changes: 25 additions & 0 deletions quantum/plugins/staq/compiler/tests/StaqCompilerTester.in.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,31 @@ TEST(StaqCompilerTester, checkFloatingPointFormat) {
EXPECT_NEAR(rxInst->getParameter(0).as<double>(), 2.5e-07, 1e-12);
}

TEST(StaqCompilerTester, checkIfStatementTranslate) {
auto xasmCompiler = xacc::getCompiler("xasm");
auto ir = xasmCompiler->compile(R"(__qpu__ void test(qbit q) {
Measure(q[0], c[0]);
if(c[0]) {
Rz(q[0], -pi/2);
}
H(q[0]);
Measure(q[0], c[1]);
CPhase(q[0], q[1], -5*pi/8);
if(c[0]) {
Rz(q[0], -pi/4);
}
if(c[1]) {
Rz(q[0], -pi/2);
}
})", nullptr);
auto staq_compiler = xacc::getCompiler("staq");
auto qasm = staq_compiler->translate(ir->getComposites()[0]);
std::cout << "HOWDY:\n" << qasm << "\n";
// Check that If statements are translated.
EXPECT_TRUE(qasm.find("if (c[0] == 1) rz") != std::string::npos);
EXPECT_TRUE(qasm.find("if (c[1] == 1) rz") != std::string::npos);
}

int main(int argc, char **argv) {
xacc::Initialize(argc, argv);
xacc::set_verbose(true);
Expand Down
42 changes: 40 additions & 2 deletions quantum/plugins/staq/utils/staq_visitors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ XACCToStaqOpenQasm::XACCToStaqOpenQasm(std::map<std::string, int> bufNamesToSize
}
}

XACCToStaqOpenQasm::XACCToStaqOpenQasm(
std::map<std::string, int> bufNamesToSize,
std::map<std::string, int> cRegNameToSize) {
ss << "OPENQASM 2.0;\ninclude \"qelib1.inc\";\n";
for (auto &kv : bufNamesToSize) {
ss << "qreg " << kv.first << "[" << kv.second << "];\n";
}
for (auto &kv : cRegNameToSize) {
ss << "creg " << kv.first << "[" << kv.second << "];\n";
}
}

void XACCToStaqOpenQasm::visit(Hadamard &h) {
ss << "h " << (h.getBufferNames().empty() ? "q" : h.getBufferName(0))
<< h.bits() << ";\n";
Expand Down Expand Up @@ -97,12 +109,38 @@ void XACCToStaqOpenQasm::visit(CPhase &cphase) {
<< "[" << cphase.bits()[0] << "], " << (cphase.getBufferNames().empty() ? "q" : cphase.getBufferName(1)) << "[" << cphase.bits()[1] << "];\n";
}
void XACCToStaqOpenQasm::visit(Measure &m) {
ss << "measure " << (m.getBufferNames().empty() ? "q" : m.getBufferName(0)) << m.bits() << " -> " << cregNames[m.getBufferName(0)] << m.bits() << ";\n";
if (m.hasClassicalRegAssignment()) {
ss << "measure " << (m.getBufferNames().empty() ? "q" : m.getBufferName(0))
<< m.bits() << " -> " << m.getBufferName(1) << "["
<< m.getClassicalBitIndex() << "]"
<< ";\n";
} else {
ss << "measure " << (m.getBufferNames().empty() ? "q" : m.getBufferName(0))
<< m.bits() << " -> " << cregNames[m.getBufferName(0)] << m.bits()
<< ";\n";
}
}
void XACCToStaqOpenQasm::visit(Identity &i) {}
void XACCToStaqOpenQasm::visit(U &u) {
ss << "u3(" << std::fixed << std::setprecision(16) << xacc::InstructionParameterToDouble(u.getParameter(0)) << "," << xacc::InstructionParameterToDouble(u.getParameter(1)) << "," <<xacc::InstructionParameterToDouble(u.getParameter(2)) << ") " << (u.getBufferNames().empty() ? "q" : u.getBufferName(0)) << u.bits() << ";\n";
}
void XACCToStaqOpenQasm::visit(IfStmt &ifStmt) {}
void XACCToStaqOpenQasm::visit(IfStmt &ifStmt) {
// Note: QASM2 'if' must be inline (no braces)
// Note: this extended syntax: if (creg[k] == 1) is not supported by IBM.
// (IBM requires comparison to the full register value, not a bit check)
for (auto i : ifStmt.getInstructions()) {
// If statement was specified by the qreg name -> find the associated cReg
if (cregNames.find(ifStmt.getBufferNames()[0]) != cregNames.end()) {
ss << "if (" << cregNames[ifStmt.getBufferNames()[0]] << "["
<< ifStmt.bits()[0] << "] == 1) ";
} else {
// Explicit creg was used:
ss << "if (" << ifStmt.getBufferNames()[0] << "[" << ifStmt.bits()[0]
<< "] == 1) ";
}

i->accept(this);
}
}
} // namespace internal_staq
} // namespace xacc
7 changes: 6 additions & 1 deletion quantum/plugins/staq/utils/staq_visitors.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,13 @@ class XACCToStaqOpenQasm : public AllGateVisitor {
public:
std::stringstream ss;
std::map<std::string, std::string> cregNames;

// Default: assume each qreg is accompanied by a creg of the same size
XACCToStaqOpenQasm(std::map<std::string, int> bufNamesToSize);
// Explicit list of creg:
// e.g., IQPE algo: we requires a much larger creg than qreg to store all
// measurements.
XACCToStaqOpenQasm(std::map<std::string, int> bufNamesToSize,
std::map<std::string, int> cRegNameToSize);
void visit(Hadamard &h) override;
void visit(CNOT &cnot) override;
void visit(Rz &rz) override;
Expand Down

0 comments on commit 50bee5d

Please sign in to comment.