use crate::complex::Complex64;
use crate::gate::{Gate1, Gate2};
use crate::{Error, Result};
fn gate1_close(a: &Gate1, b: &Gate1) -> bool {
(0..4).all(|k| (a.m[k] - b.m[k]).norm() < 1e-12)
}
fn no_name() -> Error {
Error::Qasm {
line: 0,
col: 0,
message: "gate has no OpenQASM 3 name; use a named gate constructor".into(),
}
}
pub fn gate_info_1q(gate: &Gate1) -> Result<(&'static str, String)> {
if *gate == Gate1::x() {
return Ok(("x", String::new()));
}
if *gate == Gate1::y() {
return Ok(("y", String::new()));
}
if *gate == Gate1::z() {
return Ok(("z", String::new()));
}
if *gate == Gate1::h() {
return Ok(("h", String::new()));
}
if *gate == Gate1::s() {
return Ok(("s", String::new()));
}
if *gate == Gate1::t() {
return Ok(("t", String::new()));
}
if *gate == Gate1::id() {
return Ok(("id", String::new()));
}
if gate.m[0].im == 0.0
&& gate.m[1].re == 0.0
&& gate.m[2].re == 0.0
&& gate.m[3].im == 0.0
&& (gate.m[0].re - gate.m[3].re).abs() < 1e-15
{
let theta = 2.0 * (-gate.m[2].im).atan2(gate.m[0].re);
if gate1_close(gate, &Gate1::rx(theta)) {
return Ok(("rx", format!("({theta:.17})")));
}
}
if gate.m[0].im == 0.0
&& gate.m[1].im == 0.0
&& gate.m[2].im == 0.0
&& gate.m[3].im == 0.0
&& (gate.m[0].re - gate.m[3].re).abs() < 1e-15
&& (gate.m[1].re + gate.m[2].re).abs() < 1e-15
{
let theta = 2.0 * gate.m[2].re.atan2(gate.m[0].re);
if gate1_close(gate, &Gate1::ry(theta)) {
return Ok(("ry", format!("({theta:.17})")));
}
}
if gate.m[1] == Complex64::ZERO && gate.m[2] == Complex64::ZERO {
let theta = -2.0 * gate.m[0].im.atan2(gate.m[0].re);
if gate1_close(gate, &Gate1::rz(theta)) {
return Ok(("rz", format!("({theta:.17})")));
}
let lambda = gate.m[3].im.atan2(gate.m[3].re);
if gate1_close(gate, &Gate1::phase(lambda)) {
return Ok(("p", format!("({lambda:.17})")));
}
}
Err(no_name())
}
pub fn gate_info_2q(gate: &Gate2) -> Result<&'static str> {
if *gate == Gate2::cnot() {
return Ok("cx");
}
if *gate == Gate2::cz() {
return Ok("cz");
}
if *gate == Gate2::swap() {
return Ok("swap");
}
Err(no_name())
}
pub fn lookup_gate_1q(name: &str, args: &[f64]) -> Result<Gate1> {
let err = |msg: String| Error::Qasm {
line: 0,
col: 0,
message: msg,
};
match (name, args.len()) {
("h", 0) => Ok(Gate1::h()),
("x", 0) => Ok(Gate1::x()),
("y", 0) => Ok(Gate1::y()),
("z", 0) => Ok(Gate1::z()),
("s", 0) => Ok(Gate1::s()),
("t", 0) => Ok(Gate1::t()),
("id", 0) => Ok(Gate1::id()),
("rx", 1) => Ok(Gate1::rx(args[0])),
("ry", 1) => Ok(Gate1::ry(args[0])),
("rz", 1) => Ok(Gate1::rz(args[0])),
("p" | "phase", 1) => Ok(Gate1::phase(args[0])),
(n, _) => Err(err(format!("unknown single-qubit gate '{n}'"))),
}
}
pub fn lookup_gate_2q(name: &str) -> Result<Gate2> {
match name {
"cx" | "cnot" | "CX" => Ok(Gate2::cnot()),
"cz" => Ok(Gate2::cz()),
"swap" => Ok(Gate2::swap()),
n => Err(Error::Qasm {
line: 0,
col: 0,
message: format!("unknown two-qubit gate '{n}'"),
}),
}
}