1pub mod baselines;
17pub mod optimizations;
18pub mod benchmarks;
19pub mod solvers;
20pub mod core;
21pub mod utils;
22
23#[cfg(all(target_arch = "wasm32", feature = "wasm"))]
24pub mod wasm_simple;
25
26pub mod optimized {
28 pub use crate::optimizations::optimized::*;
29}
30pub mod solver_integration {
31 pub use crate::solvers::solver_integration::*;
32}
33
34use ndarray::{Array1, Array2};
35use nalgebra::{DMatrix, DVector};
36use std::time::{Duration, Instant};
37use thiserror::Error;
38
39use solver_integration::{SparseMatrix, NeumannSolver};
41
42#[derive(Debug, Error)]
43pub enum TemporalSolverError {
44 #[error("Dimension mismatch: expected {expected}, got {got}")]
45 DimensionMismatch { expected: usize, got: usize },
46
47 #[error("Solver error: {0}")]
48 SolverError(String),
49
50 #[error("Numerical error: {0}")]
51 NumericalError(String),
52
53 #[error("Certificate validation failed: error {error} exceeds threshold {threshold}")]
54 CertificateError { error: f64, threshold: f64 },
55}
56
57type Result<T> = std::result::Result<T, TemporalSolverError>;
58
59#[derive(Debug, Clone)]
61pub struct Certificate {
62 pub error_bound: f64,
64 pub confidence: f64,
66 pub gate_pass: bool,
68 pub iterations: usize,
70 pub computational_work: usize,
72}
73
74pub struct KalmanFilter {
76 state: DVector<f64>,
78 covariance: DMatrix<f64>,
80 process_noise: DMatrix<f64>,
82 measurement_noise: DMatrix<f64>,
84 transition: DMatrix<f64>,
86 measurement: DMatrix<f64>,
88}
89
90impl KalmanFilter {
91 pub fn new(state_dim: usize) -> Self {
92 let full_dim = state_dim * 2; let mut transition = DMatrix::identity(full_dim, full_dim);
96 for i in 0..state_dim {
98 transition[(i, state_dim + i)] = 0.001;
99 }
100
101 let mut measurement = DMatrix::zeros(state_dim, full_dim);
102 for i in 0..state_dim {
103 measurement[(i, i)] = 1.0; }
105
106 Self {
107 state: DVector::zeros(full_dim),
108 covariance: DMatrix::identity(full_dim, full_dim) * 0.1,
109 process_noise: DMatrix::identity(full_dim, full_dim) * 0.001,
110 measurement_noise: DMatrix::identity(state_dim, state_dim) * 0.01,
111 transition,
112 measurement,
113 }
114 }
115
116 pub fn predict(&mut self) -> DVector<f64> {
118 self.state = &self.transition * &self.state;
120
121 self.covariance = &self.transition * &self.covariance * self.transition.transpose()
123 + &self.process_noise;
124
125 &self.measurement * &self.state
127 }
128
129 pub fn update(&mut self, measurement: &DVector<f64>) -> Result<()> {
131 let innovation = measurement - &self.measurement * &self.state;
133
134 let innovation_cov = &self.measurement * &self.covariance
136 * self.measurement.transpose() + &self.measurement_noise;
137
138 let kalman_gain = &self.covariance * self.measurement.transpose()
140 * innovation_cov.try_inverse()
141 .ok_or(TemporalSolverError::NumericalError("Singular matrix".into()))?;
142
143 self.state = &self.state + &kalman_gain * innovation;
145
146 let identity = DMatrix::identity(self.state.len(), self.state.len());
148 self.covariance = (identity - &kalman_gain * &self.measurement) * &self.covariance;
149
150 Ok(())
151 }
152}
153
154pub struct NeuralLayer {
156 weights: Array2<f32>,
157 bias: Array1<f32>,
158 activation: ActivationType,
159}
160
161#[derive(Clone, Copy)]
162pub enum ActivationType {
163 ReLU,
164 Tanh,
165 Linear,
166}
167
168impl NeuralLayer {
169 pub fn new(input_size: usize, output_size: usize, activation: ActivationType) -> Self {
170 use ndarray_rand::RandomExt;
171 use rand_distr::Normal;
172
173 let scale = (2.0 / input_size as f32).sqrt();
175 let dist = Normal::new(0.0, scale).unwrap();
176
177 Self {
178 weights: Array2::random((output_size, input_size), dist),
179 bias: Array1::zeros(output_size),
180 activation,
181 }
182 }
183
184 pub fn forward(&self, input: &Array1<f32>) -> Array1<f32> {
185 let z = self.weights.dot(input) + &self.bias;
186
187 match self.activation {
188 ActivationType::ReLU => z.mapv(|x| x.max(0.0)),
189 ActivationType::Tanh => z.mapv(|x| x.tanh()),
190 ActivationType::Linear => z,
191 }
192 }
193}
194
195pub struct TemporalNeuralNetwork {
197 layers: Vec<NeuralLayer>,
198}
199
200impl TemporalNeuralNetwork {
201 pub fn new(layer_sizes: &[usize], activations: &[ActivationType]) -> Self {
202 assert_eq!(layer_sizes.len() - 1, activations.len());
203
204 let mut layers = Vec::new();
205 for i in 0..layer_sizes.len() - 1 {
206 layers.push(NeuralLayer::new(
207 layer_sizes[i],
208 layer_sizes[i + 1],
209 activations[i],
210 ));
211 }
212
213 Self { layers }
214 }
215
216 pub fn forward(&self, input: &Array1<f32>) -> Array1<f32> {
217 self.layers.iter().fold(input.clone(), |x, layer| layer.forward(&x))
218 }
219
220 pub fn jacobian(&self, input: &Array1<f32>) -> Array2<f32> {
222 let output_dim = self.layers.last().unwrap().weights.shape()[0];
224 let input_dim = input.len();
225 let mut jacobian = Array2::zeros((output_dim, input_dim));
226
227 let epsilon = 1e-4;
228 let base_output = self.forward(input);
229
230 for i in 0..input_dim {
231 let mut perturbed_input = input.clone();
232 perturbed_input[i] += epsilon;
233 let perturbed_output = self.forward(&perturbed_input);
234
235 for j in 0..output_dim {
236 jacobian[[j, i]] = (perturbed_output[j] - base_output[j]) / epsilon;
237 }
238 }
239
240 jacobian
241 }
242}
243
244pub struct SolverGate {
246 epsilon: f64,
247 max_iterations: usize,
248 budget: usize,
249}
250
251impl SolverGate {
252 pub fn new(epsilon: f64, max_iterations: usize, budget: usize) -> Self {
253 Self {
254 epsilon,
255 max_iterations,
256 budget,
257 }
258 }
259
260 pub fn verify(
262 &self,
263 prediction: &Array1<f32>,
264 jacobian: &Array2<f32>,
265 ) -> Result<Certificate> {
266 let n = jacobian.shape()[0];
268 let m = jacobian.shape()[1];
269
270 let mut triplets = Vec::new();
273
274 for i in 0..n.min(m) {
276 triplets.push((i, i, 1.0));
277 }
278
279 for i in 0..n {
281 for j in 0..m {
282 if i < m && j < n {
283 let value = 0.1 * jacobian[[i, j]] * jacobian[[j, i]];
284 if value.abs() > 1e-10 {
285 triplets.push((i, j, value as f64));
286 }
287 }
288 }
289 }
290
291 let matrix = SparseMatrix::from_triplets(triplets, n.min(m), n.min(m));
292
293 let b: Vec<f64> = prediction.iter()
295 .take(n.min(m))
296 .map(|&x| x as f64)
297 .collect();
298
299 let solver = NeumannSolver::new(self.max_iterations, self.epsilon);
301 let result = solver.solve(&matrix, &b);
302
303 let solution_norm: f64 = result.solution.iter().map(|x| x * x).sum::<f64>().sqrt();
305 let residual_norm = result.residual_norm;
306 let error_bound = residual_norm / solution_norm.max(1.0);
307
308 Ok(Certificate {
310 error_bound,
311 confidence: 1.0 - error_bound.min(1.0),
312 gate_pass: error_bound < self.epsilon,
313 iterations: result.iterations,
314 computational_work: result.iterations * n, })
316 }
317}
318
319pub struct PageRankSelector {
321 damping: f64,
322 tolerance: f64,
323 max_iterations: usize,
324}
325
326impl PageRankSelector {
327 pub fn new() -> Self {
328 Self {
329 damping: 0.85,
330 tolerance: 1e-6,
331 max_iterations: 100,
332 }
333 }
334
335 pub fn select_samples(
337 &self,
338 adjacency: &Array2<f32>,
339 errors: &Array1<f32>,
340 k: usize,
341 ) -> Vec<usize> {
342 let n = adjacency.shape()[0];
343 let mut scores = Array1::from_elem(n, 1.0 / n as f32);
344 let mut new_scores = Array1::zeros(n);
345
346 for _ in 0..self.max_iterations {
348 new_scores.fill((1.0 - self.damping as f32) / n as f32);
350
351 for i in 0..n {
352 for j in 0..n {
353 if adjacency[[j, i]] > 0.0 {
354 let out_degree: f32 = (0..n).map(|k| adjacency[[j, k]]).sum();
355 if out_degree > 0.0 {
356 new_scores[i] += (self.damping as f32) * adjacency[[j, i]]
357 * scores[j] / out_degree;
358 }
359 }
360 }
361 }
362
363 for i in 0..n {
365 new_scores[i] *= 1.0 + errors[i];
366 }
367
368 let diff: f32 = (&new_scores - &scores)
370 .iter()
371 .map(|x| x.abs())
372 .sum();
373
374 scores.assign(&new_scores);
375
376 if diff < self.tolerance as f32 {
377 break;
378 }
379 }
380
381 let mut indexed_scores: Vec<(usize, f32)> =
383 scores.iter().enumerate().map(|(i, &s)| (i, s)).collect();
384 indexed_scores.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
385
386 indexed_scores.into_iter().take(k).map(|(i, _)| i).collect()
387 }
388}
389
390pub struct TemporalSolver {
392 neural_net: TemporalNeuralNetwork,
393 kalman_filter: KalmanFilter,
394 solver_gate: SolverGate,
395 pagerank: PageRankSelector,
396}
397
398impl TemporalSolver {
399 pub fn new(input_size: usize, hidden_size: usize, output_size: usize) -> Self {
400 let neural_net = TemporalNeuralNetwork::new(
401 &[input_size, hidden_size, output_size],
402 &[ActivationType::ReLU, ActivationType::Linear],
403 );
404
405 let kalman_filter = KalmanFilter::new(output_size);
406 let solver_gate = SolverGate::new(0.02, 100, 200000);
407 let pagerank = PageRankSelector::new();
408
409 Self {
410 neural_net,
411 kalman_filter,
412 solver_gate,
413 pagerank,
414 }
415 }
416
417 pub fn predict(&mut self, input: &Array1<f32>) -> Result<(Array1<f32>, Certificate, Duration)> {
419 let start = Instant::now();
420
421 let kalman_pred = self.kalman_filter.predict();
423 let prior: Array1<f32> = Array1::from_vec(
424 kalman_pred.iter().map(|&x| x as f32).collect()
425 );
426
427 let residual = self.neural_net.forward(input);
429
430 let prediction = &prior + &residual;
432
433 let jacobian = self.neural_net.jacobian(input);
435
436 let certificate = self.solver_gate.verify(&prediction, &jacobian)?;
438
439 if certificate.gate_pass {
441 let measurement = DVector::from_vec(
442 prediction.iter().map(|&x| x as f64).collect()
443 );
444 self.kalman_filter.update(&measurement)?;
445 }
446
447 let duration = start.elapsed();
448
449 Ok((prediction, certificate, duration))
450 }
451
452 pub fn train_step(
454 &mut self,
455 samples: &[Array1<f32>],
456 targets: &[Array1<f32>],
457 adjacency: &Array2<f32>,
458 ) -> Result<Vec<usize>> {
459 let mut errors = Array1::zeros(samples.len());
461 for (i, (sample, target)) in samples.iter().zip(targets.iter()).enumerate() {
462 let (pred, _, _) = self.predict(sample)?;
463 let error: f32 = (pred - target).mapv(|x| x * x).sum().sqrt();
464 errors[i] = error;
465 }
466
467 let selected_indices = self.pagerank.select_samples(adjacency, &errors, 15);
469
470 Ok(selected_indices)
471 }
472}
473
474#[cfg(test)]
475mod tests {
476 use super::*;
477
478 #[test]
479 fn test_real_kalman_filter() {
480 let mut kf = KalmanFilter::new(2);
481 let pred = kf.predict();
482 assert_eq!(pred.len(), 2);
483 }
484
485 #[test]
486 fn test_real_neural_network() {
487 let nn = TemporalNeuralNetwork::new(&[10, 5, 2], &[ActivationType::ReLU, ActivationType::Linear]);
488 let input = Array1::from_vec(vec![0.1; 10]);
489 let output = nn.forward(&input);
490 assert_eq!(output.len(), 2);
491 }
492
493 #[test]
494 fn test_real_solver_gate() {
495 let gate = SolverGate::new(0.02, 100, 10000);
496 let prediction = Array1::from_vec(vec![1.0, 2.0, 3.0]);
497 let jacobian = Array2::from_shape_vec((3, 3), vec![
498 2.0, -1.0, 0.0,
499 -1.0, 2.0, -1.0,
500 0.0, -1.0, 2.0,
501 ]).unwrap();
502
503 let cert = gate.verify(&prediction, &jacobian).unwrap();
504 println!("Certificate: {:?}", cert);
505 assert!(cert.error_bound >= 0.0);
506 }
507
508 #[test]
509 fn test_complete_system() {
510 let mut solver = TemporalSolver::new(128, 32, 4);
511 let input = Array1::from_vec(vec![0.1; 128]);
512
513 let (prediction, certificate, duration) = solver.predict(&input).unwrap();
514
515 println!("Prediction: {:?}", prediction);
516 println!("Certificate: {:?}", certificate);
517 println!("Duration: {:?}", duration);
518
519 assert_eq!(prediction.len(), 4);
520 assert!(duration.as_nanos() > 0);
521 }
522}