|
|
|
|
|
|
|
|
| use wasm_bindgen::prelude::*;
|
| use serde::{Deserialize, Serialize};
|
| use std::f64::consts::PI;
|
|
|
|
|
| const HBAR: f64 = 1.054_571_817e-34;
|
| const NORM_TOL: f64 = 1e-10;
|
|
|
|
|
| #[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
| 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()) }
|
| }
|
|
|
|
|
|
|
| #[wasm_bindgen]
|
| pub struct QuantumState {
|
| amplitudes: Vec<C64>,
|
| num_qubits: usize,
|
| }
|
|
|
| #[wasm_bindgen]
|
| impl QuantumState {
|
| #[wasm_bindgen(constructor)]
|
| pub fn new(num_qubits: usize) -> Self {
|
| let n = 1usize << num_qubits;
|
| let mut amplitudes = vec![C64::zero(); n];
|
| amplitudes[0] = C64::one();
|
| Self { amplitudes, num_qubits }
|
| }
|
|
|
| pub fn num_qubits(&self) -> usize { self.num_qubits }
|
| pub fn dimension(&self) -> usize { self.amplitudes.len() }
|
|
|
|
|
| pub fn norm(&self) -> f64 {
|
| self.amplitudes.iter().map(|a| a.norm_sq()).sum::<f64>().sqrt()
|
| }
|
|
|
|
|
| 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
|
| }
|
|
|
|
|
| pub fn probability(&self, i: usize) -> f64 {
|
| if i >= self.amplitudes.len() { return 0.0; }
|
| self.amplitudes[i].norm_sq()
|
| }
|
|
|
|
|
| pub fn amplitude_re(&self, i: usize) -> f64 {
|
| if i >= self.amplitudes.len() { 0.0 } else { self.amplitudes[i].re }
|
| }
|
|
|
|
|
| pub fn amplitude_im(&self, i: usize) -> f64 {
|
| if i >= self.amplitudes.len() { 0.0 } else { self.amplitudes[i].im }
|
| }
|
|
|
|
|
| pub fn set_amplitude(&mut self, i: usize, re: f64, im: f64) {
|
| if i < self.amplitudes.len() {
|
| self.amplitudes[i] = C64::new(re, im);
|
| }
|
| }
|
|
|
|
|
| pub fn clone_state(&self) -> QuantumState {
|
| QuantumState {
|
| amplitudes: self.amplitudes.clone(),
|
| num_qubits: self.num_qubits,
|
| }
|
| }
|
| }
|
|
|
|
|
|
|
| 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;
|
| }
|
| }
|
|
|
| #[wasm_bindgen]
|
| 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);
|
| }
|
|
|
| #[wasm_bindgen]
|
| 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);
|
| }
|
|
|
| #[wasm_bindgen]
|
| 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);
|
| }
|
|
|
| #[wasm_bindgen]
|
| 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);
|
| }
|
|
|
|
|
| #[wasm_bindgen]
|
| 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);
|
| }
|
|
|
|
|
| #[wasm_bindgen]
|
| pub fn apply_t_gate(state: &mut QuantumState, qubit: usize) {
|
| apply_phase(state, qubit, PI / 4.0);
|
| }
|
|
|
|
|
| #[wasm_bindgen]
|
| pub fn apply_s_gate(state: &mut QuantumState, qubit: usize) {
|
| apply_phase(state, qubit, PI / 2.0);
|
| }
|
|
|
|
|
| #[wasm_bindgen]
|
| 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;
|
| }
|
| }
|
| }
|
| }
|
|
|
|
|
| #[derive(Serialize, Deserialize)]
|
| 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,
|
| }
|
|
|
| #[wasm_bindgen]
|
| 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();
|
|
|
|
|
| let purity: f64 = probs.iter().map(|p| p * p).sum();
|
|
|
|
|
| let von_neumann_entropy: f64 = probs.iter()
|
| .filter(|&&p| p > 1e-15)
|
| .map(|&p| -p * p.ln())
|
| .sum();
|
|
|
|
|
| let linear_entropy = 1.0 - purity;
|
|
|
|
|
|
|
| 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);
|
|
|
|
|
| let participation_ratio = if purity > 1e-15 { 1.0 / purity } else { 0.0 };
|
|
|
|
|
| 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)
|
| }
|
|
|
|
|
| #[derive(Clone, Serialize, Deserialize)]
|
| pub struct Vortex {
|
| pub x: f64,
|
| pub y: f64,
|
| pub z: f64,
|
| pub winding: i32,
|
| pub phase: f64,
|
| pub energy: f64,
|
| pub coherence: f64,
|
| }
|
|
|
| #[wasm_bindgen]
|
| pub struct VortexLattice {
|
| vortices: Vec<Vortex>,
|
| nx: usize,
|
| ny: usize,
|
| coupling: f64,
|
| time: f64,
|
| dt: f64,
|
| }
|
|
|
| #[wasm_bindgen]
|
| impl VortexLattice {
|
| #[wasm_bindgen(constructor)]
|
| 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 {
|
|
|
| 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 }
|
|
|
|
|
|
|
| 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;
|
|
|
|
|
| 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;
|
|
|
|
|
| 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,
|
| };
|
| }
|
| }
|
| }
|
|
|
|
|
| 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 }
|
|
|
|
|
| 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
|
| }
|
| }
|
|
|
|
|
|
|
|
|
| #[wasm_bindgen]
|
| pub struct IsingHamiltonian {
|
| num_qubits: usize,
|
| j: f64,
|
| h: f64,
|
| }
|
|
|
| #[wasm_bindgen]
|
| impl IsingHamiltonian {
|
| #[wasm_bindgen(constructor)]
|
| pub fn new(num_qubits: usize, j: f64, h: f64) -> Self {
|
| Self { num_qubits, j, h }
|
| }
|
|
|
|
|
|
|
|
|
| pub fn trotter_step(&self, state: &mut QuantumState, dt: f64) {
|
| let nq = self.num_qubits;
|
|
|
|
|
| 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);
|
| }
|
|
|
|
|
| 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);
|
| }
|
|
|
|
|
| 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);
|
| }
|
| }
|
|
|
|
|
| pub fn energy_expectation(&self, state: &QuantumState) -> f64 {
|
| let nq = self.num_qubits;
|
| let n = state.dimension();
|
| let mut e = 0.0_f64;
|
|
|
|
|
| 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;
|
| }
|
| }
|
|
|
|
|
| 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
|
| }
|
| }
|
|
|
|
|
|
|
|
|
| #[wasm_bindgen]
|
| pub struct Rng { state: u64 }
|
|
|
| #[wasm_bindgen]
|
| impl Rng {
|
| #[wasm_bindgen(constructor)]
|
| pub fn new(seed: u64) -> Self { Self { state: if seed == 0 { 1 } else { seed } } }
|
|
|
| pub fn next_f64(&mut self) -> f64 {
|
|
|
| self.state ^= self.state << 13;
|
| self.state ^= self.state >> 7;
|
| self.state ^= self.state << 17;
|
| (self.state as f64) / (u64::MAX as f64)
|
| }
|
| }
|
|
|
| #[wasm_bindgen]
|
| pub fn measure_qubit(state: &mut QuantumState, qubit: usize, rng: &mut Rng) -> u32 {
|
|
|
| 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 };
|
|
|
|
|
| for i in 0..state.dimension() {
|
| if (i >> qubit) & 1 != outcome as usize {
|
| state.amplitudes[i] = C64::zero();
|
| }
|
| }
|
| state.normalize();
|
| outcome
|
| }
|
|
|
|
|
|
|
|
|
| #[wasm_bindgen]
|
| pub fn evolve_state(state: &mut QuantumState, ham: &IsingHamiltonian, dt: f64, steps: usize) {
|
| for _ in 0..steps {
|
| ham.trotter_step(state, dt);
|
| }
|
| state.normalize();
|
| }
|
|
|
|
|
| #[wasm_bindgen]
|
| pub struct Simulation {
|
| state: QuantumState,
|
| ham: IsingHamiltonian,
|
| lattice: VortexLattice,
|
| rng: Rng,
|
| pub time: f64,
|
| pub dt: f64,
|
| pub step_count: u64,
|
| }
|
|
|
| #[wasm_bindgen]
|
| impl Simulation {
|
| #[wasm_bindgen(constructor)]
|
| 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);
|
|
|
| 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) {
|
|
|
| self.ham.trotter_step(&mut self.state, self.dt);
|
| self.state.normalize();
|
|
|
| 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(); }
|
| }
|
|
|
|
|
| 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() }
|
|
|
|
|
| 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()
|
| }
|
|
|
|
|
| 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()
|
| }
|
|
|
|
|
| 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) }
|
|
|
|
|
| pub fn measure(&mut self, qubit: usize) -> u32 {
|
| measure_qubit(&mut self.state, qubit, &mut self.rng)
|
| }
|
|
|
|
|
| pub fn lattice_nx(&self) -> usize { self.lattice.nx }
|
| pub fn lattice_ny(&self) -> usize { self.lattice.ny }
|
| }
|
|
|
|
|
| #[wasm_bindgen]
|
| pub fn engine_version() -> String {
|
| "BOB Quantum Civilization Engine v1.0.0 β Rust/WASM port of bob_*.f90".to_string()
|
| }
|
|
|
| #[wasm_bindgen]
|
| 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()
|
| }
|
|
|