use crate::decoder::PauliType;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StabilizerType {
XStabilizer,
ZStabilizer,
}
#[derive(Debug, Clone)]
pub struct SyndromeExtraction {
pub stabilizer_type: StabilizerType,
pub data_qubits: Vec<u32>,
pub ancilla_qubit: u32,
}
#[derive(Debug, Clone)]
pub struct ScheduledCorrection {
pub target_qubit: u32,
pub correction_type: PauliType,
pub depends_on_round: Option<usize>,
}
#[derive(Debug, Clone)]
pub struct QecRound {
pub syndrome_extractions: Vec<SyndromeExtraction>,
pub corrections: Vec<ScheduledCorrection>,
pub is_feed_forward: bool,
}
#[derive(Debug, Clone)]
pub struct QecSchedule {
pub rounds: Vec<QecRound>,
pub total_classical_depth: u32,
pub total_quantum_depth: u32,
pub feed_forward_points: Vec<usize>,
}
pub fn generate_surface_code_schedule(distance: u32, num_rounds: u32) -> QecSchedule {
let d = distance;
let mut rounds = Vec::with_capacity(num_rounds as usize);
let mut feed_forward_points = Vec::new();
let data_qubit_count = d * d;
let mut next_ancilla = data_qubit_count;
let x_stabilizers = generate_x_stabilizers(d, &mut next_ancilla);
let z_stabilizers = generate_z_stabilizers(d, &mut next_ancilla);
for round_idx in 0..num_rounds {
let mut extractions = Vec::new();
for stab in &x_stabilizers {
extractions.push(stab.clone());
}
for stab in &z_stabilizers {
extractions.push(stab.clone());
}
let is_ff = true;
if is_ff {
feed_forward_points.push(round_idx as usize);
}
rounds.push(QecRound {
syndrome_extractions: extractions,
corrections: Vec::new(),
is_feed_forward: is_ff,
});
}
for (i, round) in rounds.iter_mut().enumerate() {
for q in 0..data_qubit_count {
round.corrections.push(ScheduledCorrection {
target_qubit: q,
correction_type: PauliType::X,
depends_on_round: Some(i),
});
}
}
let total_classical_depth = num_rounds;
let total_quantum_depth = compute_quantum_depth(&rounds, d);
QecSchedule {
rounds,
total_classical_depth,
total_quantum_depth,
feed_forward_points,
}
}
fn generate_x_stabilizers(d: u32, next_ancilla: &mut u32) -> Vec<SyndromeExtraction> {
let mut stabilizers = Vec::new();
if d < 2 {
return stabilizers;
}
for row in 0..(d - 1) {
for col in 0..(d - 1) {
if (row + col) % 2 != 0 {
continue;
}
let mut data_qubits = Vec::new();
data_qubits.push(row * d + col);
data_qubits.push(row * d + col + 1);
data_qubits.push((row + 1) * d + col);
data_qubits.push((row + 1) * d + col + 1);
let ancilla = *next_ancilla;
*next_ancilla += 1;
stabilizers.push(SyndromeExtraction {
stabilizer_type: StabilizerType::XStabilizer,
data_qubits,
ancilla_qubit: ancilla,
});
}
}
for row in (0..(d - 1)).step_by(2) {
if (row) % 2 == 0 && d > 2 {
continue;
}
let mut data_qubits = Vec::new();
data_qubits.push(row * d);
data_qubits.push((row + 1) * d);
let ancilla = *next_ancilla;
*next_ancilla += 1;
stabilizers.push(SyndromeExtraction {
stabilizer_type: StabilizerType::XStabilizer,
data_qubits,
ancilla_qubit: ancilla,
});
}
stabilizers
}
fn generate_z_stabilizers(d: u32, next_ancilla: &mut u32) -> Vec<SyndromeExtraction> {
let mut stabilizers = Vec::new();
if d < 2 {
return stabilizers;
}
for row in 0..(d - 1) {
for col in 0..(d - 1) {
if (row + col) % 2 != 1 {
continue;
}
let mut data_qubits = Vec::new();
data_qubits.push(row * d + col);
data_qubits.push(row * d + col + 1);
data_qubits.push((row + 1) * d + col);
data_qubits.push((row + 1) * d + col + 1);
let ancilla = *next_ancilla;
*next_ancilla += 1;
stabilizers.push(SyndromeExtraction {
stabilizer_type: StabilizerType::ZStabilizer,
data_qubits,
ancilla_qubit: ancilla,
});
}
}
for col in (1..(d - 1)).step_by(2) {
if d <= 2 {
break;
}
let mut data_qubits = Vec::new();
data_qubits.push(col);
data_qubits.push(col + 1);
let ancilla = *next_ancilla;
*next_ancilla += 1;
stabilizers.push(SyndromeExtraction {
stabilizer_type: StabilizerType::ZStabilizer,
data_qubits,
ancilla_qubit: ancilla,
});
}
stabilizers
}
fn compute_quantum_depth(rounds: &[QecRound], distance: u32) -> u32 {
let mut depth = 0u32;
for round in rounds {
if round.syndrome_extractions.is_empty() {
continue;
}
let mut layers = 0u32;
let mut scheduled = vec![false; round.syndrome_extractions.len()];
let total = round.syndrome_extractions.len();
let mut done = 0;
while done < total {
let mut used_qubits: Vec<u32> = Vec::new();
for (i, ext) in round.syndrome_extractions.iter().enumerate() {
if scheduled[i] {
continue;
}
let conflicts = ext
.data_qubits
.iter()
.any(|q| used_qubits.contains(q))
|| used_qubits.contains(&ext.ancilla_qubit);
if !conflicts {
used_qubits.extend(&ext.data_qubits);
used_qubits.push(ext.ancilla_qubit);
scheduled[i] = true;
done += 1;
}
}
layers += 6;
}
depth += layers;
if !round.corrections.is_empty() {
depth += 1;
}
}
depth.max(distance) }
pub fn optimize_feed_forward(schedule: &QecSchedule) -> QecSchedule {
let mut optimized_rounds = schedule.rounds.clone();
defer_corrections(&mut optimized_rounds);
let merged_rounds = merge_rounds(&optimized_rounds);
let (final_rounds, ff_points) = minimize_feed_forward(&merged_rounds);
let total_classical_depth = ff_points.len() as u32;
let total_quantum_depth = compute_quantum_depth(&final_rounds, 0);
QecSchedule {
rounds: final_rounds,
total_classical_depth,
total_quantum_depth,
feed_forward_points: ff_points,
}
}
fn defer_corrections(rounds: &mut [QecRound]) {
let num_rounds = rounds.len();
if num_rounds == 0 {
return;
}
for i in 0..num_rounds {
let mut deferred_indices = Vec::new();
for (ci, corr) in rounds[i].corrections.iter().enumerate() {
let qubit = corr.target_qubit;
let blocks_next_round = if i + 1 < num_rounds {
rounds[i + 1].syndrome_extractions.iter().any(|ext| {
ext.data_qubits.contains(&qubit)
&& !commutes_with_correction(&ext.stabilizer_type, &corr.correction_type)
})
} else {
true
};
if !blocks_next_round {
deferred_indices.push(ci);
}
}
for &ci in deferred_indices.iter().rev() {
rounds[i].corrections[ci].depends_on_round = None;
}
let mut kept = Vec::new();
for (ci, corr) in rounds[i].corrections.iter().enumerate() {
if !deferred_indices.contains(&ci) {
kept.push(corr.clone());
}
}
rounds[i].corrections = kept;
}
}
fn commutes_with_correction(stab: &StabilizerType, pauli: &PauliType) -> bool {
match (stab, pauli) {
(StabilizerType::XStabilizer, PauliType::X) => true,
(StabilizerType::ZStabilizer, PauliType::Z) => true,
_ => false,
}
}
fn merge_rounds(rounds: &[QecRound]) -> Vec<QecRound> {
if rounds.is_empty() {
return Vec::new();
}
let mut merged = Vec::new();
let mut current = rounds[0].clone();
for next in rounds.iter().skip(1) {
let can_merge = can_merge_rounds(¤t, next);
if can_merge {
current
.syndrome_extractions
.extend(next.syndrome_extractions.iter().cloned());
current
.corrections
.extend(next.corrections.iter().cloned());
current.is_feed_forward = current.is_feed_forward || next.is_feed_forward;
} else {
merged.push(current);
current = next.clone();
}
}
merged.push(current);
merged
}
fn can_merge_rounds(first: &QecRound, second: &QecRound) -> bool {
if second.corrections.iter().any(|c| c.depends_on_round.is_some()) {
return false;
}
let corrected_qubits: Vec<u32> = first
.corrections
.iter()
.map(|c| c.target_qubit)
.collect();
let extraction_qubits: Vec<u32> = second
.syndrome_extractions
.iter()
.flat_map(|ext| ext.data_qubits.iter().copied())
.collect();
!corrected_qubits
.iter()
.any(|q| extraction_qubits.contains(q))
}
fn minimize_feed_forward(rounds: &[QecRound]) -> (Vec<QecRound>, Vec<usize>) {
let mut result = rounds.to_vec();
let mut ff_points = Vec::new();
for (i, round) in result.iter_mut().enumerate() {
let has_dependent_corrections = round
.corrections
.iter()
.any(|c| c.depends_on_round.is_some());
if has_dependent_corrections {
round.is_feed_forward = true;
ff_points.push(i);
} else {
round.is_feed_forward = false;
}
}
(result, ff_points)
}
pub fn schedule_latency(
schedule: &QecSchedule,
gate_time_ns: u64,
classical_time_ns: u64,
) -> u64 {
let quantum_latency = schedule.total_quantum_depth as u64 * gate_time_ns;
let classical_latency = schedule.feed_forward_points.len() as u64 * classical_time_ns;
quantum_latency + classical_latency
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OperationType {
SyndromeExtract,
Decode,
Correct,
}
#[derive(Debug, Clone)]
pub struct DependencyNode {
pub round: usize,
pub operation: OperationType,
}
#[derive(Debug, Clone)]
pub struct DependencyGraph {
pub nodes: Vec<DependencyNode>,
pub edges: Vec<(usize, usize)>,
}
pub fn build_dependency_graph(schedule: &QecSchedule) -> DependencyGraph {
let mut nodes = Vec::new();
let mut edges = Vec::new();
let num_rounds = schedule.rounds.len();
for (i, round) in schedule.rounds.iter().enumerate() {
let base = i * 3;
nodes.push(DependencyNode {
round: i,
operation: OperationType::SyndromeExtract,
});
nodes.push(DependencyNode {
round: i,
operation: OperationType::Decode,
});
nodes.push(DependencyNode {
round: i,
operation: OperationType::Correct,
});
edges.push((base, base + 1));
if round.is_feed_forward {
edges.push((base + 1, base + 2));
}
if i > 0 {
let prev_base = (i - 1) * 3;
edges.push((prev_base + 2, base));
}
}
if num_rounds > 0 {
let last = (num_rounds - 1) * 3;
if !edges.contains(&(last + 1, last + 2)) {
edges.push((last + 1, last + 2));
}
}
DependencyGraph { nodes, edges }
}
pub fn critical_path_length(graph: &DependencyGraph) -> usize {
let n = graph.nodes.len();
if n == 0 {
return 0;
}
let mut adj: Vec<Vec<usize>> = vec![Vec::new(); n];
let mut in_degree = vec![0usize; n];
for &(from, to) in &graph.edges {
if from < n && to < n {
adj[from].push(to);
in_degree[to] += 1;
}
}
let mut queue: Vec<usize> = Vec::new();
for i in 0..n {
if in_degree[i] == 0 {
queue.push(i);
}
}
let mut topo_order = Vec::with_capacity(n);
let mut head = 0;
while head < queue.len() {
let u = queue[head];
head += 1;
topo_order.push(u);
for &v in &adj[u] {
in_degree[v] -= 1;
if in_degree[v] == 0 {
queue.push(v);
}
}
}
let mut dist = vec![1usize; n];
for &u in &topo_order {
for &v in &adj[u] {
if dist[v] < dist[u] + 1 {
dist[v] = dist[u] + 1;
}
}
}
dist.into_iter().max().unwrap_or(0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stabilizer_type_equality() {
assert_eq!(StabilizerType::XStabilizer, StabilizerType::XStabilizer);
assert_ne!(StabilizerType::XStabilizer, StabilizerType::ZStabilizer);
}
#[test]
fn test_syndrome_extraction_creation() {
let ext = SyndromeExtraction {
stabilizer_type: StabilizerType::XStabilizer,
data_qubits: vec![0, 1, 3, 4],
ancilla_qubit: 25,
};
assert_eq!(ext.stabilizer_type, StabilizerType::XStabilizer);
assert_eq!(ext.data_qubits.len(), 4);
assert_eq!(ext.ancilla_qubit, 25);
}
#[test]
fn test_scheduled_correction_with_dependency() {
let corr = ScheduledCorrection {
target_qubit: 5,
correction_type: PauliType::X,
depends_on_round: Some(2),
};
assert_eq!(corr.target_qubit, 5);
assert_eq!(corr.correction_type, PauliType::X);
assert_eq!(corr.depends_on_round, Some(2));
}
#[test]
fn test_scheduled_correction_deferred() {
let corr = ScheduledCorrection {
target_qubit: 3,
correction_type: PauliType::Z,
depends_on_round: None,
};
assert!(corr.depends_on_round.is_none());
}
#[test]
fn test_qec_round_empty() {
let round = QecRound {
syndrome_extractions: Vec::new(),
corrections: Vec::new(),
is_feed_forward: false,
};
assert!(round.syndrome_extractions.is_empty());
assert!(!round.is_feed_forward);
}
#[test]
fn test_qec_schedule_creation() {
let schedule = QecSchedule {
rounds: Vec::new(),
total_classical_depth: 0,
total_quantum_depth: 0,
feed_forward_points: Vec::new(),
};
assert!(schedule.rounds.is_empty());
assert_eq!(schedule.total_classical_depth, 0);
}
#[test]
fn test_generate_schedule_d3() {
let schedule = generate_surface_code_schedule(3, 5);
assert_eq!(schedule.rounds.len(), 5);
assert_eq!(schedule.total_classical_depth, 5);
for round in &schedule.rounds {
assert!(
!round.syndrome_extractions.is_empty(),
"Each round should have syndrome extractions"
);
}
}
#[test]
fn test_generate_schedule_d5() {
let schedule = generate_surface_code_schedule(5, 3);
assert_eq!(schedule.rounds.len(), 3);
let first_round = &schedule.rounds[0];
let has_x = first_round
.syndrome_extractions
.iter()
.any(|e| e.stabilizer_type == StabilizerType::XStabilizer);
let has_z = first_round
.syndrome_extractions
.iter()
.any(|e| e.stabilizer_type == StabilizerType::ZStabilizer);
assert!(has_x, "Should have X stabilizers");
assert!(has_z, "Should have Z stabilizers");
}
#[test]
fn test_generate_schedule_single_round() {
let schedule = generate_surface_code_schedule(3, 1);
assert_eq!(schedule.rounds.len(), 1);
assert_eq!(schedule.feed_forward_points.len(), 1);
}
#[test]
fn test_generate_schedule_zero_rounds() {
let schedule = generate_surface_code_schedule(3, 0);
assert!(schedule.rounds.is_empty());
assert!(schedule.feed_forward_points.is_empty());
}
#[test]
fn test_generate_schedule_d1() {
let schedule = generate_surface_code_schedule(1, 2);
assert_eq!(schedule.rounds.len(), 2);
}
#[test]
fn test_generate_schedule_stabilizer_coverage() {
let schedule = generate_surface_code_schedule(3, 1);
let round = &schedule.rounds[0];
for ext in &round.syndrome_extractions {
assert!(
ext.ancilla_qubit >= 9,
"Ancilla {} should be >= 9",
ext.ancilla_qubit
);
for &q in &ext.data_qubits {
assert!(q < 9, "Data qubit {} should be < 9", q);
}
}
}
#[test]
fn test_generate_schedule_all_rounds_have_corrections() {
let schedule = generate_surface_code_schedule(3, 3);
for round in &schedule.rounds {
assert!(
!round.corrections.is_empty(),
"Each round should have correction placeholders"
);
}
}
#[test]
fn test_x_stabilizers_d3() {
let mut next = 9;
let x_stabs = generate_x_stabilizers(3, &mut next);
assert!(!x_stabs.is_empty());
for s in &x_stabs {
assert_eq!(s.stabilizer_type, StabilizerType::XStabilizer);
assert!(!s.data_qubits.is_empty());
}
}
#[test]
fn test_z_stabilizers_d3() {
let mut next = 9;
let z_stabs = generate_z_stabilizers(3, &mut next);
assert!(!z_stabs.is_empty());
for s in &z_stabs {
assert_eq!(s.stabilizer_type, StabilizerType::ZStabilizer);
}
}
#[test]
fn test_x_stabilizers_d1() {
let mut next = 1;
let x_stabs = generate_x_stabilizers(1, &mut next);
assert!(x_stabs.is_empty(), "d=1 should have no X stabilizers");
}
#[test]
fn test_z_stabilizers_d1() {
let mut next = 1;
let z_stabs = generate_z_stabilizers(1, &mut next);
assert!(z_stabs.is_empty(), "d=1 should have no Z stabilizers");
}
#[test]
fn test_stabilizer_ancillas_unique() {
let mut next = 25; let x_stabs = generate_x_stabilizers(5, &mut next);
let z_stabs = generate_z_stabilizers(5, &mut next);
let all_ancillas: Vec<u32> = x_stabs
.iter()
.chain(z_stabs.iter())
.map(|s| s.ancilla_qubit)
.collect();
let mut unique = all_ancillas.clone();
unique.sort();
unique.dedup();
assert_eq!(
all_ancillas.len(),
unique.len(),
"Ancilla qubits must be unique"
);
}
#[test]
fn test_commutation() {
assert!(commutes_with_correction(
&StabilizerType::XStabilizer,
&PauliType::X
));
assert!(commutes_with_correction(
&StabilizerType::ZStabilizer,
&PauliType::Z
));
assert!(!commutes_with_correction(
&StabilizerType::XStabilizer,
&PauliType::Z
));
assert!(!commutes_with_correction(
&StabilizerType::ZStabilizer,
&PauliType::X
));
}
#[test]
fn test_optimize_reduces_feed_forward() {
let schedule = generate_surface_code_schedule(3, 5);
let original_ff = schedule.feed_forward_points.len();
let optimized = optimize_feed_forward(&schedule);
assert!(
optimized.feed_forward_points.len() <= original_ff,
"Optimization should reduce or maintain FF points: {} <= {}",
optimized.feed_forward_points.len(),
original_ff
);
}
#[test]
fn test_optimize_preserves_round_structure() {
let schedule = generate_surface_code_schedule(3, 3);
let optimized = optimize_feed_forward(&schedule);
assert!(!optimized.rounds.is_empty());
}
#[test]
fn test_optimize_empty_schedule() {
let schedule = QecSchedule {
rounds: Vec::new(),
total_classical_depth: 0,
total_quantum_depth: 0,
feed_forward_points: Vec::new(),
};
let optimized = optimize_feed_forward(&schedule);
assert!(optimized.rounds.is_empty());
assert!(optimized.feed_forward_points.is_empty());
}
#[test]
fn test_optimize_single_round() {
let schedule = generate_surface_code_schedule(3, 1);
let optimized = optimize_feed_forward(&schedule);
assert!(!optimized.rounds.is_empty());
}
#[test]
fn test_optimize_classical_depth_decreases() {
let schedule = generate_surface_code_schedule(5, 10);
let optimized = optimize_feed_forward(&schedule);
assert!(
optimized.total_classical_depth <= schedule.total_classical_depth,
"Classical depth should decrease: {} <= {}",
optimized.total_classical_depth,
schedule.total_classical_depth
);
}
#[test]
fn test_merge_rounds_no_conflicts() {
let rounds = vec![
QecRound {
syndrome_extractions: vec![SyndromeExtraction {
stabilizer_type: StabilizerType::XStabilizer,
data_qubits: vec![0, 1],
ancilla_qubit: 100,
}],
corrections: Vec::new(),
is_feed_forward: false,
},
QecRound {
syndrome_extractions: vec![SyndromeExtraction {
stabilizer_type: StabilizerType::ZStabilizer,
data_qubits: vec![2, 3],
ancilla_qubit: 101,
}],
corrections: Vec::new(),
is_feed_forward: false,
},
];
let merged = merge_rounds(&rounds);
assert_eq!(merged.len(), 1);
assert_eq!(merged[0].syndrome_extractions.len(), 2);
}
#[test]
fn test_merge_rounds_with_conflicts() {
let rounds = vec![
QecRound {
syndrome_extractions: vec![SyndromeExtraction {
stabilizer_type: StabilizerType::XStabilizer,
data_qubits: vec![0, 1],
ancilla_qubit: 100,
}],
corrections: vec![ScheduledCorrection {
target_qubit: 2,
correction_type: PauliType::X,
depends_on_round: Some(0),
}],
is_feed_forward: true,
},
QecRound {
syndrome_extractions: vec![SyndromeExtraction {
stabilizer_type: StabilizerType::ZStabilizer,
data_qubits: vec![2, 3],
ancilla_qubit: 101,
}],
corrections: vec![ScheduledCorrection {
target_qubit: 3,
correction_type: PauliType::Z,
depends_on_round: Some(1),
}],
is_feed_forward: true,
},
];
let merged = merge_rounds(&rounds);
assert_eq!(merged.len(), 2);
}
#[test]
fn test_merge_empty_rounds() {
let merged = merge_rounds(&[]);
assert!(merged.is_empty());
}
#[test]
fn test_schedule_latency_basic() {
let schedule = QecSchedule {
rounds: Vec::new(),
total_classical_depth: 2,
total_quantum_depth: 10,
feed_forward_points: vec![0, 1],
};
let latency = schedule_latency(&schedule, 50, 1000);
assert_eq!(latency, 2500);
}
#[test]
fn test_schedule_latency_no_feed_forward() {
let schedule = QecSchedule {
rounds: Vec::new(),
total_classical_depth: 0,
total_quantum_depth: 20,
feed_forward_points: Vec::new(),
};
let latency = schedule_latency(&schedule, 100, 500);
assert_eq!(latency, 2000);
}
#[test]
fn test_schedule_latency_zero_times() {
let schedule = QecSchedule {
rounds: Vec::new(),
total_classical_depth: 5,
total_quantum_depth: 100,
feed_forward_points: vec![0, 1, 2, 3, 4],
};
let latency = schedule_latency(&schedule, 0, 0);
assert_eq!(latency, 0);
}
#[test]
fn test_schedule_latency_optimized_is_less() {
let schedule = generate_surface_code_schedule(3, 5);
let optimized = optimize_feed_forward(&schedule);
let lat_orig = schedule_latency(&schedule, 50, 1000);
let lat_opt = schedule_latency(&optimized, 50, 1000);
assert!(
lat_opt <= lat_orig,
"Optimized latency should be <= original: {} <= {}",
lat_opt,
lat_orig
);
}
#[test]
fn test_build_dependency_graph_empty() {
let schedule = QecSchedule {
rounds: Vec::new(),
total_classical_depth: 0,
total_quantum_depth: 0,
feed_forward_points: Vec::new(),
};
let graph = build_dependency_graph(&schedule);
assert!(graph.nodes.is_empty());
assert!(graph.edges.is_empty());
}
#[test]
fn test_build_dependency_graph_single_round() {
let schedule = QecSchedule {
rounds: vec![QecRound {
syndrome_extractions: vec![SyndromeExtraction {
stabilizer_type: StabilizerType::XStabilizer,
data_qubits: vec![0, 1],
ancilla_qubit: 10,
}],
corrections: Vec::new(),
is_feed_forward: true,
}],
total_classical_depth: 1,
total_quantum_depth: 6,
feed_forward_points: vec![0],
};
let graph = build_dependency_graph(&schedule);
assert_eq!(graph.nodes.len(), 3);
assert!(graph.edges.contains(&(0, 1)));
assert!(graph.edges.contains(&(1, 2)));
}
#[test]
fn test_build_dependency_graph_two_rounds() {
let schedule = generate_surface_code_schedule(3, 2);
let graph = build_dependency_graph(&schedule);
assert_eq!(graph.nodes.len(), 6); assert!(graph.edges.contains(&(2, 3)));
}
#[test]
fn test_dependency_node_types() {
let schedule = generate_surface_code_schedule(3, 1);
let graph = build_dependency_graph(&schedule);
assert_eq!(graph.nodes[0].operation, OperationType::SyndromeExtract);
assert_eq!(graph.nodes[1].operation, OperationType::Decode);
assert_eq!(graph.nodes[2].operation, OperationType::Correct);
}
#[test]
fn test_critical_path_empty_graph() {
let graph = DependencyGraph {
nodes: Vec::new(),
edges: Vec::new(),
};
assert_eq!(critical_path_length(&graph), 0);
}
#[test]
fn test_critical_path_single_node() {
let graph = DependencyGraph {
nodes: vec![DependencyNode {
round: 0,
operation: OperationType::SyndromeExtract,
}],
edges: Vec::new(),
};
assert_eq!(critical_path_length(&graph), 1);
}
#[test]
fn test_critical_path_linear_chain() {
let graph = DependencyGraph {
nodes: vec![
DependencyNode {
round: 0,
operation: OperationType::SyndromeExtract,
},
DependencyNode {
round: 0,
operation: OperationType::Decode,
},
DependencyNode {
round: 0,
operation: OperationType::Correct,
},
],
edges: vec![(0, 1), (1, 2)],
};
assert_eq!(critical_path_length(&graph), 3);
}
#[test]
fn test_critical_path_parallel() {
let graph = DependencyGraph {
nodes: vec![
DependencyNode {
round: 0,
operation: OperationType::SyndromeExtract,
},
DependencyNode {
round: 0,
operation: OperationType::Decode,
},
DependencyNode {
round: 1,
operation: OperationType::SyndromeExtract,
},
DependencyNode {
round: 1,
operation: OperationType::Decode,
},
],
edges: vec![(0, 1), (2, 3)],
};
assert_eq!(critical_path_length(&graph), 2);
}
#[test]
fn test_critical_path_two_round_schedule() {
let schedule = generate_surface_code_schedule(3, 2);
let graph = build_dependency_graph(&schedule);
let cp = critical_path_length(&graph);
assert_eq!(cp, 6);
}
#[test]
fn test_critical_path_five_round_schedule() {
let schedule = generate_surface_code_schedule(3, 5);
let graph = build_dependency_graph(&schedule);
let cp = critical_path_length(&graph);
assert_eq!(cp, 15);
}
#[test]
fn test_full_pipeline_d3() {
let schedule = generate_surface_code_schedule(3, 4);
let optimized = optimize_feed_forward(&schedule);
let graph = build_dependency_graph(&optimized);
let cp = critical_path_length(&graph);
assert!(cp > 0);
assert!(optimized.total_classical_depth <= schedule.total_classical_depth);
let lat = schedule_latency(&optimized, 50, 1000);
assert!(lat > 0);
}
#[test]
fn test_full_pipeline_d5() {
let schedule = generate_surface_code_schedule(5, 10);
let optimized = optimize_feed_forward(&schedule);
let graph = build_dependency_graph(&optimized);
let cp = critical_path_length(&graph);
assert!(cp > 0);
let lat_orig = schedule_latency(&schedule, 50, 1000);
let lat_opt = schedule_latency(&optimized, 50, 1000);
assert!(lat_opt <= lat_orig);
}
#[test]
fn test_latency_scales_with_distance() {
let lat_d3 = schedule_latency(&generate_surface_code_schedule(3, 5), 50, 1000);
let lat_d5 = schedule_latency(&generate_surface_code_schedule(5, 5), 50, 1000);
assert!(
lat_d5 >= lat_d3,
"Larger distance should have >= latency: d5={} >= d3={}",
lat_d5,
lat_d3
);
}
#[test]
fn test_latency_scales_with_rounds() {
let lat_5 = schedule_latency(&generate_surface_code_schedule(3, 5), 50, 1000);
let lat_10 = schedule_latency(&generate_surface_code_schedule(3, 10), 50, 1000);
assert!(
lat_10 >= lat_5,
"More rounds should have >= latency: {} >= {}",
lat_10,
lat_5
);
}
#[test]
fn test_optimization_idempotent() {
let schedule = generate_surface_code_schedule(3, 4);
let opt1 = optimize_feed_forward(&schedule);
let opt2 = optimize_feed_forward(&opt1);
assert_eq!(
opt1.feed_forward_points.len(),
opt2.feed_forward_points.len()
);
}
#[test]
fn test_dependency_graph_node_count() {
for num_rounds in 1..=5 {
let schedule = generate_surface_code_schedule(3, num_rounds);
let graph = build_dependency_graph(&schedule);
assert_eq!(
graph.nodes.len(),
(num_rounds as usize) * 3,
"Should have 3 nodes per round"
);
}
}
#[test]
fn test_can_merge_no_corrections() {
let a = QecRound {
syndrome_extractions: vec![],
corrections: vec![],
is_feed_forward: false,
};
let b = QecRound {
syndrome_extractions: vec![],
corrections: vec![],
is_feed_forward: false,
};
assert!(can_merge_rounds(&a, &b));
}
#[test]
fn test_cannot_merge_with_dependency() {
let a = QecRound {
syndrome_extractions: vec![],
corrections: vec![],
is_feed_forward: false,
};
let b = QecRound {
syndrome_extractions: vec![],
corrections: vec![ScheduledCorrection {
target_qubit: 0,
correction_type: PauliType::X,
depends_on_round: Some(1),
}],
is_feed_forward: true,
};
assert!(!can_merge_rounds(&a, &b));
}
}