use clap::Parser;
use inquire::{error::InquireError, Select};
use std::{fmt::Display, vec};
use quaru::{
operation::{self, Operation},
register::Register,
};
enum Choice {
Show,
Apply,
Measure,
Exit,
}
impl Choice {
fn choices() -> Vec<Choice> {
vec![Choice::Show, Choice::Apply, Choice::Measure, Choice::Exit]
}
}
impl Display for Choice {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Choice::Apply => write!(f, "Apply"),
Choice::Measure => write!(f, "Measure"),
Choice::Show => write!(f, "Show"),
Choice::Exit => write!(f, "Exit"),
}
}
}
#[derive(Debug)]
enum OperationType {
Unary,
Binary,
}
impl OperationType {
fn types() -> Vec<OperationType> {
vec![OperationType::Unary, OperationType::Binary]
}
fn size(&self) -> usize {
match self {
OperationType::Unary => 1,
OperationType::Binary => 2,
}
}
}
impl Display for OperationType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
OperationType::Unary => write!(f, "Unary"),
OperationType::Binary => write!(f, "Binary"),
}
}
}
enum UnaryOperation {
Identity,
Hadamard,
Phase,
Not,
PauliY,
PauliZ,
}
impl UnaryOperation {
fn operations() -> Vec<UnaryOperation> {
vec![
UnaryOperation::Identity,
UnaryOperation::Hadamard,
UnaryOperation::Phase,
UnaryOperation::Not,
UnaryOperation::PauliY,
UnaryOperation::PauliZ,
]
}
}
impl Display for UnaryOperation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
UnaryOperation::Identity => write!(f, "Identity"),
UnaryOperation::Hadamard => write!(f, "Hadamard"),
UnaryOperation::Phase => write!(f, "Phase"),
UnaryOperation::Not => write!(f, "NOT"),
UnaryOperation::PauliY => write!(f, "Pauli Y"),
UnaryOperation::PauliZ => write!(f, "Pauli Z"),
}
}
}
fn unary_operation_target_name(_: &UnaryOperation) -> [&str; 1] {
["target"]
}
enum BinaryOperation {
CNot,
Swap,
}
impl BinaryOperation {
fn operations() -> Vec<BinaryOperation> {
vec![BinaryOperation::CNot, BinaryOperation::Swap]
}
}
impl Display for BinaryOperation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BinaryOperation::CNot => write!(f, "CNOT"),
BinaryOperation::Swap => write!(f, "Swap"),
}
}
}
fn binary_operation_target_names(op: &BinaryOperation) -> [&str; 2] {
match *op {
BinaryOperation::CNot => ["control", "target"],
_ => ["target"; 2],
}
}
fn size_prompt(max: usize) -> Result<usize, InquireError> {
assert!(max > 0, "Register size must be atleast 1");
let options: Vec<usize> = (1..=max).collect();
Select::new("Select a register size: ", options).prompt()
}
fn init_prompt() -> Result<Choice, InquireError> {
let options = Choice::choices();
Select::new("Select an option: ", options).prompt()
}
fn operation_prompt(size: usize) -> Result<OperationType, InquireError> {
let options = OperationType::types()
.into_iter()
.filter(|op_type| op_type.size() <= size)
.collect();
Select::new("Select an operation type: ", options).prompt()
}
fn unary_prompt() -> Result<UnaryOperation, InquireError> {
let options = UnaryOperation::operations();
Select::new("Select an operation: ", options).prompt()
}
fn binary_prompt() -> Result<BinaryOperation, InquireError> {
let options = BinaryOperation::operations();
Select::new("Select an operation: ", options).prompt()
}
fn qubit_prompt<const N: usize>(
target_names: [&str; N],
size: usize,
) -> Result<Vec<usize>, InquireError> {
assert!(
N <= size,
"Cannot call operation on more qubits than register size! ({N} > {size}"
);
let options: Vec<usize> = (0..size).collect();
let mut targets: Vec<usize> = Vec::new();
for name in target_names.iter().take(N) {
let target = Select::new(
format!("Select a {name} index: ").as_str(),
options
.clone()
.into_iter()
.filter(|o| !targets.contains(o))
.collect(),
)
.prompt()?;
targets.push(target);
}
Ok(targets)
}
fn get_unary(size: usize) -> Result<Operation, InquireError> {
let unary_op = match unary_prompt() {
Ok(op) => op,
Err(e) => panic!("Problem encountered when selecting unary operation: {e:?}"),
};
let target = match qubit_prompt(unary_operation_target_name(&unary_op), size) {
Ok(ts) => ts[0],
Err(e) => panic!("Problem encountered when selecting index: {e:?}"),
};
let op = match unary_op {
UnaryOperation::Identity => operation::identity(target),
UnaryOperation::Hadamard => operation::hadamard(target),
UnaryOperation::Phase => operation::phase(target),
UnaryOperation::Not => operation::not(target),
UnaryOperation::PauliY => operation::pauli_y(target),
UnaryOperation::PauliZ => operation::pauli_z(target),
};
Ok(op)
}
fn get_binary(size: usize) -> Result<Operation, InquireError> {
let binary_op = match binary_prompt() {
Ok(op) => op,
Err(e) => panic!("Problem encountered when selecting binary operation: {e:?}"),
};
let targets = match qubit_prompt(binary_operation_target_names(&binary_op), size) {
Ok(ts) => ts,
Err(e) => panic!("Problem encountered when selecting index: {e:?}"),
};
let a = targets[0];
let b = targets[1];
let op = match binary_op {
BinaryOperation::CNot => operation::cnot(a, b),
BinaryOperation::Swap => operation::swap(a, b),
};
Ok(op)
}
fn handle_apply(reg: &mut Register) {
let op_type = match operation_prompt(reg.size()) {
Ok(op_type) => op_type,
Err(e) => panic!("Problem encountered during operation type selection: {e:?}"),
};
let result = match op_type {
OperationType::Unary => get_unary(reg.size()),
OperationType::Binary => get_binary(reg.size()),
};
match result {
Ok(op) => reg.apply(&op),
Err(e) => panic!("Problem encountered when applying operation: {e:?}"),
};
}
fn handle_measure(reg: &mut Register) {
let index = match qubit_prompt(["target"], reg.size()) {
Ok(ts) => ts[0],
Err(e) => panic!("Problem encountered when selecting a qubit: {e:?}"),
};
let result = reg.measure(index);
println!("Qubit at index {index} measured {result}");
}
fn handle_show(reg: &Register) {
println!("Register of size: {}", reg.size());
reg.print_state();
}
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
#[arg(short, long)]
size: Option<usize>,
}
fn main() {
let args = Args::parse();
println!("{QUARU}");
let size = if let Some(n) = args.size {
n
} else {
match size_prompt(4) {
Ok(size) => size,
Err(e) => panic!("Problem when selecting a register size: {e:?}"),
}
};
let init_state = &[false].repeat(size);
let mut reg = Register::new(init_state.as_slice());
print!("{esc}[2J{esc}[1;1H", esc = 27 as char);
loop {
let init = match init_prompt() {
Ok(choice) => choice,
Err(e) => panic!("Problem selecting an option: {e:?}"),
};
print!("{esc}[2J{esc}[1;1H", esc = 27 as char);
match init {
Choice::Show => handle_show(®),
Choice::Apply => handle_apply(&mut reg),
Choice::Measure => handle_measure(&mut reg),
Choice::Exit => break,
};
}
}
const QUARU: &str = "
______ __ __ ___ .______ __ __
/ __ \\ | | | | / \\ | _ \\ | | | |
| | | | | | | | / ^ \\ | |_) | | | | |
| | | | | | | | / /_\\ \\ | / | | | |
| `--' '--.| `--' | / _____ \\ | |\\ \\----.| `--' |
\\_____\\_____\\\\______/ /__/ \\__\\ | _| `._____| \\______/
";