Skip to content

Commit

Permalink
Ion q transpiler (#68)
Browse files Browse the repository at this point in the history
* fix gmpf::exp stability issue

* Fix typo

* Formatting

* Fix stability issue for gmpf::exp negative vals

* Create ionq files

* write IonQ transpiler; initial iteration

* remove references to M_PI

---------

Co-authored-by: Kevin Guo <[email protected]>
  • Loading branch information
vsoftco and Kevin Guo authored Nov 28, 2023
1 parent 309e95f commit 8bf5aa4
Show file tree
Hide file tree
Showing 11 changed files with 571 additions and 12 deletions.
8 changes: 8 additions & 0 deletions examples/ionq_test.qasm
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];
19 changes: 12 additions & 7 deletions include/grid_synth/gmp_functions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,14 +210,15 @@ inline mpf_class reduce_angle(const mpf_class& phi) {
inline mpf_class sin(const mpf_class& theta) {
long int initial_prec = theta.get_prec();
long int tol_exp = std::log10(2) * initial_prec;
mpf_class eps(("1e-" + std::to_string(tol_exp)));
mpf_class phi = reduce_angle(theta);
mpz_class i(1);
mpf_class lasts(0);
mpf_class s = phi;
mpf_class fact(1);
mpf_class num(phi);
mpf_class sign(1);
while (gmp_abs(s - lasts) > mpf_class("1e-" + std::to_string(tol_exp))) {
while (gmp_abs(s - lasts) > eps) {
lasts = s;
i += mpf_class("2");
fact *= i * (i - mpf_class("1"));
Expand All @@ -231,14 +232,15 @@ inline mpf_class sin(const mpf_class& theta) {
inline mpf_class cos(const mpf_class& theta) {
// long int initial_prec = theta.get_prec();
long int tol_exp = std::log10(2) * theta.get_prec();
mpf_class eps(("1e-" + std::to_string(tol_exp)));
mpf_class phi = reduce_angle(theta);
mpz_class i(0);
mpf_class lasts(0);
mpf_class s(1);
mpf_class fact(1);
mpf_class num(1);
mpf_class sign(1);
while (gmp_abs(s - lasts) > mpf_class("1e-" + std::to_string(tol_exp))) {
while (gmp_abs(s - lasts) > eps) {
lasts = s;
i += mpf_class("2");
fact *= i * (i - mpf_class("1"));
Expand All @@ -250,16 +252,19 @@ inline mpf_class cos(const mpf_class& theta) {
}

inline mpf_class exp(const mpf_class& x) {
// TODO: fix stability issue when x negative
if (x < 0)
return 1 / gmpf::exp(-x);
long int tol_exp = std::log10(2) * x.get_prec();
mpz_class i = 0;
mpf_class eps(("1e-" + std::to_string(tol_exp)));
mpz_class i = 1;
mpf_class s = 1;
mpf_class term = 1;
while (gmp_abs(term) > mpf_class("1e-" + std::to_string(tol_exp))) {
mpf_class term = x;
// Use Taylor's remainder theorem bound on error
while (gmp_abs(term*s) > eps) {
s += term;
i += 1;
term *= x;
term /= i;
s += term;
}
return s;
}
Expand Down
2 changes: 1 addition & 1 deletion include/output/cirq.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ class CirqOutputter final : public ast::Visitor {
}
};

/** \brief Writes an AST in Cirq format to a stdout */
/** \brief Writes an AST in Cirq format to stdout */
void output_cirq(ast::Program& prog) {
CirqOutputter outputter(std::cout);
outputter.run(prog);
Expand Down
243 changes: 243 additions & 0 deletions include/output/ionq.hpp
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_ */
2 changes: 1 addition & 1 deletion include/output/projectq.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ class ProjectQOutputter final : public ast::Visitor {
}
};

/** \brief Writes an AST in ProjectQ format to a stdout */
/** \brief Writes an AST in ProjectQ format to stdout */
void output_projectq(ast::Program& prog) {
ProjectQOutputter outputter(std::cout);
outputter.run(prog);
Expand Down
2 changes: 1 addition & 1 deletion include/output/qsharp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ class QSharpOutputter final : public ast::Visitor {
bool ambiguous_ = false;
};

/** \brief Writes an AST in Q# format to a stdout */
/** \brief Writes an AST in Q# format to stdout */
void output_qsharp(ast::Program& prog) {
QSharpOutputter outputter(std::cout);
outputter.run(prog);
Expand Down
2 changes: 1 addition & 1 deletion include/output/quil.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ class QuilOutputter final : public ast::Visitor {
std::unordered_map<ast::symbol, std::pair<int, int>> globals_{};
};

/** \brief Writes an AST in Quil format to a stdout */
/** \brief Writes an AST in Quil format to stdout */
void output_quil(ast::Program& prog) {
QuilOutputter outputter(std::cout);
outputter.run(prog);
Expand Down
Loading

0 comments on commit 8bf5aa4

Please sign in to comment.