use super::{DistributedStatevectorBackend, required_local_qubits};
use crate::backend::Backend;
use crate::circuit::{Instruction, SmallVec};
use crate::error::Result;
use crate::gates::Gate;
const NEVER: usize = usize::MAX;
const HORIZON: usize = 1 << 12;
enum Step<'a> {
Gate(&'a Gate, &'a [usize]),
Swap(usize, usize),
Skip,
Opaque,
}
fn step(instruction: &Instruction) -> Step<'_> {
match instruction {
Instruction::Gate { gate, targets } => match gate {
Gate::Swap => Step::Swap(targets[0], targets[1]),
_ => Step::Gate(gate, targets),
},
Instruction::Conditional { gate, targets, .. } => match gate {
Gate::Swap => Step::Opaque,
_ => Step::Gate(gate, targets),
},
Instruction::Measure { .. } | Instruction::Reset { .. } | Instruction::Barrier { .. } => {
Step::Skip
}
Instruction::Region(_) | Instruction::Save { .. } => Step::Opaque,
}
}
impl DistributedStatevectorBackend {
pub(super) fn apply_planned(&mut self, instructions: &[Instruction]) -> Result<()> {
let mut start = 0;
while start < instructions.len() {
let end = self.plan_window(instructions, start);
for instruction in &instructions[start..end] {
self.apply(instruction)?;
}
start = end;
}
Ok(())
}
fn plan_window(&mut self, instructions: &[Instruction], start: usize) -> usize {
let local = self.local_qubits();
let mut alias: Vec<usize> = (0..self.num_qubits).collect();
let mut required: SmallVec<[usize; 16]> = SmallVec::new();
let mut fresh: SmallVec<[usize; 8]> = SmallVec::new();
let mut end = start;
while end < instructions.len() {
let (gate, targets) = match step(&instructions[end]) {
Step::Gate(gate, targets) => (gate, targets),
Step::Swap(a, b) => {
alias.swap(a, b);
end += 1;
continue;
}
Step::Skip => {
end += 1;
continue;
}
Step::Opaque => {
if end == start {
end += 1;
}
break;
}
};
fresh.clear();
for &q in &required_local_qubits(gate, targets) {
let a = alias[q];
if !required.contains(&a) && !fresh.contains(&a) {
fresh.push(a);
}
}
if required.len() + fresh.len() > local {
if end == start {
return start + 1;
}
break;
}
required.extend(fresh.iter().copied());
end += 1;
}
let incoming: SmallVec<[usize; 8]> = required
.iter()
.copied()
.filter(|&q| self.qubit_map[q] >= local)
.collect();
if incoming.is_empty() {
return end;
}
let mut candidates: SmallVec<[(usize, usize); 16]> = (0..local)
.filter(|&pos| !required.contains(&self.phys_map[pos]))
.map(|pos| (pos, NEVER))
.collect();
self.resolve_next_uses(
instructions,
end,
&mut alias,
&mut candidates,
incoming.len(),
);
candidates.sort_unstable_by(|a, b| b.1.cmp(&a.1).then(a.0.cmp(&b.0)));
for (&q, &(victim, _)) in incoming.iter().zip(candidates.iter()) {
let global_pos = self.qubit_map[q];
self.relabel_swap(victim, global_pos);
}
end
}
fn resolve_next_uses(
&self,
instructions: &[Instruction],
from: usize,
alias: &mut [usize],
candidates: &mut [(usize, usize)],
victims: usize,
) {
let mut unresolved = candidates.len();
let horizon = from.saturating_add(HORIZON).min(instructions.len());
for (t, instruction) in instructions[from..horizon].iter().enumerate() {
if unresolved <= victims {
break;
}
let (gate, targets) = match step(instruction) {
Step::Gate(gate, targets) => (gate, targets),
Step::Swap(a, b) => {
alias.swap(a, b);
continue;
}
Step::Skip => continue,
Step::Opaque => break,
};
for &q in &required_local_qubits(gate, targets) {
let a = alias[q];
if let Some(c) = candidates
.iter_mut()
.find(|c| c.1 == NEVER && self.phys_map[c.0] == a)
{
c.1 = from + t;
unresolved -= 1;
}
}
}
}
}