use crate::device::error::LayoutError;
use crate::device::{LogicalQubit, PhysicalQubit};
use std::collections::{BTreeMap, BTreeSet};
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Layout {
physical_qubits: BTreeSet<PhysicalQubit>,
l2p: BTreeMap<LogicalQubit, PhysicalQubit>,
p2l: BTreeMap<PhysicalQubit, LogicalQubit>,
}
impl Layout {
pub fn new(
logical: Vec<LogicalQubit>,
physical: Vec<PhysicalQubit>,
init_map: Option<BTreeMap<LogicalQubit, PhysicalQubit>>,
) -> Result<Self, LayoutError> {
if logical.len() > physical.len() {
return Err(LayoutError::TooManyLogicalQubits {
logical: logical.len(),
physical: physical.len(),
});
}
let mut logical_qubits = BTreeSet::new();
for logical in logical.iter().copied() {
if !logical_qubits.insert(logical) {
return Err(LayoutError::DuplicateLogicalQubit(logical));
}
}
let mut physical_qubits = BTreeSet::new();
for physical in physical.iter().copied() {
if !physical_qubits.insert(physical) {
return Err(LayoutError::DuplicatePhysicalQubit(physical));
}
}
let mut layout = Self {
physical_qubits,
l2p: BTreeMap::new(),
p2l: BTreeMap::new(),
};
if let Some(init_map) = init_map {
for (logical, physical) in init_map {
if !logical_qubits.contains(&logical) {
return Err(LayoutError::InvalidLogicalQubit(logical));
}
if !layout.physical_qubits.contains(&physical) {
return Err(LayoutError::InvalidPhysicalQubit(physical));
}
if layout.p2l.contains_key(&physical) {
return Err(LayoutError::PhysicalQubitAlreadyOccupied(physical));
}
layout.l2p.insert(logical, physical);
layout.p2l.insert(physical, logical);
}
}
let vacant_physical: Vec<_> = physical
.into_iter()
.filter(|physical| !layout.p2l.contains_key(physical))
.collect();
let unmapped_logical: Vec<_> = logical
.into_iter()
.filter(|logical| !layout.l2p.contains_key(logical))
.collect();
for (logical, physical) in unmapped_logical.into_iter().zip(vacant_physical) {
debug_assert!(
!layout.p2l.contains_key(&physical),
"vacant physical qubit became occupied before initial mapping completed"
);
layout.l2p.insert(logical, physical);
layout.p2l.insert(physical, logical);
}
debug_assert_eq!(
layout.l2p.len(),
logical_qubits.len(),
"logical qubit count was validated against physical capacity"
);
debug_assert_eq!(
layout.l2p.len(),
layout.p2l.len(),
"each logical mapping must have one reverse mapping"
);
Ok(layout)
}
pub fn from_pairs(
logical_physical: &[(u32, u32)],
physical_count: u32,
) -> Result<Self, LayoutError> {
let logical = logical_physical
.iter()
.map(|&(logical, _)| LogicalQubit::new(logical))
.collect::<Vec<_>>();
let physical = (0..physical_count)
.map(PhysicalQubit::new)
.collect::<Vec<_>>();
let init_map = logical_physical
.iter()
.map(|&(logical, physical)| (LogicalQubit::new(logical), PhysicalQubit::new(physical)))
.collect::<BTreeMap<_, _>>();
Self::new(logical, physical, Some(init_map))
}
pub fn num_logical(&self) -> usize {
self.l2p.len()
}
pub fn num_physical(&self) -> usize {
self.physical_qubits.len()
}
pub fn num_vacant_physical(&self) -> usize {
self.physical_qubits.len() - self.p2l.len()
}
pub fn get_physical(&self, logical: LogicalQubit) -> Option<PhysicalQubit> {
self.l2p.get(&logical).copied()
}
pub fn get_logical(&self, physical: PhysicalQubit) -> Option<LogicalQubit> {
self.p2l.get(&physical).copied()
}
pub fn logical_qubits(&self) -> impl Iterator<Item = LogicalQubit> + '_ {
self.l2p.keys().copied()
}
pub fn physical_qubits(&self) -> impl Iterator<Item = PhysicalQubit> + '_ {
self.physical_qubits.iter().copied()
}
pub fn vacant_physical_qubits(&self) -> impl Iterator<Item = PhysicalQubit> + '_ {
self.physical_qubits
.iter()
.copied()
.filter(|physical| !self.p2l.contains_key(physical))
}
pub fn is_physical_vacant(&self, physical: PhysicalQubit) -> bool {
self.physical_qubits.contains(&physical) && !self.p2l.contains_key(&physical)
}
pub fn l2p_map(&self) -> &BTreeMap<LogicalQubit, PhysicalQubit> {
&self.l2p
}
pub fn p2l_map(&self) -> &BTreeMap<PhysicalQubit, LogicalQubit> {
&self.p2l
}
pub fn bind(
&mut self,
logical: LogicalQubit,
physical: PhysicalQubit,
) -> Result<(), LayoutError> {
if !self.physical_qubits.contains(&physical) {
return Err(LayoutError::InvalidPhysicalQubit(physical));
}
if self.l2p.contains_key(&logical) {
return Err(LayoutError::LogicalQubitAlreadyBound(logical));
}
if self.p2l.contains_key(&physical) {
return Err(LayoutError::PhysicalQubitAlreadyOccupied(physical));
}
self.l2p.insert(logical, physical);
self.p2l.insert(physical, logical);
Ok(())
}
pub fn unbind(&mut self, logical: LogicalQubit) -> Result<PhysicalQubit, LayoutError> {
let physical = self
.l2p
.remove(&logical)
.ok_or(LayoutError::LogicalQubitNotBound(logical))?;
self.p2l.remove(&physical);
Ok(physical)
}
pub fn swap_physical(
&mut self,
phys_a: PhysicalQubit,
phys_b: PhysicalQubit,
) -> Result<(), LayoutError> {
if !self.physical_qubits.contains(&phys_a) {
return Err(LayoutError::InvalidPhysicalQubit(phys_a));
}
if !self.physical_qubits.contains(&phys_b) {
return Err(LayoutError::InvalidPhysicalQubit(phys_b));
}
if phys_a == phys_b {
return Ok(());
}
let logical_a = self.p2l.remove(&phys_a);
let logical_b = self.p2l.remove(&phys_b);
if let Some(logical) = logical_a {
self.l2p.insert(logical, phys_b);
self.p2l.insert(phys_b, logical);
}
if let Some(logical) = logical_b {
self.l2p.insert(logical, phys_a);
self.p2l.insert(phys_a, logical);
}
Ok(())
}
}
#[cfg(test)]
#[path = "./layout_test.rs"]
mod layout_test;