use crate::{QvmError, Result, Qubit, ClassicalBit, Topology};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceMapping {
pub qubit_mapping: Vec<usize>,
pub classical_mapping: Vec<usize>,
pub metadata: MappingMetadata,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct MappingMetadata {
pub quality_score: f64,
pub swap_count: usize,
pub routing_overhead: f64,
pub properties: HashMap<String, String>,
}
#[derive(Debug, Clone)]
pub struct QubitMapper {
topology: Topology,
config: MapperConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MapperConfig {
pub strategy: MappingStrategy,
pub enable_swaps: bool,
pub max_swap_overhead: f64,
pub prioritize_connectivity: bool,
}
impl Default for MapperConfig {
fn default() -> Self {
Self {
strategy: MappingStrategy::ConnectivityAware,
enable_swaps: true,
max_swap_overhead: 0.5,
prioritize_connectivity: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum MappingStrategy {
Linear,
Random,
ConnectivityAware,
DistanceMinimizing,
Adaptive,
}
impl QubitMapper {
pub fn new(topology: &Topology) -> Self {
Self {
topology: topology.clone(),
config: MapperConfig::default(),
}
}
pub fn create_classical_allocator(&self) -> ClassicalAllocator {
ClassicalAllocator::new(self.topology.qubit_count()) }
pub fn with_config(topology: &Topology, config: MapperConfig) -> Self {
Self {
topology: topology.clone(),
config,
}
}
pub fn create_mapping(
&self,
qubit_assignments: &[usize],
classical_assignments: &[usize],
) -> Result<ResourceMapping> {
let quality_score = self.calculate_mapping_quality(qubit_assignments);
let metadata = MappingMetadata {
quality_score,
swap_count: 0, routing_overhead: 0.0,
properties: HashMap::new(),
};
Ok(ResourceMapping {
qubit_mapping: qubit_assignments.to_vec(),
classical_mapping: classical_assignments.to_vec(),
metadata,
})
}
pub fn find_optimal_mapping(
&self,
logical_qubits: usize,
connectivity_requirements: &[(usize, usize)],
) -> Result<Vec<usize>> {
match self.config.strategy {
MappingStrategy::Linear => self.linear_mapping(logical_qubits),
MappingStrategy::Random => self.random_mapping(logical_qubits),
MappingStrategy::ConnectivityAware => {
self.connectivity_aware_mapping(logical_qubits, connectivity_requirements)
}
MappingStrategy::DistanceMinimizing => {
self.distance_minimizing_mapping(logical_qubits, connectivity_requirements)
}
MappingStrategy::Adaptive => {
self.adaptive_mapping(logical_qubits, connectivity_requirements)
}
}
}
fn linear_mapping(&self, logical_qubits: usize) -> Result<Vec<usize>> {
if logical_qubits > self.topology.qubit_count() {
return Err(QvmError::allocation_error(
"Not enough physical qubits available".to_string()
));
}
Ok((0..logical_qubits).collect())
}
fn random_mapping(&self, logical_qubits: usize) -> Result<Vec<usize>> {
use std::collections::HashSet;
if logical_qubits > self.topology.qubit_count() {
return Err(QvmError::allocation_error(
"Not enough physical qubits available".to_string()
));
}
let mut mapping = Vec::new();
let mut used_qubits = HashSet::new();
let mut seed = 12345u64;
for _ in 0..logical_qubits {
loop {
seed = (seed.wrapping_mul(1103515245).wrapping_add(12345)) & 0x7fffffff;
let physical_qubit = (seed as usize) % self.topology.qubit_count();
if !used_qubits.contains(&physical_qubit) {
mapping.push(physical_qubit);
used_qubits.insert(physical_qubit);
break;
}
}
}
Ok(mapping)
}
fn connectivity_aware_mapping(
&self,
logical_qubits: usize,
connectivity_requirements: &[(usize, usize)],
) -> Result<Vec<usize>> {
if logical_qubits > self.topology.qubit_count() {
return Err(QvmError::allocation_error(
"Not enough physical qubits available".to_string()
));
}
let mut mapping = (0..logical_qubits).collect::<Vec<_>>();
for &(logical1, logical2) in connectivity_requirements {
if logical1 >= logical_qubits || logical2 >= logical_qubits {
continue;
}
let physical1 = mapping[logical1];
let physical2 = mapping[logical2];
if !self.topology.are_connected(Qubit(physical1), Qubit(physical2)) {
if let Some(better_mapping) = self.find_connected_pair(logical1, logical2, &mapping) {
mapping = better_mapping;
}
}
}
Ok(mapping)
}
fn distance_minimizing_mapping(
&self,
logical_qubits: usize,
connectivity_requirements: &[(usize, usize)],
) -> Result<Vec<usize>> {
if logical_qubits > self.topology.qubit_count() {
return Err(QvmError::allocation_error(
"Not enough physical qubits available".to_string()
));
}
let mut mapping = vec![0; logical_qubits];
let mut used_physical = std::collections::HashSet::new();
let mut connectivity_count = vec![0; logical_qubits];
for &(q1, q2) in connectivity_requirements {
if q1 < logical_qubits { connectivity_count[q1] += 1; }
if q2 < logical_qubits { connectivity_count[q2] += 1; }
}
let mut qubit_order: Vec<_> = (0..logical_qubits).collect();
qubit_order.sort_by_key(|&q| std::cmp::Reverse(connectivity_count[q]));
let physical_centrality = self.calculate_centrality();
let mut physical_order: Vec<_> = (0..self.topology.qubit_count()).collect();
physical_order.sort_by(|&a, &b| {
physical_centrality[b].partial_cmp(&physical_centrality[a]).unwrap_or(std::cmp::Ordering::Equal)
});
for (i, &logical_qubit) in qubit_order.iter().enumerate() {
if i < physical_order.len() {
mapping[logical_qubit] = physical_order[i];
used_physical.insert(physical_order[i]);
}
}
Ok(mapping)
}
fn adaptive_mapping(
&self,
logical_qubits: usize,
connectivity_requirements: &[(usize, usize)],
) -> Result<Vec<usize>> {
let connectivity_density = connectivity_requirements.len() as f64 / (logical_qubits * logical_qubits) as f64;
if connectivity_density > 0.5 {
self.distance_minimizing_mapping(logical_qubits, connectivity_requirements)
} else if connectivity_density > 0.1 {
self.connectivity_aware_mapping(logical_qubits, connectivity_requirements)
} else {
self.linear_mapping(logical_qubits)
}
}
fn calculate_centrality(&self) -> Vec<f64> {
let qubit_count = self.topology.qubit_count();
let mut centrality = vec![0.0; qubit_count];
for i in 0..qubit_count {
centrality[i] = self.topology.neighbors(Qubit(i)).len() as f64;
}
centrality
}
fn find_connected_pair(
&self,
logical1: usize,
logical2: usize,
current_mapping: &[usize],
) -> Option<Vec<usize>> {
let mut used_physical: std::collections::HashSet<_> = current_mapping.iter().collect();
for physical1 in 0..self.topology.qubit_count() {
if used_physical.contains(&physical1) && current_mapping[logical1] != physical1 {
continue;
}
for neighbor in self.topology.neighbors(Qubit(physical1)) {
let physical2 = neighbor.index();
if used_physical.contains(&physical2) && current_mapping[logical2] != physical2 {
continue;
}
let mut new_mapping = current_mapping.to_vec();
new_mapping[logical1] = physical1;
new_mapping[logical2] = physical2;
return Some(new_mapping);
}
}
None
}
fn calculate_mapping_quality(&self, mapping: &[usize]) -> f64 {
if mapping.is_empty() {
return 1.0;
}
let mut total_distance = 0.0;
let mut pair_count = 0;
for i in 0..(mapping.len() - 1) {
if let Some(path) = self.topology.shortest_path(Qubit(mapping[i]), Qubit(mapping[i + 1])) {
total_distance += (path.len() - 1) as f64;
pair_count += 1;
}
}
if pair_count == 0 {
return 1.0;
}
let avg_distance = total_distance / pair_count as f64;
1.0 / (1.0 + avg_distance - 1.0)
}
pub fn route_two_qubit_gate(
&self,
control: usize,
target: usize,
mapping: &mut [usize],
) -> Result<Vec<SwapOperation>> {
let physical_control = mapping[control];
let physical_target = mapping[target];
if self.topology.are_connected(Qubit(physical_control), Qubit(physical_target)) {
return Ok(vec![]);
}
if !self.config.enable_swaps {
return Err(QvmError::allocation_error(
"Qubits not connected and SWAP insertion disabled".to_string()
));
}
if let Some(path) = self.topology.shortest_path(Qubit(physical_control), Qubit(physical_target)) {
let mut swaps = Vec::new();
for i in 0..(path.len() - 2) {
let swap_op = SwapOperation {
qubit1: path[i].index(),
qubit2: path[i + 1].index(),
};
swaps.push(swap_op);
}
let overhead = swaps.len() as f64 / mapping.len() as f64;
if overhead > self.config.max_swap_overhead {
return Err(QvmError::allocation_error(
"SWAP overhead too high".to_string()
));
}
Ok(swaps)
} else {
Err(QvmError::allocation_error(
"No path found between qubits".to_string()
))
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SwapOperation {
pub qubit1: usize,
pub qubit2: usize,
}
#[derive(Debug, Clone)]
pub struct ClassicalAllocator {
total_bits: usize,
allocated_bits: std::collections::HashSet<usize>,
strategy: ClassicalAllocationStrategy,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ClassicalAllocationStrategy {
Sequential,
Random,
ConflictMinimizing,
}
impl ClassicalAllocator {
pub fn new(total_bits: usize) -> Self {
Self {
total_bits,
allocated_bits: std::collections::HashSet::new(),
strategy: ClassicalAllocationStrategy::Sequential,
}
}
pub fn allocate_bits(&mut self, required_bits: usize) -> Result<Vec<usize>> {
if required_bits > self.available_bits() {
return Err(QvmError::allocation_error(
format!("Not enough classical bits: need {}, have {}",
required_bits, self.available_bits())
));
}
match self.strategy {
ClassicalAllocationStrategy::Sequential => self.allocate_sequential(required_bits),
ClassicalAllocationStrategy::Random => self.allocate_random(required_bits),
ClassicalAllocationStrategy::ConflictMinimizing => self.allocate_conflict_minimizing(required_bits),
}
}
fn allocate_sequential(&mut self, required_bits: usize) -> Result<Vec<usize>> {
let mut allocation = Vec::new();
for bit in 0..self.total_bits {
if !self.allocated_bits.contains(&bit) {
allocation.push(bit);
self.allocated_bits.insert(bit);
if allocation.len() >= required_bits {
break;
}
}
}
Ok(allocation)
}
fn allocate_random(&mut self, required_bits: usize) -> Result<Vec<usize>> {
let mut allocation = Vec::new();
let available: Vec<_> = (0..self.total_bits)
.filter(|bit| !self.allocated_bits.contains(bit))
.collect();
let mut seed = 54321u64;
for _ in 0..required_bits {
if available.is_empty() {
break;
}
seed = (seed.wrapping_mul(1103515245).wrapping_add(12345)) & 0x7fffffff;
let idx = (seed as usize) % available.len();
let bit = available[idx];
allocation.push(bit);
self.allocated_bits.insert(bit);
}
Ok(allocation)
}
fn allocate_conflict_minimizing(&mut self, required_bits: usize) -> Result<Vec<usize>> {
self.allocate_sequential(required_bits)
}
pub fn release_bits(&mut self, bits: &[usize]) {
for &bit in bits {
self.allocated_bits.remove(&bit);
}
}
pub fn available_bits(&self) -> usize {
self.total_bits - self.allocated_bits.len()
}
pub fn reset(&mut self) {
self.allocated_bits.clear();
}
pub fn is_allocated(&self, bit: usize) -> bool {
self.allocated_bits.contains(&bit)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::topology::TopologyBuilder;
#[test]
fn test_qubit_mapper_creation() {
let topology = TopologyBuilder::grid(3, 3);
let mapper = QubitMapper::new(&topology);
assert_eq!(mapper.config.strategy, MappingStrategy::ConnectivityAware);
}
#[test]
fn test_linear_mapping() {
let topology = TopologyBuilder::grid(3, 3);
let mapper = QubitMapper::new(&topology);
let mapping = mapper.linear_mapping(4).unwrap();
assert_eq!(mapping, vec![0, 1, 2, 3]);
}
#[test]
fn test_mapping_quality() {
let topology = TopologyBuilder::linear(5);
let mapper = QubitMapper::new(&topology);
let good_mapping = vec![0, 1, 2]; let bad_mapping = vec![0, 2, 4];
let quality_good = mapper.calculate_mapping_quality(&good_mapping);
let quality_bad = mapper.calculate_mapping_quality(&bad_mapping);
assert!(quality_good > quality_bad);
}
#[test]
fn test_resource_mapping_creation() {
let topology = TopologyBuilder::grid(2, 2);
let mapper = QubitMapper::new(&topology);
let qubit_assignments = vec![0, 1];
let classical_assignments = vec![0, 1];
let mapping = mapper.create_mapping(&qubit_assignments, &classical_assignments).unwrap();
assert_eq!(mapping.qubit_mapping, vec![0, 1]);
assert_eq!(mapping.classical_mapping, vec![0, 1]);
assert!(mapping.metadata.quality_score > 0.0);
}
#[test]
fn test_connectivity_aware_mapping() {
let topology = TopologyBuilder::grid(3, 3);
let mapper = QubitMapper::new(&topology);
let connectivity_requirements = vec![(0, 1), (1, 2)];
let mapping = mapper.connectivity_aware_mapping(3, &connectivity_requirements).unwrap();
assert_eq!(mapping.len(), 3);
}
}