use ndarray::{Array2, Array1, ArrayView1};
use rand::{thread_rng, Rng, distributions::Uniform};
use std::f32::consts::PI;
pub struct ReservoirComputer {
reservoir_size: usize,
spectral_radius: f32,
sparsity: f32,
leak_rate: f32,
w_in: Array2<f32>, w_res: Array2<f32>, w_out: Array2<f32>,
state: Array1<f32>,
state_history: Vec<Array1<f32>>,
history_index: usize,
max_history: usize,
}
impl ReservoirComputer {
pub fn new(input_dim: usize, reservoir_size: usize, output_dim: usize) -> Self {
let mut rng = thread_rng();
let uniform = Uniform::new(-1.0, 1.0);
let w_in = Array2::from_shape_fn((reservoir_size, input_dim), |_|
rng.sample(uniform));
let sparsity = 0.9; let mut w_res = Array2::zeros((reservoir_size, reservoir_size));
for i in 0..reservoir_size {
for j in 0..reservoir_size {
if rng.gen::<f32>() > sparsity {
w_res[[i, j]] = rng.sample(uniform);
}
}
}
let spectral_radius = 0.9;
w_res = Self::scale_spectral_radius(w_res, spectral_radius);
let w_out = Array2::zeros((output_dim, reservoir_size + input_dim));
Self {
reservoir_size,
spectral_radius,
sparsity,
leak_rate: 0.3,
w_in,
w_res,
w_out,
state: Array1::zeros(reservoir_size),
state_history: Vec::new(),
history_index: 0,
max_history: 1000,
}
}
fn scale_spectral_radius(mut matrix: Array2<f32>, target_radius: f32) -> Array2<f32> {
let n = matrix.nrows();
let mut v = Array1::from_shape_fn(n, |_| rand::random::<f32>());
for _ in 0..20 {
v = matrix.dot(&v);
let norm = v.dot(&v).sqrt();
if norm > 0.0 {
v = v / norm;
}
}
let eigenvalue = v.dot(&matrix.dot(&v)) / v.dot(&v);
if eigenvalue.abs() > 0.0 {
matrix = matrix * (target_radius / eigenvalue.abs());
}
matrix
}
pub fn update_state(&mut self, input: &Array1<f32>) -> Array1<f32> {
let input_contribution = self.w_in.dot(input);
let recurrent_contribution = self.w_res.dot(&self.state);
let new_state = &input_contribution + &recurrent_contribution;
let activated = new_state.mapv(|x| x.tanh());
self.state = &self.state * (1.0 - self.leak_rate) + &activated * self.leak_rate;
if self.state_history.len() < self.max_history {
self.state_history.push(self.state.clone());
} else {
self.state_history[self.history_index] = self.state.clone();
}
self.history_index = (self.history_index + 1) % self.max_history;
self.state.clone()
}
pub fn collect_states(&mut self, inputs: &[Vec<f32>], washout: usize)
-> (Array2<f32>, Vec<Array1<f32>>) {
let n_samples = inputs.len();
let mut all_states = Vec::new();
self.state = Array1::zeros(self.reservoir_size);
for (i, input) in inputs.iter().enumerate() {
let input_arr = Array1::from_vec(input.clone());
let state = self.update_state(&input_arr);
if i >= washout {
let mut extended = Vec::from(state.as_slice().unwrap());
extended.extend_from_slice(input);
all_states.push(Array1::from_vec(extended));
}
}
let n_train = all_states.len();
let state_dim = self.reservoir_size + inputs[0].len();
let mut state_matrix = Array2::zeros((n_train, state_dim));
for (i, state) in all_states.iter().enumerate() {
state_matrix.row_mut(i).assign(state);
}
(state_matrix, all_states)
}
pub fn train_ridge(&mut self, x: &Vec<Vec<f32>>, y: &Vec<f32>,
regularization: f32) {
let washout = 100.min(x.len() / 10);
let (states, _) = self.collect_states(x, washout);
let targets = &y[washout..];
let lambda = regularization;
let n = states.nrows();
let d = states.ncols();
let gram = states.t().dot(&states);
let mut reg_gram = gram + Array2::<f32>::eye(d) * lambda;
let y_mat = Array2::from_shape_vec((1, targets.len()), targets.to_vec())
.expect("Shape mismatch");
let pinv = Self::simple_pinv(®_gram); let temp = states.dot(&pinv); self.w_out = y_mat.dot(&temp); }
fn simple_pinv(matrix: &Array2<f32>) -> Array2<f32> {
let n = matrix.nrows();
let reg = Array2::<f32>::eye(n) * 0.01;
let stabilized = matrix + ®
stabilized.mapv(|x| 1.0 / (x + 0.001))
}
pub fn predict(&mut self, x: &[Vec<f32>]) -> Vec<f32> {
self.state = Array1::zeros(self.reservoir_size);
let mut predictions = Vec::new();
for input in x {
let input_arr = Array1::from_vec(input.clone());
let state = self.update_state(&input_arr);
let mut extended = Vec::from(state.as_slice().unwrap());
extended.extend_from_slice(input);
let extended_arr = Array1::from_vec(extended);
let output = self.w_out.dot(&extended_arr);
predictions.push(output[0]);
}
predictions
}
pub fn predict_class(&mut self, x: &[Vec<f32>]) -> Vec<usize> {
let outputs = self.predict(x);
outputs.iter().map(|&val| {
if val < -0.25 { 0 }
else if val > 0.25 { 2 }
else { 1 }
}).collect()
}
}
pub struct QuantumReservoir {
pub classical_reservoir: ReservoirComputer,
quantum_layer_size: usize,
phase_matrix: Array2<f32>,
entanglement_strength: f32,
}
impl QuantumReservoir {
pub fn new(input_dim: usize, reservoir_size: usize, output_dim: usize) -> Self {
let classical_reservoir = ReservoirComputer::new(input_dim, reservoir_size, output_dim);
let quantum_layer_size = 16;
let mut rng = thread_rng();
let phase_matrix = Array2::from_shape_fn((quantum_layer_size, quantum_layer_size),
|_| rng.gen::<f32>() * 2.0 * PI);
Self {
classical_reservoir,
quantum_layer_size,
phase_matrix,
entanglement_strength: 0.5,
}
}
fn quantum_transform(&self, state: &Array1<f32>) -> Array1<f32> {
let n = self.quantum_layer_size.min(state.len());
let mut quantum_state = Array1::zeros(n);
for i in 0..n {
quantum_state[i] = state[i % state.len()];
}
let mut result = Array1::<f32>::zeros(n);
for i in 0..n {
for j in 0..n {
let phase = self.phase_matrix[[i, j]];
let amplitude = quantum_state[j] * phase.cos()
+ quantum_state[(j + 1) % n] * phase.sin();
result[i] += amplitude * self.entanglement_strength;
}
}
let norm = result.dot(&result).sqrt();
if norm > 0.0 {
result = result / norm;
}
result
}
pub fn train(&mut self, x: &Vec<Vec<f32>>, y: &Vec<f32>) {
self.classical_reservoir.train_ridge(x, y, 0.001);
}
pub fn predict_quantum(&mut self, x: &[Vec<f32>]) -> Vec<usize> {
let classical_pred = self.classical_reservoir.predict_class(x);
classical_pred.iter().enumerate().map(|(i, &pred)| {
let quantum_factor = (i as f32 * 0.1).sin();
if quantum_factor > 0.3 && pred < 2 {
pred + 1
} else if quantum_factor < -0.3 && pred > 0 {
pred - 1
} else {
pred
}
}).collect()
}
}