use super::gate::GateCategory;
use super::GateInfo;
use crate::states::{ProductState, SuperPosition};
use crate::{Circuit, Gate};
use core::iter::zip;
use num_complex::Complex;
use std::collections::HashMap;
use std::ops::{Add, Mul};
impl Circuit {
pub(super) fn simulate_with_register(&self, register: &mut SuperPosition) {
let mut qubit_counter: usize = 0;
let number_gates: usize = self.circuit_gates.len();
let mut categorised_gates: Vec<GateCategory> = Vec::with_capacity(number_gates);
for gate in &self.circuit_gates {
categorised_gates.push(Gate::linker(gate));
}
if self.config_progress {
println!("Starting circuit simulation...");
}
for (cat_gate, gate) in zip(categorised_gates, &self.circuit_gates) {
if cat_gate == GateCategory::Identity {
qubit_counter += 1;
continue;
}
let gate_pos: usize = qubit_counter % self.num_qubits;
if self.config_progress {
Self::print_circuit_log(gate, &gate_pos, &qubit_counter, &number_gates);
}
let gate_to_apply: GateInfo = GateInfo {
cat_gate,
position: gate_pos,
};
Circuit::apply_gate(gate_to_apply, register);
qubit_counter += 1;
}
}
pub(super) fn apply_gate(gate: GateInfo, register: &mut SuperPosition) {
let mut mapped_states: HashMap<ProductState, Complex<f64>> = Default::default();
let mut untouched_states: HashMap<ProductState, Complex<f64>> = Default::default();
for (prod_state, amp) in register.into_iter() {
let mut acting_positions: Vec<usize> = Vec::<usize>::with_capacity(3);
let wrapped_super_pos: Option<SuperPosition> = match gate.cat_gate {
GateCategory::Identity => None,
GateCategory::Single(func) => Some(func(prod_state.get_qubits()[gate.position])),
GateCategory::SingleArg(arg, func) => {
Some(func(prod_state.get_qubits()[gate.position], arg))
}
GateCategory::Double(c, func) => {
acting_positions.push(c);
let qubits = prod_state.get_qubits();
Some(func(qubits[c], qubits[gate.position]))
}
GateCategory::DoubleArg(arg, c, func) => {
acting_positions.push(c);
let qubits = prod_state.get_qubits();
Some(func(qubits[c], qubits[gate.position], arg))
}
GateCategory::DoubleArgInt(arg_int, c, func) => {
acting_positions.push(c);
let qubits = prod_state.get_qubits();
Some(func(qubits[c], qubits[gate.position], arg_int))
}
GateCategory::Triple(c1, c2, func) => {
acting_positions.push(c2);
acting_positions.push(c1);
let qubits = prod_state.get_qubits();
Some(func(qubits[c1], qubits[c2], qubits[gate.position]))
}
GateCategory::Custom(func, controls) => {
acting_positions.extend(controls.iter().rev());
Self::custom_gate_on_wires(func, controls, gate.position, &prod_state)
}
};
if let Some(super_pos) = wrapped_super_pos {
if !acting_positions.is_empty() {
acting_positions.reverse()
};
acting_positions.push(gate.position);
Self::insert_gate_image_into_product_state(
super_pos,
acting_positions,
prod_state,
amp,
&mut mapped_states,
);
} else {
untouched_states.insert(prod_state, amp);
}
}
for (k, v) in untouched_states {
mapped_states
.entry(k)
.and_modify(|map_v| {
*map_v = v;
})
.or_insert(v);
}
register.set_amplitudes_from_states_unchecked(mapped_states);
}
fn custom_gate_on_wires(
operator: fn(ProductState) -> Option<SuperPosition>,
controls: &[usize],
position: usize,
prod_state: &ProductState,
) -> Option<SuperPosition> {
if !controls.is_empty() {
let mut concat_prodstate: ProductState = prod_state.get_unchecked(controls[0]).into();
for c in &controls[1..] {
concat_prodstate = concat_prodstate.kronecker_prod(prod_state.get_unchecked(*c));
}
concat_prodstate = concat_prodstate.kronecker_prod(prod_state.get_unchecked(position));
operator(concat_prodstate)
} else {
operator(ProductState::from(prod_state.qubits[position]))
}
}
fn insert_gate_image_into_product_state(
gate_image: SuperPosition,
gate_positions: Vec<usize>,
prod_state: ProductState,
amp: Complex<f64>,
mapped_states: &mut HashMap<ProductState, Complex<f64>>,
) {
for (state, state_amp) in gate_image.into_iter() {
let mut swapped_state: ProductState = prod_state.clone();
swapped_state.insert_qubits(state.qubits.as_slice(), gate_positions.as_slice());
mapped_states
.entry(swapped_state)
.and_modify(|existing_amp| {
*existing_amp = existing_amp.add(state_amp.mul(amp));
})
.or_insert(state_amp.mul(amp));
}
}
pub(super) fn print_circuit_log(
gate: &Gate,
gate_pos: &usize,
qubit_counter: &usize,
number_gates: &usize,
) {
println!(
"Applying {:?} on wire {} # {}/{} ",
gate,
gate_pos,
qubit_counter + 1,
number_gates
);
if *qubit_counter + 1 == *number_gates {
println!("Finished circuit simulation.")
}
}
}