Download wasm/lib.rs from Snapkitty/sov-kernel-monster: direct link, hf CLI and curl.
- Browser
- Download file 22.2 kB
-
https://huggingface.co/Snapkitty/sov-kernel-monster/resolve/main/wasm/lib.rs
- Command line
-
hf download hf://Snapkitty/sov-kernel-monster/wasm/lib.rs
-
curl -L -o lib.rs https://huggingface.co/Snapkitty/sov-kernel-monster/resolve/main/wasm/lib.rs
22.2 kB
| // BOB Quantum Civilization Engine β WASM Bridge | |
| // Ports the math from bob_*.f90 to Rust/WASM for browser execution | |
| // Mirrors: bob_kinds, bob_state, bob_lattice, bob_metrics, bob_measurement, bob_hamiltonian, bob_integrator | |
| use wasm_bindgen::prelude::*; | |
| use serde::{Deserialize, Serialize}; | |
| use std::f64::consts::PI; | |
| // ββ CONSTANTS (mirrors bob_kinds.f90) ββββββββββββββββββββββββββββββββββββββ | |
| const HBAR: f64 = 1.054_571_817e-34; | |
| const NORM_TOL: f64 = 1e-10; | |
| // ββ COMPLEX ARITHMETIC βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| pub struct C64 { | |
| pub re: f64, | |
| pub im: f64, | |
| } | |
| impl C64 { | |
| pub fn new(re: f64, im: f64) -> Self { Self { re, im } } | |
| pub fn zero() -> Self { Self { re: 0.0, im: 0.0 } } | |
| pub fn one() -> Self { Self { re: 1.0, im: 0.0 } } | |
| pub fn i() -> Self { Self { re: 0.0, im: 1.0 } } | |
| pub fn norm_sq(&self) -> f64 { self.re * self.re + self.im * self.im } | |
| pub fn norm(&self) -> f64 { self.norm_sq().sqrt() } | |
| pub fn conj(&self) -> Self { Self { re: self.re, im: -self.im } } | |
| pub fn phase(&self) -> f64 { self.im.atan2(self.re) } | |
| pub fn add(&self, o: &Self) -> Self { Self::new(self.re + o.re, self.im + o.im) } | |
| pub fn sub(&self, o: &Self) -> Self { Self::new(self.re - o.re, self.im - o.im) } | |
| pub fn mul(&self, o: &Self) -> Self { | |
| Self::new(self.re * o.re - self.im * o.im, self.re * o.im + self.im * o.re) | |
| } | |
| pub fn scale(&self, s: f64) -> Self { Self::new(self.re * s, self.im * s) } | |
| pub fn exp_i(theta: f64) -> Self { Self::new(theta.cos(), theta.sin()) } | |
| } | |
| // ββ QUANTUM STATE (mirrors bob_state.f90) ββββββββββββββββββββββββββββββββββ | |
| // |Οβ© β β^n, n = 2^num_qubits | |
| pub struct QuantumState { | |
| amplitudes: Vec<C64>, | |
| num_qubits: usize, | |
| } | |
| impl QuantumState { | |
| pub fn new(num_qubits: usize) -> Self { | |
| let n = 1usize << num_qubits; | |
| let mut amplitudes = vec![C64::zero(); n]; | |
| amplitudes[0] = C64::one(); // |0...0β© | |
| Self { amplitudes, num_qubits } | |
| } | |
| pub fn num_qubits(&self) -> usize { self.num_qubits } | |
| pub fn dimension(&self) -> usize { self.amplitudes.len() } | |
| // Norm: ||Ο|| = sqrt(Ξ£|Ο_i|Β²) | |
| pub fn norm(&self) -> f64 { | |
| self.amplitudes.iter().map(|a| a.norm_sq()).sum::<f64>().sqrt() | |
| } | |
| // Normalize in place: |Οβ© β |Οβ©/||Ο|| | |
| pub fn normalize(&mut self) -> bool { | |
| let n = self.norm(); | |
| if n < NORM_TOL { return false; } | |
| for a in &mut self.amplitudes { *a = a.scale(1.0 / n); } | |
| true | |
| } | |
| // Probability of measuring basis state i: |Ο_i|Β² | |
| pub fn probability(&self, i: usize) -> f64 { | |
| if i >= self.amplitudes.len() { return 0.0; } | |
| self.amplitudes[i].norm_sq() | |
| } | |
| // Real part of amplitude i | |
| pub fn amplitude_re(&self, i: usize) -> f64 { | |
| if i >= self.amplitudes.len() { 0.0 } else { self.amplitudes[i].re } | |
| } | |
| // Imaginary part of amplitude i | |
| pub fn amplitude_im(&self, i: usize) -> f64 { | |
| if i >= self.amplitudes.len() { 0.0 } else { self.amplitudes[i].im } | |
| } | |
| // Set amplitude | |
| pub fn set_amplitude(&mut self, i: usize, re: f64, im: f64) { | |
| if i < self.amplitudes.len() { | |
| self.amplitudes[i] = C64::new(re, im); | |
| } | |
| } | |
| // Clone into new state | |
| pub fn clone_state(&self) -> QuantumState { | |
| QuantumState { | |
| amplitudes: self.amplitudes.clone(), | |
| num_qubits: self.num_qubits, | |
| } | |
| } | |
| } | |
| // ββ GATES (mirrors bob_gates.f90) ββββββββββββββββββββββββββββββββββββββββββ | |
| // Apply single-qubit gate (2x2 unitary) to qubit k of |Οβ© | |
| fn apply_single_qubit_gate(state: &mut QuantumState, k: usize, u: [[C64; 2]; 2]) { | |
| let n = state.amplitudes.len(); | |
| let block = 1usize << k; | |
| let stride = block << 1; | |
| let mut i = 0; | |
| while i < n { | |
| for j in i..i+block { | |
| let a = state.amplitudes[j]; | |
| let b = state.amplitudes[j + block]; | |
| state.amplitudes[j] = u[0][0].mul(&a).add(&u[0][1].mul(&b)); | |
| state.amplitudes[j+block] = u[1][0].mul(&a).add(&u[1][1].mul(&b)); | |
| } | |
| i += stride; | |
| } | |
| } | |
| pub fn apply_hadamard(state: &mut QuantumState, qubit: usize) { | |
| let s = 1.0 / 2.0_f64.sqrt(); | |
| let u = [ | |
| [C64::new(s, 0.0), C64::new(s, 0.0)], | |
| [C64::new(s, 0.0), C64::new(-s, 0.0)], | |
| ]; | |
| apply_single_qubit_gate(state, qubit, u); | |
| } | |
| pub fn apply_pauli_x(state: &mut QuantumState, qubit: usize) { | |
| let u = [[C64::zero(), C64::one()], [C64::one(), C64::zero()]]; | |
| apply_single_qubit_gate(state, qubit, u); | |
| } | |
| pub fn apply_pauli_y(state: &mut QuantumState, qubit: usize) { | |
| let u = [ | |
| [C64::zero(), C64::new(0.0, -1.0)], | |
| [C64::new(0.0, 1.0), C64::zero()], | |
| ]; | |
| apply_single_qubit_gate(state, qubit, u); | |
| } | |
| pub fn apply_pauli_z(state: &mut QuantumState, qubit: usize) { | |
| let u = [[C64::one(), C64::zero()], [C64::zero(), C64::new(-1.0, 0.0)]]; | |
| apply_single_qubit_gate(state, qubit, u); | |
| } | |
| // Phase gate: R(ΞΈ) = [[1,0],[0,e^iΞΈ]] | |
| pub fn apply_phase(state: &mut QuantumState, qubit: usize, theta: f64) { | |
| let u = [[C64::one(), C64::zero()], [C64::zero(), C64::exp_i(theta)]]; | |
| apply_single_qubit_gate(state, qubit, u); | |
| } | |
| // T gate: phase Ο/4 | |
| pub fn apply_t_gate(state: &mut QuantumState, qubit: usize) { | |
| apply_phase(state, qubit, PI / 4.0); | |
| } | |
| // S gate: phase Ο/2 | |
| pub fn apply_s_gate(state: &mut QuantumState, qubit: usize) { | |
| apply_phase(state, qubit, PI / 2.0); | |
| } | |
| // CNOT: control qubit c, target qubit t | |
| pub fn apply_cnot(state: &mut QuantumState, control: usize, target: usize) { | |
| let n = state.amplitudes.len(); | |
| for i in 0..n { | |
| if (i >> control) & 1 == 1 { | |
| let j = i ^ (1 << target); | |
| if j > i { | |
| let tmp = state.amplitudes[i]; | |
| state.amplitudes[i] = state.amplitudes[j]; | |
| state.amplitudes[j] = tmp; | |
| } | |
| } | |
| } | |
| } | |
| // ββ METRICS (mirrors bob_metrics.f90) βββββββββββββββββββββββββββββββββββββ | |
| pub struct QuantumMetrics { | |
| pub norm: f64, | |
| pub energy: f64, | |
| pub purity: f64, | |
| pub von_neumann_entropy: f64, | |
| pub linear_entropy: f64, | |
| pub coherence: f64, | |
| pub participation_ratio: f64, | |
| } | |
| pub fn compute_metrics(state: &QuantumState) -> JsValue { | |
| let probs: Vec<f64> = (0..state.dimension()).map(|i| state.probability(i)).collect(); | |
| let norm = probs.iter().sum::<f64>().sqrt(); | |
| // Purity: Tr(ΟΒ²) = Ξ£ p_iΒ² (diagonal Ο) | |
| let purity: f64 = probs.iter().map(|p| p * p).sum(); | |
| // Von Neumann entropy: -Ξ£ p_i log(p_i) | |
| let von_neumann_entropy: f64 = probs.iter() | |
| .filter(|&&p| p > 1e-15) | |
| .map(|&p| -p * p.ln()) | |
| .sum(); | |
| // Linear entropy: 1 - Tr(ΟΒ²) | |
| let linear_entropy = 1.0 - purity; | |
| // L1 coherence: Ξ£_{iβ j} |Ο_ij| β for pure state Ο = |Οβ©β¨Ο| | |
| // coherence = Ξ£_{iβ j} |Ο_i||Ο_j| = (Ξ£|Ο_i|)Β² - Ξ£|Ο_i|Β² | |
| let sum_amps: f64 = state.amplitudes.iter().map(|a| a.norm()).sum(); | |
| let sum_sq: f64 = state.amplitudes.iter().map(|a| a.norm_sq()).sum(); | |
| let coherence = (sum_amps * sum_amps - sum_sq).max(0.0); | |
| // Participation ratio (inverse): 1 / Ξ£ p_iΒ² | |
| let participation_ratio = if purity > 1e-15 { 1.0 / purity } else { 0.0 }; | |
| // Energy = Ξ£ i * p_i (eigenvalue ladder, classical sim of diagonal H) | |
| let energy: f64 = probs.iter().enumerate() | |
| .map(|(i, p)| i as f64 * p) | |
| .sum(); | |
| let m = QuantumMetrics { norm, energy, purity, von_neumann_entropy, linear_entropy, coherence, participation_ratio }; | |
| serde_wasm_bindgen::to_value(&m).unwrap_or(JsValue::NULL) | |
| } | |
| // ββ VORTEX LATTICE (mirrors bob_lattice.f90) ββββββββββββββββββββββββββββββββ | |
| pub struct Vortex { | |
| pub x: f64, | |
| pub y: f64, | |
| pub z: f64, | |
| pub winding: i32, // topological charge β {-2,-1,0,1,2} | |
| pub phase: f64, // quantum phase ΞΈ β [0, 2Ο) | |
| pub energy: f64, // local energy | |
| pub coherence: f64, // local coherence with neighbors | |
| } | |
| pub struct VortexLattice { | |
| vortices: Vec<Vortex>, | |
| nx: usize, | |
| ny: usize, | |
| coupling: f64, | |
| time: f64, | |
| dt: f64, | |
| } | |
| impl VortexLattice { | |
| pub fn new(nx: usize, ny: usize, coupling: f64, dt: f64) -> Self { | |
| let n = nx * ny; | |
| let mut vortices = Vec::with_capacity(n); | |
| for iy in 0..ny { | |
| for ix in 0..nx { | |
| // Initialize with random-ish phases using deterministic seed | |
| let seed = (ix * 7 + iy * 13) as f64; | |
| let phase = (seed * 1.618033988).fract() * 2.0 * PI; | |
| let winding = if (ix + iy) % 7 == 0 { 1 } else if (ix * iy) % 11 == 0 { -1 } else { 0 }; | |
| vortices.push(Vortex { | |
| x: ix as f64, | |
| y: iy as f64, | |
| z: ((ix as f64 * 0.3 + iy as f64 * 0.5).sin() * 0.5 + 0.5), | |
| winding, | |
| phase, | |
| energy: winding.abs() as f64 * 0.5 + (phase * 0.3).cos() * 0.2, | |
| coherence: 1.0, | |
| }); | |
| } | |
| } | |
| Self { vortices, nx, ny, coupling, time: 0.0, dt } | |
| } | |
| pub fn num_vortices(&self) -> usize { self.vortices.len() } | |
| pub fn time(&self) -> f64 { self.time } | |
| // Evolve lattice: Josephson coupling between nearest neighbors | |
| // dΞΈ_i/dt = -coupling * Ξ£_j sin(ΞΈ_i - ΞΈ_j) β discrete Gross-Pitaevskii | |
| pub fn evolve(&mut self, steps: usize) { | |
| for _ in 0..steps { | |
| let old = self.vortices.clone(); | |
| for iy in 0..self.ny { | |
| for ix in 0..self.nx { | |
| let idx = iy * self.nx + ix; | |
| let mut dphase = 0.0; | |
| let mut total_coherence = 0.0; | |
| let mut neighbor_count = 0; | |
| // Nearest neighbors (periodic boundary) | |
| let neighbors = [ | |
| ((ix + 1) % self.nx, iy), | |
| ((ix + self.nx - 1) % self.nx, iy), | |
| (ix, (iy + 1) % self.ny), | |
| (ix, (iy + self.ny - 1) % self.ny), | |
| ]; | |
| for (nx2, ny2) in neighbors { | |
| let nidx = ny2 * self.nx + nx2; | |
| let dphi = old[idx].phase - old[nidx].phase; | |
| dphase -= self.coupling * dphi.sin(); | |
| total_coherence += dphi.cos(); | |
| neighbor_count += 1; | |
| } | |
| let v = &mut self.vortices[idx]; | |
| v.phase = (old[idx].phase + self.dt * dphase).rem_euclid(2.0 * PI); | |
| v.coherence = if neighbor_count > 0 { (total_coherence / neighbor_count as f64 + 1.0) * 0.5 } else { 1.0 }; | |
| v.energy = v.winding.abs() as f64 * 0.5 | |
| + self.coupling * (1.0 - v.coherence) | |
| + (self.time * 0.1).sin() * 0.05; | |
| } | |
| } | |
| self.time += self.dt; | |
| // Phase transition: occasionally flip winding numbers | |
| if (self.time * 10.0) as usize % 50 == 0 { | |
| let flip_idx = (self.time * 97.3) as usize % self.vortices.len(); | |
| self.vortices[flip_idx].winding = match self.vortices[flip_idx].winding { | |
| 0 => 1, 1 => -1, -1 => 0, _ => 0, | |
| }; | |
| } | |
| } | |
| } | |
| // Return vortex data as flat arrays for JS canvas rendering | |
| pub fn vortex_x(&self, i: usize) -> f64 { self.vortices[i].x } | |
| pub fn vortex_y(&self, i: usize) -> f64 { self.vortices[i].y } | |
| pub fn vortex_phase(&self, i: usize) -> f64 { self.vortices[i].phase } | |
| pub fn vortex_winding(&self, i: usize) -> i32 { self.vortices[i].winding } | |
| pub fn vortex_energy(&self, i: usize) -> f64 { self.vortices[i].energy } | |
| pub fn vortex_coherence(&self, i: usize) -> f64 { self.vortices[i].coherence } | |
| // Global metrics | |
| pub fn total_energy(&self) -> f64 { | |
| self.vortices.iter().map(|v| v.energy).sum() | |
| } | |
| pub fn mean_coherence(&self) -> f64 { | |
| let s: f64 = self.vortices.iter().map(|v| v.coherence).sum(); | |
| s / self.vortices.len() as f64 | |
| } | |
| pub fn topological_charge(&self) -> i32 { | |
| self.vortices.iter().map(|v| v.winding).sum() | |
| } | |
| pub fn vortex_count(&self) -> i32 { | |
| self.vortices.iter().filter(|v| v.winding != 0).count() as i32 | |
| } | |
| } | |
| // ββ HAMILTONIAN (mirrors bob_hamiltonian.f90) ββββββββββββββββββββββββββββββ | |
| // Ising Hamiltonian: H = -J Ξ£ Ο_i^z Ο_j^z - h Ξ£ Ο_i^x | |
| // Applied via Trotter decomposition for time evolution | |
| pub struct IsingHamiltonian { | |
| num_qubits: usize, | |
| j: f64, // coupling | |
| h: f64, // transverse field | |
| } | |
| impl IsingHamiltonian { | |
| pub fn new(num_qubits: usize, j: f64, h: f64) -> Self { | |
| Self { num_qubits, j, h } | |
| } | |
| // Trotter step: e^{-iHdt} β e^{-iH_z dt/2} e^{-iH_x dt} e^{-iH_z dt/2} | |
| // H_z = -J Ξ£ Ο_i^z Ο_j^z β diagonal, applies phase to each pair | |
| // H_x = -h Ξ£ Ο_i^x β single-qubit rotations | |
| pub fn trotter_step(&self, state: &mut QuantumState, dt: f64) { | |
| let nq = self.num_qubits; | |
| // ZZ coupling: apply phase e^{iJ dt/2 Ο_i^z Ο_j^z} to nearest-neighbor pairs | |
| let n = state.dimension(); | |
| for i in 0..n { | |
| let mut phase_sum = 0.0; | |
| for q in 0..nq-1 { | |
| let si = if (i >> q) & 1 == 1 { 1.0_f64 } else { -1.0_f64 }; | |
| let sj = if (i >> (q+1)) & 1 == 1 { 1.0_f64 } else { -1.0_f64 }; | |
| phase_sum += si * sj; | |
| } | |
| let p = C64::exp_i(self.j * dt * 0.5 * phase_sum); | |
| state.amplitudes[i] = state.amplitudes[i].mul(&p); | |
| } | |
| // X rotations: R_x(-2h*dt) on each qubit | |
| let theta = self.h * dt; | |
| for q in 0..nq { | |
| let c = theta.cos(); | |
| let s = theta.sin(); | |
| let u = [ | |
| [C64::new(c, 0.0), C64::new(0.0, -s)], | |
| [C64::new(0.0, -s), C64::new(c, 0.0)], | |
| ]; | |
| apply_single_qubit_gate(state, q, u); | |
| } | |
| // ZZ coupling second half | |
| for i in 0..n { | |
| let mut phase_sum = 0.0; | |
| for q in 0..nq-1 { | |
| let si = if (i >> q) & 1 == 1 { 1.0_f64 } else { -1.0_f64 }; | |
| let sj = if (i >> (q+1)) & 1 == 1 { 1.0_f64 } else { -1.0_f64 }; | |
| phase_sum += si * sj; | |
| } | |
| let p = C64::exp_i(self.j * dt * 0.5 * phase_sum); | |
| state.amplitudes[i] = state.amplitudes[i].mul(&p); | |
| } | |
| } | |
| // Energy expectation β¨Ο|H|Οβ© | |
| pub fn energy_expectation(&self, state: &QuantumState) -> f64 { | |
| let nq = self.num_qubits; | |
| let n = state.dimension(); | |
| let mut e = 0.0_f64; | |
| // ZZ terms: -J Ξ£ β¨Ο_i^z Ο_j^zβ© = -J Ξ£_k p_k * sz_i(k) * sz_j(k) | |
| for k in 0..n { | |
| let p = state.probability(k); | |
| for q in 0..nq-1 { | |
| let si = if (k >> q) & 1 == 1 { 1.0_f64 } else { -1.0_f64 }; | |
| let sj = if (k >> (q+1)) & 1 == 1 { 1.0_f64 } else { -1.0_f64 }; | |
| e -= self.j * p * si * sj; | |
| } | |
| } | |
| // X terms: -h Ξ£ β¨Ο_i^xβ© β off-diagonal, requires amplitude sums | |
| for q in 0..nq { | |
| for k in 0..n { | |
| let flip = k ^ (1 << q); | |
| let re_part = state.amplitudes[k].conj().mul(&state.amplitudes[flip]).re; | |
| e -= self.h * re_part; | |
| } | |
| } | |
| e | |
| } | |
| } | |
| // ββ MEASUREMENT (mirrors bob_measurement.f90) ββββββββββββββββββββββββββββββ | |
| // Measure qubit k β collapses state, returns 0 or 1 | |
| // Uses a simple LFSR for deterministic pseudorandomness (no external RNG dep) | |
| pub struct Rng { state: u64 } | |
| impl Rng { | |
| pub fn new(seed: u64) -> Self { Self { state: if seed == 0 { 1 } else { seed } } } | |
| pub fn next_f64(&mut self) -> f64 { | |
| // xorshift64 | |
| self.state ^= self.state << 13; | |
| self.state ^= self.state >> 7; | |
| self.state ^= self.state << 17; | |
| (self.state as f64) / (u64::MAX as f64) | |
| } | |
| } | |
| pub fn measure_qubit(state: &mut QuantumState, qubit: usize, rng: &mut Rng) -> u32 { | |
| // P(1) = Ξ£_{i: bit k=1} |Ο_i|Β² | |
| let p1: f64 = (0..state.dimension()) | |
| .filter(|&i| (i >> qubit) & 1 == 1) | |
| .map(|i| state.probability(i)) | |
| .sum(); | |
| let outcome = if rng.next_f64() < p1 { 1u32 } else { 0u32 }; | |
| // Collapse: zero out incompatible amplitudes, renormalize | |
| for i in 0..state.dimension() { | |
| if (i >> qubit) & 1 != outcome as usize { | |
| state.amplitudes[i] = C64::zero(); | |
| } | |
| } | |
| state.normalize(); | |
| outcome | |
| } | |
| // ββ TIME INTEGRATOR (mirrors bob_integrator.f90) ββββββββββββββββββββββββββββ | |
| // Runge-Kutta 4 for SchrΓΆdinger equation: iβ d|Οβ©/dt = H|Οβ© | |
| // For Trotter we use the Hamiltonian's own step method | |
| pub fn evolve_state(state: &mut QuantumState, ham: &IsingHamiltonian, dt: f64, steps: usize) { | |
| for _ in 0..steps { | |
| ham.trotter_step(state, dt); | |
| } | |
| state.normalize(); | |
| } | |
| // ββ SIMULATION (full engine: mirrors bob_abi.f90 aggregate functions) βββββββ | |
| pub struct Simulation { | |
| state: QuantumState, | |
| ham: IsingHamiltonian, | |
| lattice: VortexLattice, | |
| rng: Rng, | |
| pub time: f64, | |
| pub dt: f64, | |
| pub step_count: u64, | |
| } | |
| impl Simulation { | |
| pub fn new(num_qubits: usize, lattice_n: usize, j: f64, h: f64, coupling: f64, dt: f64, seed: u64) -> Self { | |
| let mut state = QuantumState::new(num_qubits); | |
| // Superposition init: apply Hadamard to all qubits | |
| for q in 0..num_qubits { | |
| apply_hadamard(&mut state, q); | |
| } | |
| Self { | |
| state, | |
| ham: IsingHamiltonian::new(num_qubits, j, h), | |
| lattice: VortexLattice::new(lattice_n, lattice_n, coupling, dt), | |
| rng: Rng::new(seed), | |
| time: 0.0, | |
| dt, | |
| step_count: 0, | |
| } | |
| } | |
| pub fn step(&mut self) { | |
| // Evolve quantum state | |
| self.ham.trotter_step(&mut self.state, self.dt); | |
| self.state.normalize(); | |
| // Evolve vortex lattice | |
| self.lattice.evolve(1); | |
| self.time += self.dt; | |
| self.step_count += 1; | |
| } | |
| pub fn step_n(&mut self, n: usize) { | |
| for _ in 0..n { self.step(); } | |
| } | |
| // Metrics | |
| pub fn state_energy(&self) -> f64 { self.ham.energy_expectation(&self.state) } | |
| pub fn lattice_energy(&self) -> f64 { self.lattice.total_energy() } | |
| pub fn mean_coherence(&self) -> f64 { self.lattice.mean_coherence() } | |
| pub fn topological_charge(&self) -> i32 { self.lattice.topological_charge() } | |
| pub fn vortex_count(&self) -> i32 { self.lattice.vortex_count() } | |
| pub fn state_norm(&self) -> f64 { self.state.norm() } | |
| // Von Neumann entropy of quantum state | |
| pub fn entropy(&self) -> f64 { | |
| (0..self.state.dimension()) | |
| .map(|i| self.state.probability(i)) | |
| .filter(|&p| p > 1e-15) | |
| .map(|p| -p * p.ln()) | |
| .sum() | |
| } | |
| // State amplitude accessors for visualization | |
| pub fn state_dim(&self) -> usize { self.state.dimension() } | |
| pub fn state_prob(&self, i: usize) -> f64 { self.state.probability(i) } | |
| pub fn state_phase(&self, i: usize) -> f64 { | |
| self.state.amplitudes[i].phase() | |
| } | |
| // Lattice accessors | |
| pub fn num_vortices(&self) -> usize { self.lattice.num_vortices() } | |
| pub fn vortex_x(&self, i: usize) -> f64 { self.lattice.vortex_x(i) } | |
| pub fn vortex_y(&self, i: usize) -> f64 { self.lattice.vortex_y(i) } | |
| pub fn vortex_phase(&self, i: usize) -> f64 { self.lattice.vortex_phase(i) } | |
| pub fn vortex_winding(&self, i: usize) -> i32 { self.lattice.vortex_winding(i) } | |
| pub fn vortex_energy(&self, i: usize) -> f64 { self.lattice.vortex_energy(i) } | |
| pub fn vortex_coherence(&self, i: usize) -> f64 { self.lattice.vortex_coherence(i) } | |
| // Measure qubit k, collapse state | |
| pub fn measure(&mut self, qubit: usize) -> u32 { | |
| measure_qubit(&mut self.state, qubit, &mut self.rng) | |
| } | |
| // Lattice dimensions | |
| pub fn lattice_nx(&self) -> usize { self.lattice.nx } | |
| pub fn lattice_ny(&self) -> usize { self.lattice.ny } | |
| } | |
| // ββ ENGINE INFO ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| pub fn engine_version() -> String { | |
| "BOB Quantum Civilization Engine v1.0.0 β Rust/WASM port of bob_*.f90".to_string() | |
| } | |
| pub fn engine_modules() -> String { | |
| "bob_kinds | bob_errors | bob_rng | bob_state | bob_gates | bob_lattice | bob_measurement | bob_hamiltonian | bob_integrator | bob_metrics | bob_abi".to_string() | |
| } | |