-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
11 changed files
with
571 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
OPENQASM 2.0; | ||
include "qelib1.inc"; | ||
|
||
qreg q[2]; | ||
qreg a[2]; | ||
|
||
tdg a[0]; | ||
crz(2) a[1], q[0]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,243 @@ | ||
/* | ||
* This file is part of staq. | ||
* | ||
* Copyright (c) 2019 - 2023 softwareQ Inc. All rights reserved. | ||
* | ||
* MIT License | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
* SOFTWARE. | ||
*/ | ||
|
||
/** | ||
* \file output/ionq.hpp | ||
* \brief IonQ outputter | ||
*/ | ||
|
||
#ifndef OUTPUT_IONQ_HPP_ | ||
#define OUTPUT_IONQ_HPP_ | ||
|
||
#include <fstream> | ||
#include <iomanip> | ||
#include <typeinfo> | ||
|
||
#include "qasmtools/ast/ast.hpp" | ||
|
||
namespace staq { | ||
namespace output { | ||
|
||
namespace ast = qasmtools::ast; | ||
|
||
/** \brief Equivalent IonQ standard gates for qasm standard gates */ | ||
std::unordered_map<std::string, std::string> qasmstd_to_ionq{ | ||
{"sdg", "si"}, {"tdg", "ti"}, {"u1", "rz"}}; | ||
|
||
/** | ||
* \class staq::output::IonQOutputter | ||
* \brief Visitor for converting a QASM AST to IonQ | ||
*/ | ||
class IonQOutputter final : public ast::Visitor { | ||
public: | ||
IonQOutputter(std::ostream& os) : Visitor(), os_(os) {} | ||
~IonQOutputter() = default; | ||
|
||
void run(ast::Program& prog) { | ||
prefix_ = ""; | ||
first_gate = true; | ||
|
||
prog.accept(*this); | ||
} | ||
|
||
// Variables | ||
void visit(ast::VarAccess& ap) {} | ||
|
||
// Expressions | ||
void visit(ast::BExpr& expr) {} | ||
|
||
void visit(ast::UExpr& expr) {} | ||
|
||
void visit(ast::PiExpr&) {} | ||
|
||
void visit(ast::IntExpr& expr) {} | ||
|
||
void visit(ast::RealExpr& expr) {} | ||
|
||
void visit(ast::VarExpr& expr) {} | ||
|
||
// Statements | ||
void visit(ast::MeasureStmt& stmt) {} | ||
|
||
void visit(ast::ResetStmt& stmt) {} | ||
|
||
void visit(ast::IfStmt& stmt) {} | ||
|
||
// Gates | ||
void visit(ast::UGate& gate) {} | ||
|
||
void visit(ast::CNOTGate& gate) {} | ||
|
||
void visit(ast::BarrierGate&) {} | ||
|
||
void visit(ast::DeclaredGate& gate) { | ||
// JSON output: avoid outputting comma before first gate | ||
if (first_gate) { | ||
first_gate = false; | ||
} else { | ||
os_ << ",\n"; | ||
} | ||
|
||
os_ << prefix_ << "{\n"; | ||
prefix_ += " "; | ||
|
||
std::string name = gate.name(); | ||
if (auto it = qasmstd_to_ionq.find(gate.name()); | ||
it != qasmstd_to_ionq.end()) | ||
name = it->second; | ||
|
||
int cur_qarg = 0; | ||
bool ctrl = false; | ||
// Handle control gates | ||
// Control is the first qarg, if applicable | ||
if (name[0] == 'c') { | ||
cur_qarg = 1; | ||
ctrl = true; | ||
name = name.substr(1); | ||
} | ||
|
||
// IonQ handles gates with multiple control args, | ||
// but the QE standard gates have at most one control arg. | ||
if (ctrl) { | ||
os_ << prefix_ << "\"control\": " << gate.qarg(0).offset().value() | ||
<< ",\n"; | ||
} | ||
|
||
if (gate.num_qargs() - cur_qarg == 1) { | ||
// Single target gate | ||
os_ << prefix_ | ||
<< "\"target\": " << gate.qarg(cur_qarg).offset().value() | ||
<< ",\n"; | ||
} else { | ||
// Multi target gate | ||
os_ << prefix_ << "\"targets\": ["; | ||
for (int i = cur_qarg; i < gate.num_qargs(); ++i) { | ||
os_ << gate.qarg(i).offset().value(); | ||
if (i + 1 != gate.num_qargs()) | ||
os_ << ","; | ||
} | ||
|
||
os_ << "],\n"; | ||
} | ||
|
||
if (gate.num_cargs()) { | ||
// TODO: assert that there is exactly one carg | ||
// TODO: assert that this is a rotation gate | ||
// TODO: Handle multiples of pi nicely | ||
os_ << prefix_ << "\"angle\": " << std::fixed | ||
<< gate.carg(0).constant_eval().value() / qasmtools::utils::pi << ",\n"; | ||
} | ||
|
||
os_ << prefix_ << "\"gate\": \"" << name << "\"\n"; // no comma | ||
|
||
prefix_.resize(prefix_.size() - 4); | ||
os_ << prefix_ | ||
<< "}"; // Do not output newline for proper JSON formatting | ||
} | ||
|
||
// Declarations | ||
void visit(ast::GateDecl& decl) {} | ||
|
||
void visit(ast::OracleDecl& decl) {} | ||
|
||
void visit(ast::RegisterDecl& decl) { | ||
if (decl.is_quantum()) { | ||
os_ << prefix_ << "\"qubits\": " << decl.size() << ",\n"; | ||
} else { | ||
// should never occur | ||
} | ||
} | ||
|
||
void visit(ast::AncillaDecl& decl) {} | ||
|
||
// Program | ||
void visit(ast::Program& prog) { | ||
os_ << prefix_ << "{\n"; | ||
prefix_ += " "; | ||
|
||
os_ << prefix_ << "\"format\": \"ionq.circuit.v0\",\n"; | ||
os_ << prefix_ << "\"gateset\": \"qis\",\n"; | ||
|
||
// Print the qubit count line (global register decl) | ||
prog.foreach_stmt([this](auto& stmt) { | ||
if (typeid(stmt) == typeid(ast::RegisterDecl)) | ||
stmt.accept(*this); | ||
}); | ||
|
||
os_ << prefix_ << "\"circuit\": [\n"; | ||
prefix_ += " "; | ||
|
||
// Program body | ||
prog.foreach_stmt([this](auto& stmt) { | ||
// Skip the gate declarations from qelib1.inc | ||
// and the global register decl | ||
if ((typeid(stmt) != typeid(ast::GateDecl)) && | ||
(typeid(stmt) != typeid(ast::RegisterDecl))) | ||
stmt.accept(*this); | ||
}); | ||
|
||
// Close circuit | ||
os_ << "\n"; | ||
prefix_.resize(prefix_.size() - 4); | ||
os_ << prefix_ << "]\n"; | ||
|
||
// Close input | ||
prefix_.resize(prefix_.size() - 4); | ||
os_ << prefix_ << "}\n"; | ||
} | ||
|
||
private: | ||
std::ostream& os_; | ||
|
||
std::string prefix_ = ""; | ||
bool first_gate = true; | ||
}; | ||
|
||
/** \brief Writes an AST in IonQ format to stdout */ | ||
void output_ionq(ast::Program& prog) { | ||
IonQOutputter outputter(std::cout); | ||
outputter.run(prog); | ||
} | ||
|
||
/** \brief Writes an AST in IonQ format to a given output stream */ | ||
void write_ionq(ast::Program& prog, std::string fname) { | ||
std::ofstream ofs; | ||
ofs.open(fname); | ||
|
||
if (!ofs.good()) { | ||
std::cerr << "Error: failed to open output file " << fname << "\n"; | ||
} else { | ||
IonQOutputter outputter(ofs); | ||
outputter.run(prog); | ||
} | ||
|
||
ofs.close(); | ||
} | ||
|
||
} /* namespace output */ | ||
} /* namespace staq */ | ||
|
||
#endif /* OUTPUT_IONQ_HPP_ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
/* | ||
* This file is part of staq. | ||
* | ||
* Copyright (c) 2019 - 2023 softwareQ Inc. All rights reserved. | ||
* | ||
* MIT License | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
* SOFTWARE. | ||
*/ | ||
|
||
/** | ||
* \file transformations/replace_ugate.hpp | ||
* \brief Replacing common U gates with QE standard gates | ||
*/ | ||
|
||
#ifndef TRANSFORMATIONS_REPLACE_UGATE_HPP_ | ||
#define TRANSFORMATIONS_REPLACE_UGATE_HPP_ | ||
|
||
#include <list> | ||
#include <unordered_map> | ||
#include <variant> | ||
|
||
#include "qasmtools/ast/replacer.hpp" | ||
|
||
namespace staq { | ||
namespace transformations { | ||
|
||
namespace ast = qasmtools::ast; | ||
|
||
/** | ||
* \brief Replacing UGates | ||
* | ||
* Visits an AST and replaces common U gates with QE standard | ||
* gates if possible. Assumes qelib1.inc is included. | ||
*/ | ||
void replace_ugates(ast::ASTNode& node); | ||
|
||
static constexpr double pi = qasmtools::utils::pi; | ||
static constexpr double EPS = 1e-9; | ||
|
||
struct UArgs { | ||
double theta; | ||
double phi; | ||
double lambda; | ||
}; | ||
|
||
// clang-format off | ||
static const std::vector<std::pair<UArgs,std::string>> standard_gates{ | ||
{{pi, 0, pi}, "x"}, | ||
{{pi, pi/2, pi/2}, "y"}, | ||
{{0, 0, pi}, "z"}, | ||
{{pi/2, 0, pi}, "h"}, | ||
{{0, 0, pi/2}, "s"}, | ||
{{0, 0, -pi/2}, "sdg"}, | ||
{{0, 0, pi/4}, "t"}, | ||
{{0, 0, -pi/4}, "tdg"} | ||
}; | ||
// clang-format on | ||
|
||
/* Implementation */ | ||
class ReplaceUGateImpl final : public ast::Replacer { | ||
public: | ||
ReplaceUGateImpl() = default; | ||
~ReplaceUGateImpl() = default; | ||
|
||
void run(ast::ASTNode& node) { node.accept(*this); } | ||
|
||
// Replace ast::CNOT with ast::DeclaredGate cx | ||
std::optional<std::list<ast::ptr<ast::Gate>>> | ||
replace(ast::CNOTGate& gate) override { | ||
std::cerr << "CNOT\n"; | ||
std::vector<ast::ptr<ast::Expr>> c_args; | ||
std::vector<ast::VarAccess> q_args{gate.ctrl(), gate.tgt()}; | ||
|
||
std::list<ast::ptr<ast::Gate>> ret; | ||
ret.emplace_back(std::make_unique<ast::DeclaredGate>(ast::DeclaredGate( | ||
gate.pos(), "cx", std::move(c_args), std::move(q_args)))); | ||
return std::move(ret); | ||
} | ||
|
||
std::optional<std::list<ast::ptr<ast::Gate>>> | ||
replace(ast::UGate& gate) override { | ||
double theta, phi, lambda; | ||
try { | ||
theta = gate.theta().constant_eval().value(); | ||
phi = gate.phi().constant_eval().value(); | ||
lambda = gate.lambda().constant_eval().value(); | ||
} catch (...) { | ||
std::cerr << "found VarExpr in UGate args" | ||
<< "\n"; | ||
// this should never happen; if this is reached then the Replacer | ||
// is recurring too far down the AST | ||
throw; | ||
} | ||
|
||
std::string name = ""; | ||
std::vector<ast::ptr<ast::Expr>> c_args; | ||
std::vector<ast::VarAccess> q_args{gate.arg()}; | ||
|
||
// Simple cases: x y z h s sdg t tdg | ||
for (auto& [g_args, g_name] : standard_gates) { | ||
if (std::abs(theta - g_args.theta) < EPS && | ||
std::abs(phi - g_args.phi) < EPS && | ||
std::abs(lambda - g_args.lambda) < EPS) { | ||
|
||
name = g_name; | ||
break; | ||
} | ||
} | ||
|
||
// Remaining cases: rz ry rx | ||
if (name == "") { | ||
if (std::abs(theta) < EPS && std::abs(phi) < EPS) { | ||
name = "rz"; | ||
// TODO: Directly copy the exprs from the U gate | ||
// to avoid precision loss | ||
c_args.emplace_back( | ||
std::make_unique<ast::RealExpr>(gate.pos(), lambda)); | ||
} else if (std::abs(phi) < EPS && std::abs(lambda) < EPS) { | ||
name = "ry"; | ||
c_args.emplace_back( | ||
std::make_unique<ast::RealExpr>(gate.pos(), theta)); | ||
} else if (std::abs(phi + pi / 2) < EPS && | ||
std::abs(lambda - pi / 2) < EPS) { | ||
name = "rx"; | ||
c_args.emplace_back( | ||
std::make_unique<ast::RealExpr>(gate.pos(), theta)); | ||
} | ||
} | ||
|
||
// Throw error if U gate is not a QE standard gate | ||
if (name == "") { | ||
throw std::logic_error{""}; | ||
return std::nullopt; | ||
} | ||
|
||
std::list<ast::ptr<ast::Gate>> ret; | ||
ret.emplace_back(std::make_unique<ast::DeclaredGate>(ast::DeclaredGate( | ||
gate.pos(), name, std::move(c_args), std::move(q_args)))); | ||
return std::move(ret); | ||
} | ||
|
||
// Avoid visiting children of GateDecl | ||
void visit(ast::GateDecl& decl) override {} | ||
}; | ||
|
||
void replace_ugates(ast::ASTNode& node) { | ||
ReplaceUGateImpl alg; | ||
alg.run(node); | ||
} | ||
|
||
} /* namespace transformations */ | ||
} /* namespace staq */ | ||
|
||
#endif /* TRANSFORMATIONS_REPLACE_UGATE_HPP_ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/* | ||
* This file is part of staq. | ||
* | ||
* Copyright (c) 2019 - 2023 softwareQ Inc. All rights reserved. | ||
* | ||
* MIT License | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
* SOFTWARE. | ||
*/ | ||
|
||
#include <CLI/CLI.hpp> | ||
|
||
#include "mapping/device.hpp" | ||
#include "mapping/layout/basic.hpp" | ||
#include "output/ionq.hpp" | ||
#include "qasmtools/parser/parser.hpp" | ||
#include "transformations/desugar.hpp" | ||
#include "transformations/expression_simplifier.hpp" | ||
#include "transformations/inline.hpp" | ||
#include "transformations/replace_ugate.hpp" | ||
|
||
static const std::set<std::string_view> ionq_overrides{ | ||
"x", "y", "z", "h", "s", "sdg", "t", "tdg", "rx", | ||
"ry", "rz", "cz", "cy", "swap", "cx", "u1", "ch", "crz"}; | ||
|
||
int main(int argc, char** argv) { | ||
using namespace staq; | ||
using qasmtools::parser::parse_stdin; | ||
|
||
std::string filename = ""; | ||
|
||
CLI::App app{"QASM to IonQ transpiler"}; | ||
|
||
app.add_option("-o,--output", filename, "Output to a file"); | ||
|
||
CLI11_PARSE(app, argc, argv); | ||
auto program = parse_stdin(); | ||
|
||
if (program) { | ||
transformations::desugar(*program); | ||
|
||
// Flatten qregs into one global qreg. | ||
// IonQ Simulator has 29 qubits; | ||
// the other IonQ devices have less than 29 qubits. | ||
// For now let's set a cap of 11 qubits; change this later. | ||
auto device = mapping::fully_connected(11); | ||
auto layout = mapping::compute_basic_layout(device, *program); | ||
mapping::apply_layout(layout, device, *program); | ||
|
||
// Inline declared gates | ||
transformations::Inliner::config params{false, ionq_overrides}; | ||
transformations::inline_ast(*program, params); | ||
|
||
// Evaluate expressions | ||
// TODO: Handle multiples of pi nicely | ||
transformations::expr_simplify(*program, true); | ||
|
||
// Replace U gates | ||
transformations::replace_ugates(*program); | ||
|
||
if (filename.empty()) | ||
output::output_ionq(*program); | ||
else | ||
output::write_ionq(*program, filename); | ||
} else { | ||
std::cerr << "Parsing failed\n"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters