Unverified Commit 50bee5de authored by Mccaskey, Alex's avatar Mccaskey, Alex Committed by GitHub
Browse files

Merge pull request #481 from tnguyen-ornl/tnguyen/ifstmt-to-extended-qasm2

Support IfStmt to OpenQASM2 generation
parents b1cf4a2e 044bff71
Pipeline #160431 passed with stage
in 34 minutes and 28 seconds
......@@ -76,6 +76,9 @@ public:
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; }
......
......@@ -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
......
......@@ -60,7 +60,8 @@ public:
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,
......
......@@ -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];
......@@ -735,6 +748,8 @@ bool CircuitOptimizer::tryRotationMergingUsingPhasePolynomials(std::shared_ptr<C
}
}
}
// Move the instruction index
++i;
}
else {
// Not a CNOT gate, continue
......
......@@ -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);
......
......@@ -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();
......
......@@ -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);
......
......@@ -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";
......@@ -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
\ No newline at end of file
......@@ -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;
......
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