1use std::collections::HashMap;
9use std::hash::{Hash, Hasher};
10
11use crate::arch::addr::{LaneAddr, LocationAddr, ZoneAddr};
12use crate::arch::types::ArchSpec;
13
14#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct AtomStateData {
25 pub locations_to_qubit: HashMap<LocationAddr, u32>,
27 pub qubit_to_locations: HashMap<u32, LocationAddr>,
29 pub collision: HashMap<u32, u32>,
35 pub prev_lanes: HashMap<u32, LaneAddr>,
38 pub move_count: HashMap<u32, u32>,
41}
42
43impl Hash for AtomStateData {
44 fn hash<H: Hasher>(&self, state: &mut H) {
45 fn hash_sorted_map<H: Hasher, K: Ord + Hash, V: Hash>(
48 state: &mut H,
49 tag: u8,
50 entries: &mut [(K, V)],
51 ) {
52 tag.hash(state);
53 entries.len().hash(state);
54 entries.sort_by(|a, b| a.0.cmp(&b.0));
55 for (k, v) in entries.iter() {
56 k.hash(state);
57 v.hash(state);
58 }
59 }
60
61 let mut loc_entries: Vec<_> = self
62 .locations_to_qubit
63 .iter()
64 .map(|(k, v)| (k.encode(), *v))
65 .collect();
66 hash_sorted_map(state, 0, &mut loc_entries);
67
68 let mut qubit_entries: Vec<_> = self
69 .qubit_to_locations
70 .iter()
71 .map(|(k, v)| (*k, v.encode()))
72 .collect();
73 hash_sorted_map(state, 1, &mut qubit_entries);
74
75 let mut collision_entries: Vec<_> = self.collision.iter().map(|(k, v)| (*k, *v)).collect();
76 hash_sorted_map(state, 2, &mut collision_entries);
77
78 let mut lane_entries: Vec<_> = self
79 .prev_lanes
80 .iter()
81 .map(|(k, v)| (*k, v.encode_u64()))
82 .collect();
83 hash_sorted_map(state, 3, &mut lane_entries);
84
85 let mut count_entries: Vec<_> = self.move_count.iter().map(|(k, v)| (*k, *v)).collect();
86 hash_sorted_map(state, 4, &mut count_entries);
87 }
88}
89
90impl AtomStateData {
91 pub fn new() -> Self {
93 Self {
94 locations_to_qubit: HashMap::new(),
95 qubit_to_locations: HashMap::new(),
96 collision: HashMap::new(),
97 prev_lanes: HashMap::new(),
98 move_count: HashMap::new(),
99 }
100 }
101
102 pub fn from_locations(locations: &[(u32, LocationAddr)]) -> Self {
107 let mut locations_to_qubit = HashMap::new();
108 let mut qubit_to_locations = HashMap::new();
109
110 for &(qubit, loc) in locations {
111 qubit_to_locations.insert(qubit, loc);
112 locations_to_qubit.insert(loc, qubit);
113 }
114
115 Self {
116 locations_to_qubit,
117 qubit_to_locations,
118 collision: HashMap::new(),
119 prev_lanes: HashMap::new(),
120 move_count: HashMap::new(),
121 }
122 }
123
124 pub fn add_atoms(&self, locations: &[(u32, LocationAddr)]) -> Result<Self, &'static str> {
133 let mut qubit_to_locations = self.qubit_to_locations.clone();
134 let mut locations_to_qubit = self.locations_to_qubit.clone();
135
136 for &(qubit, loc) in locations {
137 if qubit_to_locations.contains_key(&qubit) {
138 return Err("Attempted to add atom that already exists");
139 }
140 if locations_to_qubit.contains_key(&loc) {
141 return Err("Attempted to add atom to occupied location");
142 }
143 qubit_to_locations.insert(qubit, loc);
144 locations_to_qubit.insert(loc, qubit);
145 }
146
147 Ok(Self {
148 locations_to_qubit,
149 qubit_to_locations,
150 collision: HashMap::new(),
151 prev_lanes: HashMap::new(),
152 move_count: HashMap::new(),
153 })
154 }
155
156 pub fn apply_moves(&self, lanes: &[LaneAddr], arch_spec: &ArchSpec) -> Option<Self> {
168 let mut qubit_to_locations = self.qubit_to_locations.clone();
169 let mut locations_to_qubit = self.locations_to_qubit.clone();
170 let mut collisions = self.collision.clone();
171 let mut move_count = self.move_count.clone();
172 let mut prev_lanes: HashMap<u32, LaneAddr> = HashMap::new();
173
174 for lane in lanes {
175 let (src, dst) = arch_spec.lane_endpoints(lane)?;
176
177 let qubit = match locations_to_qubit.remove(&src) {
178 Some(q) => q,
179 None => continue,
180 };
181
182 *move_count.entry(qubit).or_insert(0) += 1;
183 prev_lanes.insert(qubit, *lane);
184
185 if let Some(other_qubit) = locations_to_qubit.remove(&dst) {
186 qubit_to_locations.remove(&qubit);
187 qubit_to_locations.remove(&other_qubit);
188 collisions.insert(qubit, other_qubit);
189 } else {
190 qubit_to_locations.insert(qubit, dst);
191 locations_to_qubit.insert(dst, qubit);
192 }
193 }
194
195 Some(Self {
196 locations_to_qubit,
197 qubit_to_locations,
198 prev_lanes,
199 collision: collisions,
200 move_count,
201 })
202 }
203
204 pub fn get_qubit(&self, location: &LocationAddr) -> Option<u32> {
206 self.locations_to_qubit.get(location).copied()
207 }
208
209 pub fn get_qubit_pairing(
221 &self,
222 zone: &ZoneAddr,
223 arch_spec: &ArchSpec,
224 ) -> Option<(Vec<u32>, Vec<u32>, Vec<u32>)> {
225 let zone_data = arch_spec.zone_by_id(zone.zone_id)?;
226 let word_ids: std::collections::HashSet<u32> = zone_data.words.iter().copied().collect();
227
228 let mut controls = Vec::new();
229 let mut targets = Vec::new();
230 let mut unpaired = Vec::new();
231 let mut visited = std::collections::HashSet::new();
232
233 let mut sorted_qubits: Vec<_> = self.qubit_to_locations.iter().collect();
235 sorted_qubits.sort_by_key(|(qubit, _)| **qubit);
236
237 for (qubit, loc) in &sorted_qubits {
238 let qubit = **qubit;
239 let loc = **loc;
240 if visited.contains(&qubit) {
241 continue;
242 }
243 visited.insert(qubit);
244
245 if !word_ids.contains(&loc.word_id) {
246 continue;
247 }
248
249 let blockaded = match arch_spec.get_blockaded_location(&loc) {
250 Some(b) => b,
251 None => {
252 unpaired.push(qubit);
253 continue;
254 }
255 };
256
257 let target_qubit = match self.get_qubit(&blockaded) {
258 Some(t) => t,
259 None => {
260 unpaired.push(qubit);
261 continue;
262 }
263 };
264
265 controls.push(qubit);
266 targets.push(target_qubit);
267 visited.insert(target_qubit);
268 }
269
270 Some((controls, targets, unpaired))
271 }
272}
273
274impl Default for AtomStateData {
275 fn default() -> Self {
276 Self::new()
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use super::*;
283 use crate::arch::example_arch_spec;
284
285 #[test]
286 fn new_state_is_empty() {
287 let state = AtomStateData::new();
288 assert!(state.locations_to_qubit.is_empty());
289 assert!(state.qubit_to_locations.is_empty());
290 assert!(state.collision.is_empty());
291 assert!(state.prev_lanes.is_empty());
292 assert!(state.move_count.is_empty());
293 }
294
295 #[test]
296 fn from_locations_creates_bidirectional_map() {
297 let locs = vec![
298 (
299 0,
300 LocationAddr {
301 word_id: 0,
302 site_id: 0,
303 },
304 ),
305 (
306 1,
307 LocationAddr {
308 word_id: 1,
309 site_id: 0,
310 },
311 ),
312 ];
313 let state = AtomStateData::from_locations(&locs);
314 assert_eq!(
315 state.get_qubit(&LocationAddr {
316 word_id: 0,
317 site_id: 0
318 }),
319 Some(0)
320 );
321 assert_eq!(
322 state.get_qubit(&LocationAddr {
323 word_id: 1,
324 site_id: 0
325 }),
326 Some(1)
327 );
328 }
329
330 #[test]
331 fn add_atoms_succeeds_and_fields_match() {
332 let state = AtomStateData::new();
333 let loc0 = LocationAddr {
334 word_id: 0,
335 site_id: 0,
336 };
337 let loc1 = LocationAddr {
338 word_id: 1,
339 site_id: 0,
340 };
341 let new_state = state.add_atoms(&[(0, loc0), (1, loc1)]).unwrap();
342
343 assert_eq!(new_state.qubit_to_locations.len(), 2);
344 assert_eq!(new_state.qubit_to_locations[&0], loc0);
345 assert_eq!(new_state.qubit_to_locations[&1], loc1);
346 assert_eq!(new_state.locations_to_qubit[&loc0], 0);
347 assert_eq!(new_state.locations_to_qubit[&loc1], 1);
348 assert!(new_state.collision.is_empty());
349 assert!(new_state.prev_lanes.is_empty());
350 assert!(new_state.move_count.is_empty());
351 }
352
353 #[test]
354 fn add_atoms_duplicate_qubit_fails() {
355 let state = AtomStateData::from_locations(&[(
356 0,
357 LocationAddr {
358 word_id: 0,
359 site_id: 0,
360 },
361 )]);
362 let result = state.add_atoms(&[(
363 0,
364 LocationAddr {
365 word_id: 1,
366 site_id: 0,
367 },
368 )]);
369 assert!(result.is_err());
370 }
371
372 #[test]
373 fn add_atoms_occupied_location_fails() {
374 let state = AtomStateData::from_locations(&[(
375 0,
376 LocationAddr {
377 word_id: 0,
378 site_id: 0,
379 },
380 )]);
381 let result = state.add_atoms(&[(
382 1,
383 LocationAddr {
384 word_id: 0,
385 site_id: 0,
386 },
387 )]);
388 assert!(result.is_err());
389 }
390
391 #[test]
392 fn apply_moves_basic() {
393 let spec = example_arch_spec();
394 let state = AtomStateData::from_locations(&[
395 (
396 0,
397 LocationAddr {
398 word_id: 0,
399 site_id: 0,
400 },
401 ),
402 (
403 1,
404 LocationAddr {
405 word_id: 1,
406 site_id: 0,
407 },
408 ),
409 ]);
410
411 let lane = LaneAddr {
413 direction: crate::arch::addr::Direction::Forward,
414 move_type: crate::arch::addr::MoveType::SiteBus,
415 word_id: 0,
416 site_id: 0,
417 bus_id: 0,
418 };
419
420 let new_state = state.apply_moves(&[lane], &spec).unwrap();
421 assert_eq!(
422 new_state.get_qubit(&LocationAddr {
423 word_id: 0,
424 site_id: 5
425 }),
426 Some(0)
427 );
428 assert_eq!(
429 new_state.get_qubit(&LocationAddr {
430 word_id: 0,
431 site_id: 0
432 }),
433 None
434 );
435 assert_eq!(*new_state.move_count.get(&0).unwrap(), 1);
436 }
437
438 #[test]
439 fn apply_moves_collision() {
440 let spec = example_arch_spec();
441 let state = AtomStateData::from_locations(&[
442 (
443 0,
444 LocationAddr {
445 word_id: 0,
446 site_id: 0,
447 },
448 ),
449 (
450 1,
451 LocationAddr {
452 word_id: 0,
453 site_id: 5,
454 },
455 ),
456 ]);
457
458 let lane = LaneAddr {
459 direction: crate::arch::addr::Direction::Forward,
460 move_type: crate::arch::addr::MoveType::SiteBus,
461 word_id: 0,
462 site_id: 0,
463 bus_id: 0,
464 };
465
466 let new_state = state.apply_moves(&[lane], &spec).unwrap();
467 assert!(new_state.collision.contains_key(&0));
468 assert_eq!(*new_state.collision.get(&0).unwrap(), 1);
469 assert!(new_state.qubit_to_locations.is_empty());
470 }
471
472 #[test]
473 fn apply_moves_verifies_all_fields() {
474 let spec = example_arch_spec();
475 let loc_0_0 = LocationAddr {
476 word_id: 0,
477 site_id: 0,
478 };
479 let loc_0_5 = LocationAddr {
480 word_id: 0,
481 site_id: 5,
482 };
483 let loc_1_0 = LocationAddr {
484 word_id: 1,
485 site_id: 0,
486 };
487 let state = AtomStateData::from_locations(&[(0, loc_0_0), (1, loc_1_0)]);
488
489 let lane = LaneAddr {
490 direction: crate::arch::addr::Direction::Forward,
491 move_type: crate::arch::addr::MoveType::SiteBus,
492 word_id: 0,
493 site_id: 0,
494 bus_id: 0,
495 };
496
497 let new_state = state.apply_moves(&[lane], &spec).unwrap();
498
499 assert_eq!(new_state.qubit_to_locations[&0], loc_0_5);
501 assert_eq!(new_state.locations_to_qubit[&loc_0_5], 0);
502 assert_eq!(new_state.qubit_to_locations[&1], loc_1_0);
504 assert_eq!(new_state.locations_to_qubit[&loc_1_0], 1);
505 assert!(!new_state.locations_to_qubit.contains_key(&loc_0_0));
507 assert_eq!(new_state.prev_lanes.len(), 1);
509 assert_eq!(new_state.prev_lanes[&0], lane);
510 assert_eq!(new_state.move_count[&0], 1);
512 assert!(new_state.collision.is_empty());
514 }
515
516 #[test]
517 fn apply_moves_collision_verifies_all_fields() {
518 let spec = example_arch_spec();
519 let state = AtomStateData::from_locations(&[
520 (
521 0,
522 LocationAddr {
523 word_id: 0,
524 site_id: 0,
525 },
526 ),
527 (
528 1,
529 LocationAddr {
530 word_id: 0,
531 site_id: 5,
532 },
533 ),
534 ]);
535
536 let lane = LaneAddr {
537 direction: crate::arch::addr::Direction::Forward,
538 move_type: crate::arch::addr::MoveType::SiteBus,
539 word_id: 0,
540 site_id: 0,
541 bus_id: 0,
542 };
543
544 let new_state = state.apply_moves(&[lane], &spec).unwrap();
545
546 assert!(new_state.qubit_to_locations.is_empty());
548 assert!(new_state.locations_to_qubit.is_empty());
549 assert_eq!(new_state.collision[&0], 1);
551 assert_eq!(new_state.prev_lanes[&0], lane);
553 assert_eq!(new_state.move_count[&0], 1);
555 }
556
557 #[test]
558 fn apply_moves_skips_empty_source() {
559 let spec = example_arch_spec();
560 let state = AtomStateData::from_locations(&[(
562 1,
563 LocationAddr {
564 word_id: 1,
565 site_id: 0,
566 },
567 )]);
568
569 let lane = LaneAddr {
570 direction: crate::arch::addr::Direction::Forward,
571 move_type: crate::arch::addr::MoveType::SiteBus,
572 word_id: 0,
573 site_id: 0,
574 bus_id: 0,
575 };
576
577 let new_state = state.apply_moves(&[lane], &spec).unwrap();
578 assert_eq!(new_state.qubit_to_locations.len(), 1);
580 assert!(new_state.prev_lanes.is_empty());
581 assert!(new_state.move_count.is_empty());
582 }
583
584 #[test]
585 fn apply_moves_invalid_lane_returns_none() {
586 let spec = example_arch_spec();
587 let state = AtomStateData::from_locations(&[(
588 0,
589 LocationAddr {
590 word_id: 0,
591 site_id: 0,
592 },
593 )]);
594
595 let bad_lane = LaneAddr {
596 direction: crate::arch::addr::Direction::Forward,
597 move_type: crate::arch::addr::MoveType::SiteBus,
598 word_id: 0,
599 site_id: 0,
600 bus_id: 99, };
602
603 assert!(state.apply_moves(&[bad_lane], &spec).is_none());
604 }
605
606 #[test]
607 fn apply_moves_accumulates_move_count() {
608 let spec = example_arch_spec();
609 let state = AtomStateData::from_locations(&[(
610 0,
611 LocationAddr {
612 word_id: 0,
613 site_id: 0,
614 },
615 )]);
616
617 let lane_fwd = LaneAddr {
619 direction: crate::arch::addr::Direction::Forward,
620 move_type: crate::arch::addr::MoveType::SiteBus,
621 word_id: 0,
622 site_id: 0,
623 bus_id: 0,
624 };
625 let state2 = state.apply_moves(&[lane_fwd], &spec).unwrap();
626 assert_eq!(state2.move_count[&0], 1);
627
628 let lane_bwd = LaneAddr {
631 direction: crate::arch::addr::Direction::Backward,
632 move_type: crate::arch::addr::MoveType::SiteBus,
633 word_id: 0,
634 site_id: 0,
635 bus_id: 0,
636 };
637 let state3 = state2.apply_moves(&[lane_bwd], &spec).unwrap();
638 assert_eq!(state3.move_count[&0], 2);
639 }
640
641 #[test]
642 fn get_qubit_empty_location() {
643 let state = AtomStateData::from_locations(&[(
644 0,
645 LocationAddr {
646 word_id: 0,
647 site_id: 0,
648 },
649 )]);
650 assert_eq!(
651 state.get_qubit(&LocationAddr {
652 word_id: 1,
653 site_id: 0
654 }),
655 None
656 );
657 }
658
659 #[test]
660 fn get_qubit_pairing_all_unpaired() {
661 let spec = example_arch_spec();
662 let state = AtomStateData::from_locations(&[
665 (
666 0,
667 LocationAddr {
668 word_id: 0,
669 site_id: 0,
670 },
671 ),
672 (
673 1,
674 LocationAddr {
675 word_id: 0,
676 site_id: 1,
677 },
678 ),
679 (
680 2,
681 LocationAddr {
682 word_id: 1,
683 site_id: 0,
684 },
685 ),
686 ]);
687
688 let zone = ZoneAddr { zone_id: 0 };
689 let (controls, targets, unpaired) = state.get_qubit_pairing(&zone, &spec).unwrap();
690
691 assert!(controls.is_empty());
692 assert!(targets.is_empty());
693 assert_eq!(unpaired.len(), 3);
694 }
695
696 #[test]
697 fn get_qubit_pairing_with_pairs() {
698 let spec = example_arch_spec();
699 let state = AtomStateData::from_locations(&[
702 (
703 0,
704 LocationAddr {
705 word_id: 0,
706 site_id: 0,
707 },
708 ),
709 (
710 1,
711 LocationAddr {
712 word_id: 0,
713 site_id: 5,
714 },
715 ),
716 (
717 2,
718 LocationAddr {
719 word_id: 1,
720 site_id: 0,
721 },
722 ),
723 (
724 3,
725 LocationAddr {
726 word_id: 1,
727 site_id: 5,
728 },
729 ),
730 (
731 4,
732 LocationAddr {
733 word_id: 0,
734 site_id: 1,
735 },
736 ),
737 ]);
738
739 let zone = ZoneAddr { zone_id: 0 };
740 let (controls, targets, unpaired) = state.get_qubit_pairing(&zone, &spec).unwrap();
741
742 assert_eq!(controls.len(), 2);
744 assert_eq!(targets.len(), 2);
745 use std::collections::HashSet;
746 let control_set: HashSet<u32> = controls.iter().copied().collect();
747 let target_set: HashSet<u32> = targets.iter().copied().collect();
748 assert_eq!(control_set, HashSet::from([0, 2]));
749 assert_eq!(target_set, HashSet::from([1, 3]));
750 assert_eq!(unpaired, vec![4]);
752 }
753
754 #[test]
755 fn get_qubit_pairing_invalid_zone() {
756 let spec = example_arch_spec();
757 let state = AtomStateData::new();
758 let zone = ZoneAddr { zone_id: 99 };
759 assert!(state.get_qubit_pairing(&zone, &spec).is_none());
760 }
761
762 #[test]
763 fn get_qubit_pairing_skips_qubits_outside_zone() {
764 let spec = example_arch_spec();
765 let state = AtomStateData::from_locations(&[(
768 0,
769 LocationAddr {
770 word_id: 0,
771 site_id: 0,
772 },
773 )]);
774
775 let zone = ZoneAddr { zone_id: 0 };
777 let (controls, targets, unpaired) = state.get_qubit_pairing(&zone, &spec).unwrap();
778
779 assert!(controls.is_empty());
780 assert!(targets.is_empty());
781 assert_eq!(unpaired, vec![0]);
782 }
783
784 #[test]
785 fn default_is_empty() {
786 let state = AtomStateData::default();
787 assert!(state.locations_to_qubit.is_empty());
788 assert!(state.qubit_to_locations.is_empty());
789 }
790
791 #[test]
792 fn clone_produces_equal_state() {
793 let state = AtomStateData::from_locations(&[
794 (
795 0,
796 LocationAddr {
797 word_id: 0,
798 site_id: 0,
799 },
800 ),
801 (
802 1,
803 LocationAddr {
804 word_id: 1,
805 site_id: 0,
806 },
807 ),
808 ]);
809 let cloned = state.clone();
810 assert_eq!(state, cloned);
811 }
812
813 #[test]
814 fn hash_is_deterministic() {
815 use std::collections::hash_map::DefaultHasher;
816
817 let state1 = AtomStateData::from_locations(&[
818 (
819 0,
820 LocationAddr {
821 word_id: 0,
822 site_id: 0,
823 },
824 ),
825 (
826 1,
827 LocationAddr {
828 word_id: 1,
829 site_id: 0,
830 },
831 ),
832 ]);
833 let state2 = AtomStateData::from_locations(&[
834 (
835 1,
836 LocationAddr {
837 word_id: 1,
838 site_id: 0,
839 },
840 ),
841 (
842 0,
843 LocationAddr {
844 word_id: 0,
845 site_id: 0,
846 },
847 ),
848 ]);
849
850 let mut h1 = DefaultHasher::new();
851 let mut h2 = DefaultHasher::new();
852 state1.hash(&mut h1);
853 state2.hash(&mut h2);
854 assert_eq!(h1.finish(), h2.finish());
855 }
856}