1#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
19pub enum WaveForm {
20 #[default]
23 Particle,
24 Humanoid,
27 Vehicle,
29 Fluid,
31}
32
33impl WaveForm {
34 pub fn label(self) -> &'static str {
35 match self {
36 Self::Particle => "Particle",
37 Self::Humanoid => "Humanoid",
38 Self::Vehicle => "Vehicle",
39 Self::Fluid => "Fluid",
40 }
41 }
42}
43
44#[derive(Debug, Clone)]
55pub struct DecoherenceField {
56 pub position: [f32; 3],
58 pub radius: f32,
60 pub target_form: WaveForm,
62 pub gamma: f32,
65 pub animation_clip: String,
68 pub source_tag: String,
70}
71
72impl DecoherenceField {
73 #[inline]
76 pub fn effective_gamma(&self, observer_pos: &[f32; 3]) -> f32 {
77 let dx = observer_pos[0] - self.position[0];
78 let dy = observer_pos[1] - self.position[1];
79 let dz = observer_pos[2] - self.position[2];
80 let dist = (dx * dx + dy * dy + dz * dz).sqrt();
81 if dist >= self.radius {
82 return 0.0;
83 }
84 self.gamma * (1.0 - dist / self.radius)
85 }
86}
87
88#[derive(Debug, Clone)]
91pub struct DecoherenceState {
92 pub coherence: f32,
95 pub time_in_field: f32,
97 pub active_gamma: f32,
99 pub current_form: WaveForm,
101 pub target_form: Option<WaveForm>,
103 pub recoherence_rate: f32,
105 pub in_field: bool,
107 pub active_clip: String,
109}
110
111impl Default for DecoherenceState {
112 fn default() -> Self {
113 Self {
114 coherence: 1.0,
115 time_in_field: 0.0,
116 active_gamma: 0.0,
117 current_form: WaveForm::Particle,
118 target_form: None,
119 recoherence_rate: 2.0,
120 in_field: false,
121 active_clip: String::new(),
122 }
123 }
124}
125
126pub struct DreamWaveController {
139 pub particle: super::particle::ParticleController,
141 pub decoherence: DecoherenceState,
143 pub quantum: QuantumLocomotionState,
145 pub bone_targets: Vec<[f32; 3]>,
148 pub particle_weights: Vec<f32>,
151 #[allow(dead_code)]
154 active_fields: Vec<DecoherenceField>,
155 #[allow(dead_code)]
158 field_scratch: Vec<DecoherenceField>,
159}
160
161impl DreamWaveController {
162 pub fn new(position: [f32; 3], particle_count: u32) -> Self {
163 Self {
164 particle: super::particle::ParticleController::new(position, particle_count),
165 decoherence: DecoherenceState::default(),
166 quantum: QuantumLocomotionState::new(particle_count),
167 bone_targets: vec![[0.0; 3]; particle_count as usize],
168 particle_weights: vec![1.0; particle_count as usize],
169 active_fields: Vec::with_capacity(4),
170 field_scratch: Vec::with_capacity(8),
171 }
172 }
173
174 pub fn update(
177 &mut self,
178 forward: bool,
179 back: bool,
180 left: bool,
181 right: bool,
182 jump: bool,
183 sprint: bool,
184 camera_yaw: f32,
185 dt: f32,
186 fields: &[DecoherenceField],
187 ) -> bool {
188 let moved = self
190 .particle
191 .update(forward, back, left, right, jump, sprint, camera_yaw, dt);
192
193 let moving = forward || back || left || right;
195 let input_bias = [
196 if !moving { 2.0 } else { 0.0 }, if moving && !sprint { 2.0 } else { 0.0 }, if jump { 3.0 } else { 0.0 }, if self.particle.landing_timer > 0.0 { 2.0 } else { 0.0 }, if sprint && moving { 2.0 } else { 0.0 }, ];
202 let epsilon = if self.particle.grounded { 0.3 * dt } else { 0.05 * dt };
204 let _measured_mode = self.quantum.tick(dt, &input_bias, epsilon);
205
206 let pos = &self.particle.position;
208 let mut strongest_gamma = 0.0f32;
209 let mut strongest_field: Option<&DecoherenceField> = None;
210 for field in fields {
211 let g = field.effective_gamma(pos);
212 if g > strongest_gamma {
213 strongest_gamma = g;
214 strongest_field = Some(field);
215 }
216 }
217
218 if let Some(field) = strongest_field {
220 self.decoherence.in_field = true;
222 self.decoherence.active_gamma = strongest_gamma;
223
224 let modulated_gamma = if sprint { strongest_gamma * 2.0 } else { strongest_gamma };
226
227 self.decoherence.time_in_field += dt;
228
229 self.decoherence.coherence = (-modulated_gamma * self.decoherence.time_in_field).exp();
232
233 if self.decoherence.target_form.is_none() || self.decoherence.target_form != Some(field.target_form) {
235 self.decoherence.target_form = Some(field.target_form);
236 self.decoherence.active_clip = field.animation_clip.clone();
237 }
238
239 if self.decoherence.coherence < 0.01 {
241 self.decoherence.coherence = 0.0;
242 self.decoherence.current_form = field.target_form;
243 }
244 } else {
245 self.decoherence.in_field = false;
247 self.decoherence.time_in_field = 0.0;
248
249 if self.decoherence.coherence < 1.0 {
250 let rate = self.decoherence.recoherence_rate;
253 self.decoherence.coherence += (1.0 - self.decoherence.coherence) * (1.0 - (-rate * dt).exp());
254
255 if self.decoherence.coherence > 0.99 {
256 self.decoherence.coherence = 1.0;
257 self.decoherence.current_form = WaveForm::Particle;
258 self.decoherence.target_form = None;
259 self.decoherence.active_clip.clear();
260 }
261 }
262 }
263
264 let w = self.decoherence.coherence;
266 for pw in &mut self.particle_weights {
267 *pw = w;
268 }
269
270 moved
271 }
272
273 pub fn coherence(&self) -> f32 {
275 self.decoherence.coherence
276 }
277
278 pub fn current_form(&self) -> WaveForm {
280 self.decoherence.current_form
281 }
282
283 pub fn is_decohering(&self) -> bool {
285 self.decoherence.in_field && self.decoherence.coherence > 0.01
286 }
287
288 pub fn is_collapsed(&self) -> bool {
290 self.decoherence.coherence < 0.01
291 }
292
293 pub fn is_recohering(&self) -> bool {
295 !self.decoherence.in_field && self.decoherence.coherence < 0.99
296 }
297
298 pub fn set_bone_targets(&mut self, bones: &[[f32; 3]]) {
301 let particle_count = self.particle.particle_count as usize;
303 let offsets = super::particle::particle_sphere_offsets(self.particle.particle_count);
304 let params = self.particle.shape_params();
305 let radius = self.particle.cloud_radius * params.radius_scale;
306
307 for i in 0..particle_count.min(self.bone_targets.len()) {
308 if i >= offsets.len() {
309 break;
310 }
311 let px = self.particle.position[0] + offsets[i][0] * params.stretch[0] * radius;
313 let py = self.particle.position[1] + offsets[i][1] * params.stretch[1] * radius;
314 let pz = self.particle.position[2] + offsets[i][2] * params.stretch[2] * radius;
315
316 let mut best_dist_sq = f32::MAX;
318 let mut best_bone = [0.0f32; 3];
319 for bone in bones {
320 let dx = px - bone[0];
321 let dy = py - bone[1];
322 let dz = pz - bone[2];
323 let d2 = dx * dx + dy * dy + dz * dz;
324 if d2 < best_dist_sq {
325 best_dist_sq = d2;
326 best_bone = *bone;
327 }
328 }
329 self.bone_targets[i] = best_bone;
330 }
331 }
332}
333
334#[derive(Debug, Clone, Copy, Default)]
348pub struct Complex {
349 pub re: f32,
350 pub im: f32,
351}
352
353impl Complex {
354 pub const ZERO: Self = Self { re: 0.0, im: 0.0 };
355 pub const ONE: Self = Self { re: 1.0, im: 0.0 };
356
357 pub fn new(re: f32, im: f32) -> Self {
358 Self { re, im }
359 }
360 pub fn from_real(r: f32) -> Self {
361 Self { re: r, im: 0.0 }
362 }
363
364 pub fn conj(self) -> Self {
365 Self {
366 re: self.re,
367 im: -self.im,
368 }
369 }
370 pub fn norm_sq(self) -> f32 {
371 self.re * self.re + self.im * self.im
372 }
373 pub fn norm(self) -> f32 {
374 self.norm_sq().sqrt()
375 }
376
377 pub fn mul(self, other: Self) -> Self {
378 Self {
379 re: self.re * other.re - self.im * other.im,
380 im: self.re * other.im + self.im * other.re,
381 }
382 }
383
384 pub fn add(self, other: Self) -> Self {
385 Self {
386 re: self.re + other.re,
387 im: self.im + other.im,
388 }
389 }
390
391 pub fn sub(self, other: Self) -> Self {
392 Self {
393 re: self.re - other.re,
394 im: self.im - other.im,
395 }
396 }
397
398 pub fn scale(self, s: f32) -> Self {
399 Self {
400 re: self.re * s,
401 im: self.im * s,
402 }
403 }
404
405 #[allow(dead_code)]
407 pub fn exp_i(theta: f32) -> Self {
408 Self {
409 re: theta.cos(),
410 im: theta.sin(),
411 }
412 }
413}
414
415pub struct DensityMatrix5 {
425 pub entries: [Complex; 25],
427}
428
429impl DensityMatrix5 {
430 pub fn pure_state(k: usize) -> Self {
432 let mut entries = [Complex::ZERO; 25];
433 if k < 5 {
434 entries[k * 5 + k] = Complex::ONE;
435 }
436 Self { entries }
437 }
438
439 pub fn from_state_vector(amplitudes: &[Complex; 5]) -> Self {
442 let mut entries = [Complex::ZERO; 25];
443 for i in 0..5 {
444 for j in 0..5 {
445 entries[i * 5 + j] = amplitudes[i].mul(amplitudes[j].conj());
446 }
447 }
448 Self { entries }
449 }
450
451 pub fn trace(&self) -> f32 {
453 (0..5).map(|k| self.entries[k * 5 + k].re).sum()
454 }
455
456 pub fn populations(&self) -> [f32; 5] {
458 let mut p = [0.0f32; 5];
459 for k in 0..5 {
460 p[k] = self.entries[k * 5 + k].re;
461 }
462 p
463 }
464
465 pub fn coherence_magnitude(&self) -> f32 {
467 let mut c = 0.0f32;
468 for i in 0..5 {
469 for j in (i + 1)..5 {
470 c += self.entries[i * 5 + j].norm();
471 }
472 }
473 c
474 }
475
476 pub fn dephased(&self) -> Self {
478 let mut result = Self {
479 entries: [Complex::ZERO; 25],
480 };
481 for k in 0..5 {
482 result.entries[k * 5 + k] = self.entries[k * 5 + k];
483 }
484 result
485 }
486
487 pub fn apply_dephasing(&mut self, epsilon: f32) {
490 let retain = 1.0 - epsilon.clamp(0.0, 1.0);
491 for i in 0..5 {
492 for j in 0..5 {
493 if i != j {
494 self.entries[i * 5 + j] = self.entries[i * 5 + j].scale(retain);
495 }
496 }
497 }
498 }
499
500 pub fn apply_unitary(&mut self, u: &[Complex; 25]) {
503 let mut u_rho = [Complex::ZERO; 25];
505 for i in 0..5 {
506 for j in 0..5 {
507 let mut sum = Complex::ZERO;
508 for k in 0..5 {
509 sum = sum.add(u[i * 5 + k].mul(self.entries[k * 5 + j]));
510 }
511 u_rho[i * 5 + j] = sum;
512 }
513 }
514 for i in 0..5 {
516 for j in 0..5 {
517 let mut sum = Complex::ZERO;
518 for k in 0..5 {
519 sum = sum.add(u_rho[i * 5 + k].mul(u[j * 5 + k].conj()));
520 }
521 self.entries[i * 5 + j] = sum;
522 }
523 }
524 }
525
526 pub fn measure(&self, seed: u64) -> usize {
530 let pops = self.populations();
531 let mut s = seed;
533 s = s.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
534 let r = (s >> 33) as f32 / (1u64 << 31) as f32; let mut cumulative = 0.0;
536 for (i, &p) in pops.iter().enumerate() {
537 cumulative += p;
538 if r < cumulative {
539 return i;
540 }
541 }
542 4 }
544}
545
546pub struct LocomotionHamiltonian {
549 pub hbar_eff: f32,
551 pub bias: [f32; 5],
553 pub couplings: [f32; 5],
556}
557
558impl Default for LocomotionHamiltonian {
559 fn default() -> Self {
560 Self {
561 hbar_eff: 1.0,
562 bias: [0.0, 0.2, 0.5, 0.3, 0.4], couplings: [1.0, 0.8, 0.6, 0.9, 0.7], }
565 }
566}
567
568impl LocomotionHamiltonian {
569 pub fn build_matrix(&self, input_bias: &[f32; 5], phase_time: f32) -> [Complex; 25] {
572 let mut h = [Complex::ZERO; 25];
573
574 for k in 0..5 {
576 h[k * 5 + k] = Complex::from_real(self.bias[k] + input_bias[k] + 0.1 * (k as f32 * phase_time).sin());
577 }
578
579 let pairs: [(usize, usize); 5] = [(0, 1), (1, 4), (1, 2), (2, 3), (3, 0)];
582 for (idx, &(i, j)) in pairs.iter().enumerate() {
583 let w = Complex::from_real(self.couplings[idx]);
584 h[i * 5 + j] = w;
585 h[j * 5 + i] = w.conj(); }
587
588 h
589 }
590
591 pub fn compute_unitary(&self, h: &[Complex; 25], dt: f32) -> [Complex; 25] {
595 let scale = dt / self.hbar_eff;
596 let mut u = [Complex::ZERO; 25];
597
598 for k in 0..5 {
600 u[k * 5 + k] = Complex::ONE;
601 }
602
603 for i in 0..5 {
605 for j in 0..5 {
606 let ih = Complex::new(h[i * 5 + j].im * scale, -h[i * 5 + j].re * scale);
608 u[i * 5 + j] = u[i * 5 + j].sub(ih);
609 }
610 }
611
612 let s2 = scale * scale * 0.5;
614 for i in 0..5 {
615 for j in 0..5 {
616 let mut h2_ij = Complex::ZERO;
617 for k in 0..5 {
618 h2_ij = h2_ij.add(h[i * 5 + k].mul(h[k * 5 + j]));
619 }
620 u[i * 5 + j] = u[i * 5 + j].sub(h2_ij.scale(s2));
621 }
622 }
623
624 u
625 }
626}
627
628pub struct InterferenceKernels {
637 pub kernels: Vec<[[Complex; 3]; 10]>,
642}
643
644impl InterferenceKernels {
645 pub fn generate(particle_count: u32) -> Self {
649 let n = particle_count as usize;
650 let mut kernels = Vec::with_capacity(n);
651 let golden = (1.0 + 5.0f32.sqrt()) / 2.0;
652
653 for p in 0..n {
654 let t = p as f32 / n.max(1) as f32;
655 let mut pair_kernels = [[Complex::ZERO; 3]; 10];
656
657 pair_kernels[0] = [
663 Complex::new(0.3 * (t * 12.0).sin(), 0.1 * (t * 8.0).cos()),
664 Complex::new(0.0, 0.15 * (t * 10.0).sin()),
665 Complex::new(0.2 * (t * 6.0).cos(), 0.0),
666 ];
667
668 pair_kernels[1] = [
670 Complex::new(0.0, 0.1 * (t * 4.0).sin()),
671 Complex::new(0.4 * (t * 8.0).sin(), 0.2 * (t * golden * 5.0).cos()),
672 Complex::new(0.0, 0.1 * (t * 4.0).cos()),
673 ];
674
675 pair_kernels[2] = [
677 Complex::new(0.15 * (t * 16.0).sin(), 0.0),
678 Complex::new(-0.3 * (t * 12.0).cos(), 0.1),
679 Complex::new(0.15 * (t * 16.0).cos(), 0.0),
680 ];
681
682 pair_kernels[3] = [
684 Complex::new(0.1, 0.2 * (t * 20.0).sin()),
685 Complex::new(0.0, 0.05),
686 Complex::new(0.35 * (t * 14.0).sin(), 0.15 * (t * 7.0).cos()),
687 ];
688
689 pair_kernels[4] = [
691 Complex::new(0.1 * (t * 6.0).cos(), 0.0),
692 Complex::new(0.25 * (t * 10.0).sin(), 0.2 * (t * 5.0).cos()),
693 Complex::new(0.15 * (t * 8.0).sin(), 0.0),
694 ];
695
696 pair_kernels[5] = [
698 Complex::new(0.2 * (t * 14.0).sin(), 0.1 * (t * 7.0).cos()),
699 Complex::new(-0.15 * (t * 18.0).cos(), 0.0),
700 Complex::new(0.2 * (t * 14.0).cos(), -0.1 * (t * 7.0).sin()),
701 ];
702
703 pair_kernels[6] = [
705 Complex::new(0.05, 0.1 * (t * 22.0).sin()),
706 Complex::new(0.0, 0.05),
707 Complex::new(0.3 * (t * 18.0).sin(), 0.2 * (t * 9.0).cos()),
708 ];
709
710 pair_kernels[7] = [
712 Complex::new(0.1 * (t * 8.0).cos(), 0.0),
713 Complex::new(0.35 * (t * 16.0).sin(), -0.15 * (t * 8.0).cos()),
714 Complex::new(0.1 * (t * 8.0).sin(), 0.0),
715 ];
716
717 pair_kernels[8] = [
719 Complex::new(0.15 * (t * 10.0).sin(), 0.1),
720 Complex::new(0.2 * (t * 12.0).cos(), 0.15 * (t * 6.0).sin()),
721 Complex::new(0.25 * (t * 15.0).sin(), 0.0),
722 ];
723
724 pair_kernels[9] = [
726 Complex::new(0.2 * (t * 20.0).sin(), 0.1 * (t * 10.0).cos()),
727 Complex::new(-0.1 * (t * 24.0).cos(), 0.05),
728 Complex::new(0.15 * (t * 16.0).cos(), 0.15 * (t * 8.0).sin()),
729 ];
730
731 kernels.push(pair_kernels);
732 }
733
734 Self { kernels }
735 }
736
737 pub fn interference_displacement(&self, n: usize, rho: &DensityMatrix5) -> [f32; 3] {
741 if n >= self.kernels.len() {
742 return [0.0; 3];
743 }
744 let k = &self.kernels[n];
745 let mut disp = [0.0f32; 3];
746
747 let pairs: [(usize, usize); 10] = [
749 (0, 1),
750 (0, 2),
751 (0, 3),
752 (0, 4),
753 (1, 2),
754 (1, 3),
755 (1, 4),
756 (2, 3),
757 (2, 4),
758 (3, 4),
759 ];
760 for (idx, &(i, j)) in pairs.iter().enumerate() {
761 let rho_ij = rho.entries[i * 5 + j];
762 for axis in 0..3 {
763 let product = k[idx][axis].mul(rho_ij);
765 disp[axis] += 2.0 * product.re;
766 }
767 }
768
769 disp
770 }
771}
772
773pub struct QuantumLocomotionState {
776 pub rho: DensityMatrix5,
778 pub hamiltonian: LocomotionHamiltonian,
780 pub kernels: InterferenceKernels,
782 pub phase_time: f32,
784 pub frame: u64,
786}
787
788impl QuantumLocomotionState {
789 pub fn new(particle_count: u32) -> Self {
790 Self {
792 rho: DensityMatrix5::pure_state(0),
793 hamiltonian: LocomotionHamiltonian::default(),
794 kernels: InterferenceKernels::generate(particle_count),
795 phase_time: 0.0,
796 frame: 0,
797 }
798 }
799
800 pub fn tick(&mut self, dt: f32, input_bias: &[f32; 5], decoherence_epsilon: f32) -> usize {
806 self.phase_time += dt;
807 self.frame += 1;
808
809 let h = self.hamiltonian.build_matrix(input_bias, self.phase_time);
811
812 let u = self.hamiltonian.compute_unitary(&h, dt);
814 self.rho.apply_unitary(&u);
815
816 self.rho.apply_dephasing(decoherence_epsilon);
818
819 self.rho.measure(self.frame)
821 }
822
823 pub fn particle_position_with_interference(
826 &self,
827 n: usize,
828 root: [f32; 3],
829 mode_templates: &[[f32; 3]; 5],
830 ) -> [f32; 3] {
831 let pops = self.rho.populations();
832 let interference = self.kernels.interference_displacement(n, &self.rho);
833
834 let mut pos = root;
835 for k in 0..5 {
836 pos[0] += pops[k] * mode_templates[k][0];
837 pos[1] += pops[k] * mode_templates[k][1];
838 pos[2] += pops[k] * mode_templates[k][2];
839 }
840 pos[0] += interference[0];
841 pos[1] += interference[1];
842 pos[2] += interference[2];
843
844 pos
845 }
846}
847
848#[derive(Debug, Clone, PartialEq)]
850pub enum DecoherenceTagValue {
851 Form(WaveForm, String),
853 Gamma(f32),
855}
856
857pub fn parse_decoherence_tag(tag: &str) -> Option<DecoherenceTagValue> {
864 if let Some(rest) = tag.strip_prefix("decohere:") {
865 let mut parts = rest.splitn(2, ':');
866 let form_str = parts.next()?;
867 let form = match form_str {
868 "humanoid" => WaveForm::Humanoid,
869 "vehicle" => WaveForm::Vehicle,
870 "fluid" => WaveForm::Fluid,
871 "particle" => WaveForm::Particle,
872 _ => return None,
873 };
874 let clip = parts.next().unwrap_or("").to_string();
875 Some(DecoherenceTagValue::Form(form, clip))
876 } else if let Some(rest) = tag.strip_prefix("gamma:") {
877 let rate: f32 = rest.parse().ok()?;
878 Some(DecoherenceTagValue::Gamma(rate))
879 } else {
880 None
881 }
882}
883
884#[cfg(test)]
885mod tests {
886 use super::*;
887
888 #[test]
889 fn wave_form_default_is_particle() {
890 assert_eq!(WaveForm::default(), WaveForm::Particle);
891 }
892
893 #[test]
894 fn decoherence_field_outside_radius_zero() {
895 let field = DecoherenceField {
896 position: [0.0, 0.0, 0.0],
897 radius: 5.0,
898 target_form: WaveForm::Humanoid,
899 gamma: 4.0,
900 animation_clip: String::new(),
901 source_tag: String::new(),
902 };
903 let g = field.effective_gamma(&[10.0, 0.0, 0.0]);
905 assert_eq!(g, 0.0, "gamma should be 0 outside field radius");
906 }
907
908 #[test]
909 fn decoherence_field_at_center_full_gamma() {
910 let field = DecoherenceField {
911 position: [5.0, 0.0, 5.0],
912 radius: 10.0,
913 target_form: WaveForm::Humanoid,
914 gamma: 8.0,
915 animation_clip: String::new(),
916 source_tag: String::new(),
917 };
918 let g = field.effective_gamma(&[5.0, 0.0, 5.0]);
920 assert!(
921 (g - 8.0).abs() < 1e-5,
922 "gamma at center should equal field gamma (8.0), got {}",
923 g
924 );
925 }
926
927 #[test]
928 fn decoherence_field_at_edge_near_zero() {
929 let field = DecoherenceField {
930 position: [0.0, 0.0, 0.0],
931 radius: 10.0,
932 target_form: WaveForm::Fluid,
933 gamma: 4.0,
934 animation_clip: String::new(),
935 source_tag: String::new(),
936 };
937 let g = field.effective_gamma(&[9.99, 0.0, 0.0]);
939 assert!(g < 0.01, "gamma near the edge should be near zero, got {}", g);
940 assert!(g > 0.0, "gamma just inside edge should be >0, got {}", g);
941 }
942
943 #[test]
944 fn dream_wave_controller_starts_coherent() {
945 let ctrl = DreamWaveController::new([0.0, 1.0, 0.0], 64);
946 assert!(
947 (ctrl.coherence() - 1.0).abs() < 1e-5,
948 "new controller should start fully coherent (1.0), got {}",
949 ctrl.coherence()
950 );
951 assert_eq!(ctrl.current_form(), WaveForm::Particle);
952 assert!(!ctrl.is_decohering());
953 assert!(!ctrl.is_collapsed());
954 assert!(!ctrl.is_recohering());
955 }
956
957 #[test]
958 fn dream_wave_controller_decoheres_in_field() {
959 let mut ctrl = DreamWaveController::new([0.0, 1.0, 0.0], 64);
960 let fields = vec![DecoherenceField {
961 position: [0.0, 1.0, 0.0],
962 radius: 10.0,
963 target_form: WaveForm::Humanoid,
964 gamma: 4.0,
965 animation_clip: "idle".into(),
966 source_tag: "decohere:humanoid".into(),
967 }];
968
969 for _ in 0..60 {
971 ctrl.update(false, false, false, false, false, false, 0.0, 0.016, &fields);
972 }
973
974 assert!(
975 ctrl.coherence() < 1.0,
976 "coherence should decrease inside a field, got {}",
977 ctrl.coherence()
978 );
979 assert!(ctrl.decoherence.in_field, "should be marked as in_field");
980 assert_eq!(
981 ctrl.decoherence.target_form,
982 Some(WaveForm::Humanoid),
983 "target form should be Humanoid"
984 );
985 }
986
987 #[test]
988 fn dream_wave_controller_recoheres_outside_field() {
989 let mut ctrl = DreamWaveController::new([0.0, 1.0, 0.0], 64);
990 let fields = vec![DecoherenceField {
991 position: [0.0, 1.0, 0.0],
992 radius: 10.0,
993 target_form: WaveForm::Humanoid,
994 gamma: 8.0,
995 animation_clip: String::new(),
996 source_tag: String::new(),
997 }];
998
999 for _ in 0..120 {
1001 ctrl.update(false, false, false, false, false, false, 0.0, 0.016, &fields);
1002 }
1003 let coherence_in_field = ctrl.coherence();
1004 assert!(
1005 coherence_in_field < 0.5,
1006 "should be significantly decohered after 120 frames at gamma=8, got {}",
1007 coherence_in_field
1008 );
1009
1010 let no_fields: Vec<DecoherenceField> = vec![];
1012 for _ in 0..300 {
1013 ctrl.update(false, false, false, false, false, false, 0.0, 0.016, &no_fields);
1014 }
1015
1016 assert!(
1017 ctrl.coherence() > coherence_in_field,
1018 "coherence should increase after leaving field: was {}, now {}",
1019 coherence_in_field,
1020 ctrl.coherence()
1021 );
1022 assert!(!ctrl.decoherence.in_field, "should not be in_field after leaving");
1023 }
1024
1025 #[test]
1026 fn parse_decoherence_tag_humanoid() {
1027 let result = parse_decoherence_tag("decohere:humanoid:climb_stairs");
1028 assert_eq!(
1029 result,
1030 Some(DecoherenceTagValue::Form(WaveForm::Humanoid, "climb_stairs".into()))
1031 );
1032 }
1033
1034 #[test]
1035 fn parse_decoherence_tag_gamma() {
1036 let result = parse_decoherence_tag("gamma:4.0");
1037 assert_eq!(result, Some(DecoherenceTagValue::Gamma(4.0)));
1038 }
1039
1040 #[test]
1043 fn parse_decoherence_tag_fluid_no_clip() {
1044 let result = parse_decoherence_tag("decohere:fluid");
1045 assert_eq!(result, Some(DecoherenceTagValue::Form(WaveForm::Fluid, String::new())));
1046 }
1047
1048 #[test]
1049 fn parse_decoherence_tag_unknown_form_returns_none() {
1050 assert_eq!(parse_decoherence_tag("decohere:dragon"), None);
1051 }
1052
1053 #[test]
1054 fn parse_decoherence_tag_unrelated_returns_none() {
1055 assert_eq!(parse_decoherence_tag("some_other_tag"), None);
1056 }
1057
1058 #[test]
1059 fn parse_decoherence_tag_gamma_invalid_returns_none() {
1060 assert_eq!(parse_decoherence_tag("gamma:notanumber"), None);
1061 }
1062
1063 #[test]
1064 fn wave_form_labels() {
1065 assert_eq!(WaveForm::Particle.label(), "Particle");
1066 assert_eq!(WaveForm::Humanoid.label(), "Humanoid");
1067 assert_eq!(WaveForm::Vehicle.label(), "Vehicle");
1068 assert_eq!(WaveForm::Fluid.label(), "Fluid");
1069 }
1070
1071 #[test]
1072 fn dream_wave_controller_fully_collapses() {
1073 let mut ctrl = DreamWaveController::new([0.0, 1.0, 0.0], 32);
1074 let fields = vec![DecoherenceField {
1075 position: [0.0, 1.0, 0.0],
1076 radius: 10.0,
1077 target_form: WaveForm::Vehicle,
1078 gamma: 20.0, animation_clip: String::new(),
1080 source_tag: String::new(),
1081 }];
1082
1083 for _ in 0..200 {
1085 ctrl.update(false, false, false, false, false, false, 0.0, 0.016, &fields);
1086 }
1087
1088 assert!(ctrl.is_collapsed(), "should be fully collapsed");
1089 assert_eq!(ctrl.current_form(), WaveForm::Vehicle);
1090 assert_eq!(ctrl.coherence(), 0.0);
1091 }
1092
1093 #[test]
1094 fn dream_wave_controller_particle_weights_match_coherence() {
1095 let mut ctrl = DreamWaveController::new([0.0, 1.0, 0.0], 16);
1096 let fields = vec![DecoherenceField {
1097 position: [0.0, 1.0, 0.0],
1098 radius: 10.0,
1099 target_form: WaveForm::Humanoid,
1100 gamma: 4.0,
1101 animation_clip: String::new(),
1102 source_tag: String::new(),
1103 }];
1104
1105 ctrl.update(false, false, false, false, false, false, 0.0, 0.016, &fields);
1106 let c = ctrl.coherence();
1107 for &w in &ctrl.particle_weights {
1108 assert!(
1109 (w - c).abs() < 1e-5,
1110 "particle weight {} should match coherence {}",
1111 w,
1112 c
1113 );
1114 }
1115 }
1116
1117 #[test]
1120 fn ablation_interference_changes_output() {
1121 let mut state = QuantumLocomotionState::new(64);
1123
1124 let sq2 = 1.0 / 2.0f32.sqrt();
1126 state.rho = DensityMatrix5::from_state_vector(&[
1127 Complex::new(sq2, 0.0), Complex::new(sq2, 0.0), Complex::ZERO,
1130 Complex::ZERO,
1131 Complex::ZERO,
1132 ]);
1133
1134 let root = [0.0; 3];
1135 let templates: [[f32; 3]; 5] = [
1136 [0.0, 0.0, 0.0], [0.0, 0.0, 1.0], [0.0, 1.0, 0.0], [0.0, -0.5, 0.0], [0.0, 0.0, 2.0], ];
1142
1143 let pos_quantum = state.particle_position_with_interference(0, root, &templates);
1145
1146 let dephased = state.rho.dephased();
1148 let old_rho_entries = state.rho.entries;
1149 state.rho = dephased;
1150 let pos_dephased = state.particle_position_with_interference(0, root, &templates);
1151 state.rho.entries = old_rho_entries;
1152
1153 let diff_sq = (pos_quantum[0] - pos_dephased[0]).powi(2)
1155 + (pos_quantum[1] - pos_dephased[1]).powi(2)
1156 + (pos_quantum[2] - pos_dephased[2]).powi(2);
1157
1158 assert!(
1159 diff_sq > 1e-6,
1160 "QUANTUM.md S7 FAILED: interference does not affect output. diff_sq={diff_sq}"
1161 );
1162 }
1163
1164 #[test]
1165 fn density_matrix_trace_one() {
1166 let rho = DensityMatrix5::pure_state(2);
1167 assert!((rho.trace() - 1.0).abs() < 1e-6);
1168 }
1169
1170 #[test]
1171 fn density_matrix_from_state_vector_trace() {
1172 let sq2 = 1.0 / 2.0f32.sqrt();
1173 let rho = DensityMatrix5::from_state_vector(&[
1174 Complex::new(sq2, 0.0),
1175 Complex::new(sq2, 0.0),
1176 Complex::ZERO,
1177 Complex::ZERO,
1178 Complex::ZERO,
1179 ]);
1180 assert!((rho.trace() - 1.0).abs() < 1e-6);
1181 assert!(rho.coherence_magnitude() > 0.1);
1183 }
1184
1185 #[test]
1186 fn dephasing_removes_coherence() {
1187 let sq2 = 1.0 / 2.0f32.sqrt();
1188 let rho = DensityMatrix5::from_state_vector(&[
1189 Complex::new(sq2, 0.0),
1190 Complex::new(sq2, 0.0),
1191 Complex::ZERO,
1192 Complex::ZERO,
1193 Complex::ZERO,
1194 ]);
1195 let dephased = rho.dephased();
1196 assert!(dephased.coherence_magnitude() < 1e-6);
1197 assert!((dephased.trace() - 1.0).abs() < 1e-6);
1198 }
1199
1200 #[test]
1201 fn hamiltonian_evolution_preserves_trace() {
1202 let mut state = QuantumLocomotionState::new(16);
1203 let input = [1.0, 0.5, 0.0, 0.0, 0.0];
1204 state.tick(1.0 / 60.0, &input, 0.0);
1205 assert!(
1207 (state.rho.trace() - 1.0).abs() < 0.1,
1208 "Trace drifted: {}",
1209 state.rho.trace()
1210 );
1211 }
1212
1213 #[test]
1214 fn born_rule_measurement_valid() {
1215 let rho = DensityMatrix5::pure_state(2); for seed in 0..100 {
1218 assert_eq!(rho.measure(seed), 2);
1219 }
1220 }
1221
1222 #[test]
1223 fn quantum_locomotion_full_tick_cycle() {
1224 let mut state = QuantumLocomotionState::new(32);
1225 for _ in 0..120 {
1227 state.tick(1.0 / 60.0, &[0.0, 2.0, 0.0, 0.0, 0.0], 0.02);
1228 }
1229 let pops = state.rho.populations();
1231 assert!(pops[1] > 0.1, "Moving population should be significant: {:?}", pops);
1232 let trace: f32 = pops.iter().sum();
1234 assert!((trace - 1.0).abs() < 0.2, "Trace should be near 1.0: {}", trace);
1235 }
1236}