1use crate::builder::Circuit;
7use crate::dag::{circuit_to_dag, CircuitDag, DagNode};
8use crate::slicing::{CircuitSlice, SlicingStrategy};
9use quantrs2_core::{
10 error::{QuantRS2Error, QuantRS2Result},
11 gate::GateOp,
12 qubit::QubitId,
13};
14use std::collections::{HashMap, HashSet, VecDeque};
15use std::sync::Arc;
16
17#[derive(Debug, Clone)]
19pub struct CrosstalkModel {
20 pub crosstalk_coefficients: HashMap<((usize, usize), (usize, usize)), f64>,
23 pub single_qubit_crosstalk: HashMap<(usize, usize), f64>,
25 pub threshold: f64,
27 pub coupling_map: Vec<(usize, usize)>,
29}
30
31impl Default for CrosstalkModel {
32 fn default() -> Self {
33 Self::new()
34 }
35}
36
37impl CrosstalkModel {
38 #[must_use]
40 pub fn new() -> Self {
41 Self {
42 crosstalk_coefficients: HashMap::new(),
43 single_qubit_crosstalk: HashMap::new(),
44 threshold: 0.01,
45 coupling_map: Vec::new(),
46 }
47 }
48
49 #[must_use]
51 pub fn uniform(num_qubits: usize, crosstalk_rate: f64) -> Self {
52 let mut model = Self::new();
53
54 for i in 0..num_qubits {
56 for j in (i + 1)..num_qubits {
57 let dist = f64::from((i as i32 - j as i32).abs());
58 let crosstalk = crosstalk_rate / dist; model.single_qubit_crosstalk.insert((i, j), crosstalk);
62 model.single_qubit_crosstalk.insert((j, i), crosstalk);
63
64 for k in 0..num_qubits {
66 for l in (k + 1)..num_qubits {
67 if (i, j) != (k, l) {
68 let key = ((i.min(j), i.max(j)), (k.min(l), k.max(l)));
69 model.crosstalk_coefficients.insert(key, crosstalk * 2.0);
70 }
71 }
72 }
73 }
74 }
75
76 for i in 0..(num_qubits - 1) {
78 model.coupling_map.push((i, i + 1));
79 }
80
81 model
82 }
83
84 #[must_use]
86 pub fn from_characterization(data: &CrosstalkCharacterization) -> Self {
87 let mut model = Self::new();
88
89 model
91 .crosstalk_coefficients
92 .clone_from(&data.measured_crosstalk);
93 model.threshold = data.significance_threshold;
94 model.coupling_map.clone_from(&data.device_coupling);
95
96 for (&(q1, q2), &value) in &data.single_qubit_measurements {
98 model.single_qubit_crosstalk.insert((q1, q2), value);
99 }
100
101 model
102 }
103
104 pub fn get_crosstalk(&self, gate1: &dyn GateOp, gate2: &dyn GateOp) -> f64 {
106 let qubits1 = gate1.qubits();
107 let qubits2 = gate2.qubits();
108
109 for q1 in &qubits1 {
111 for q2 in &qubits2 {
112 if q1.id() == q2.id() {
113 return f64::INFINITY; }
115 }
116 }
117
118 match (qubits1.len(), qubits2.len()) {
120 (1, 1) => {
121 let q1 = qubits1[0].id() as usize;
123 let q2 = qubits2[0].id() as usize;
124 self.single_qubit_crosstalk
125 .get(&(q1, q2))
126 .copied()
127 .unwrap_or(0.0)
128 }
129 (2, 2) => {
130 let pair1 = (qubits1[0].id() as usize, qubits1[1].id() as usize);
132 let pair2 = (qubits2[0].id() as usize, qubits2[1].id() as usize);
133 let key = (
134 (pair1.0.min(pair1.1), pair1.0.max(pair1.1)),
135 (pair2.0.min(pair2.1), pair2.0.max(pair2.1)),
136 );
137 self.crosstalk_coefficients
138 .get(&key)
139 .copied()
140 .unwrap_or(0.0)
141 }
142 (1, 2) | (2, 1) => {
143 let mut total = 0.0;
146 let mut count = 0;
147 for q1 in &qubits1 {
148 for q2 in &qubits2 {
149 let key = (q1.id() as usize, q2.id() as usize);
150 if let Some(&crosstalk) = self.single_qubit_crosstalk.get(&key) {
151 total += crosstalk;
152 count += 1;
153 }
154 }
155 }
156 if count > 0 {
157 total / f64::from(count)
158 } else {
159 0.0
160 }
161 }
162 _ => 0.0, }
164 }
165
166 pub fn can_parallelize(&self, gate1: &dyn GateOp, gate2: &dyn GateOp) -> bool {
168 self.get_crosstalk(gate1, gate2) < self.threshold
169 }
170}
171
172#[derive(Debug, Clone)]
174pub struct CrosstalkCharacterization {
175 pub measured_crosstalk: HashMap<((usize, usize), (usize, usize)), f64>,
176 pub single_qubit_measurements: HashMap<(usize, usize), f64>,
177 pub significance_threshold: f64,
178 pub device_coupling: Vec<(usize, usize)>,
179}
180
181#[derive(Debug)]
183pub struct CrosstalkSchedule<const N: usize> {
184 pub time_slices: Vec<TimeSlice>,
186 pub circuit: Circuit<N>,
188 pub total_crosstalk: f64,
190 pub execution_time: f64,
192}
193
194#[derive(Debug, Clone)]
196pub struct TimeSlice {
197 pub gates: Vec<usize>, pub max_crosstalk: f64,
201 pub duration: f64,
203}
204
205pub struct CrosstalkScheduler {
207 model: CrosstalkModel,
208 strategy: SchedulingStrategy,
209}
210
211#[derive(Debug, Clone)]
212pub enum SchedulingStrategy {
213 MinimizeCrosstalk,
215 MinimizeTime { max_crosstalk: f64 },
217 Balanced {
219 time_weight: f64,
220 crosstalk_weight: f64,
221 },
222}
223
224impl CrosstalkScheduler {
225 #[must_use]
227 pub const fn new(model: CrosstalkModel) -> Self {
228 Self {
229 model,
230 strategy: SchedulingStrategy::MinimizeCrosstalk,
231 }
232 }
233
234 #[must_use]
236 pub const fn with_strategy(mut self, strategy: SchedulingStrategy) -> Self {
237 self.strategy = strategy;
238 self
239 }
240
241 pub fn schedule<const N: usize>(
243 &self,
244 circuit: &Circuit<N>,
245 ) -> QuantRS2Result<CrosstalkSchedule<N>> {
246 let dag = circuit_to_dag(circuit);
247
248 match self.strategy {
249 SchedulingStrategy::MinimizeCrosstalk => self.schedule_min_crosstalk(&dag, circuit),
250 SchedulingStrategy::MinimizeTime { max_crosstalk } => {
251 self.schedule_min_time(&dag, circuit, max_crosstalk)
252 }
253 SchedulingStrategy::Balanced {
254 time_weight,
255 crosstalk_weight,
256 } => self.schedule_balanced(&dag, circuit, time_weight, crosstalk_weight),
257 }
258 }
259
260 fn schedule_min_crosstalk<const N: usize>(
262 &self,
263 dag: &CircuitDag,
264 circuit: &Circuit<N>,
265 ) -> QuantRS2Result<CrosstalkSchedule<N>> {
266 let gates = circuit.gates();
267 let mut time_slices = Vec::new();
268 let mut scheduled = vec![false; gates.len()];
269 let mut total_crosstalk = 0.0;
270
271 let topo_order = dag
273 .topological_sort()
274 .map_err(QuantRS2Error::InvalidInput)?;
275
276 while scheduled.iter().any(|&s| !s) {
277 let mut current_slice = TimeSlice {
278 gates: Vec::new(),
279 max_crosstalk: 0.0,
280 duration: 0.0,
281 };
282
283 let ready_gates: Vec<usize> = (0..gates.len())
285 .filter(|&gate_idx| {
286 !scheduled[gate_idx] && self.dependencies_met(&scheduled, dag, gate_idx)
287 })
288 .collect();
289
290 for &gate_idx in &ready_gates {
292 let gate = &gates[gate_idx];
293
294 let mut slice_crosstalk: f64 = 0.0;
296 let mut can_add = true;
297
298 for &other_idx in ¤t_slice.gates {
299 let other_gate = &gates[other_idx];
300 let crosstalk = self.model.get_crosstalk(gate.as_ref(), other_gate.as_ref());
301
302 if crosstalk == f64::INFINITY {
303 can_add = false;
304 break;
305 }
306
307 slice_crosstalk = slice_crosstalk.max(crosstalk);
308 }
309
310 if can_add && slice_crosstalk < self.model.threshold {
311 current_slice.gates.push(gate_idx);
312 current_slice.max_crosstalk = current_slice.max_crosstalk.max(slice_crosstalk);
313 scheduled[gate_idx] = true;
314
315 current_slice.duration = 100.0;
317 }
318 }
319
320 if current_slice.gates.is_empty() && !ready_gates.is_empty() {
322 let gate_idx = ready_gates[0];
323 current_slice.gates.push(gate_idx);
324 scheduled[gate_idx] = true;
325 current_slice.duration = 100.0;
326 }
327
328 total_crosstalk += current_slice.max_crosstalk;
329 time_slices.push(current_slice);
330 }
331
332 let execution_time = time_slices.iter().map(|s| s.duration).sum();
333
334 Ok(CrosstalkSchedule {
335 time_slices,
336 circuit: circuit.clone(),
337 total_crosstalk,
338 execution_time,
339 })
340 }
341
342 fn schedule_min_time<const N: usize>(
344 &self,
345 dag: &CircuitDag,
346 circuit: &Circuit<N>,
347 max_crosstalk: f64,
348 ) -> QuantRS2Result<CrosstalkSchedule<N>> {
349 self.schedule_min_crosstalk(dag, circuit) }
353
354 fn schedule_balanced<const N: usize>(
356 &self,
357 dag: &CircuitDag,
358 circuit: &Circuit<N>,
359 time_weight: f64,
360 crosstalk_weight: f64,
361 ) -> QuantRS2Result<CrosstalkSchedule<N>> {
362 self.schedule_min_crosstalk(dag, circuit) }
365
366 fn dependencies_met(&self, scheduled: &[bool], dag: &CircuitDag, gate_idx: usize) -> bool {
368 for node in dag.nodes() {
370 if node.id == gate_idx {
371 return node.predecessors.iter().all(|&pred_id| {
373 if pred_id < scheduled.len() {
374 scheduled[pred_id]
375 } else {
376 true }
378 });
379 }
380 }
381 false
382 }
383}
384
385pub struct CrosstalkAnalyzer {
387 model: CrosstalkModel,
388}
389
390impl CrosstalkAnalyzer {
391 #[must_use]
393 pub const fn new(model: CrosstalkModel) -> Self {
394 Self { model }
395 }
396
397 #[must_use]
399 pub fn analyze<const N: usize>(&self, circuit: &Circuit<N>) -> CrosstalkAnalysis {
400 let gates = circuit.gates();
401 let mut problematic_pairs = Vec::new();
402 let mut crosstalk_graph = HashMap::new();
403
404 for i in 0..gates.len() {
406 for j in (i + 1)..gates.len() {
407 let crosstalk = self
408 .model
409 .get_crosstalk(gates[i].as_ref(), gates[j].as_ref());
410
411 if crosstalk > self.model.threshold && crosstalk < f64::INFINITY {
412 problematic_pairs.push((i, j, crosstalk));
413 }
414
415 if crosstalk > 0.0 && crosstalk < f64::INFINITY {
416 crosstalk_graph.insert((i, j), crosstalk);
417 }
418 }
419 }
420
421 problematic_pairs
423 .sort_by(|a, b| b.2.partial_cmp(&a.2).unwrap_or(std::cmp::Ordering::Equal));
424
425 let max_crosstalk = problematic_pairs.first().map_or(0.0, |p| p.2);
426
427 CrosstalkAnalysis {
428 total_gates: gates.len(),
429 problematic_pairs,
430 crosstalk_graph,
431 max_crosstalk,
432 }
433 }
434
435 pub fn suggest_reordering<const N: usize>(
437 &self,
438 circuit: &Circuit<N>,
439 ) -> QuantRS2Result<Vec<GateReordering>> {
440 let analysis = self.analyze(circuit);
441 let mut suggestions = Vec::new();
442
443 for (i, j, crosstalk) in analysis.problematic_pairs.iter().take(5) {
445 suggestions.push(GateReordering {
446 gate1: *i,
447 gate2: *j,
448 reason: format!("High crosstalk: {crosstalk:.4}"),
449 expected_improvement: crosstalk * 0.5, });
451 }
452
453 Ok(suggestions)
454 }
455}
456
457#[derive(Debug)]
459pub struct CrosstalkAnalysis {
460 pub total_gates: usize,
461 pub problematic_pairs: Vec<(usize, usize, f64)>, pub crosstalk_graph: HashMap<(usize, usize), f64>,
463 pub max_crosstalk: f64,
464}
465
466#[derive(Debug)]
468pub struct GateReordering {
469 pub gate1: usize,
470 pub gate2: usize,
471 pub reason: String,
472 pub expected_improvement: f64,
473}
474
475#[cfg(test)]
476mod tests {
477 use super::*;
478 use quantrs2_core::gate::multi::CNOT;
479 use quantrs2_core::gate::single::Hadamard;
480
481 #[test]
482 fn test_crosstalk_model() {
483 let model = CrosstalkModel::uniform(4, 0.05);
484
485 assert!(model.single_qubit_crosstalk.contains_key(&(0, 1)));
487
488 assert!(!model.crosstalk_coefficients.is_empty());
490 }
491
492 #[test]
493 fn test_gate_crosstalk() {
494 let model = CrosstalkModel::uniform(4, 0.05);
495
496 let gate1 = Hadamard { target: QubitId(0) };
497 let gate2 = Hadamard { target: QubitId(1) };
498 let gate3 = Hadamard { target: QubitId(0) }; let crosstalk = model.get_crosstalk(&gate1, &gate2);
501 assert!(crosstalk > 0.0 && crosstalk < 1.0);
502
503 let crosstalk_same = model.get_crosstalk(&gate1, &gate3);
504 assert_eq!(crosstalk_same, f64::INFINITY); }
506
507 #[test]
508 fn test_scheduling() {
509 let model = CrosstalkModel::uniform(4, 0.05);
510 let scheduler = CrosstalkScheduler::new(model);
511
512 let mut circuit = Circuit::<4>::new();
513 circuit
514 .add_gate(Hadamard { target: QubitId(0) })
515 .expect("Failed to add Hadamard gate on qubit 0");
516 circuit
517 .add_gate(Hadamard { target: QubitId(1) })
518 .expect("Failed to add Hadamard gate on qubit 1");
519 circuit
520 .add_gate(CNOT {
521 control: QubitId(0),
522 target: QubitId(1),
523 })
524 .expect("Failed to add CNOT gate");
525
526 let schedule = scheduler
527 .schedule(&circuit)
528 .expect("Failed to schedule circuit");
529 assert!(schedule.time_slices.len() >= 2); }
531
532 #[test]
533 fn test_crosstalk_analysis() {
534 let model = CrosstalkModel::uniform(4, 0.05);
535 let analyzer = CrosstalkAnalyzer::new(model);
536
537 let mut circuit = Circuit::<4>::new();
538 circuit
539 .add_gate(CNOT {
540 control: QubitId(0),
541 target: QubitId(1),
542 })
543 .expect("Failed to add first CNOT gate");
544 circuit
545 .add_gate(CNOT {
546 control: QubitId(2),
547 target: QubitId(3),
548 })
549 .expect("Failed to add second CNOT gate");
550
551 let analysis = analyzer.analyze(&circuit);
552 assert_eq!(analysis.total_gates, 2);
553 assert!(analysis.max_crosstalk > 0.0);
554 }
555}