temporal_neural_solver/solvers/
solver_integration.rs1use ndarray::{Array1, Array2};
5use std::time::Instant;
6
7pub struct SparseMatrix {
9 pub rows: usize,
10 pub cols: usize,
11 pub values: Vec<(usize, usize, f64)>,
12}
13
14impl SparseMatrix {
15 pub fn from_triplets(triplets: Vec<(usize, usize, f64)>, rows: usize, cols: usize) -> Self {
16 SparseMatrix {
17 rows,
18 cols,
19 values: triplets,
20 }
21 }
22
23 pub fn multiply(&self, x: &[f64]) -> Vec<f64> {
25 let mut result = vec![0.0; self.rows];
26 for (i, j, val) in &self.values {
27 if *j < x.len() {
28 result[*i] += val * x[*j];
29 }
30 }
31 result
32 }
33}
34
35pub struct NeumannSolver {
37 max_iterations: usize,
38 tolerance: f64,
39}
40
41impl NeumannSolver {
42 pub fn new(max_iterations: usize, tolerance: f64) -> Self {
43 Self {
44 max_iterations,
45 tolerance,
46 }
47 }
48
49 pub fn solve(&self, a: &SparseMatrix, b: &[f64]) -> SolverResult {
52 let start = Instant::now();
53 let n = b.len();
54
55 let mut x = b.to_vec();
57 let mut residual = vec![0.0; n];
58 let mut iterations = 0;
59
60 let mut diagonal = vec![1.0; n];
62 for (i, j, val) in &a.values {
63 if i == j {
64 diagonal[*i] = *val;
65 }
66 }
67
68 for iter in 0..self.max_iterations {
70 let ax = a.multiply(&x);
72 for i in 0..n {
73 residual[i] = b[i] - ax[i];
74 }
75
76 let residual_norm: f64 = residual.iter().map(|r| r * r).sum::<f64>().sqrt();
78 if residual_norm < self.tolerance {
79 iterations = iter + 1;
80 break;
81 }
82
83 for i in 0..n {
85 if diagonal[i].abs() > 1e-10 {
86 x[i] += residual[i] / diagonal[i];
87 }
88 }
89
90 iterations = iter + 1;
91 }
92
93 let ax_final = a.multiply(&x);
95 let final_residual: Vec<f64> = (0..n).map(|i| b[i] - ax_final[i]).collect();
96 let residual_norm = final_residual.iter().map(|r| r * r).sum::<f64>().sqrt();
97
98 SolverResult {
99 solution: x,
100 residual_norm,
101 iterations,
102 time_elapsed: start.elapsed(),
103 }
104 }
105}
106
107pub struct SolverResult {
108 pub solution: Vec<f64>,
109 pub residual_norm: f64,
110 pub iterations: usize,
111 pub time_elapsed: std::time::Duration,
112}
113
114pub struct ForwardPushSolver {
116 epsilon: f64,
117 max_iterations: usize,
118}
119
120impl ForwardPushSolver {
121 pub fn new(epsilon: f64, max_iterations: usize) -> Self {
122 Self {
123 epsilon,
124 max_iterations,
125 }
126 }
127
128 pub fn solve(&self, adjacency: &Array2<f32>, teleport: &Array1<f32>) -> Array1<f32> {
130 let n = adjacency.shape()[0];
131 let mut estimate = Array1::zeros(n);
132 let mut residual = teleport.clone();
133
134 for _ in 0..self.max_iterations {
135 let (max_idx, &max_residual) = residual
137 .iter()
138 .enumerate()
139 .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
140 .unwrap();
141
142 if max_residual < self.epsilon as f32 {
143 break;
144 }
145
146 estimate[max_idx] += residual[max_idx];
148
149 let out_degree: f32 = (0..n).map(|j| adjacency[[max_idx, j]]).sum();
151 if out_degree > 0.0 {
152 for j in 0..n {
153 if adjacency[[max_idx, j]] > 0.0 {
154 residual[j] += 0.85 * residual[max_idx] * adjacency[[max_idx, j]] / out_degree;
155 }
156 }
157 }
158
159 residual[max_idx] = 0.0;
160 }
161
162 estimate
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
171 fn test_neumann_solver() {
172 let matrix = SparseMatrix::from_triplets(
177 vec![
178 (0, 0, 2.0), (0, 1, -1.0),
179 (1, 0, -1.0), (1, 1, 2.0), (1, 2, -1.0),
180 (2, 1, -1.0), (2, 2, 2.0),
181 ],
182 3,
183 3,
184 );
185
186 let b = vec![1.0, 0.0, 1.0];
187
188 let solver = NeumannSolver::new(100, 1e-6);
189 let result = solver.solve(&matrix, &b);
190
191 println!("Solution: {:?}", result.solution);
192 println!("Iterations: {}", result.iterations);
193 println!("Residual norm: {}", result.residual_norm);
194 println!("Time: {:?}", result.time_elapsed);
195
196 assert!(result.residual_norm < 1e-5);
198 assert!(result.iterations < 100);
199 }
200
201 #[test]
202 fn test_forward_push() {
203 let mut adjacency = Array2::zeros((3, 3));
204 adjacency[[0, 1]] = 1.0;
205 adjacency[[1, 2]] = 1.0;
206 adjacency[[2, 0]] = 1.0;
207
208 let teleport = Array1::from_vec(vec![0.33, 0.33, 0.34]);
209
210 let solver = ForwardPushSolver::new(1e-6, 100);
211 let result = solver.solve(&adjacency, &teleport);
212
213 println!("PageRank scores: {:?}", result);
214 assert!(result.sum() > 0.0);
215 }
216}