mod decoder;
pub mod encoder;
pub mod extension;
pub use encoder::{default_encoder_config, Tk1EncoderConfig, Tk1EncoderContext};
pub use extension::PytketEmitter;
use hugr::core::HugrNode;
use hugr::{Hugr, Wire};
use itertools::Itertools;
#[cfg(test)]
mod tests;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::path::Path;
use std::{fs, io};
use hugr::ops::OpType;
use derive_more::{Display, Error, From};
use tket_json_rs::circuit_json::SerialCircuit;
use tket_json_rs::register::{Bit, ElementId, Qubit};
use crate::circuit::Circuit;
use self::decoder::Tk1DecoderContext;
pub use crate::passes::pytket::lower_to_pytket;
pub const METADATA_PREFIX: &str = "TKET1";
const METADATA_PHASE: &str = "TKET1.phase";
const METADATA_Q_REGISTERS: &str = "TKET1.qubit_registers";
const METADATA_Q_OUTPUT_REGISTERS: &str = "TKET1.qubit_output_registers";
const METADATA_B_REGISTERS: &str = "TKET1.bit_registers";
const METADATA_B_OUTPUT_REGISTERS: &str = "TKET1.bit_output_registers";
const METADATA_OPGROUP: &str = "TKET1.opgroup";
const METADATA_INPUT_PARAMETERS: &str = "TKET1.input_parameters";
pub trait TKETDecode: Sized {
type DecodeError;
type EncodeError;
fn decode(self) -> Result<Circuit, Self::DecodeError>;
fn encode(circuit: &Circuit) -> Result<Self, Self::EncodeError>;
fn encode_with_config(
circuit: &Circuit,
config: Tk1EncoderConfig<Hugr>,
) -> Result<Self, Self::EncodeError>;
}
impl TKETDecode for SerialCircuit {
type DecodeError = Tk1ConvertError;
type EncodeError = Tk1ConvertError;
fn decode(self) -> Result<Circuit, Self::DecodeError> {
let mut decoder = Tk1DecoderContext::try_new(&self)?;
if !self.phase.is_empty() {
}
for com in self.commands {
decoder.add_command(com)?;
}
Ok(decoder.finish().into())
}
fn encode(circuit: &Circuit) -> Result<Self, Self::EncodeError> {
let config = default_encoder_config();
Self::encode_with_config(circuit, config)
}
fn encode_with_config(
circuit: &Circuit,
config: Tk1EncoderConfig<Hugr>,
) -> Result<Self, Self::EncodeError> {
let mut encoder = Tk1EncoderContext::new(circuit, circuit.parent(), config)?;
encoder.run_encoder(circuit, circuit.parent())?;
let (serial, _) = encoder.finish(circuit, circuit.parent())?;
Ok(serial)
}
}
pub fn load_tk1_json_file(path: impl AsRef<Path>) -> Result<Circuit, Tk1ConvertError> {
let file = fs::File::open(path)?;
let reader = io::BufReader::new(file);
load_tk1_json_reader(reader)
}
pub fn load_tk1_json_reader(json: impl io::Read) -> Result<Circuit, Tk1ConvertError> {
let ser: SerialCircuit = serde_json::from_reader(json)?;
let circ: Circuit = ser.decode()?;
Ok(circ)
}
pub fn load_tk1_json_str(json: &str) -> Result<Circuit, Tk1ConvertError> {
let reader = json.as_bytes();
load_tk1_json_reader(reader)
}
pub fn save_tk1_json_file(circ: &Circuit, path: impl AsRef<Path>) -> Result<(), Tk1ConvertError> {
let file = fs::File::create(path)?;
let writer = io::BufWriter::new(file);
save_tk1_json_writer(circ, writer)
}
pub fn save_tk1_json_writer(circ: &Circuit, w: impl io::Write) -> Result<(), Tk1ConvertError> {
let serial_circ = SerialCircuit::encode(circ)?;
serde_json::to_writer(w, &serial_circ)?;
Ok(())
}
pub fn save_tk1_json_str(circ: &Circuit) -> Result<String, Tk1ConvertError> {
let mut buf = io::BufWriter::new(Vec::new());
save_tk1_json_writer(circ, &mut buf)?;
let bytes = buf.into_inner().unwrap();
Ok(String::from_utf8(bytes)?)
}
#[derive(Display, derive_more::Debug, Error)]
#[non_exhaustive]
#[debug(bounds(N: HugrNode))]
pub enum OpConvertError<N = hugr::Node> {
#[display("Cannot serialize tket2 operation: {op}")]
UnsupportedOpSerialization {
op: OpType,
},
#[display(
"Operation {} is missing encoded parameters. Expected at least {expected} but only \"{}\" were specified.",
optype,
params.iter().join(", "),
)]
MissingSerialisedParams {
optype: OpType,
expected: usize,
params: Vec<String>,
},
#[display(
"Operation {} is missing encoded arguments. Expected {expected_qubits} and {expected_bits}, but only \"{args:?}\" were specified.",
optype,
)]
MissingSerialisedArguments {
optype: OpType,
expected_qubits: usize,
expected_bits: usize,
args: Vec<ElementId>,
},
#[display("Could not find values associated with wire {wire}.")]
WireHasNoValues {
wire: Wire<N>,
},
#[display("Tried to register values for wire {wire}, but it already has associated values.")]
WireAlreadyHasValues {
wire: Wire<N>,
},
}
#[derive(derive_more::Debug, Display, Error, From)]
#[non_exhaustive]
#[debug(bounds(N: HugrNode))]
pub enum Tk1ConvertError<N = hugr::Node> {
#[display("Cannot encode non-dataflow region at {region} with type {optype}.")]
NonDataflowRegion {
region: N,
optype: String,
},
#[from]
OpConversionError(OpConvertError<N>),
#[display("Register {register} in the circuit has multiple indices. Tket2 does not support multi-indexed registers.")]
MultiIndexedRegister {
register: String,
},
#[display("Invalid pytket JSON. {_0}")]
#[from]
InvalidJson(serde_json::Error),
#[display("Invalid JSON encoding. {_0}")]
#[from]
InvalidJsonEncoding(std::string::FromUtf8Error),
#[display("Unable to load pytket json file. {_0}")]
#[from]
FileLoadError(io::Error),
#[display("Error while encoding operation: {msg}")]
CustomError {
msg: String,
},
}
impl<N> Tk1ConvertError<N> {
pub fn custom(msg: impl Into<String>) -> Self {
Self::CustomError { msg: msg.into() }
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
struct RegisterHash {
hash: u64,
}
impl From<&ElementId> for RegisterHash {
fn from(reg: &ElementId) -> Self {
let mut hasher = DefaultHasher::new();
reg.hash(&mut hasher);
Self {
hash: hasher.finish(),
}
}
}
impl From<&Qubit> for RegisterHash {
fn from(reg: &Qubit) -> Self {
let mut hasher = DefaultHasher::new();
reg.hash(&mut hasher);
Self {
hash: hasher.finish(),
}
}
}
impl From<&Bit> for RegisterHash {
fn from(reg: &Bit) -> Self {
let mut hasher = DefaultHasher::new();
reg.hash(&mut hasher);
Self {
hash: hasher.finish(),
}
}
}