diff --git a/include/staq/output/json.hpp b/include/staq/output/json.hpp new file mode 100644 index 00000000..82e3f0ff --- /dev/null +++ b/include/staq/output/json.hpp @@ -0,0 +1,290 @@ +/* + * This file is part of staq. + * + * Copyright (c) 2019 - 2025 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. + */ + +#ifndef OUTPUT_JSON_HPP_ +#define OUTPUT_JSON_HPP_ + +#include +#include +#include +#include +#include "qasmtools/ast/decl.hpp" +#include "qasmtools/ast/stmt.hpp" +#include "qasmtools/ast/var.hpp" +#include "qasmtools/parser/parser.hpp" + +#include + +#include "qasmtools/ast/ast.hpp" + +namespace staq { +namespace output { + +using namespace qasmtools::ast; +using json = nlohmann::json; + +/** + * \class staq::output::JSONOutputter + * \brief Visitor for converting a QASM AST to JSON + */ +class JSONOutputter final : public Visitor { + public: + JSONOutputter() = default; + ~JSONOutputter() = default; + + void run(Program& prog) { prog.accept(*this); } + + void visit(UGate& gd) override { + json j; + j["type"] = "Gate"; + JSONOutputter arg0, arg1, arg2, arg3; + gd.arg().accept(arg0); + gd.arg().accept(arg1); + gd.arg().accept(arg2); + gd.arg().accept(arg3); + j["name"] = "UGate"; + j["qargs"] = {arg0.json_val()}; + j["cargs"] = {arg1.json_val(), arg2.json_val(), arg3.json_val()}; + json_.push_back(j); + } + void visit(CNOTGate& gd) override { + json j; + j["type"] = "Gate"; + JSONOutputter arg0, arg1; + gd.ctrl().accept(arg0); + gd.tgt().accept(arg1); + j["name"] = "CNOTGate"; + j["qargs"] = {arg0.json_val(), arg1.json_val()}; + } + void visit(BarrierGate& gd) override { + using namespace qasmtools::ast; + json j; + j["type"] = "Gate"; + j["name"] = "BarrierGate"; + j["qargs"] = {}; + gd.foreach_arg([&j](VarAccess& va) { + JSONOutputter jva; + va.accept(jva); + j["qargs"].push_back(jva.json_val()); + }); + } + void visit(DeclaredGate& gd) override { + using namespace qasmtools::ast; + json j; + j["type"] = "Gate"; + j["name"] = gd.name(); + j["qargs"] = {}; + j["cargs"] = {}; + gd.foreach_qarg([&j](VarAccess& va) { + JSONOutputter jva; + va.accept(jva); + j["qargs"].push_back(jva.json_val()); + }); + gd.foreach_carg([&j](Expr& va) { + JSONOutputter jva; + va.accept(jva); + j["cargs"].push_back(jva.json_val()); + }); + } + + void visit(AncillaDecl& ad) override { + json j; + j["type"] = "AncillaDecl"; + j["name"] = ad.id(); + j["size"] = ad.size(); + j["is_dirty"] = (ad.is_dirty() ? 1 : 0); + json_.push_back(j); + } + + void visit(RegisterDecl& rd) override { + json j; + j["type"] = "RegisterDecl"; + j["name"] = rd.id(); + j["is_quantum"] = (rd.is_quantum() ? 1 : 0); + j["size"] = rd.size(); + json_.push_back(j); + } + + void visit(OracleDecl& od) override { + // TODO: verify that this is doing what it needs to do + json j; + j["type"] = "OracleDecl"; + j["name"] = od.fname(); + j["params"] = od.params(); + json_.push_back(j); + } + + void visit(IfStmt& ist) override { + // TODO: Improve this later. + json j; + std::stringstream in; + ist.pretty_print(in, false); + j["type"] = "IfStmt"; + j["name"] = "If"; + j["body"] = in.str(); + json_.push_back(j); + } + + void visit(ResetStmt& rst) override { + json j; + JSONOutputter arg_json_outputter; + rst.arg().accept(arg_json_outputter); + j["type"] = "ResetStmt"; + j["name"] = "Reset"; + j["qarg"] = arg_json_outputter.json_val(); + json_.push_back(j); + } + + void visit(MeasureStmt& mst) override { + json j; + JSONOutputter arg_json_1, arg_json_2; + mst.q_arg().accept(arg_json_1); + mst.c_arg().accept(arg_json_2); + j["type"] = "MeasureStmt"; + j["name"] = "Measurement"; + j["qarg"] = arg_json_1.json_val(); + j["carg"] = arg_json_2.json_val(); + json_.push_back(j); + } + + void visit(VarAccess& va) override { + json j; + j["type"] = "VarAccess"; + j["name"] = "qubit"; + j["symbol"] = va.var(); + j["offset"] = {}; + if (va.offset().has_value()) { + j["offset"].push_back(va.offset().value()); + } + json_.push_back(j); + } + + // Expressions + void visit(BExpr& e) override {}; + void visit(UExpr& e) override {}; + void visit(PiExpr& e) override {}; + void visit(IntExpr& e) override {}; + void visit(RealExpr& e) override {}; + void visit(VarExpr& e) override {}; + + void visit(Expr& expr) { + json j; + std::stringstream in; + expr.pretty_print(in); + auto ev = expr.constant_eval(); + j["type"] = "Expr"; + j["expr"] = in.str(); + j["val"] = {}; + if (ev.has_value()) { + j["val"].push_back(ev.value()); + } + json_.push_back(j); + } + + // void visit(qasmtools::ast::Gate& g) override { + // using namespace qasmtools::ast; + // json j; + // + // j["type"] = "Gate"; + // if (UGate* gd = dynamic_cast(&g)) { + // JSONOutputter arg0, arg1, arg2, arg3; + // gd->arg().accept(arg0); + // gd->arg().accept(arg1); + // gd->arg().accept(arg2); + // gd->arg().accept(arg3); + // j["name"] = "UGate"; + // j["qargs"] = {arg0.json_val()}; + // j["cargs"] = {arg1.json_val(), arg2.json_val(), arg3.json_val()}; + // } else if (CNOTGate* gd = dynamic_cast(&g)) { + // //(typeid(g) == typeid(CNOTGate)) { + // JSONOutputter arg0, arg1; + // gd->ctrl().accept(arg0); + // gd->tgt().accept(arg1); + // j["name"] = "CNOTGate"; + // j["qargs"] = {arg0.json_val(), arg1.json_val()}; + // } else if (BarrierGate* gd = dynamic_cast(&g)) { + // j["name"] = "BarrierGate"; + // j["qargs"] = {}; + // gd->foreach_arg([&j](VarAccess& va) { + // JSONOutputter jva; + // va.accept(jva); + // j["qargs"].push_back(jva.json_val()); + // }); + // } else if (DeclaredGate* gd = dynamic_cast(&g)) { + // j["name"] = gd->name(); + // j["qargs"] = {}; + // j["cargs"] = {}; + // gd->foreach_qarg([&j](VarAccess& va) { + // JSONOutputter jva; + // va.accept(jva); + // j["qargs"].push_back(jva.json_val()); + // }); + // gd->foreach_carg([&j](Expr& va) { + // JSONOutputter jva; + // va.accept(jva); + // j["cargs"].push_back(jva.json_val()); + // }); + // } else { + // throw ""; + // } + // json_.push_back(j); + // } + + void visit(GateDecl& gd) override { + using namespace qasmtools::ast; + json j; + j["type"] = "GateDecl"; + j["name"] = gd.id(); + j["q_params"] = gd.q_params(); + j["c_params"] = gd.c_params(); + j["body"] = {}; // process the body of a GateDecl; + gd.foreach_stmt([&j](Stmt& st) { + JSONOutputter jst; + st.accept(jst); + j["body"].push_back(jst.json_val()); + }); + json_.push_back(j); + } + + void visit(Program& p) override { + using namespace qasmtools::ast; + p.foreach_stmt([&](Stmt& st) { + JSONOutputter jst; + st.accept(jst); + json_.push_back(jst.json_val()); + }); + } + + json json_val() { return json_; } + + private: + json json_; +}; + +} /* namespace output */ +} /* namespace staq */ + +#endif /* OUTPUT_JSON_HPP_ */ diff --git a/pystaq/include/pystaq/pystaq_common.hpp b/pystaq/include/pystaq/pystaq_common.hpp index 28f368cb..29aafb61 100644 --- a/pystaq/include/pystaq/pystaq_common.hpp +++ b/pystaq/include/pystaq/pystaq_common.hpp @@ -60,6 +60,7 @@ #include "staq/output/cirq.hpp" #include "staq/output/ionq.hpp" +#include "staq/output/json.hpp" #include "staq/output/lattice_surgery.hpp" #include "staq/output/projectq.hpp" #include "staq/output/qsharp.hpp" diff --git a/pystaq/staq_wrapper.cpp b/pystaq/staq_wrapper.cpp index c383cd22..96a7e7ea 100644 --- a/pystaq/staq_wrapper.cpp +++ b/pystaq/staq_wrapper.cpp @@ -158,6 +158,11 @@ class Program { outputter.run(*prog_); return oss.str(); } + std::string to_json() { + staq::output::JSONOutputter outputter; + outputter.run(*prog_); + return outputter.json_val().dump(); + } std::string lattice_surgery() { return staq::output::lattice_surgery(*prog_); } @@ -283,6 +288,7 @@ PYBIND11_MODULE(pystaq, m) { .def("to_qsharp", &Program::to_qsharp, "Get the Q# representation") .def("to_quil", &Program::to_quil, "Get the Quil representation") .def("to_ionq", &Program::to_ionq, "Get the IonQ representation") + .def("to_json", &Program::to_json, "Get the JSON representation") .def("__repr__", [](const Program& p) { std::ostringstream oss; oss << p; diff --git a/src/tools/json.cpp b/src/tools/json.cpp new file mode 100644 index 00000000..4f8b2a79 --- /dev/null +++ b/src/tools/json.cpp @@ -0,0 +1,241 @@ +#include "staq/output/json.hpp" +#include +#include +#include +#include +#include "qasmtools/ast/decl.hpp" +#include "qasmtools/ast/stmt.hpp" +#include "qasmtools/ast/var.hpp" +#include "qasmtools/parser/parser.hpp" + +// using json = nlohmann::json; +// +// /* Forward declarations */ +// // [x] class VarAccess; +// // [x] class BExpr,UExpr,PiExpr,IntExpr,RealExpr,VarExpr; +// // [ ] class MeasureStmt,ResetStmt,IfStmt, +// // [x] class UGate,CNOTGate,BarrierGate,DeclaredGate, +// // [ ] class GateDecl,OracleDecl,RegisterDecl,AncillaDecl; +// // [ ] class Program; +// +// json jsonify(qasmtools::ast::VarAccess&); +// json jsonify(qasmtools::ast::Expr&); +// json jsonify(qasmtools::ast::Gate&); +// json jsonify(qasmtools::ast::GateDecl&); +// json jsonify(qasmtools::ast::Stmt&); +// json jsonify(qasmtools::ast::MeasureStmt&); +// json jsonify(qasmtools::ast::ResetStmt&); +// json jsonify(qasmtools::ast::OracleDecl&); +// json jsonify(qasmtools::ast::RegisterDecl&); +// json jsonify(qasmtools::ast::AncillaDecl&); +// json jsonify(qasmtools::ast::IfStmt&); +// json jsonify(qasmtools::ast::Program&); +// +// json jsonify(qasmtools::ast::AncillaDecl& ad) { +// json j; +// j["type"] = "AncillaDecl"; +// j["name"] = ad.id(); +// j["size"] = ad.size(); +// j["is_dirty"] = (ad.is_dirty() ? 1 : 0); +// return j; +// } +// +// json jsonify(qasmtools::ast::RegisterDecl& rd) { +// json j; +// j["type"] = "RegisterDecl"; +// j["name"] = rd.id(); +// j["is_quantum"] = (rd.is_quantum() ? 1 : 0); +// j["size"] = rd.size(); +// return j; +// } +// +// json jsonify(qasmtools::ast::OracleDecl& od) { +// // TODO: verify that this is doing what it needs to do +// json j; +// j["type"] = "OracleDecl"; +// j["name"] = od.fname(); +// j["params"] = od.params(); +// return j; +// } +// +// json jsonify(qasmtools::ast::IfStmt& ist) { +// // TODO: Improve this later. +// json j; +// std::stringstream in; +// ist.pretty_print(in, false); +// j["type"] = "IfStmt"; +// j["name"] = "If"; +// j["body"] = in.str(); +// return j; +// } +// +// json jsonify(qasmtools::ast::ResetStmt& rst) { +// json j; +// j["type"] = "ResetStmt"; +// j["name"] = "Reset"; +// j["qarg"] = jsonify(rst.arg()); +// return j; +// } +// +// json jsonify(qasmtools::ast::MeasureStmt& mst) { +// json j; +// j["type"] = "MeasureStmt"; +// j["name"] = "Measurement"; +// j["qarg"] = jsonify(mst.q_arg()); +// j["carg"] = jsonify(mst.c_arg()); +// return j; +// } +// +// json jsonify(qasmtools::ast::VarAccess& va) { +// json j; +// j["type"] = "VarAccess"; +// j["name"] = "qubit"; +// j["symbol"] = va.var(); +// j["offset"] = {}; +// if (va.offset().has_value()) { +// j["offset"].push_back(va.offset().value()); +// } +// return j; +// } +// +// json jsonify(qasmtools::ast::Expr& expr) { +// json j; +// std::stringstream in; +// expr.pretty_print(in); +// auto ev = expr.constant_eval(); +// j["type"] = "Expr"; +// j["expr"] = in.str(); +// j["val"] = {}; +// if (ev.has_value()) { +// j["val"].push_back(ev.value()); +// } +// return j; +// } +// +// json jsonify(qasmtools::ast::Gate& g) { +// using namespace qasmtools::ast; +// json j; +// j["type"] = "Gate"; +// if (UGate* gd = dynamic_cast(&g)) { +// j["name"] = "UGate"; +// j["qargs"] = {jsonify(gd->arg())}; +// j["cargs"] = {jsonify(gd->theta()), jsonify(gd->phi()), +// jsonify(gd->lambda())}; +// } else if (CNOTGate* gd = dynamic_cast(&g)) { +// j["name"] = "CNOTGate"; +// j["qargs"] = {jsonify(gd->ctrl()), jsonify(gd->tgt())}; +// } else if (BarrierGate* gd = dynamic_cast(&g)) { +// j["name"] = "BarrierGate"; +// j["qargs"] = {}; +// gd->foreach_arg( +// [&j](VarAccess& va) { j["qargs"].push_back(jsonify(va)); }); +// } else if (DeclaredGate* gd = dynamic_cast(&g)) { +// j["name"] = gd->name(); +// j["qargs"] = {}; +// j["cargs"] = {}; +// gd->foreach_qarg( +// [&j](VarAccess& va) { j["qargs"].push_back(jsonify(va)); }); +// gd->foreach_carg([&j](Expr& va) { j["cargs"].push_back(jsonify(va)); +// }); +// } else { +// throw ""; +// } +// return j; +// } +// +// json jsonify(qasmtools::ast::GateDecl& gd) { +// using namespace qasmtools::ast; +// json j; +// j["type"] = "GateDecl"; +// j["name"] = gd.id(); +// j["q_params"] = gd.q_params(); +// j["c_params"] = gd.c_params(); +// j["body"] = {}; // process the body of a GateDecl; +// gd.foreach_stmt([&j](Stmt& st) { j["body"].push_back(jsonify(st)); }); +// return j; +// } +// +// json jsonify(qasmtools::ast::Stmt& st) { +// using namespace qasmtools::ast; +// if (GateDecl* gd = dynamic_cast(&st)) { +// return jsonify(*gd); +// } else if (Gate* gd = dynamic_cast(&st)) { +// return jsonify(*gd); +// } else if (Gate* gd = dynamic_cast(&st)) { +// return jsonify(*gd); +// } else if (MeasureStmt* gd = dynamic_cast(&st)) { +// return jsonify(*gd); +// } else if (ResetStmt* gd = dynamic_cast(&st)) { +// return jsonify(*gd); +// } else if (OracleDecl* gd = dynamic_cast(&st)) { +// return jsonify(*gd); +// } else if (RegisterDecl* gd = dynamic_cast(&st)) { +// return jsonify(*gd); +// } else if (AncillaDecl* gd = dynamic_cast(&st)) { +// return jsonify(*gd); +// } else if (IfStmt* gd = dynamic_cast(&st)) { +// return jsonify(*gd); +// } else { +// throw ""; +// } +// } +// +// json jsonify(qasmtools::ast::Program& p) { +// using namespace qasmtools::ast; +// json j; +// p.foreach_stmt([&](Stmt& st) { j.push_back(jsonify(st)); }); +// return j; +// } + +int main(int argc, char** argv) { + // using namespace staq; + using qasmtools::parser::parse_file; + + if (argc == 1) { + std::cout << "Usage: staq [PASSES/OPTIONS] FILE.qasm\n" + << "Run with --help for more information.\n"; + return 0; + } + std::string input_qasm; + + CLI::App app{"staq -- A full-stack quantum processing toolkit"}; + app.allow_extras(); + + app.add_option("FILE.qasm", input_qasm, "OpenQASM circuit") + ->required() + ->check(CLI::ExistingFile); + + CLI11_PARSE(app, argc, argv); + try { + auto prog = parse_file(input_qasm); // Default std_include=true + if (!prog) { + // This case might not be reached if parse_file throws on error, + // but good to keep for parsers that might return nullptr. + std::cerr << "Error: failed to parse \"" << input_qasm + << "\" (parser returned null).\n"; + return 1; + } + + // was working before: + // json jsonified = jsonify(*prog); + // std::cout << jsonified.dump() << std::endl; + + staq::output::JSONOutputter jo; + prog->accept(jo); + std::cout << jo.json_val().dump() << std::endl; + return 0; + } catch (const qasmtools::parser::ParseError& e) { + std::cerr << "ParseError: " << e.what() << std::endl; + std::cerr + << "Parsing failed. The error messages above this one (if any) " + "from the parser provide details about the location of the " + "syntax error(s) in the QASM file." + << std::endl; + return 1; + } catch (const std::exception& e) { + std::cerr << "An unexpected error occurred: " << e.what() << std::endl; + return 1; + } + + return 0; +}