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: ...@@ -76,6 +76,9 @@ public:
std::vector<InstructionParameter> getParameters() override { std::vector<InstructionParameter> getParameters() override {
return {bufferName}; return {bufferName};
} }
std::vector<std::string> getBufferNames() override { return {bufferName}; }
const int nParameters() override { return 1; } const int nParameters() override { return 1; }
const int nRequiredBits() const override { return 1; } const int nRequiredBits() const override { return 1; }
......
...@@ -832,6 +832,14 @@ IbmqNoiseModel::averageTwoQubitGateFidelity() const { ...@@ -832,6 +832,14 @@ IbmqNoiseModel::averageTwoQubitGateFidelity() const {
return result; 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 quantum
} // namespace xacc } // namespace xacc
......
...@@ -60,7 +60,8 @@ public: ...@@ -60,7 +60,8 @@ public:
void apply(std::shared_ptr<AcceleratorBuffer> buffer, void apply(std::shared_ptr<AcceleratorBuffer> buffer,
std::shared_ptr<Instruction> inst) override; std::shared_ptr<Instruction> inst) override;
bool isInitialized() const { return initialized; } bool isInitialized() const { return initialized; }
std::string getNativeCode(std::shared_ptr<CompositeInstruction> program,
const HeterogeneousMap &config = {}) override;
private: private:
static double calcExpectationValueZ( static double calcExpectationValueZ(
const std::vector<std::pair<double, double>> &in_stateVec, const std::vector<std::pair<double, double>> &in_stateVec,
......
...@@ -678,6 +678,19 @@ bool CircuitOptimizer::tryRotationMergingUsingPhasePolynomials(std::shared_ptr<C ...@@ -678,6 +678,19 @@ bool CircuitOptimizer::tryRotationMergingUsingPhasePolynomials(std::shared_ptr<C
} }
} }
else if (instruction->bits().size() == 2) { 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"); assert(instruction->name() == "CNOT");
// If the control is *outside* the boundary, we need to terminate, hence prune the subcircuit. // If the control is *outside* the boundary, we need to terminate, hence prune the subcircuit.
const auto controlIdx = instruction->bits()[0]; const auto controlIdx = instruction->bits()[0];
...@@ -735,6 +748,8 @@ bool CircuitOptimizer::tryRotationMergingUsingPhasePolynomials(std::shared_ptr<C ...@@ -735,6 +748,8 @@ bool CircuitOptimizer::tryRotationMergingUsingPhasePolynomials(std::shared_ptr<C
} }
} }
} }
// Move the instruction index
++i;
} }
else { else {
// Not a CNOT gate, continue // Not a CNOT gate, continue
......
...@@ -818,6 +818,53 @@ measure q[3] -> c[3]; ...@@ -818,6 +818,53 @@ measure q[3] -> c[3];
EXPECT_EQ(truthTableBefore, truthTableAfter); 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) { int main(int argc, char **argv) {
xacc::Initialize(argc, argv); xacc::Initialize(argc, argv);
::testing::InitGoogleTest(&argc, argv); ::testing::InitGoogleTest(&argc, argv);
......
...@@ -428,27 +428,56 @@ std::shared_ptr<IR> StaqCompiler::compile(const std::string &src) { ...@@ -428,27 +428,56 @@ std::shared_ptr<IR> StaqCompiler::compile(const std::string &src) {
const std::string const std::string
StaqCompiler::translate(std::shared_ptr<xacc::CompositeInstruction> function) { StaqCompiler::translate(std::shared_ptr<xacc::CompositeInstruction> function) {
std::map<std::string, int> bufNamesToSize; std::map<std::string, int> bufNamesToSize;
std::map<std::string, int> cRegNamesToSize;
InstructionIterator iter(function); InstructionIterator iter(function);
// First search buffer names and see if we have // First search buffer names and see if we have
while (iter.hasNext()) { while (iter.hasNext()) {
auto &next = *iter.next(); auto &next = *iter.next();
if (next.isEnabled()) { if (!next.isComposite() && next.isEnabled()) {
for (int i = 0; i < next.nRequiredBits(); i++) { if (next.name() == "Measure") {
auto bufName = next.getBufferName(i); xacc::quantum::Measure &m = (xacc::quantum::Measure &)next;
int size = next.bits()[i] + 1; if (m.hasClassicalRegAssignment()) {
if (bufNamesToSize.count(bufName)) { auto cregName = next.getBufferName(1);
if (bufNamesToSize[bufName] < size) { int size = m.getClassicalBitIndex() + 1;
bufNamesToSize[bufName] = size; if (cRegNamesToSize.count(cregName)) {
if (cRegNamesToSize[cregName] < size) {
cRegNamesToSize[cregName] = size;
}
} else {
cRegNamesToSize.insert({cregName, size});
} }
} else { } 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 = 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); InstructionIterator iter2(function);
while (iter2.hasNext()) { while (iter2.hasNext()) {
auto &next = *iter2.next(); auto &next = *iter2.next();
......
...@@ -246,6 +246,31 @@ TEST(StaqCompilerTester, checkFloatingPointFormat) { ...@@ -246,6 +246,31 @@ TEST(StaqCompilerTester, checkFloatingPointFormat) {
EXPECT_NEAR(rxInst->getParameter(0).as<double>(), 2.5e-07, 1e-12); 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) { int main(int argc, char **argv) {
xacc::Initialize(argc, argv); xacc::Initialize(argc, argv);
xacc::set_verbose(true); xacc::set_verbose(true);
......
...@@ -24,6 +24,18 @@ XACCToStaqOpenQasm::XACCToStaqOpenQasm(std::map<std::string, int> bufNamesToSize ...@@ -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) { void XACCToStaqOpenQasm::visit(Hadamard &h) {
ss << "h " << (h.getBufferNames().empty() ? "q" : h.getBufferName(0)) ss << "h " << (h.getBufferNames().empty() ? "q" : h.getBufferName(0))
<< h.bits() << ";\n"; << h.bits() << ";\n";
...@@ -97,12 +109,38 @@ void XACCToStaqOpenQasm::visit(CPhase &cphase) { ...@@ -97,12 +109,38 @@ void XACCToStaqOpenQasm::visit(CPhase &cphase) {
<< "[" << cphase.bits()[0] << "], " << (cphase.getBufferNames().empty() ? "q" : cphase.getBufferName(1)) << "[" << cphase.bits()[1] << "];\n"; << "[" << cphase.bits()[0] << "], " << (cphase.getBufferNames().empty() ? "q" : cphase.getBufferName(1)) << "[" << cphase.bits()[1] << "];\n";
} }
void XACCToStaqOpenQasm::visit(Measure &m) { 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(Identity &i) {}
void XACCToStaqOpenQasm::visit(U &u) { 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"; 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 internal_staq
} // namespace xacc } // namespace xacc
\ No newline at end of file
...@@ -266,8 +266,13 @@ class XACCToStaqOpenQasm : public AllGateVisitor { ...@@ -266,8 +266,13 @@ class XACCToStaqOpenQasm : public AllGateVisitor {
public: public:
std::stringstream ss; std::stringstream ss;
std::map<std::string, std::string> cregNames; 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); 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(Hadamard &h) override;
void visit(CNOT &cnot) override; void visit(CNOT &cnot) override;
void visit(Rz &rz) 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