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 word_id: addr.word_id,
176 site_id: addr.site_id,
177 },
178 });
179 }
180 }
181 Instruction::LaneConst(LaneConstInstruction::ConstLane(d0, d1)) => {
182 let addr = LaneAddr::decode(*d0, *d1);
183 for msg in arch.check_lane(&addr) {
184 errors.push(ValidationError::LaneGroupValidation {
185 pc,
186 error: LaneGroupError::InvalidLane { message: msg },
187 });
188 }
189 }
190 Instruction::LaneConst(LaneConstInstruction::ConstZone(bits)) => {
191 let addr = ZoneAddr::decode(*bits);
192 if arch.check_zone(&addr).is_some() {
193 errors.push(ValidationError::InvalidZone {
194 pc,
195 zone_id: addr.zone_id,
196 });
197 }
198 }
199 _ => {}
200 }
201 }
202
203 errors
204}
205
206pub fn validate_capabilities(program: &Program, arch: &ArchSpec) -> Vec<ValidationError> {
212 let mut errors = Vec::new();
213 let mut measure_count = 0u32;
214
215 for (pc, instr) in program.instructions.iter().enumerate() {
216 match instr {
217 Instruction::Measurement(MeasurementInstruction::Measure { .. }) => {
218 measure_count += 1;
219 if !arch.feed_forward && measure_count > 1 {
220 errors.push(ValidationError::FeedForwardNotSupported { pc });
221 }
222 }
223 Instruction::AtomArrangement(AtomArrangementInstruction::Fill { .. }) => {
224 if !arch.atom_reloading {
225 errors.push(ValidationError::AtomReloadingNotSupported { pc });
226 }
227 }
228 _ => {}
229 }
230 }
231
232 errors
233}
234
235pub fn validate_arch_constraints(program: &Program, arch: &ArchSpec) -> Vec<ValidationError> {
241 let mut errors = validate_addresses(program, arch);
242 errors.extend(validate_capabilities(program, arch));
243 errors
244}
245
246#[derive(Debug, Clone)]
248struct SimEntry {
249 tag: u8,
250 #[allow(dead_code)]
251 value: Option<u64>,
252}
253
254struct StackSimulator<'a> {
258 stack: Vec<SimEntry>,
259 errors: Vec<ValidationError>,
260 arch: Option<&'a ArchSpec>,
261 pc: usize,
262}
263
264impl<'a> StackSimulator<'a> {
265 fn new(arch: Option<&'a ArchSpec>) -> Self {
266 Self {
267 stack: Vec::new(),
268 errors: Vec::new(),
269 arch,
270 pc: 0,
271 }
272 }
273
274 fn pop_any(&mut self) {
278 if self.stack.pop().is_none() {
279 self.errors
280 .push(ValidationError::StackUnderflow { pc: self.pc });
281 }
282 }
283
284 fn pop_typed(&mut self, expected_tag: u8) {
286 match self.stack.pop() {
287 Some(entry) if entry.tag != expected_tag => {
288 self.errors.push(ValidationError::TypeMismatch {
289 pc: self.pc,
290 expected: expected_tag,
291 got: entry.tag,
292 });
293 }
294 Some(_) => {}
295 None => {
296 self.errors
297 .push(ValidationError::StackUnderflow { pc: self.pc });
298 }
299 }
300 }
301
302 fn pop_typed_n(&mut self, expected_tag: u8, count: u32) {
304 for _ in 0..count {
305 self.pop_typed(expected_tag);
306 }
307 }
308
309 fn pop_lane(&mut self) -> Option<u64> {
311 match self.stack.pop() {
312 Some(entry) => {
313 if entry.tag != TAG_LANE {
314 self.errors.push(ValidationError::TypeMismatch {
315 pc: self.pc,
316 expected: TAG_LANE,
317 got: entry.tag,
318 });
319 None
320 } else {
321 entry.value
322 }
323 }
324 None => {
325 self.errors
326 .push(ValidationError::StackUnderflow { pc: self.pc });
327 None
328 }
329 }
330 }
331
332 fn pop_location(&mut self) -> Option<u64> {
334 match self.stack.pop() {
335 Some(entry) => {
336 if entry.tag != TAG_LOCATION {
337 self.errors.push(ValidationError::TypeMismatch {
338 pc: self.pc,
339 expected: TAG_LOCATION,
340 got: entry.tag,
341 });
342 None
343 } else {
344 entry.value
345 }
346 }
347 None => {
348 self.errors
349 .push(ValidationError::StackUnderflow { pc: self.pc });
350 None
351 }
352 }
353 }
354
355 fn push_location_group_errors(&mut self, errors: Vec<LocationGroupError>) {
357 let pc = self.pc;
358 for error in errors {
359 self.errors
360 .push(ValidationError::LocationGroupValidation { pc, error });
361 }
362 }
363
364 fn push_lane_group_errors(&mut self, errors: Vec<LaneGroupError>) {
366 let pc = self.pc;
367 for error in errors {
368 self.errors
369 .push(ValidationError::LaneGroupValidation { pc, error });
370 }
371 }
372
373 fn check_duplicate_locations_standalone(&mut self, locations: &[LocationAddr]) {
376 let mut seen = HashSet::new();
377 let mut reported = HashSet::new();
378 for loc in locations {
379 let bits = loc.encode();
380 if !seen.insert(bits) && reported.insert(bits) {
381 self.errors.push(ValidationError::LocationGroupValidation {
382 pc: self.pc,
383 error: LocationGroupError::DuplicateAddress { address: bits },
384 });
385 }
386 }
387 }
388
389 fn check_duplicate_lanes_standalone(&mut self, lanes: &[LaneAddr]) {
392 let mut seen = HashSet::new();
393 let mut reported = HashSet::new();
394 for lane in lanes {
395 let (d0, d1) = lane.encode();
396 let combined = (d0 as u64) | ((d1 as u64) << 32);
397 if !seen.insert(combined) && reported.insert(combined) {
398 self.errors.push(ValidationError::LaneGroupValidation {
399 pc: self.pc,
400 error: LaneGroupError::DuplicateAddress { address: (d0, d1) },
401 });
402 }
403 }
404 }
405
406 fn push_const(&mut self, tag: u8, bits: u64) {
410 self.stack.push(SimEntry {
411 tag,
412 value: Some(bits),
413 });
414 }
415
416 fn sim_dup(&mut self) {
418 if let Some(top) = self.stack.last().cloned() {
419 self.stack.push(top);
420 } else {
421 self.errors
422 .push(ValidationError::StackUnderflow { pc: self.pc });
423 }
424 }
425
426 fn sim_swap(&mut self) {
428 let len = self.stack.len();
429 if len >= 2 {
430 self.stack.swap(len - 1, len - 2);
431 } else {
432 self.errors
433 .push(ValidationError::StackUnderflow { pc: self.pc });
434 }
435 }
436
437 fn pop_and_validate_locations(&mut self, arity: u32) {
440 let loc_values: Vec<Option<u64>> = (0..arity).map(|_| self.pop_location()).collect();
441 let locations: Vec<LocationAddr> = loc_values
442 .iter()
443 .filter_map(|v| v.map(|bits| LocationAddr::decode(bits as u32)))
444 .collect();
445 if let Some(arch) = self.arch {
446 self.push_location_group_errors(arch.check_locations(&locations));
447 } else {
448 self.check_duplicate_locations_standalone(&locations);
449 }
450 }
451
452 fn sim_fill(&mut self, arity: u32) {
454 self.pop_and_validate_locations(arity);
455 }
456
457 fn sim_move(&mut self, arity: u32) {
461 let lane_values: Vec<Option<u64>> = (0..arity).map(|_| self.pop_lane()).collect();
462 let lanes: Vec<LaneAddr> = lane_values
463 .iter()
464 .filter_map(|v| {
465 v.map(|bits| {
466 let d0 = bits as u32;
467 let d1 = (bits >> 32) as u32;
468 LaneAddr::decode(d0, d1)
469 })
470 })
471 .collect();
472 if let Some(arch) = self.arch {
473 self.push_lane_group_errors(arch.check_lanes(&lanes));
474 } else {
475 self.check_duplicate_lanes_standalone(&lanes);
476 }
477 }
478
479 fn sim_local_r(&mut self, arity: u32) {
481 self.pop_typed_n(TAG_FLOAT, 2);
482 self.pop_and_validate_locations(arity);
483 }
484
485 fn sim_local_rz(&mut self, arity: u32) {
487 self.pop_typed_n(TAG_FLOAT, 1);
488 self.pop_and_validate_locations(arity);
489 }
490
491 fn sim_global_r(&mut self) {
493 self.pop_typed_n(TAG_FLOAT, 2);
494 }
495
496 fn sim_global_rz(&mut self) {
498 self.pop_typed_n(TAG_FLOAT, 1);
499 }
500
501 fn sim_cz(&mut self) {
503 self.pop_typed(TAG_ZONE);
504 }
505
506 fn sim_measure(&mut self, arity: u32) {
508 self.pop_typed_n(TAG_ZONE, arity);
509 for _ in 0..arity {
510 self.stack.push(SimEntry {
511 tag: TAG_MEASURE_FUTURE,
512 value: None,
513 });
514 }
515 }
516
517 fn sim_await_measure(&mut self) {
519 self.pop_typed(TAG_MEASURE_FUTURE);
520 self.stack.push(SimEntry {
521 tag: TAG_ARRAY_REF,
522 value: None,
523 });
524 }
525
526 fn sim_new_array(&mut self, dim0: u16, dim1: u16) {
528 let count = dim0 * if dim1 == 0 { 1 } else { dim1 };
529 for _ in 0..count {
530 self.pop_any();
531 }
532 self.stack.push(SimEntry {
533 tag: TAG_ARRAY_REF,
534 value: None,
535 });
536 }
537
538 fn sim_get_item(&mut self, ndims: u16) {
541 self.pop_typed_n(TAG_INT, ndims.into());
542 self.pop_typed(TAG_ARRAY_REF);
543 self.stack.push(SimEntry {
544 tag: TAG_FLOAT,
545 value: None,
546 });
547 }
548
549 fn sim_set_ref(&mut self, out_tag: u8) {
552 self.pop_typed(TAG_ARRAY_REF);
553 self.stack.push(SimEntry {
554 tag: out_tag,
555 value: None,
556 });
557 }
558
559 fn sim_return(&mut self) {
561 self.pop_any();
562 }
563
564 fn dispatch(&mut self, inst: &Instruction) {
568 match inst {
569 Instruction::Cpu(CpuInstruction::ConstFloat(v)) => {
570 self.push_const(TAG_FLOAT, v.to_bits());
571 }
572 Instruction::Cpu(CpuInstruction::ConstInt(v)) => {
573 self.push_const(TAG_INT, *v as u64);
574 }
575 Instruction::LaneConst(LaneConstInstruction::ConstLoc(v)) => {
576 self.push_const(TAG_LOCATION, *v as u64);
577 }
578 Instruction::LaneConst(LaneConstInstruction::ConstLane(d0, d1)) => {
579 let combined = (*d0 as u64) | ((*d1 as u64) << 32);
580 self.push_const(TAG_LANE, combined);
581 }
582 Instruction::LaneConst(LaneConstInstruction::ConstZone(v)) => {
583 self.push_const(TAG_ZONE, *v as u64);
584 }
585
586 Instruction::Cpu(CpuInstruction::Pop) => self.pop_any(),
587 Instruction::Cpu(CpuInstruction::Dup) => self.sim_dup(),
588 Instruction::Cpu(CpuInstruction::Swap) => self.sim_swap(),
589
590 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity })
591 | Instruction::AtomArrangement(AtomArrangementInstruction::Fill { arity }) => {
592 self.sim_fill(*arity);
593 }
594 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity }) => {
595 self.sim_move(*arity);
596 }
597
598 Instruction::QuantumGate(QuantumGateInstruction::LocalR { arity }) => {
599 self.sim_local_r(*arity);
600 }
601 Instruction::QuantumGate(QuantumGateInstruction::LocalRz { arity }) => {
602 self.sim_local_rz(*arity);
603 }
604 Instruction::QuantumGate(QuantumGateInstruction::GlobalR) => self.sim_global_r(),
605 Instruction::QuantumGate(QuantumGateInstruction::GlobalRz) => self.sim_global_rz(),
606 Instruction::QuantumGate(QuantumGateInstruction::CZ) => self.sim_cz(),
607
608 Instruction::Measurement(MeasurementInstruction::Measure { arity }) => {
609 self.sim_measure(*arity);
610 }
611 Instruction::Measurement(MeasurementInstruction::AwaitMeasure) => {
612 self.sim_await_measure()
613 }
614
615 Instruction::Array(ArrayInstruction::NewArray {
616 type_tag: _,
617 dim0,
618 dim1,
619 }) => {
620 self.sim_new_array(*dim0, *dim1);
621 }
622 Instruction::Array(ArrayInstruction::GetItem { ndims }) => {
623 self.sim_get_item(*ndims);
624 }
625 Instruction::DetectorObservable(DetectorObservableInstruction::SetDetector) => {
626 self.sim_set_ref(TAG_DETECTOR_REF);
627 }
628 Instruction::DetectorObservable(DetectorObservableInstruction::SetObservable) => {
629 self.sim_set_ref(TAG_OBSERVABLE_REF);
630 }
631
632 Instruction::Cpu(CpuInstruction::Return) => self.sim_return(),
633 Instruction::Cpu(CpuInstruction::Halt) => {}
634 }
635 }
636
637 fn run(mut self, program: &Program) -> Vec<ValidationError> {
641 for (pc, inst) in program.instructions.iter().enumerate() {
642 self.pc = pc;
643 self.dispatch(inst);
644 }
645 self.errors
646 }
647}
648
649pub fn simulate_stack(program: &Program, arch: Option<&ArchSpec>) -> Vec<ValidationError> {
653 let sim = StackSimulator::new(arch);
654 sim.run(program)
655}
656
657#[cfg(test)]
658mod tests {
659 use super::*;
660 use crate::version::Version;
661
662 #[test]
665 fn test_valid_program_no_errors() {
666 let program = Program {
667 version: Version::new(1, 0),
668 instructions: vec![
669 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0)),
670 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity: 1 }),
671 Instruction::Cpu(CpuInstruction::Halt),
672 ],
673 };
674 assert!(validate_structure(&program).is_empty());
675 }
676
677 #[test]
678 fn test_initial_fill_not_first() {
679 let program = Program {
680 version: Version::new(1, 0),
681 instructions: vec![
682 Instruction::Cpu(CpuInstruction::Halt),
683 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0)),
684 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity: 1 }),
685 ],
686 };
687 let errors = validate_structure(&program);
688 assert_eq!(errors.len(), 1);
689 assert!(matches!(
690 errors[0],
691 ValidationError::InitialFillNotFirst { pc: 2 }
692 ));
693 }
694
695 #[test]
696 fn test_initial_fill_after_constants_ok() {
697 let program = Program {
698 version: Version::new(1, 0),
699 instructions: vec![
700 Instruction::Cpu(CpuInstruction::ConstFloat(1.0)),
701 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0)),
702 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity: 1 }),
703 ],
704 };
705 assert!(validate_structure(&program).is_empty());
706 }
707
708 #[test]
709 fn test_new_array_zero_dim0() {
710 let program = Program {
711 version: Version::new(1, 0),
712 instructions: vec![Instruction::Array(ArrayInstruction::NewArray {
713 type_tag: 0,
714 dim0: 0,
715 dim1: 0,
716 })],
717 };
718 let errors = validate_structure(&program);
719 assert!(
720 errors
721 .iter()
722 .any(|e| matches!(e, ValidationError::NewArrayZeroDim0 { pc: 0 }))
723 );
724 }
725
726 #[test]
727 fn test_new_array_invalid_type_tag() {
728 let program = Program {
729 version: Version::new(1, 0),
730 instructions: vec![Instruction::Array(ArrayInstruction::NewArray {
731 type_tag: 0xF,
732 dim0: 1,
733 dim1: 0,
734 })],
735 };
736 let errors = validate_structure(&program);
737 assert!(errors.iter().any(|e| matches!(
738 e,
739 ValidationError::NewArrayInvalidTypeTag {
740 pc: 0,
741 type_tag: 0xF
742 }
743 )));
744 }
745
746 #[test]
749 fn test_stack_sim_valid_program() {
750 let program = Program {
751 version: Version::new(1, 0),
752 instructions: vec![
753 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0)),
754 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity: 1 }),
755 Instruction::Cpu(CpuInstruction::Halt),
756 ],
757 };
758 assert!(simulate_stack(&program, None).is_empty());
759 }
760
761 #[test]
762 fn test_stack_sim_underflow() {
763 let program = Program {
764 version: Version::new(1, 0),
765 instructions: vec![Instruction::Cpu(CpuInstruction::Pop)],
766 };
767 let errors = simulate_stack(&program, None);
768 assert_eq!(errors.len(), 1);
769 assert!(matches!(
770 errors[0],
771 ValidationError::StackUnderflow { pc: 0 }
772 ));
773 }
774
775 #[test]
776 fn test_stack_sim_type_mismatch() {
777 let program = Program {
778 version: Version::new(1, 0),
779 instructions: vec![
780 Instruction::Cpu(CpuInstruction::ConstFloat(1.0)),
782 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity: 1 }),
783 ],
784 };
785 let errors = simulate_stack(&program, None);
786 assert_eq!(errors.len(), 1);
787 assert!(matches!(
788 errors[0],
789 ValidationError::TypeMismatch {
790 pc: 1,
791 expected,
792 got
793 } if expected == TAG_LOCATION && got == TAG_FLOAT
794 ));
795 }
796
797 #[test]
798 fn test_stack_sim_move() {
799 let program = Program {
800 version: Version::new(1, 0),
801 instructions: vec![
802 Instruction::LaneConst(LaneConstInstruction::ConstLane(0x100, 0)),
803 Instruction::LaneConst(LaneConstInstruction::ConstLane(0x200, 0)),
804 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 2 }),
805 ],
806 };
807 assert!(simulate_stack(&program, None).is_empty());
808 }
809
810 #[test]
811 fn test_stack_sim_local_r() {
812 let program = Program {
813 version: Version::new(1, 0),
814 instructions: vec![
815 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0)),
816 Instruction::Cpu(CpuInstruction::ConstFloat(1.0)), Instruction::Cpu(CpuInstruction::ConstFloat(0.5)), Instruction::QuantumGate(QuantumGateInstruction::LocalR { arity: 1 }),
819 ],
820 };
821 assert!(simulate_stack(&program, None).is_empty());
822 }
823
824 #[test]
825 fn test_stack_sim_measure_and_await() {
826 let program = Program {
827 version: Version::new(1, 0),
828 instructions: vec![
829 Instruction::LaneConst(LaneConstInstruction::ConstZone(0)),
830 Instruction::Measurement(MeasurementInstruction::Measure { arity: 1 }),
831 Instruction::Measurement(MeasurementInstruction::AwaitMeasure),
832 Instruction::Cpu(CpuInstruction::Return),
833 ],
834 };
835 assert!(simulate_stack(&program, None).is_empty());
836 }
837
838 #[test]
839 fn test_stack_sim_dup_and_swap() {
840 let program = Program {
841 version: Version::new(1, 0),
842 instructions: vec![
843 Instruction::Cpu(CpuInstruction::ConstFloat(1.0)),
844 Instruction::Cpu(CpuInstruction::Dup),
845 Instruction::QuantumGate(QuantumGateInstruction::GlobalR),
846 ],
847 };
848 assert!(simulate_stack(&program, None).is_empty());
849 }
850
851 #[test]
852 fn test_stack_sim_cz_type_mismatch() {
853 let program = Program {
854 version: Version::new(1, 0),
855 instructions: vec![
856 Instruction::Cpu(CpuInstruction::ConstFloat(1.0)),
857 Instruction::QuantumGate(QuantumGateInstruction::CZ),
858 ],
859 };
860 let errors = simulate_stack(&program, None);
861 assert_eq!(errors.len(), 1);
862 assert!(matches!(
863 errors[0],
864 ValidationError::TypeMismatch {
865 pc: 1,
866 expected,
867 got
868 } if expected == TAG_ZONE && got == TAG_FLOAT
869 ));
870 }
871
872 #[test]
873 fn test_stack_sim_set_detector() {
874 let program = Program {
875 version: Version::new(1, 0),
876 instructions: vec![
877 Instruction::LaneConst(LaneConstInstruction::ConstZone(0)),
878 Instruction::Measurement(MeasurementInstruction::Measure { arity: 1 }),
879 Instruction::Measurement(MeasurementInstruction::AwaitMeasure),
880 Instruction::DetectorObservable(DetectorObservableInstruction::SetDetector),
881 Instruction::Cpu(CpuInstruction::Return),
882 ],
883 };
884 assert!(simulate_stack(&program, None).is_empty());
885 }
886
887 fn test_arch_spec() -> ArchSpec {
890 let json = r#"{
891 "version": "1.0",
892 "geometry": {
893 "sites_per_word": 2,
894 "words": [
895 {
896 "positions": { "x_start": 1.0, "y_start": 2.5, "x_spacing": [2.0], "y_spacing": [] },
897 "site_indices": [[0, 0], [1, 0]]
898 }
899 ]
900 },
901 "buses": {
902 "site_buses": [
903 { "src": [0], "dst": [1] }
904 ],
905 "word_buses": []
906 },
907 "words_with_site_buses": [0],
908 "sites_with_word_buses": [],
909 "zones": [
910 { "words": [0] }
911 ],
912 "entangling_zones": [0],
913 "measurement_mode_zones": [0]
914 }"#;
915 ArchSpec::from_json(json).unwrap()
916 }
917
918 #[test]
919 fn test_addr_valid_location() {
920 let arch = test_arch_spec();
921 let program = Program {
922 version: Version::new(1, 0),
923 instructions: vec![Instruction::LaneConst(LaneConstInstruction::ConstLoc(
924 0x0001,
925 ))],
926 };
927 assert!(validate_addresses(&program, &arch).is_empty());
928 }
929
930 #[test]
931 fn test_addr_invalid_location() {
932 let arch = test_arch_spec();
933 let program = Program {
934 version: Version::new(1, 0),
935 instructions: vec![Instruction::LaneConst(LaneConstInstruction::ConstLoc(
936 0x0005,
937 ))],
938 };
939 let errors = validate_addresses(&program, &arch);
940 assert_eq!(errors.len(), 1);
941 assert!(matches!(
942 errors[0],
943 ValidationError::LocationGroupValidation {
944 pc: 0,
945 error: LocationGroupError::InvalidAddress {
946 word_id: 0,
947 site_id: 5
948 }
949 }
950 ));
951 }
952
953 #[test]
954 fn test_addr_invalid_zone() {
955 let arch = test_arch_spec();
956 let program = Program {
957 version: Version::new(1, 0),
958 instructions: vec![Instruction::LaneConst(LaneConstInstruction::ConstZone(99))],
959 };
960 let errors = validate_addresses(&program, &arch);
961 assert_eq!(errors.len(), 1);
962 assert!(matches!(
963 errors[0],
964 ValidationError::InvalidZone { pc: 0, zone_id: 99 }
965 ));
966 }
967
968 #[test]
969 fn test_addr_valid_lane() {
970 let arch = test_arch_spec();
971 let program = Program {
972 version: Version::new(1, 0),
973 instructions: vec![Instruction::LaneConst(LaneConstInstruction::ConstLane(
974 0x00000000, 0x00000000,
975 ))],
976 };
977 assert!(validate_addresses(&program, &arch).is_empty());
978 }
979
980 #[test]
981 fn test_addr_invalid_lane_bus() {
982 let arch = test_arch_spec();
983 let program = Program {
985 version: Version::new(1, 0),
986 instructions: vec![Instruction::LaneConst(LaneConstInstruction::ConstLane(
987 0x00000000, 0x00000005,
988 ))],
989 };
990 let errors = validate_addresses(&program, &arch);
991 assert!(!errors.is_empty());
992 }
993
994 use crate::arch::addr::{Direction, LaneAddr, MoveType};
997
998 fn lane_group_arch_spec() -> ArchSpec {
999 crate::arch::example_arch_spec()
1000 }
1001
1002 fn make_lane(
1003 dir: Direction,
1004 mt: MoveType,
1005 word_id: u32,
1006 site_id: u32,
1007 bus_id: u32,
1008 ) -> (u32, u32) {
1009 LaneAddr {
1010 direction: dir,
1011 move_type: mt,
1012 word_id,
1013 site_id,
1014 bus_id,
1015 }
1016 .encode()
1017 }
1018
1019 #[test]
1020 fn test_lane_group_consistent_passes() {
1021 let arch = lane_group_arch_spec();
1022 let lane0 = make_lane(Direction::Forward, MoveType::SiteBus, 0, 0, 0);
1023 let lane1 = make_lane(Direction::Forward, MoveType::SiteBus, 0, 1, 0);
1024 let program = Program {
1025 version: Version::new(1, 0),
1026 instructions: vec![
1027 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane0.0, lane0.1)),
1028 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane1.0, lane1.1)),
1029 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 2 }),
1030 ],
1031 };
1032 let errors = simulate_stack(&program, Some(&arch));
1033 assert!(errors.is_empty(), "expected no errors, got: {:?}", errors);
1034 }
1035
1036 #[test]
1037 fn test_lane_group_inconsistent_bus_id() {
1038 let arch = lane_group_arch_spec();
1039 let lane0 = make_lane(Direction::Forward, MoveType::SiteBus, 0, 0, 0);
1040 let lane1 = make_lane(Direction::Forward, MoveType::SiteBus, 0, 1, 1);
1041 let program = Program {
1042 version: Version::new(1, 0),
1043 instructions: vec![
1044 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane0.0, lane0.1)),
1045 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane1.0, lane1.1)),
1046 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 2 }),
1047 ],
1048 };
1049 let errors = simulate_stack(&program, Some(&arch));
1050 assert!(errors.iter().any(|e| matches!(
1051 e,
1052 ValidationError::LaneGroupValidation {
1053 pc: 2,
1054 error: LaneGroupError::Inconsistent { .. }
1055 }
1056 )));
1057 }
1058
1059 #[test]
1060 fn test_lane_group_inconsistent_direction() {
1061 let arch = lane_group_arch_spec();
1062 let lane0 = make_lane(Direction::Forward, MoveType::SiteBus, 0, 0, 0);
1063 let lane1 = make_lane(Direction::Backward, MoveType::SiteBus, 0, 1, 0);
1064 let program = Program {
1065 version: Version::new(1, 0),
1066 instructions: vec![
1067 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane0.0, lane0.1)),
1068 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane1.0, lane1.1)),
1069 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 2 }),
1070 ],
1071 };
1072 let errors = simulate_stack(&program, Some(&arch));
1073 assert!(errors.iter().any(|e| matches!(
1074 e,
1075 ValidationError::LaneGroupValidation {
1076 pc: 2,
1077 error: LaneGroupError::Inconsistent { .. }
1078 }
1079 )));
1080 }
1081
1082 #[test]
1083 fn test_lane_group_word_not_in_site_bus_list() {
1084 let json = r#"{
1085 "version": "1.0",
1086 "geometry": {
1087 "sites_per_word": 2,
1088 "words": [
1089 {
1090 "positions": { "x_start": 1.0, "y_start": 2.5, "x_spacing": [2.0], "y_spacing": [] },
1091 "site_indices": [[0, 0], [1, 0]]
1092 },
1093 {
1094 "positions": { "x_start": 1.0, "y_start": 2.5, "x_spacing": [2.0], "y_spacing": [] },
1095 "site_indices": [[0, 0], [1, 0]]
1096 }
1097 ]
1098 },
1099 "buses": {
1100 "site_buses": [{ "src": [0], "dst": [1] }],
1101 "word_buses": []
1102 },
1103 "words_with_site_buses": [0],
1104 "sites_with_word_buses": [],
1105 "zones": [{ "words": [0, 1] }],
1106 "entangling_zones": [0],
1107 "measurement_mode_zones": [0]
1108 }"#;
1109 let arch = ArchSpec::from_json(json).unwrap();
1110
1111 let lane0 = make_lane(Direction::Forward, MoveType::SiteBus, 1, 0, 0);
1112 let lane1 = make_lane(Direction::Forward, MoveType::SiteBus, 1, 1, 0);
1113 let program = Program {
1114 version: Version::new(1, 0),
1115 instructions: vec![
1116 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane0.0, lane0.1)),
1117 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane1.0, lane1.1)),
1118 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 2 }),
1119 ],
1120 };
1121 let errors = simulate_stack(&program, Some(&arch));
1122 assert!(errors.iter().any(|e| matches!(
1123 e,
1124 ValidationError::LaneGroupValidation {
1125 pc: 2,
1126 error: LaneGroupError::WordNotInSiteBusList { word_id: 1 }
1127 }
1128 )));
1129 }
1130
1131 #[test]
1132 fn test_lane_group_aod_constraint_rectangle_passes() {
1133 let arch = lane_group_arch_spec();
1134 let lanes: Vec<(u32, u32)> = vec![
1140 make_lane(Direction::Forward, MoveType::SiteBus, 0, 0, 0),
1141 make_lane(Direction::Forward, MoveType::SiteBus, 0, 1, 0),
1142 make_lane(Direction::Forward, MoveType::SiteBus, 1, 0, 0),
1143 make_lane(Direction::Forward, MoveType::SiteBus, 1, 1, 0),
1144 ];
1145 let program = Program {
1146 version: Version::new(1, 0),
1147 instructions: vec![
1148 Instruction::LaneConst(LaneConstInstruction::ConstLane(lanes[0].0, lanes[0].1)),
1149 Instruction::LaneConst(LaneConstInstruction::ConstLane(lanes[1].0, lanes[1].1)),
1150 Instruction::LaneConst(LaneConstInstruction::ConstLane(lanes[2].0, lanes[2].1)),
1151 Instruction::LaneConst(LaneConstInstruction::ConstLane(lanes[3].0, lanes[3].1)),
1152 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 4 }),
1153 ],
1154 };
1155 let errors = simulate_stack(&program, Some(&arch));
1156 assert!(errors.is_empty(), "expected no errors, got: {:?}", errors);
1157 }
1158
1159 #[test]
1160 fn test_lane_group_aod_constraint_not_rectangle() {
1161 let arch = lane_group_arch_spec();
1162 let lanes: Vec<(u32, u32)> = vec![
1167 make_lane(Direction::Forward, MoveType::SiteBus, 0, 0, 0),
1168 make_lane(Direction::Forward, MoveType::SiteBus, 0, 1, 0),
1169 make_lane(Direction::Forward, MoveType::SiteBus, 1, 0, 0),
1170 ];
1171 let program = Program {
1172 version: Version::new(1, 0),
1173 instructions: vec![
1174 Instruction::LaneConst(LaneConstInstruction::ConstLane(lanes[0].0, lanes[0].1)),
1175 Instruction::LaneConst(LaneConstInstruction::ConstLane(lanes[1].0, lanes[1].1)),
1176 Instruction::LaneConst(LaneConstInstruction::ConstLane(lanes[2].0, lanes[2].1)),
1177 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 3 }),
1178 ],
1179 };
1180 let errors = simulate_stack(&program, Some(&arch));
1181 assert!(
1182 errors.iter().any(|e| matches!(
1183 e,
1184 ValidationError::LaneGroupValidation {
1185 pc: 3,
1186 error: LaneGroupError::AODConstraintViolation { .. }
1187 }
1188 )),
1189 "expected AOD constraint violation error, got: {:?}",
1190 errors
1191 );
1192 }
1193
1194 #[test]
1195 fn test_lane_group_no_arch_skips_validation() {
1196 let lane0 = make_lane(Direction::Forward, MoveType::SiteBus, 0, 0, 0);
1197 let lane1 = make_lane(Direction::Backward, MoveType::WordBus, 1, 1, 5);
1198 let program = Program {
1199 version: Version::new(1, 0),
1200 instructions: vec![
1201 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane0.0, lane0.1)),
1202 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane1.0, lane1.1)),
1203 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 2 }),
1204 ],
1205 };
1206 let errors = simulate_stack(&program, None);
1207 assert!(errors.is_empty());
1208 }
1209
1210 #[test]
1213 fn test_duplicate_location_in_initial_fill() {
1214 let addr = LocationAddr {
1215 word_id: 0,
1216 site_id: 9,
1217 }
1218 .encode();
1219 let program = Program {
1220 version: Version::new(1, 0),
1221 instructions: vec![
1222 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1223 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0x0007)),
1224 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1225 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity: 3 }),
1226 Instruction::Cpu(CpuInstruction::Halt),
1227 ],
1228 };
1229 let errors = simulate_stack(&program, None);
1230 assert!(
1231 errors
1232 .iter()
1233 .any(|e| matches!(e, ValidationError::LocationGroupValidation { pc: 3, error: LocationGroupError::DuplicateAddress { address } } if *address == addr)),
1234 "expected DuplicateLocationAddress, got: {:?}",
1235 errors
1236 );
1237 }
1238
1239 #[test]
1240 fn test_duplicate_location_in_fill() {
1241 let addr = LocationAddr {
1242 word_id: 1,
1243 site_id: 2,
1244 }
1245 .encode();
1246 let program = Program {
1247 version: Version::new(1, 0),
1248 instructions: vec![
1249 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1250 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1251 Instruction::AtomArrangement(AtomArrangementInstruction::Fill { arity: 2 }),
1252 Instruction::Cpu(CpuInstruction::Halt),
1253 ],
1254 };
1255 let errors = simulate_stack(&program, None);
1256 assert!(
1257 errors.iter().any(|e| matches!(
1258 e,
1259 ValidationError::LocationGroupValidation {
1260 pc: 2,
1261 error: LocationGroupError::DuplicateAddress { .. }
1262 }
1263 )),
1264 "expected DuplicateLocationAddress, got: {:?}",
1265 errors
1266 );
1267 }
1268
1269 #[test]
1270 fn test_duplicate_location_in_local_r() {
1271 let addr = LocationAddr {
1272 word_id: 0,
1273 site_id: 3,
1274 }
1275 .encode();
1276 let program = Program {
1277 version: Version::new(1, 0),
1278 instructions: vec![
1279 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1280 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1281 Instruction::Cpu(CpuInstruction::ConstFloat(0.5)),
1282 Instruction::Cpu(CpuInstruction::ConstFloat(1.0)),
1283 Instruction::QuantumGate(QuantumGateInstruction::LocalR { arity: 2 }),
1284 ],
1285 };
1286 let errors = simulate_stack(&program, None);
1287 assert!(
1288 errors.iter().any(|e| matches!(
1289 e,
1290 ValidationError::LocationGroupValidation {
1291 pc: 4,
1292 error: LocationGroupError::DuplicateAddress { .. }
1293 }
1294 )),
1295 "expected DuplicateLocationAddress, got: {:?}",
1296 errors
1297 );
1298 }
1299
1300 #[test]
1301 fn test_duplicate_location_in_local_rz() {
1302 let addr = LocationAddr {
1303 word_id: 0,
1304 site_id: 5,
1305 }
1306 .encode();
1307 let program = Program {
1308 version: Version::new(1, 0),
1309 instructions: vec![
1310 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1311 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1312 Instruction::Cpu(CpuInstruction::ConstFloat(0.5)),
1313 Instruction::QuantumGate(QuantumGateInstruction::LocalRz { arity: 2 }),
1314 ],
1315 };
1316 let errors = simulate_stack(&program, None);
1317 assert!(
1318 errors.iter().any(|e| matches!(
1319 e,
1320 ValidationError::LocationGroupValidation {
1321 pc: 3,
1322 error: LocationGroupError::DuplicateAddress { .. }
1323 }
1324 )),
1325 "expected DuplicateLocationAddress, got: {:?}",
1326 errors
1327 );
1328 }
1329
1330 #[test]
1331 fn test_duplicate_lane_in_move() {
1332 let lane = make_lane(Direction::Forward, MoveType::SiteBus, 0, 9, 0);
1333 let lane_other = make_lane(Direction::Forward, MoveType::SiteBus, 0, 7, 0);
1334 let program = Program {
1335 version: Version::new(1, 0),
1336 instructions: vec![
1337 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane.0, lane.1)),
1338 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane_other.0, lane_other.1)),
1339 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane.0, lane.1)),
1340 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 3 }),
1341 ],
1342 };
1343 let errors = simulate_stack(&program, None);
1344 assert!(
1345 errors
1346 .iter()
1347 .any(|e| matches!(e, ValidationError::LaneGroupValidation { pc: 3, error: LaneGroupError::DuplicateAddress { address } } if *address == lane)),
1348
1349 "expected DuplicateLaneAddress, got: {:?}",
1350 errors
1351 );
1352 }
1353
1354 #[test]
1355 fn test_duplicate_location_reported_once() {
1356 let addr = LocationAddr {
1357 word_id: 1,
1358 site_id: 2,
1359 }
1360 .encode();
1361 let program = Program {
1362 version: Version::new(1, 0),
1363 instructions: vec![
1364 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1365 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1366 Instruction::LaneConst(LaneConstInstruction::ConstLoc(addr)),
1367 Instruction::AtomArrangement(AtomArrangementInstruction::Fill { arity: 3 }),
1368 Instruction::Cpu(CpuInstruction::Halt),
1369 ],
1370 };
1371 let errors = simulate_stack(&program, None);
1372 let dup_count = errors
1373 .iter()
1374 .filter(|e| {
1375 matches!(
1376 e,
1377 ValidationError::LocationGroupValidation {
1378 error: LocationGroupError::DuplicateAddress { .. },
1379 ..
1380 }
1381 )
1382 })
1383 .count();
1384 assert_eq!(
1385 dup_count, 1,
1386 "expected exactly 1 DuplicateAddress, got: {:?}",
1387 errors
1388 );
1389 }
1390
1391 #[test]
1392 fn test_duplicate_lane_reported_once() {
1393 let lane = make_lane(Direction::Forward, MoveType::SiteBus, 0, 9, 0);
1394 let program = Program {
1395 version: Version::new(1, 0),
1396 instructions: vec![
1397 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane.0, lane.1)),
1398 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane.0, lane.1)),
1399 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane.0, lane.1)),
1400 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 3 }),
1401 ],
1402 };
1403 let errors = simulate_stack(&program, None);
1404 let dup_count = errors
1405 .iter()
1406 .filter(|e| {
1407 matches!(
1408 e,
1409 ValidationError::LaneGroupValidation {
1410 error: LaneGroupError::DuplicateAddress { .. },
1411 ..
1412 }
1413 )
1414 })
1415 .count();
1416 assert_eq!(
1417 dup_count, 1,
1418 "expected exactly 1 DuplicateAddress, got: {:?}",
1419 errors
1420 );
1421 }
1422
1423 #[test]
1424 fn test_no_duplicate_location_passes() {
1425 let program = Program {
1426 version: Version::new(1, 0),
1427 instructions: vec![
1428 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0x0000)),
1429 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0x0001)),
1430 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0x0002)),
1431 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity: 3 }),
1432 Instruction::Cpu(CpuInstruction::Halt),
1433 ],
1434 };
1435 let errors = simulate_stack(&program, None);
1436 assert!(errors.is_empty(), "expected no errors, got: {:?}", errors);
1437 }
1438
1439 fn test_arch_with_caps(feed_forward: bool, atom_reloading: bool) -> ArchSpec {
1442 let mut arch = test_arch_spec();
1443 arch.feed_forward = feed_forward;
1444 arch.atom_reloading = atom_reloading;
1445 arch
1446 }
1447
1448 #[test]
1449 fn test_cap_single_measure_allowed() {
1450 let arch = test_arch_with_caps(false, false);
1451 let program = Program {
1452 version: Version::new(1, 0),
1453 instructions: vec![
1454 Instruction::LaneConst(LaneConstInstruction::ConstZone(0)),
1455 Instruction::Measurement(MeasurementInstruction::Measure { arity: 1 }),
1456 ],
1457 };
1458 assert!(validate_capabilities(&program, &arch).is_empty());
1459 }
1460
1461 #[test]
1462 fn test_cap_multiple_measure_rejected() {
1463 let arch = test_arch_with_caps(false, false);
1464 let program = Program {
1465 version: Version::new(1, 0),
1466 instructions: vec![
1467 Instruction::LaneConst(LaneConstInstruction::ConstZone(0)),
1468 Instruction::Measurement(MeasurementInstruction::Measure { arity: 1 }),
1469 Instruction::LaneConst(LaneConstInstruction::ConstZone(0)),
1470 Instruction::Measurement(MeasurementInstruction::Measure { arity: 1 }),
1471 ],
1472 };
1473 let errors = validate_capabilities(&program, &arch);
1474 assert_eq!(errors.len(), 1);
1475 assert!(matches!(
1476 errors[0],
1477 ValidationError::FeedForwardNotSupported { pc: 3 }
1478 ));
1479 }
1480
1481 #[test]
1482 fn test_cap_multiple_measure_with_feed_forward() {
1483 let arch = test_arch_with_caps(true, false);
1484 let program = Program {
1485 version: Version::new(1, 0),
1486 instructions: vec![
1487 Instruction::LaneConst(LaneConstInstruction::ConstZone(0)),
1488 Instruction::Measurement(MeasurementInstruction::Measure { arity: 1 }),
1489 Instruction::LaneConst(LaneConstInstruction::ConstZone(0)),
1490 Instruction::Measurement(MeasurementInstruction::Measure { arity: 1 }),
1491 ],
1492 };
1493 assert!(validate_capabilities(&program, &arch).is_empty());
1494 }
1495
1496 #[test]
1497 fn test_cap_fill_rejected() {
1498 let arch = test_arch_with_caps(false, false);
1499 let program = Program {
1500 version: Version::new(1, 0),
1501 instructions: vec![
1502 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0)),
1503 Instruction::AtomArrangement(AtomArrangementInstruction::Fill { arity: 1 }),
1504 ],
1505 };
1506 let errors = validate_capabilities(&program, &arch);
1507 assert_eq!(errors.len(), 1);
1508 assert!(matches!(
1509 errors[0],
1510 ValidationError::AtomReloadingNotSupported { pc: 1 }
1511 ));
1512 }
1513
1514 #[test]
1515 fn test_cap_fill_with_atom_reloading() {
1516 let arch = test_arch_with_caps(false, true);
1517 let program = Program {
1518 version: Version::new(1, 0),
1519 instructions: vec![
1520 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0)),
1521 Instruction::AtomArrangement(AtomArrangementInstruction::Fill { arity: 1 }),
1522 ],
1523 };
1524 assert!(validate_capabilities(&program, &arch).is_empty());
1525 }
1526
1527 #[test]
1528 fn test_cap_initial_fill_always_allowed() {
1529 let arch = test_arch_with_caps(false, false);
1530 let program = Program {
1531 version: Version::new(1, 0),
1532 instructions: vec![
1533 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0)),
1534 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity: 1 }),
1535 ],
1536 };
1537 assert!(validate_capabilities(&program, &arch).is_empty());
1538 }
1539
1540 #[test]
1541 fn test_no_duplicate_lane_passes() {
1542 let lane0 = make_lane(Direction::Forward, MoveType::SiteBus, 0, 0, 0);
1543 let lane1 = make_lane(Direction::Forward, MoveType::SiteBus, 0, 1, 0);
1544 let lane2 = make_lane(Direction::Forward, MoveType::SiteBus, 0, 2, 0);
1545 let program = Program {
1546 version: Version::new(1, 0),
1547 instructions: vec![
1548 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane0.0, lane0.1)),
1549 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane1.0, lane1.1)),
1550 Instruction::LaneConst(LaneConstInstruction::ConstLane(lane2.0, lane2.1)),
1551 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 3 }),
1552 ],
1553 };
1554 let errors = simulate_stack(&program, None);
1555 assert!(errors.is_empty(), "expected no errors, got: {:?}", errors);
1556 }
1557}