use std::{
env, error,
fs::File,
io,
path::{Path, PathBuf},
};
use bicycle_cliffords::{
MeasurementChoices, MeasurementTableBuilder, native_measurement::NativeMeasurement,
};
use bicycle_compiler::language::{AnglePrecision, PbcOperation};
use io::Write;
use bicycle_compiler::{PathArchitecture, optimize};
use clap::{Parser, Subcommand};
use log::{debug, info};
use serde_json::Deserializer;
#[derive(Parser)]
#[command(version, about, long_about=None)]
struct Cli {
code: MeasurementChoices,
#[command(subcommand)]
commands: Option<Commands>,
#[arg(long)]
measurement_table: Option<String>,
#[arg(short, long, default_value_t = AnglePrecision::lit("1e-9"))]
accuracy: AnglePrecision,
}
#[derive(Subcommand, Clone, PartialEq, Eq)]
enum Commands {
Generate {
measurement_table: String,
},
}
fn main() -> Result<(), Box<dyn error::Error>> {
if env::var("RUST_LOG").is_err() {
unsafe { env::set_var("RUST_LOG", "info") };
}
env_logger::init();
let cli = Cli::parse();
if let Some(Commands::Generate {
measurement_table: cache_str,
}) = cli.commands
{
info!("Generating measurement table.");
let cache_path = Path::new(&cache_str);
match cache_path.parent() {
Some(cache_dir) => {
let temp_filename = "dummy_file_check";
let mut temp_file_path = PathBuf::from(cache_dir);
temp_file_path.push(temp_filename);
match File::create(&temp_file_path) {
Ok(_) => {
std::fs::remove_file(temp_file_path)?;
}
Err(e) => {
eprintln!(
"Cannot create measurement_table output file in the target directory: {e}"
);
std::process::exit(1);
}
}
}
None => {
eprintln!("No parent directory found for {cache_str}");
std::process::exit(1);
}
}
let mut builder =
MeasurementTableBuilder::new(NativeMeasurement::all(), cli.code.measurement());
builder.build();
let measurement_table = builder.complete()?;
let serialized =
bitcode::serialize(&measurement_table).expect("The table should be serializable");
info!("Done generating measurement table, writing.");
let f = File::create(cache_path);
match f {
Ok(mut f) => {
f.write_all(&serialized)
.expect("The serialized table should be writable to the cache");
}
Err(e) => {
eprintln!(
"Cannot create measurement_table output file in the target directory: {e}"
);
std::process::exit(1);
}
}
info!("Done writing measurement table, exiting.");
std::process::exit(0);
}
let measurement_table = if let Some(cache_str) = cli.measurement_table {
let cache_path = Path::new(&cache_str);
bicycle_compiler::deserialize_table(cache_path)?
} else {
let mut builder =
MeasurementTableBuilder::new(NativeMeasurement::all(), cli.code.measurement());
builder.build();
builder.complete()?
};
let reader = io::stdin().lock();
let de = Deserializer::from_reader(reader);
let ops = de.into_iter::<PbcOperation>().map(|op| op.unwrap());
let mut ops = ops.peekable();
let first_op = ops.peek();
let architecture = if let Some(op) = first_op {
PathArchitecture::for_qubits(op.basis().len())
} else {
return Ok(());
};
let compiled = ops.map(|op| op.compile(&architecture, &measurement_table, cli.accuracy));
let optimized_auts = compiled.map(optimize::remove_trivial_automorphisms);
let mut optimized_chunked_ops = optimize::remove_duplicate_measurements_chunked(optimized_auts);
let mut stdout = io::stdout();
let err: Result<(), io::Error> = optimized_chunked_ops.try_for_each(|chunk| {
let out = serde_json::to_string(&chunk)?;
writeln!(stdout, "{out}")
});
debug!("Encountered error while writing to stdout: {err:?}");
Ok(())
}