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