From fd65df27b5cac0ba93f0b1ed1847b237f48e2901 Mon Sep 17 00:00:00 2001 From: Vlad Gheorghiu Date: Tue, 6 Feb 2024 12:37:44 -0500 Subject: [PATCH] Transformation fixes (#73) * update error messages * rename replace_ugate to replace_ugates * avoid conflating u1 and rz gates * add group_qregs transformation * add comments * clone exprs to avoid precision loss --------- Co-authored-by: Kevin Guo --- include/output/ionq.hpp | 6 +- include/transformations/group_qregs.hpp | 151 ++++++++++++++++++ include/transformations/qasm_synth.hpp | 2 +- .../{replace_ugate.hpp => replace_ugates.hpp} | 49 +++--- src/tools/group_qregs.cpp | 41 +++++ src/tools/ionq.cpp | 12 +- 6 files changed, 226 insertions(+), 35 deletions(-) create mode 100644 include/transformations/group_qregs.hpp rename include/transformations/{replace_ugate.hpp => replace_ugates.hpp} (79%) create mode 100644 src/tools/group_qregs.cpp diff --git a/include/output/ionq.hpp b/include/output/ionq.hpp index e482eb63..b8641d57 100644 --- a/include/output/ionq.hpp +++ b/include/output/ionq.hpp @@ -44,8 +44,10 @@ namespace output { namespace ast = qasmtools::ast; /** \brief Equivalent IonQ standard gates for qasm standard gates */ -std::unordered_map qasmstd_to_ionq{ - {"sdg", "si"}, {"tdg", "ti"}, {"u1", "rz"}}; +std::unordered_map qasmstd_to_ionq{{"sdg", "si"}, + {"tdg", "ti"}}; +// Note: u1 and rz gates are equivalent up to a global phase. This substitution +// has been left out for now; unsure if global phase matters in IonQ. /** * \class staq::output::IonQOutputter diff --git a/include/transformations/group_qregs.hpp b/include/transformations/group_qregs.hpp new file mode 100644 index 00000000..a96c373f --- /dev/null +++ b/include/transformations/group_qregs.hpp @@ -0,0 +1,151 @@ +/* + * 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/group_qregs.hpp + * \brief Group individual qregs into one global register. + */ + +/** + * The classes in this file transformations::LayoutTransformer and + * transformations::BasicLayout are slightly modified versions of + * mapping::LayoutTransformer and mapping::BasicLayout, as defined in + * mapping/layout/basic.hpp. + */ + +#ifndef TRANSFORMATIONS_GROUP_QREGS_HPP_ +#define TRANSFORMATIONS_GROUP_QREGS_HPP_ + +#include "qasmtools/ast/replacer.hpp" +#include "qasmtools/ast/traversal.hpp" +#include "transformations/substitution.hpp" + +namespace staq { +namespace transformations { + +namespace ast = qasmtools::ast; +namespace parser = qasmtools::parser; + +using layout = std::unordered_map; + +/** + * \class staq::transformations::LayoutTransformer + * \brief Applies a hardware layout to a circuit + * + * Accepts a layout -- that is, a mapping from variable + * accesses to addresses of physical qubits -- and rewrites + * the AST so that all variable accesses refer to the + * relevant address of a global register representing + * the physical qubits + */ +class LayoutTransformer final : public ast::Replacer { + public: + /** + * \class staq::mapping::LayoutTransformer::config + * \brief Holds configuration options + */ + struct config { + std::string register_name = "q"; + }; + + LayoutTransformer() = default; + LayoutTransformer(const config& params) : Replacer(), config_(params) {} + ~LayoutTransformer() = default; + + /** \brief Main transformation method */ + void run(ast::Program& prog, const layout& l) { + // Visit entire program, removing register declarations, then + // add the physical register & apply substitutions + prog.accept(*this); + + // Physical register declaration + prog.body().emplace_front( + std::make_unique(ast::RegisterDecl( + prog.pos(), config_.register_name, true, l.size()))); + + // Substitution + std::unordered_map subst; + for (auto const& [access, idx] : l) { + subst.insert({access, ast::VarAccess(parser::Position(), + config_.register_name, idx)}); + } + transformations::subst_ap_ap(subst, prog); + } + + std::optional>> + replace(ast::RegisterDecl& decl) override { + if (decl.is_quantum()) + return std::list>(); + else + return std::nullopt; + } + + private: + config config_; +}; + +/** + * \class staq::transformations::BasicLayout + * \brief A simple layout generation algorithm + * + * Allocates physical qubits on a first-come, first-serve basis + */ +class BasicLayout final : public ast::Traverse { + public: + BasicLayout() : Traverse() {} + ~BasicLayout() = default; + + /** \brief Main generation method */ + layout generate(ast::Program& prog) { + current_ = layout(); + prog.accept(*this); + return current_; + } + + void visit(ast::RegisterDecl& decl) override { + if (decl.is_quantum()) { + for (auto i = 0; i < decl.size(); i++) { + current_[ast::VarAccess(parser::Position(), decl.id(), i)] = + static_cast(current_.size()); + } + } + } + + private: + layout current_; +}; + +inline void group_qregs(ast::Program& prog) { + BasicLayout gen; + layout l = gen.generate(prog); + LayoutTransformer alg; + alg.run(prog, l); +} + +} // namespace transformations +} /* namespace staq */ + +#endif /* TRANSFORMATIONS_GROUP_QREGS_HPP_ */ diff --git a/include/transformations/qasm_synth.hpp b/include/transformations/qasm_synth.hpp index 62889b6a..7272d4bb 100644 --- a/include/transformations/qasm_synth.hpp +++ b/include/transformations/qasm_synth.hpp @@ -80,7 +80,7 @@ class QASMSynthImpl final : public ast::Replacer { std::optional expr_val = theta_arg.constant_eval_gmp(); if (!expr_val) { std::cerr << gate.pos() << ": VarExpr found in classical arg0, " - << "please inline the code.\n"; + << "please inline the code first.\n"; throw qasmtools::parser::ParseError(); } real_t angle = expr_val.value(); diff --git a/include/transformations/replace_ugate.hpp b/include/transformations/replace_ugates.hpp similarity index 79% rename from include/transformations/replace_ugate.hpp rename to include/transformations/replace_ugates.hpp index 8b3b77b3..c4ee6f02 100644 --- a/include/transformations/replace_ugate.hpp +++ b/include/transformations/replace_ugates.hpp @@ -25,12 +25,12 @@ */ /** - * \file transformations/replace_ugate.hpp + * \file transformations/replace_ugates.hpp * \brief Replacing common U gates with QE standard gates */ -#ifndef TRANSFORMATIONS_REPLACE_UGATE_HPP_ -#define TRANSFORMATIONS_REPLACE_UGATE_HPP_ +#ifndef TRANSFORMATIONS_REPLACE_UGATES_HPP_ +#define TRANSFORMATIONS_REPLACE_UGATES_HPP_ #include #include @@ -44,7 +44,7 @@ namespace transformations { namespace ast = qasmtools::ast; /** - * \brief Replacing UGates + * \brief Replace UGates * * Visits an AST and replaces common U gates with QE standard * gates if possible. Assumes qelib1.inc is included. @@ -60,6 +60,9 @@ struct UArgs { double lambda; }; +/** + * List of replacements to make, e.g. replace U(pi,0,pi) with x. + */ // clang-format off static const std::vector> standard_gates{ {{pi, 0, pi}, "x"}, @@ -74,10 +77,10 @@ static const std::vector> standard_gates{ // clang-format on /* Implementation */ -class ReplaceUGateImpl final : public ast::Replacer { +class ReplaceUGatesImpl final : public ast::Replacer { public: - ReplaceUGateImpl() = default; - ~ReplaceUGateImpl() = default; + ReplaceUGatesImpl() = default; + ~ReplaceUGatesImpl() = default; void run(ast::ASTNode& node) { node.accept(*this); } @@ -101,11 +104,10 @@ class ReplaceUGateImpl final : public ast::Replacer { 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" + } catch (const std::bad_optional_access& e) { + std::cerr << "error: VarExpr found in UGate args, please inline " + "the code first." << "\n"; - // this should never happen; if this is reached then the Replacer - // is recurring too far down the AST throw; } @@ -127,20 +129,19 @@ class ReplaceUGateImpl final : public ast::Replacer { // 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(gate.pos(), lambda)); + name = "rz"; // U(0,0,lambda) = rz(lambda) + // assumes rz == u1; ignores the global phase + c_args.emplace_back(std::unique_ptr( + ast::object::clone(gate.lambda()))); } else if (std::abs(phi) < EPS && std::abs(lambda) < EPS) { - name = "ry"; - c_args.emplace_back( - std::make_unique(gate.pos(), theta)); + name = "ry"; // U(theta,0,0) = ry(theta) + c_args.emplace_back(std::unique_ptr( + ast::object::clone(gate.theta()))); } else if (std::abs(phi + pi / 2) < EPS && std::abs(lambda - pi / 2) < EPS) { - name = "rx"; - c_args.emplace_back( - std::make_unique(gate.pos(), theta)); + name = "rx"; // U(theta,-pi/2,pi/2) = rx(theta) + c_args.emplace_back(std::unique_ptr( + ast::object::clone(gate.theta()))); } } @@ -161,11 +162,11 @@ class ReplaceUGateImpl final : public ast::Replacer { }; void replace_ugates(ast::ASTNode& node) { - ReplaceUGateImpl alg; + ReplaceUGatesImpl alg; alg.run(node); } } /* namespace transformations */ } /* namespace staq */ -#endif /* TRANSFORMATIONS_REPLACE_UGATE_HPP_ */ +#endif /* TRANSFORMATIONS_REPLACE_UGATES_HPP_ */ diff --git a/src/tools/group_qregs.cpp b/src/tools/group_qregs.cpp new file mode 100644 index 00000000..a841d2f0 --- /dev/null +++ b/src/tools/group_qregs.cpp @@ -0,0 +1,41 @@ +/* + * 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 "qasmtools/parser/parser.hpp" +#include "transformations/group_qregs.hpp" + +int main() { + using namespace staq; + using qasmtools::parser::parse_stdin; + + auto program = parse_stdin(); + if (program) { + transformations::group_qregs(*program); + std::cout << *program; + } else { + std::cerr << "Parsing failed\n"; + } +} diff --git a/src/tools/ionq.cpp b/src/tools/ionq.cpp index 6d7d364a..bb7d1678 100644 --- a/src/tools/ionq.cpp +++ b/src/tools/ionq.cpp @@ -32,12 +32,13 @@ #include "qasmtools/parser/parser.hpp" #include "transformations/desugar.hpp" #include "transformations/expression_simplifier.hpp" +#include "transformations/group_qregs.hpp" #include "transformations/inline.hpp" -#include "transformations/replace_ugate.hpp" +#include "transformations/replace_ugates.hpp" static const std::set ionq_overrides{ "x", "y", "z", "h", "s", "sdg", "t", "tdg", "rx", - "ry", "rz", "cz", "cy", "swap", "cx", "u1", "ch", "crz"}; + "ry", "rz", "cz", "cy", "swap", "cx", "ch", "crz"}; int main(int argc, char** argv) { using namespace staq; @@ -56,12 +57,7 @@ int main(int argc, char** argv) { 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); + transformations::group_qregs(*program); // Inline declared gates transformations::Inliner::config params{false, ionq_overrides};