use crate::{
circuit::Circuit,
compiler::ir::InstructionIR,
components::gate::Gate,
components::{
measurement::MeasurementOperation,
operator::{
CNOT, Hadamard, Identity, Pauli, PhaseS, PhaseSdag, PhaseShift, PhaseT, PhaseTdag,
RotateX, RotateY, RotateZ, SWAP, Toffoli, Unitary2,
},
},
errors::CompilerError,
};
use dyn_clone::DynClone;
use rayon::prelude::*;
use std::convert::TryFrom;
pub trait Compilable: DynClone + Send + Sync + 'static {
fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR>;
}
dyn_clone::clone_trait_object!(Compilable);
pub(crate) struct CompilableCircuit {
pub num_qubits: usize,
pub gates: Vec<CompilableGate>,
}
impl CompilableCircuit {
pub(crate) fn to_ir(&self) -> Vec<InstructionIR> {
self.gates
.par_iter()
.flat_map(|gate| {
gate.operator
.to_ir(gate.targets.clone(), gate.controls.clone())
})
.collect()
}
}
pub(crate) struct CompilableGate {
pub operator: Box<dyn Compilable>,
pub targets: Vec<usize>,
pub controls: Vec<usize>,
}
impl TryFrom<&Circuit> for CompilableCircuit {
type Error = CompilerError;
fn try_from(circuit: &Circuit) -> Result<Self, CompilerError> {
let num_qubits: usize = circuit.get_num_qubits();
let mut compilable_gates: Vec<CompilableGate> = Vec::with_capacity(circuit.gates.len() * 2);
for gate in &circuit.gates {
match gate {
Gate::Operator(op, _targets, _controls) => {
if let Some(compilable_op) = op.to_compilable() {
let targets = _targets.clone();
let controls = _controls.clone();
let operator = dyn_clone::clone_box(compilable_op);
let gate: CompilableGate = CompilableGate {
operator,
targets,
controls,
};
compilable_gates.push(gate);
} else {
return Err(CompilerError::UnsupportedOperator(
"Operator does not implement Compilable trait".to_string(),
));
}
}
Gate::Measurement(measurement_basis, targets) => {
let measurement_op = MeasurementOperation {
basis: *measurement_basis,
};
let gate = CompilableGate {
operator: Box::new(measurement_op),
targets: targets.clone(),
controls: vec![],
};
compilable_gates.push(gate);
},
Gate::Parametric(_, _, _) => {
unreachable!("All parametric gates should be converted to concrete gates before compilation")
},
Gate::PauliString(_) => {
unreachable!("All Pauli strings should be converted to Pauli gates before compilation")
},
Gate::PauliTimeEvolution(_, _) => {
unimplemented!("Compilation for Pauli time evolution gates is not yet implemented")
}
}
}
Ok(CompilableCircuit {
num_qubits,
gates: compilable_gates,
})
}
}
impl Compilable for Hadamard {
fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
targets
.par_iter()
.map(|&target| InstructionIR::Hadamard(target, controls.clone()))
.collect()
}
}
impl Compilable for Pauli {
fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
targets
.par_iter()
.map(|&target| match self {
Pauli::X => InstructionIR::PauliX(target, controls.clone()),
Pauli::Y => InstructionIR::PauliY(target, controls.clone()),
Pauli::Z => InstructionIR::PauliZ(target, controls.clone()),
})
.collect()
}
}
impl Compilable for CNOT {
fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
targets
.par_iter()
.map(|&target| {
if controls.is_empty() {
InstructionIR::PauliX(target, vec![])
} else {
InstructionIR::PauliX(target, vec![controls[0]])
}
})
.collect()
}
}
impl Compilable for SWAP {
fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
if targets.len() >= 2 {
targets
.par_chunks(2)
.filter_map(|chunk| {
if chunk.len() == 2 {
Some(InstructionIR::Swap(chunk[0], chunk[1], controls.clone()))
} else {
None
}
})
.collect()
} else {
vec![]
}
}
}
impl Compilable for Toffoli {
fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
targets
.par_iter()
.map(|&target| {
InstructionIR::PauliX(target, controls.clone())
})
.collect()
}
}
impl Compilable for Identity {
fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
targets
.par_iter()
.map(|&target| InstructionIR::Id(target, controls.clone()))
.collect()
}
}
impl Compilable for PhaseS {
fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
targets
.par_iter()
.map(|&target| InstructionIR::S(target, controls.clone()))
.collect()
}
}
impl Compilable for PhaseT {
fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
targets
.par_iter()
.map(|&target| InstructionIR::T(target, controls.clone()))
.collect()
}
}
impl Compilable for PhaseSdag {
fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
targets
.par_iter()
.map(|&target| InstructionIR::Sdg(target, controls.clone()))
.collect()
}
}
impl Compilable for PhaseTdag {
fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
targets
.par_iter()
.map(|&target| InstructionIR::Tdg(target, controls.clone()))
.collect()
}
}
impl Compilable for PhaseShift {
fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
targets
.par_iter()
.map(|&target| InstructionIR::Phase(self.angle, target, controls.clone()))
.collect()
}
}
impl Compilable for RotateX {
fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
targets
.par_iter()
.map(|&target| InstructionIR::Rx(self.angle, target, controls.clone()))
.collect()
}
}
impl Compilable for RotateY {
fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
targets
.par_iter()
.map(|&target| InstructionIR::Ry(self.angle, target, controls.clone()))
.collect()
}
}
impl Compilable for RotateZ {
fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
targets
.par_iter()
.map(|&target| InstructionIR::Rz(self.angle, target, controls.clone()))
.collect()
}
}
impl Compilable for Unitary2 {
fn to_ir(&self, targets: Vec<usize>, controls: Vec<usize>) -> Vec<InstructionIR> {
targets
.par_iter()
.map(|&target| InstructionIR::Unitary(self.matrix, target, controls.clone()))
.collect()
}
}