1use std::collections::HashSet;
10use std::fmt;
11
12use super::instruction::{
13 ArrayInstruction, AtomArrangementInstruction, CpuInstruction, DetectorObservableInstruction,
14 Instruction, LaneConstInstruction, MeasurementInstruction, QuantumGateInstruction,
15};
16use super::program::Program;
17use super::value::{
18 TAG_ARRAY_REF, TAG_DETECTOR_REF, TAG_FLOAT, TAG_INT, TAG_LANE, TAG_LOCATION,
19 TAG_MEASURE_FUTURE, TAG_OBSERVABLE_REF, TAG_ZONE,
20};
21use crate::arch::addr::{LaneAddr, LocationAddr, ZoneAddr};
22use crate::arch::query::{LaneGroupError, LocationGroupError};
23use crate::arch::types::ArchSpec;
24
25const MAX_TYPE_TAG: u8 = 0x8;
27
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub enum ValidationError {
30 NewArrayZeroDim0 { pc: usize },
32 NewArrayInvalidTypeTag { pc: usize, type_tag: u8 },
34 InitialFillNotFirst { pc: usize },
36 StackUnderflow { pc: usize },
38 TypeMismatch { pc: usize, expected: u8, got: u8 },
40 InvalidZone { pc: usize, zone_id: u32 },
42 LocationGroupValidation {
44 pc: usize,
45 error: LocationGroupError,
46 },
47 LaneGroupValidation { pc: usize, error: LaneGroupError },
49 FeedForwardNotSupported { pc: usize },
51 AtomReloadingNotSupported { pc: usize },
53}
54
55impl fmt::Display for ValidationError {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 match self {
58 ValidationError::NewArrayZeroDim0 { pc } => {
59 write!(f, "pc {}: new_array dim0 must be > 0", pc)
60 }
61 ValidationError::NewArrayInvalidTypeTag { pc, type_tag } => {
62 write!(f, "pc {}: invalid type tag 0x{:x}", pc, type_tag)
63 }
64 ValidationError::InitialFillNotFirst { pc } => {
65 write!(
66 f,
67 "pc {}: initial_fill must be the first non-constant instruction",
68 pc
69 )
70 }
71 ValidationError::StackUnderflow { pc } => {
72 write!(f, "pc {}: stack underflow", pc)
73 }
74 ValidationError::TypeMismatch { pc, expected, got } => {
75 write!(
76 f,
77 "pc {}: type mismatch: expected tag 0x{:x}, got 0x{:x}",
78 pc, expected, got
79 )
80 }
81 ValidationError::InvalidZone { pc, zone_id } => {
82 write!(f, "pc {}: invalid zone_id={}", pc, zone_id)
83 }
84 ValidationError::LocationGroupValidation { pc, error } => {
85 write!(f, "pc {}: {}", pc, error)
86 }
87 ValidationError::LaneGroupValidation { pc, error } => {
88 write!(f, "pc {}: {}", pc, error)
89 }
90 ValidationError::FeedForwardNotSupported { pc } => {
91 write!(
92 f,
93 "pc {}: multiple measure instructions require feed_forward capability",
94 pc
95 )
96 }
97 ValidationError::AtomReloadingNotSupported { pc } => {
98 write!(
99 f,
100 "pc {}: fill instruction requires atom_reloading capability",
101 pc
102 )
103 }
104 }
105 }
106}
107
108impl std::error::Error for ValidationError {}
109
110pub fn validate_structure(program: &Program) -> Vec<ValidationError> {
112 let mut errors = Vec::new();
113 let mut seen_non_constant = false;
114
115 for (pc, instr) in program.instructions.iter().enumerate() {
116 match instr {
117 Instruction::Array(ArrayInstruction::NewArray {
118 type_tag,
119 dim0,
120 dim1: _,
121 }) => {
122 if *dim0 == 0 {
123 errors.push(ValidationError::NewArrayZeroDim0 { pc });
124 }
125 if *type_tag > MAX_TYPE_TAG {
126 errors.push(ValidationError::NewArrayInvalidTypeTag {
127 pc,
128 type_tag: *type_tag,
129 });
130 }
131 }
132 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { .. }) => {
133 if seen_non_constant {
134 errors.push(ValidationError::InitialFillNotFirst { pc });
135 }
136 seen_non_constant = true;
137 }
138 Instruction::AtomArrangement(AtomArrangementInstruction::Fill { .. })
139 | Instruction::AtomArrangement(AtomArrangementInstruction::Move { .. })
140 | Instruction::QuantumGate(QuantumGateInstruction::LocalR { .. })
141 | Instruction::QuantumGate(QuantumGateInstruction::LocalRz { .. })
142 | Instruction::Measurement(MeasurementInstruction::Measure { .. }) => {
143 seen_non_constant = true;
144 }
145 Instruction::Cpu(CpuInstruction::ConstFloat(_))
147 | Instruction::Cpu(CpuInstruction::ConstInt(_))
148 | Instruction::LaneConst(LaneConstInstruction::ConstLoc(_))
149 | Instruction::LaneConst(LaneConstInstruction::ConstLane(_, _))
150 | Instruction::LaneConst(LaneConstInstruction::ConstZone(_)) => {}
151 _ => {
153 seen_non_constant = true;
154 }
155 }
156 }
157
158 errors
159}
160
161pub fn validate_addresses(program: &Program, arch: &ArchSpec) -> Vec<ValidationError> {
165 let mut errors = Vec::new();
166
167 for (pc, instr) in program.instructions.iter().enumerate() {
168 match instr {
169 Instruction::LaneConst(LaneConstInstruction::ConstLoc(bits)) => {
170 let addr = LocationAddr::decode(*bits);
171 if arch.check_location(&addr).is_some() {
172 errors.push(ValidationError::LocationGroupValidation {
173 pc,
174 error: LocationGroupError::InvalidAddress {
175 zone_id: addr.zone_id,
176 word_id: addr.word_id,
177 site_id: addr.site_id,
178 },
179 });
180 }
181 }
182 Instruction::LaneConst(LaneConstInstruction::ConstLane(d0, d1)) => {
183 let addr = LaneAddr::decode(*d0, *d1);
184 for msg in arch.check_lane(&addr) {
185 errors.push(ValidationError::LaneGroupValidation {
186 pc,
187 error: LaneGroupError::InvalidLane { message: msg },
188 });
189 }
190 }
191 Instruction::LaneConst(LaneConstInstruction::ConstZone(bits)) => {
192 let addr = ZoneAddr::decode(*bits);
193 if arch.check_zone(&addr).is_some() {
194 errors.push(ValidationError::InvalidZone {
195 pc,
196 zone_id: addr.zone_id,
197 });
198 }
199 }
200 _ => {}
201 }
202 }
203
204 errors
205}
206
207pub fn validate_capabilities(program: &Program, arch: &ArchSpec) -> Vec<ValidationError> {
213 let mut errors = Vec::new();
214 let mut measure_count = 0u32;
215
216 for (pc, instr) in program.instructions.iter().enumerate() {
217 match instr {
218 Instruction::Measurement(MeasurementInstruction::Measure { .. }) => {
219 measure_count += 1;
220 if !arch.feed_forward && measure_count > 1 {
221 errors.push(ValidationError::FeedForwardNotSupported { pc });
222 }
223 }
224 Instruction::AtomArrangement(AtomArrangementInstruction::Fill { .. })
225 if !arch.atom_reloading =>
226 {
227 errors.push(ValidationError::AtomReloadingNotSupported { pc });
228 }
229 _ => {}
230 }
231 }
232
233 errors
234}
235
236pub fn validate_arch_constraints(program: &Program, arch: &ArchSpec) -> Vec<ValidationError> {
242 let mut errors = validate_addresses(program, arch);
243 errors.extend(validate_capabilities(program, arch));
244 errors
245}
246
247#[derive(Debug, Clone)]
249struct SimEntry {
250 tag: u8,
251 #[allow(dead_code)]
252 value: Option<u64>,
253}
254
255struct StackSimulator<'a> {
259 stack: Vec<SimEntry>,
260 errors: Vec<ValidationError>,
261 arch: Option<&'a ArchSpec>,
262 pc: usize,
263}
264
265impl<'a> StackSimulator<'a> {
266 fn new(arch: Option<&'a ArchSpec>) -> Self {
267 Self {
268 stack: Vec::new(),
269 errors: Vec::new(),
270 arch,
271 pc: 0,
272 }
273 }
274
275 fn pop_any(&mut self) {
279 if self.stack.pop().is_none() {
280 self.errors
281 .push(ValidationError::StackUnderflow { pc: self.pc });
282 }
283 }
284
285 fn pop_typed(&mut self, expected_tag: u8) {
287 match self.stack.pop() {
288 Some(entry) if entry.tag != expected_tag => {
289 self.errors.push(ValidationError::TypeMismatch {
290 pc: self.pc,
291 expected: expected_tag,
292 got: entry.tag,
293 });
294 }
295 Some(_) => {}
296 None => {
297 self.errors
298 .push(ValidationError::StackUnderflow { pc: self.pc });
299 }
300 }
301 }
302
303 fn pop_typed_n(&mut self, expected_tag: u8, count: u32) {
305 for _ in 0..count {
306 self.pop_typed(expected_tag);
307 }
308 }
309
310 fn pop_lane(&mut self) -> Option<u64> {
312 match self.stack.pop() {
313 Some(entry) => {
314 if entry.tag != TAG_LANE {
315 self.errors.push(ValidationError::TypeMismatch {
316 pc: self.pc,
317 expected: TAG_LANE,
318 got: entry.tag,
319 });
320 None
321 } else {
322 entry.value
323 }
324 }
325 None => {
326 self.errors
327 .push(ValidationError::StackUnderflow { pc: self.pc });
328 None
329 }
330 }
331 }
332
333 fn pop_location(&mut self) -> Option<u64> {
335 match self.stack.pop() {
336 Some(entry) => {
337 if entry.tag != TAG_LOCATION {
338 self.errors.push(ValidationError::TypeMismatch {
339 pc: self.pc,
340 expected: TAG_LOCATION,
341 got: entry.tag,
342 });
343 None
344 } else {
345 entry.value
346 }
347 }
348 None => {
349 self.errors
350 .push(ValidationError::StackUnderflow { pc: self.pc });
351 None
352 }
353 }
354 }
355
356 fn push_location_group_errors(&mut self, errors: Vec<LocationGroupError>) {
358 let pc = self.pc;
359 for error in errors {
360 self.errors
361 .push(ValidationError::LocationGroupValidation { pc, error });
362 }
363 }
364
365 fn push_lane_group_errors(&mut self, errors: Vec<LaneGroupError>) {
367 let pc = self.pc;
368 for error in errors {
369 self.errors
370 .push(ValidationError::LaneGroupValidation { pc, error });
371 }
372 }
373
374 fn check_duplicate_locations_standalone(&mut self, locations: &[LocationAddr]) {
377 let mut seen = HashSet::new();
378 let mut reported = HashSet::new();
379 for loc in locations {
380 let bits = loc.encode();
381 if !seen.insert(bits) && reported.insert(bits) {
382 self.errors.push(ValidationError::LocationGroupValidation {
383 pc: self.pc,
384 error: LocationGroupError::DuplicateAddress { address: bits },
385 });
386 }
387 }
388 }
389
390 fn check_duplicate_lanes_standalone(&mut self, lanes: &[LaneAddr]) {
393 let mut seen = HashSet::new();
394 let mut reported = HashSet::new();
395 for lane in lanes {
396 let (d0, d1) = lane.encode();
397 let combined = (d0 as u64) | ((d1 as u64) << 32);
398 if !seen.insert(combined) && reported.insert(combined) {
399 self.errors.push(ValidationError::LaneGroupValidation {
400 pc: self.pc,
401 error: LaneGroupError::DuplicateAddress { address: (d0, d1) },
402 });
403 }
404 }
405 }
406
407 fn push_const(&mut self, tag: u8, bits: u64) {
411 self.stack.push(SimEntry {
412 tag,
413 value: Some(bits),
414 });
415 }
416
417 fn sim_dup(&mut self) {
419 if let Some(top) = self.stack.last().cloned() {
420 self.stack.push(top);
421 } else {
422 self.errors
423 .push(ValidationError::StackUnderflow { pc: self.pc });
424 }
425 }
426
427 fn sim_swap(&mut self) {
429 let len = self.stack.len();
430 if len >= 2 {
431 self.stack.swap(len - 1, len - 2);
432 } else {
433 self.errors
434 .push(ValidationError::StackUnderflow { pc: self.pc });
435 }
436 }
437
438 fn pop_and_validate_locations(&mut self, arity: u32) {
441 let loc_values: Vec<Option<u64>> = (0..arity).map(|_| self.pop_location()).collect();
442 let locations: Vec<LocationAddr> = loc_values
443 .iter()
444 .filter_map(|v| v.map(LocationAddr::decode))
445 .collect();
446 if let Some(arch) = self.arch {
447 self.push_location_group_errors(arch.check_locations(&locations));
448 } else {
449 self.check_duplicate_locations_standalone(&locations);
450 }
451 }
452
453 fn sim_fill(&mut self, arity: u32) {
455 self.pop_and_validate_locations(arity);
456 }
457
458 fn sim_move(&mut self, arity: u32) {
462 let lane_values: Vec<Option<u64>> = (0..arity).map(|_| self.pop_lane()).collect();
463 let lanes: Vec<LaneAddr> = lane_values
464 .iter()
465 .filter_map(|v| {
466 v.map(|bits| {
467 let d0 = bits as u32;
468 let d1 = (bits >> 32) as u32;
469 LaneAddr::decode(d0, d1)
470 })
471 })
472 .collect();
473 if let Some(arch) = self.arch {
474 self.push_lane_group_errors(arch.check_lanes(&lanes));
475 } else {
476 self.check_duplicate_lanes_standalone(&lanes);
477 }
478 }
479
480 fn sim_local_r(&mut self, arity: u32) {
482 self.pop_typed_n(TAG_FLOAT, 2);
483 self.pop_and_validate_locations(arity);
484 }
485
486 fn sim_local_rz(&mut self, arity: u32) {
488 self.pop_typed_n(TAG_FLOAT, 1);
489 self.pop_and_validate_locations(arity);
490 }
491
492 fn sim_global_r(&mut self) {
494 self.pop_typed_n(TAG_FLOAT, 2);
495 }
496
497 fn sim_global_rz(&mut self) {
499 self.pop_typed_n(TAG_FLOAT, 1);
500 }
501
502 fn sim_cz(&mut self) {
504 self.pop_typed(TAG_ZONE);
505 }
506
507 fn sim_measure(&mut self, arity: u32) {
509 self.pop_typed_n(TAG_ZONE, arity);
510 for _ in 0..arity {
511 self.stack.push(SimEntry {
512 tag: TAG_MEASURE_FUTURE,
513 value: None,
514 });
515 }
516 }
517
518 fn sim_await_measure(&mut self) {
520 self.pop_typed(TAG_MEASURE_FUTURE);
521 self.stack.push(SimEntry {
522 tag: TAG_ARRAY_REF,
523 value: None,
524 });
525 }
526
527 fn sim_new_array(&mut self, dim0: u16, dim1: u16) {
529 let count = dim0 * if dim1 == 0 { 1 } else { dim1 };
530 for _ in 0..count {
531 self.pop_any();
532 }
533 self.stack.push(SimEntry {
534 tag: TAG_ARRAY_REF,
535 value: None,
536 });
537 }
538
539 fn sim_get_item(&mut self, ndims: u16) {
542 self.pop_typed_n(TAG_INT, ndims.into());
543 self.pop_typed(TAG_ARRAY_REF);
544 self.stack.push(SimEntry {
545 tag: TAG_FLOAT,
546 value: None,
547 });
548 }
549
550 fn sim_set_ref(&mut self, out_tag: u8) {
553 self.pop_typed(TAG_ARRAY_REF);
554 self.stack.push(SimEntry {
555 tag: out_tag,
556 value: None,
557 });
558 }
559
560 fn sim_return(&mut self) {
562 self.pop_any();
563 }
564
565 fn dispatch(&mut self, inst: &Instruction) {
569 match inst {
570 Instruction::Cpu(CpuInstruction::ConstFloat(v)) => {
571 self.push_const(TAG_FLOAT, v.to_bits());
572 }
573 Instruction::Cpu(CpuInstruction::ConstInt(v)) => {
574 self.push_const(TAG_INT, *v as u64);
575 }
576 Instruction::LaneConst(LaneConstInstruction::ConstLoc(v)) => {
577 self.push_const(TAG_LOCATION, *v);
578 }
579 Instruction::LaneConst(LaneConstInstruction::ConstLane(d0, d1)) => {
580 let combined = (*d0 as u64) | ((*d1 as u64) << 32);
581 self.push_const(TAG_LANE, combined);
582 }
583 Instruction::LaneConst(LaneConstInstruction::ConstZone(v)) => {
584 self.push_const(TAG_ZONE, *v as u64);
585 }
586
587 Instruction::Cpu(CpuInstruction::Pop) => self.pop_any(),
588 Instruction::Cpu(CpuInstruction::Dup) => self.sim_dup(),
589 Instruction::Cpu(CpuInstruction::Swap) => self.sim_swap(),
590
591 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity })
592 | Instruction::AtomArrangement(AtomArrangementInstruction::Fill { arity }) => {
593 self.sim_fill(*arity);
594 }
595 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity }) => {
596 self.sim_move(*arity);
597 }
598
599 Instruction::QuantumGate(QuantumGateInstruction::LocalR { arity }) => {
600 self.sim_local_r(*arity);
601 }
602 Instruction::QuantumGate(QuantumGateInstruction::LocalRz { arity }) => {
603 self.sim_local_rz(*arity);
604 }
605 Instruction::QuantumGate(QuantumGateInstruction::GlobalR) => self.sim_global_r(),
606 Instruction::QuantumGate(QuantumGateInstruction::GlobalRz) => self.sim_global_rz(),
607 Instruction::QuantumGate(QuantumGateInstruction::CZ) => self.sim_cz(),
608
609 Instruction::Measurement(MeasurementInstruction::Measure { arity }) => {
610 self.sim_measure(*arity);
611 }
612 Instruction::Measurement(MeasurementInstruction::AwaitMeasure) => {
613 self.sim_await_measure()
614 }
615
616 Instruction::Array(ArrayInstruction::NewArray {
617 type_tag: _,
618 dim0,
619 dim1,
620 }) => {
621 self.sim_new_array(*dim0, *dim1);
622 }
623 Instruction::Array(ArrayInstruction::GetItem { ndims }) => {
624 self.sim_get_item(*ndims);
625 }
626 Instruction::DetectorObservable(DetectorObservableInstruction::SetDetector) => {
627 self.sim_set_ref(TAG_DETECTOR_REF);
628 }
629 Instruction::DetectorObservable(DetectorObservableInstruction::SetObservable) => {
630 self.sim_set_ref(TAG_OBSERVABLE_REF);
631 }
632
633 Instruction::Cpu(CpuInstruction::Return) => self.sim_return(),
634 Instruction::Cpu(CpuInstruction::Halt) => {}
635 }
636 }
637
638 fn run(mut self, program: &Program) -> Vec<ValidationError> {
642 for (pc, inst) in program.instructions.iter().enumerate() {
643 self.pc = pc;
644 self.dispatch(inst);
645 }
646 self.errors
647 }
648}
649
650pub fn simulate_stack(program: &Program, arch: Option<&ArchSpec>) -> Vec<ValidationError> {
654 let sim = StackSimulator::new(arch);
655 sim.run(program)
656}
657
658#[cfg(test)]
659mod tests {
660 use super::*;
661 use crate::arch::addr::{Direction, LaneAddr, MoveType, SiteRef, WordRef};
662 use crate::arch::types::{Bus, Grid, Mode, Word, Zone};
663 use crate::version::Version;
664
665 #[test]
668 fn test_valid_program_no_errors() {
669 let program = Program {
670 version: Version::new(1, 0),
671 instructions: vec![
672 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0)),
673 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity: 1 }),
674 Instruction::Cpu(CpuInstruction::Halt),
675 ],
676 };
677 assert!(validate_structure(&program).is_empty());
678 }
679
680 #[test]
681 fn test_initial_fill_not_first() {
682 let program = Program {
683 version: Version::new(1, 0),
684 instructions: vec![
685 Instruction::Cpu(CpuInstruction::Halt),
686 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0)),
687 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity: 1 }),
688 ],
689 };
690 let errors = validate_structure(&program);
691 assert_eq!(errors.len(), 1);
692 assert!(matches!(
693 errors[0],
694 ValidationError::InitialFillNotFirst { pc: 2 }
695 ));
696 }
697
698 #[test]
699 fn test_initial_fill_after_constants_ok() {
700 let program = Program {
701 version: Version::new(1, 0),
702 instructions: vec![
703 Instruction::Cpu(CpuInstruction::ConstFloat(1.0)),
704 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0)),
705 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity: 1 }),
706 ],
707 };
708 assert!(validate_structure(&program).is_empty());
709 }
710
711 #[test]
712 fn test_new_array_zero_dim0() {
713 let program = Program {
714 version: Version::new(1, 0),
715 instructions: vec![Instruction::Array(ArrayInstruction::NewArray {
716 type_tag: 0,
717 dim0: 0,
718 dim1: 0,
719 })],
720 };
721 let errors = validate_structure(&program);
722 assert!(
723 errors
724 .iter()
725 .any(|e| matches!(e, ValidationError::NewArrayZeroDim0 { pc: 0 }))
726 );
727 }
728
729 #[test]
730 fn test_new_array_invalid_type_tag() {
731 let program = Program {
732 version: Version::new(1, 0),
733 instructions: vec![Instruction::Array(ArrayInstruction::NewArray {
734 type_tag: 0xF,
735 dim0: 1,
736 dim1: 0,
737 })],
738 };
739 let errors = validate_structure(&program);
740 assert!(errors.iter().any(|e| matches!(
741 e,
742 ValidationError::NewArrayInvalidTypeTag {
743 pc: 0,
744 type_tag: 0xF
745 }
746 )));
747 }
748
749 #[test]
752 fn test_stack_sim_valid_program() {
753 let program = Program {
754 version: Version::new(1, 0),
755 instructions: vec![
756 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0)),
757 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity: 1 }),
758 Instruction::Cpu(CpuInstruction::Halt),
759 ],
760 };
761 assert!(simulate_stack(&program, None).is_empty());
762 }
763
764 #[test]
765 fn test_stack_sim_underflow() {
766 let program = Program {
767 version: Version::new(1, 0),
768 instructions: vec![Instruction::Cpu(CpuInstruction::Pop)],
769 };
770 let errors = simulate_stack(&program, None);
771 assert_eq!(errors.len(), 1);
772 assert!(matches!(
773 errors[0],
774 ValidationError::StackUnderflow { pc: 0 }
775 ));
776 }
777
778 #[test]
779 fn test_stack_sim_type_mismatch() {
780 let program = Program {
781 version: Version::new(1, 0),
782 instructions: vec![
783 Instruction::Cpu(CpuInstruction::ConstFloat(1.0)),
785 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity: 1 }),
786 ],
787 };
788 let errors = simulate_stack(&program, None);
789 assert_eq!(errors.len(), 1);
790 assert!(matches!(
791 errors[0],
792 ValidationError::TypeMismatch {
793 pc: 1,
794 expected,
795 got
796 } if expected == TAG_LOCATION && got == TAG_FLOAT
797 ));
798 }
799
800 #[test]
801 fn test_stack_sim_move() {
802 let program = Program {
803 version: Version::new(1, 0),
804 instructions: vec![
805 Instruction::LaneConst(LaneConstInstruction::ConstLane(0x100, 0)),
806 Instruction::LaneConst(LaneConstInstruction::ConstLane(0x200, 0)),
807 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 2 }),
808 ],
809 };
810 assert!(simulate_stack(&program, None).is_empty());
811 }
812
813 #[test]
814 fn test_stack_sim_local_r() {
815 let program = Program {
816 version: Version::new(1, 0),
817 instructions: vec![
818 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0)),
819 Instruction::Cpu(CpuInstruction::ConstFloat(1.0)), Instruction::Cpu(CpuInstruction::ConstFloat(0.5)), Instruction::QuantumGate(QuantumGateInstruction::LocalR { arity: 1 }),
822 ],
823 };
824 assert!(simulate_stack(&program, None).is_empty());
825 }
826
827 #[test]
828 fn test_stack_sim_measure_and_await() {
829 let program = Program {
830 version: Version::new(1, 0),
831 instructions: vec![
832 Instruction::LaneConst(LaneConstInstruction::ConstZone(0)),
833 Instruction::Measurement(MeasurementInstruction::Measure { arity: 1 }),
834 Instruction::Measurement(MeasurementInstruction::AwaitMeasure),
835 Instruction::Cpu(CpuInstruction::Return),
836 ],
837 };
838 assert!(simulate_stack(&program, None).is_empty());
839 }
840
841 #[test]
842 fn test_stack_sim_dup_and_swap() {
843 let program = Program {
844 version: Version::new(1, 0),
845 instructions: vec![
846 Instruction::Cpu(CpuInstruction::ConstFloat(1.0)),
847 Instruction::Cpu(CpuInstruction::Dup),
848 Instruction::QuantumGate(QuantumGateInstruction::GlobalR),
849 ],
850 };
851 assert!(simulate_stack(&program, None).is_empty());
852 }
853
854 #[test]
855 fn test_stack_sim_cz_type_mismatch() {
856 let program = Program {
857 version: Version::new(1, 0),
858 instructions: vec![
859 Instruction::Cpu(CpuInstruction::ConstFloat(1.0)),
860 Instruction::QuantumGate(QuantumGateInstruction::CZ),
861 ],
862 };
863 let errors = simulate_stack(&program, None);
864 assert_eq!(errors.len(), 1);
865 assert!(matches!(
866 errors[0],
867 ValidationError::TypeMismatch {
868 pc: 1,
869 expected,
870 got
871 } if expected == TAG_ZONE && got == TAG_FLOAT
872 ));
873 }
874
875 #[test]
876 fn test_stack_sim_set_detector() {
877 let program = Program {
878 version: Version::new(1, 0),
879 instructions: vec![
880 Instruction::LaneConst(LaneConstInstruction::ConstZone(0)),
881 Instruction::Measurement(MeasurementInstruction::Measure { arity: 1 }),
882 Instruction::Measurement(MeasurementInstruction::AwaitMeasure),
883 Instruction::DetectorObservable(DetectorObservableInstruction::SetDetector),
884 Instruction::Cpu(CpuInstruction::Return),
885 ],
886 };
887 assert!(simulate_stack(&program, None).is_empty());
888 }
889
890 fn test_arch_spec() -> ArchSpec {
893 let grid = Grid::from_positions(&[0.0, 5.0], &[0.0]);
895 ArchSpec {
896 version: Version::new(2, 0),
897 words: vec![Word {
898 sites: vec![[0, 0], [1, 0]],
899 }],
900 zones: vec![Zone {
901 name: String::new(),
902 grid,
903 site_buses: vec![Bus {
904 src: vec![SiteRef(0)],
905 dst: vec![SiteRef(1)],
906 }],
907 word_buses: vec![],
908 words_with_site_buses: vec![0],
909 sites_with_word_buses: vec![],
910 entangling_pairs: vec![],
911 }],
912 zone_buses: vec![],
913 modes: vec![Mode {
914 name: "full".to_string(),
915 zones: vec![0],
916 bitstring_order: vec![],
917 }],
918 paths: None,
919 feed_forward: false,
920 atom_reloading: false,
921 blockade_radius: None,
922 }
923 }
924
925 #[test]
926 fn test_addr_valid_location() {
927 let arch = test_arch_spec();
928 let valid_loc = LocationAddr {
930 zone_id: 0,
931 word_id: 0,
932 site_id: 1,
933 }
934 .encode();
935 let program = Program {
936 version: Version::new(1, 0),
937 instructions: vec![Instruction::LaneConst(LaneConstInstruction::ConstLoc(
938 valid_loc,
939 ))],
940 };
941 assert!(validate_addresses(&program, &arch).is_empty());
942 }
943
944 #[test]
945 fn test_addr_invalid_location() {
946 let arch = test_arch_spec();
947 let bad_loc = LocationAddr {
949 zone_id: 0,
950 word_id: 0,
951 site_id: 5,
952 }
953 .encode();
954 let program = Program {
955 version: Version::new(1, 0),
956 instructions: vec![Instruction::LaneConst(LaneConstInstruction::ConstLoc(
957 bad_loc,
958 ))],
959 };
960 let errors = validate_addresses(&program, &arch);
961 assert_eq!(errors.len(), 1);
962 assert!(matches!(
963 errors[0],
964 ValidationError::LocationGroupValidation {
965 pc: 0,
966 error: LocationGroupError::InvalidAddress {
967 zone_id: 0,
968 word_id: 0,
969 site_id: 5
970 }
971 }
972 ));
973 }
974
975 #[test]
976 fn test_addr_invalid_zone() {
977 let arch = test_arch_spec();
978 let program = Program {
979 version: Version::new(1, 0),
980 instructions: vec![Instruction::LaneConst(LaneConstInstruction::ConstZone(99))],
981 };
982 let errors = validate_addresses(&program, &arch);
983 assert_eq!(errors.len(), 1);
984 assert!(matches!(
985 errors[0],
986 ValidationError::InvalidZone { pc: 0, zone_id: 99 }
987 ));
988 }
989
990 #[test]
991 fn test_addr_valid_lane() {
992 let arch = test_arch_spec();
993 let program = Program {
994 version: Version::new(1, 0),
995 instructions: vec![Instruction::LaneConst(LaneConstInstruction::ConstLane(
996 0x00000000, 0x00000000,
997 ))],
998 };
999 assert!(validate_addresses(&program, &arch).is_empty());
1000 }
1001
1002 #[test]
1003 fn test_addr_invalid_lane_bus() {
1004 let arch = test_arch_spec();
1005 let program = Program {
1007 version: Version::new(1, 0),
1008 instructions: vec![Instruction::LaneConst(LaneConstInstruction::ConstLane(
1009 0x00000000, 0x00000005,
1010 ))],
1011 };
1012 let errors = validate_addresses(&program, &arch);
1013 assert!(!errors.is_empty());
1014 }
1015
1016 fn lane_group_arch_spec() -> ArchSpec {
1019 let grid0 = Grid::from_positions(&[0.0, 2.0, 4.0, 6.0], &[0.0, 10.0]);
1024
1025 ArchSpec {
1026 version: Version::new(2, 0),
1027 words: vec![
1028 Word {
1029 sites: vec![[0, 0], [1, 0], [2, 0], [3, 0]],
1030 },
1031 Word {
1032 sites: vec![[0, 1], [1, 1], [2, 1], [3, 1]],
1033 },
1034 ],
1035 zones: vec![Zone {
1036 name: String::new(),
1037 grid: grid0,
1038 site_buses: vec![Bus {
1039 src: vec![SiteRef(0), SiteRef(1)],
1040 dst: vec![SiteRef(2), SiteRef(3)],
1041 }],
1042 word_buses: vec![Bus {
1043 src: vec![WordRef(0)],
1044 dst: vec![WordRef(1)],
1045 }],
1046 words_with_site_buses: vec![0, 1],
1047 sites_with_word_buses: vec![0],
1048 entangling_pairs: vec![],
1049 }],
1050 zone_buses: vec![],
1051 modes: vec![Mode {
1052 name: "full".to_string(),
1053 zones: vec![0],
1054 bitstring_order: vec![],
1055 }],
1056 paths: None,
1057 feed_forward: false,
1058 atom_reloading: false,
1059 blockade_radius: None,
1060 }
1061 }
1062
1063 fn make_lane(
1064 dir: Direction,
1065 mt: MoveType,
1066 word_id: u32,
1067 site_id: u32,
1068 bus_id: u32,
1069 ) -> (u32, u32) {
1070 LaneAddr {
1071 direction: dir,
1072 move_type: mt,
1073 zone_id: 0,
1074 word_id,
1075 site_id,
1076 bus_id,
1077 }
1078 .encode()
1079 }
1080
1081 #[test]
1082 fn test_lane_group_consistent_passes() {
1083 let arch = lane_group_arch_spec();
1084 let lane0 = make_lane(Direction::Forward, MoveType::SiteBus, 0, 0, 0);
1086 let lane1 = make_lane(Direction::Forward, MoveType::SiteBus, 1, 0, 0);
1087 let program = Program {
1088 version: Version::new(1, 0),
1089 instructions: vec![
1090 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane0.0, lane0.1)),
1091 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane1.0, lane1.1)),
1092 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 2 }),
1093 ],
1094 };
1095 let errors = simulate_stack(&program, Some(&arch));
1096 assert!(errors.is_empty(), "expected no errors, got: {:?}", errors);
1097 }
1098
1099 #[test]
1100 fn test_lane_group_inconsistent_bus_id() {
1101 let arch = lane_group_arch_spec();
1102 let lane0 = make_lane(Direction::Forward, MoveType::SiteBus, 0, 0, 0);
1104 let lane1 = make_lane(Direction::Forward, MoveType::SiteBus, 1, 0, 1);
1105 let program = Program {
1106 version: Version::new(1, 0),
1107 instructions: vec![
1108 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane0.0, lane0.1)),
1109 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane1.0, lane1.1)),
1110 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 2 }),
1111 ],
1112 };
1113 let errors = simulate_stack(&program, Some(&arch));
1114 assert!(errors.iter().any(|e| matches!(
1115 e,
1116 ValidationError::LaneGroupValidation {
1117 pc: 2,
1118 error: LaneGroupError::Inconsistent { .. }
1119 }
1120 )));
1121 }
1122
1123 #[test]
1124 fn test_lane_group_inconsistent_direction() {
1125 let arch = lane_group_arch_spec();
1126 let lane0 = make_lane(Direction::Forward, MoveType::SiteBus, 0, 0, 0);
1128 let lane1 = make_lane(Direction::Backward, MoveType::SiteBus, 1, 0, 0);
1129 let program = Program {
1130 version: Version::new(1, 0),
1131 instructions: vec![
1132 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane0.0, lane0.1)),
1133 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane1.0, lane1.1)),
1134 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 2 }),
1135 ],
1136 };
1137 let errors = simulate_stack(&program, Some(&arch));
1138 assert!(errors.iter().any(|e| matches!(
1139 e,
1140 ValidationError::LaneGroupValidation {
1141 pc: 2,
1142 error: LaneGroupError::Inconsistent { .. }
1143 }
1144 )));
1145 }
1146
1147 #[test]
1148 fn test_lane_group_word_not_in_site_bus_list() {
1149 let grid = Grid::from_positions(&[0.0, 5.0, 10.0], &[0.0, 3.0]);
1152 let arch = ArchSpec {
1153 version: Version::new(2, 0),
1154 words: vec![
1155 Word {
1156 sites: vec![[0, 0], [1, 0]],
1157 },
1158 Word {
1159 sites: vec![[0, 0], [1, 0]],
1160 },
1161 ],
1162 zones: vec![Zone {
1163 name: String::new(),
1164 grid,
1165 site_buses: vec![Bus {
1166 src: vec![SiteRef(0)],
1167 dst: vec![SiteRef(1)],
1168 }],
1169 word_buses: vec![],
1170 words_with_site_buses: vec![0],
1171 sites_with_word_buses: vec![],
1172 entangling_pairs: vec![],
1173 }],
1174 zone_buses: vec![],
1175 modes: vec![Mode {
1176 name: "full".to_string(),
1177 zones: vec![0],
1178 bitstring_order: vec![],
1179 }],
1180 paths: None,
1181 feed_forward: false,
1182 atom_reloading: false,
1183 blockade_radius: None,
1184 };
1185
1186 let lane0 = make_lane(Direction::Forward, MoveType::SiteBus, 1, 0, 0);
1187 let lane1 = make_lane(Direction::Forward, MoveType::SiteBus, 1, 1, 0);
1188 let program = Program {
1189 version: Version::new(1, 0),
1190 instructions: vec![
1191 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane0.0, lane0.1)),
1192 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane1.0, lane1.1)),
1193 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 2 }),
1194 ],
1195 };
1196 let errors = simulate_stack(&program, Some(&arch));
1197 assert!(errors.iter().any(|e| matches!(
1198 e,
1199 ValidationError::LaneGroupValidation {
1200 pc: 2,
1201 error: LaneGroupError::WordNotInSiteBusList {
1202 zone_id: 0,
1203 word_id: 1
1204 }
1205 }
1206 )));
1207 }
1208
1209 #[test]
1210 fn test_lane_group_aod_constraint_rectangle_passes() {
1211 let arch = lane_group_arch_spec();
1212 let lanes: Vec<(u32, u32)> = vec![
1218 make_lane(Direction::Forward, MoveType::SiteBus, 0, 0, 0),
1219 make_lane(Direction::Forward, MoveType::SiteBus, 0, 1, 0),
1220 make_lane(Direction::Forward, MoveType::SiteBus, 1, 0, 0),
1221 make_lane(Direction::Forward, MoveType::SiteBus, 1, 1, 0),
1222 ];
1223 let program = Program {
1224 version: Version::new(1, 0),
1225 instructions: vec![
1226 Instruction::LaneConst(LaneConstInstruction::ConstLane(lanes[0].0, lanes[0].1)),
1227 Instruction::LaneConst(LaneConstInstruction::ConstLane(lanes[1].0, lanes[1].1)),
1228 Instruction::LaneConst(LaneConstInstruction::ConstLane(lanes[2].0, lanes[2].1)),
1229 Instruction::LaneConst(LaneConstInstruction::ConstLane(lanes[3].0, lanes[3].1)),
1230 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 4 }),
1231 ],
1232 };
1233 let errors = simulate_stack(&program, Some(&arch));
1234 assert!(errors.is_empty(), "expected no errors, got: {:?}", errors);
1235 }
1236
1237 #[test]
1238 fn test_lane_group_aod_constraint_not_rectangle() {
1239 let arch = lane_group_arch_spec();
1240 let lanes: Vec<(u32, u32)> = vec![
1245 make_lane(Direction::Forward, MoveType::SiteBus, 0, 0, 0),
1246 make_lane(Direction::Forward, MoveType::SiteBus, 0, 1, 0),
1247 make_lane(Direction::Forward, MoveType::SiteBus, 1, 0, 0),
1248 ];
1249 let program = Program {
1250 version: Version::new(1, 0),
1251 instructions: vec![
1252 Instruction::LaneConst(LaneConstInstruction::ConstLane(lanes[0].0, lanes[0].1)),
1253 Instruction::LaneConst(LaneConstInstruction::ConstLane(lanes[1].0, lanes[1].1)),
1254 Instruction::LaneConst(LaneConstInstruction::ConstLane(lanes[2].0, lanes[2].1)),
1255 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 3 }),
1256 ],
1257 };
1258 let errors = simulate_stack(&program, Some(&arch));
1259 assert!(
1260 errors.iter().any(|e| matches!(
1261 e,
1262 ValidationError::LaneGroupValidation {
1263 pc: 3,
1264 error: LaneGroupError::AODConstraintViolation { .. }
1265 }
1266 )),
1267 "expected AOD constraint violation error, got: {:?}",
1268 errors
1269 );
1270 }
1271
1272 #[test]
1273 fn test_lane_group_no_arch_skips_validation() {
1274 let lane0 = make_lane(Direction::Forward, MoveType::SiteBus, 0, 0, 0);
1275 let lane1 = make_lane(Direction::Backward, MoveType::WordBus, 1, 1, 5);
1276 let program = Program {
1277 version: Version::new(1, 0),
1278 instructions: vec![
1279 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane0.0, lane0.1)),
1280 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane1.0, lane1.1)),
1281 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 2 }),
1282 ],
1283 };
1284 let errors = simulate_stack(&program, None);
1285 assert!(errors.is_empty());
1286 }
1287
1288 #[test]
1291 fn test_duplicate_location_in_initial_fill() {
1292 let addr = LocationAddr {
1293 zone_id: 0,
1294 word_id: 0,
1295 site_id: 9,
1296 }
1297 .encode();
1298 let program = Program {
1299 version: Version::new(1, 0),
1300 instructions: vec![
1301 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1302 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0x0007)),
1303 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1304 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity: 3 }),
1305 Instruction::Cpu(CpuInstruction::Halt),
1306 ],
1307 };
1308 let errors = simulate_stack(&program, None);
1309 assert!(
1310 errors
1311 .iter()
1312 .any(|e| matches!(e, ValidationError::LocationGroupValidation { pc: 3, error: LocationGroupError::DuplicateAddress { address } } if *address == addr)),
1313 "expected DuplicateLocationAddress, got: {:?}",
1314 errors
1315 );
1316 }
1317
1318 #[test]
1319 fn test_duplicate_location_in_fill() {
1320 let addr = LocationAddr {
1321 zone_id: 0,
1322 word_id: 1,
1323 site_id: 2,
1324 }
1325 .encode();
1326 let program = Program {
1327 version: Version::new(1, 0),
1328 instructions: vec![
1329 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1330 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1331 Instruction::AtomArrangement(AtomArrangementInstruction::Fill { arity: 2 }),
1332 Instruction::Cpu(CpuInstruction::Halt),
1333 ],
1334 };
1335 let errors = simulate_stack(&program, None);
1336 assert!(
1337 errors.iter().any(|e| matches!(
1338 e,
1339 ValidationError::LocationGroupValidation {
1340 pc: 2,
1341 error: LocationGroupError::DuplicateAddress { .. }
1342 }
1343 )),
1344 "expected DuplicateLocationAddress, got: {:?}",
1345 errors
1346 );
1347 }
1348
1349 #[test]
1350 fn test_duplicate_location_in_local_r() {
1351 let addr = LocationAddr {
1352 zone_id: 0,
1353 word_id: 0,
1354 site_id: 3,
1355 }
1356 .encode();
1357 let program = Program {
1358 version: Version::new(1, 0),
1359 instructions: vec![
1360 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1361 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1362 Instruction::Cpu(CpuInstruction::ConstFloat(0.5)),
1363 Instruction::Cpu(CpuInstruction::ConstFloat(1.0)),
1364 Instruction::QuantumGate(QuantumGateInstruction::LocalR { arity: 2 }),
1365 ],
1366 };
1367 let errors = simulate_stack(&program, None);
1368 assert!(
1369 errors.iter().any(|e| matches!(
1370 e,
1371 ValidationError::LocationGroupValidation {
1372 pc: 4,
1373 error: LocationGroupError::DuplicateAddress { .. }
1374 }
1375 )),
1376 "expected DuplicateLocationAddress, got: {:?}",
1377 errors
1378 );
1379 }
1380
1381 #[test]
1382 fn test_duplicate_location_in_local_rz() {
1383 let addr = LocationAddr {
1384 zone_id: 0,
1385 word_id: 0,
1386 site_id: 5,
1387 }
1388 .encode();
1389 let program = Program {
1390 version: Version::new(1, 0),
1391 instructions: vec![
1392 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1393 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1394 Instruction::Cpu(CpuInstruction::ConstFloat(0.5)),
1395 Instruction::QuantumGate(QuantumGateInstruction::LocalRz { arity: 2 }),
1396 ],
1397 };
1398 let errors = simulate_stack(&program, None);
1399 assert!(
1400 errors.iter().any(|e| matches!(
1401 e,
1402 ValidationError::LocationGroupValidation {
1403 pc: 3,
1404 error: LocationGroupError::DuplicateAddress { .. }
1405 }
1406 )),
1407 "expected DuplicateLocationAddress, got: {:?}",
1408 errors
1409 );
1410 }
1411
1412 #[test]
1413 fn test_duplicate_lane_in_move() {
1414 let lane = make_lane(Direction::Forward, MoveType::SiteBus, 0, 9, 0);
1415 let lane_other = make_lane(Direction::Forward, MoveType::SiteBus, 0, 7, 0);
1416 let program = Program {
1417 version: Version::new(1, 0),
1418 instructions: vec![
1419 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane.0, lane.1)),
1420 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane_other.0, lane_other.1)),
1421 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane.0, lane.1)),
1422 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 3 }),
1423 ],
1424 };
1425 let errors = simulate_stack(&program, None);
1426 assert!(
1427 errors
1428 .iter()
1429 .any(|e| matches!(e, ValidationError::LaneGroupValidation { pc: 3, error: LaneGroupError::DuplicateAddress { address } } if *address == lane)),
1430
1431 "expected DuplicateLaneAddress, got: {:?}",
1432 errors
1433 );
1434 }
1435
1436 #[test]
1437 fn test_duplicate_location_reported_once() {
1438 let addr = LocationAddr {
1439 zone_id: 0,
1440 word_id: 1,
1441 site_id: 2,
1442 }
1443 .encode();
1444 let program = Program {
1445 version: Version::new(1, 0),
1446 instructions: vec![
1447 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1448 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1449 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1450 Instruction::AtomArrangement(AtomArrangementInstruction::Fill { arity: 3 }),
1451 Instruction::Cpu(CpuInstruction::Halt),
1452 ],
1453 };
1454 let errors = simulate_stack(&program, None);
1455 let dup_count = errors
1456 .iter()
1457 .filter(|e| {
1458 matches!(
1459 e,
1460 ValidationError::LocationGroupValidation {
1461 error: LocationGroupError::DuplicateAddress { .. },
1462 ..
1463 }
1464 )
1465 })
1466 .count();
1467 assert_eq!(
1468 dup_count, 1,
1469 "expected exactly 1 DuplicateAddress, got: {:?}",
1470 errors
1471 );
1472 }
1473
1474 #[test]
1475 fn test_duplicate_lane_reported_once() {
1476 let lane = make_lane(Direction::Forward, MoveType::SiteBus, 0, 9, 0);
1477 let program = Program {
1478 version: Version::new(1, 0),
1479 instructions: vec![
1480 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane.0, lane.1)),
1481 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane.0, lane.1)),
1482 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane.0, lane.1)),
1483 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 3 }),
1484 ],
1485 };
1486 let errors = simulate_stack(&program, None);
1487 let dup_count = errors
1488 .iter()
1489 .filter(|e| {
1490 matches!(
1491 e,
1492 ValidationError::LaneGroupValidation {
1493 error: LaneGroupError::DuplicateAddress { .. },
1494 ..
1495 }
1496 )
1497 })
1498 .count();
1499 assert_eq!(
1500 dup_count, 1,
1501 "expected exactly 1 DuplicateAddress, got: {:?}",
1502 errors
1503 );
1504 }
1505
1506 #[test]
1507 fn test_no_duplicate_location_passes() {
1508 let loc0 = LocationAddr {
1510 zone_id: 0,
1511 word_id: 0,
1512 site_id: 0,
1513 }
1514 .encode();
1515 let loc1 = LocationAddr {
1516 zone_id: 0,
1517 word_id: 0,
1518 site_id: 1,
1519 }
1520 .encode();
1521 let loc2 = LocationAddr {
1522 zone_id: 0,
1523 word_id: 1,
1524 site_id: 0,
1525 }
1526 .encode();
1527 let program = Program {
1528 version: Version::new(1, 0),
1529 instructions: vec![
1530 Instruction::LaneConst(LaneConstInstruction::ConstLoc(loc0)),
1531 Instruction::LaneConst(LaneConstInstruction::ConstLoc(loc1)),
1532 Instruction::LaneConst(LaneConstInstruction::ConstLoc(loc2)),
1533 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity: 3 }),
1534 Instruction::Cpu(CpuInstruction::Halt),
1535 ],
1536 };
1537 let errors = simulate_stack(&program, None);
1538 assert!(errors.is_empty(), "expected no errors, got: {:?}", errors);
1539 }
1540
1541 fn test_arch_with_caps(feed_forward: bool, atom_reloading: bool) -> ArchSpec {
1544 let mut arch = test_arch_spec();
1545 arch.feed_forward = feed_forward;
1546 arch.atom_reloading = atom_reloading;
1547 arch
1548 }
1549
1550 #[test]
1551 fn test_cap_single_measure_allowed() {
1552 let arch = test_arch_with_caps(false, false);
1553 let program = Program {
1554 version: Version::new(1, 0),
1555 instructions: vec![
1556 Instruction::LaneConst(LaneConstInstruction::ConstZone(0)),
1557 Instruction::Measurement(MeasurementInstruction::Measure { arity: 1 }),
1558 ],
1559 };
1560 assert!(validate_capabilities(&program, &arch).is_empty());
1561 }
1562
1563 #[test]
1564 fn test_cap_multiple_measure_rejected() {
1565 let arch = test_arch_with_caps(false, false);
1566 let program = Program {
1567 version: Version::new(1, 0),
1568 instructions: vec![
1569 Instruction::LaneConst(LaneConstInstruction::ConstZone(0)),
1570 Instruction::Measurement(MeasurementInstruction::Measure { arity: 1 }),
1571 Instruction::LaneConst(LaneConstInstruction::ConstZone(0)),
1572 Instruction::Measurement(MeasurementInstruction::Measure { arity: 1 }),
1573 ],
1574 };
1575 let errors = validate_capabilities(&program, &arch);
1576 assert_eq!(errors.len(), 1);
1577 assert!(matches!(
1578 errors[0],
1579 ValidationError::FeedForwardNotSupported { pc: 3 }
1580 ));
1581 }
1582
1583 #[test]
1584 fn test_cap_multiple_measure_with_feed_forward() {
1585 let arch = test_arch_with_caps(true, false);
1586 let program = Program {
1587 version: Version::new(1, 0),
1588 instructions: vec![
1589 Instruction::LaneConst(LaneConstInstruction::ConstZone(0)),
1590 Instruction::Measurement(MeasurementInstruction::Measure { arity: 1 }),
1591 Instruction::LaneConst(LaneConstInstruction::ConstZone(0)),
1592 Instruction::Measurement(MeasurementInstruction::Measure { arity: 1 }),
1593 ],
1594 };
1595 assert!(validate_capabilities(&program, &arch).is_empty());
1596 }
1597
1598 #[test]
1599 fn test_cap_fill_rejected() {
1600 let arch = test_arch_with_caps(false, false);
1601 let program = Program {
1602 version: Version::new(1, 0),
1603 instructions: vec![
1604 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0)),
1605 Instruction::AtomArrangement(AtomArrangementInstruction::Fill { arity: 1 }),
1606 ],
1607 };
1608 let errors = validate_capabilities(&program, &arch);
1609 assert_eq!(errors.len(), 1);
1610 assert!(matches!(
1611 errors[0],
1612 ValidationError::AtomReloadingNotSupported { pc: 1 }
1613 ));
1614 }
1615
1616 #[test]
1617 fn test_cap_fill_with_atom_reloading() {
1618 let arch = test_arch_with_caps(false, true);
1619 let program = Program {
1620 version: Version::new(1, 0),
1621 instructions: vec![
1622 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0)),
1623 Instruction::AtomArrangement(AtomArrangementInstruction::Fill { arity: 1 }),
1624 ],
1625 };
1626 assert!(validate_capabilities(&program, &arch).is_empty());
1627 }
1628
1629 #[test]
1630 fn test_cap_initial_fill_always_allowed() {
1631 let arch = test_arch_with_caps(false, false);
1632 let program = Program {
1633 version: Version::new(1, 0),
1634 instructions: vec![
1635 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0)),
1636 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity: 1 }),
1637 ],
1638 };
1639 assert!(validate_capabilities(&program, &arch).is_empty());
1640 }
1641
1642 #[test]
1643 fn test_no_duplicate_lane_passes() {
1644 let lane0 = make_lane(Direction::Forward, MoveType::SiteBus, 0, 0, 0);
1645 let lane1 = make_lane(Direction::Forward, MoveType::SiteBus, 0, 1, 0);
1646 let lane2 = make_lane(Direction::Forward, MoveType::SiteBus, 0, 2, 0);
1647 let program = Program {
1648 version: Version::new(1, 0),
1649 instructions: vec![
1650 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane0.0, lane0.1)),
1651 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane1.0, lane1.1)),
1652 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane2.0, lane2.1)),
1653 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 3 }),
1654 ],
1655 };
1656 let errors = simulate_stack(&program, None);
1657 assert!(errors.is_empty(), "expected no errors, got: {:?}", errors);
1658 }
1659}