temporal_neural_solver/baselines/
numpy_style.rs1use ndarray::{Array1, Array2, Axis};
7use ndarray_rand::RandomExt;
8use rand_distr::Normal;
9use std::time::{Duration, Instant};
10
11pub struct NumpyStyleNetwork {
13 weights1: Array2<f32>,
14 bias1: Array1<f32>,
15 weights2: Array2<f32>,
16 bias2: Array1<f32>,
17}
18
19impl NumpyStyleNetwork {
20 pub fn new_standard() -> Self {
21 let scale1 = (2.0 / 128.0_f32).sqrt();
23 let scale2 = (2.0 / 32.0_f32).sqrt();
24
25 let dist1 = Normal::new(0.0, scale1).unwrap();
26 let dist2 = Normal::new(0.0, scale2).unwrap();
27
28 Self {
29 weights1: Array2::random((32, 128), dist1),
30 bias1: Array1::zeros(32),
31 weights2: Array2::random((4, 32), dist2),
32 bias2: Array1::zeros(4),
33 }
34 }
35
36 pub fn forward(&self, input: &Array1<f32>) -> Array1<f32> {
38 let z1 = self.weights1.dot(input) + &self.bias1;
40
41 let a1 = z1.mapv(|x| x.max(0.0));
43
44 let z2 = self.weights2.dot(&a1) + &self.bias2;
46
47 z2
48 }
49
50 pub fn forward_batch(&self, inputs: &Array2<f32>) -> Array2<f32> {
52 let batch_size = inputs.shape()[0];
56 let mut outputs = Array2::zeros((batch_size, 4));
57
58 for (i, input_row) in inputs.axis_iter(Axis(0)).enumerate() {
60 let input_vec = input_row.to_owned();
61 let output = self.forward(&input_vec);
62 outputs.row_mut(i).assign(&output);
63 }
64
65 outputs
66 }
67
68 pub fn predict_timed(&self, input: &Array1<f32>) -> (Array1<f32>, Duration) {
70 let start = Instant::now();
71 let output = self.forward(input);
72 let duration = start.elapsed();
73 (output, duration)
74 }
75
76 pub fn predict_batch_timed(&self, inputs: &Array2<f32>) -> (Array2<f32>, Duration) {
78 let start = Instant::now();
79 let outputs = self.forward_batch(inputs);
80 let duration = start.elapsed();
81 (outputs, duration)
82 }
83
84 pub fn compute_gradients(&self, input: &Array1<f32>, target: &Array1<f32>) -> f32 {
86 let prediction = self.forward(input);
87
88 let diff = &prediction - target;
90 let loss = diff.mapv(|x| x * x).sum() / prediction.len() as f32;
91
92 loss
93 }
94}
95
96pub struct OptimizedNumpyStyle {
98 w1_data: Vec<f32>, b1_data: Vec<f32>, w2_data: Vec<f32>, b2_data: Vec<f32>, }
104
105impl OptimizedNumpyStyle {
106 pub fn new_standard() -> Self {
107 let scale1 = (2.0 / 128.0_f32).sqrt();
108 let scale2 = (2.0 / 32.0_f32).sqrt();
109
110 use rand::Rng;
111 let mut rng = rand::thread_rng();
112
113 let w1_data: Vec<f32> = (0..32*128)
115 .map(|_| rng.gen::<f32>() * scale1 * 2.0 - scale1)
116 .collect();
117
118 let w2_data: Vec<f32> = (0..4*32)
119 .map(|_| rng.gen::<f32>() * scale2 * 2.0 - scale2)
120 .collect();
121
122 Self {
123 w1_data,
124 b1_data: vec![0.0; 32],
125 w2_data,
126 b2_data: vec![0.0; 4],
127 }
128 }
129
130 pub fn forward(&self, input: &[f32; 128]) -> [f32; 4] {
132 let mut hidden = [0.0f32; 32];
134
135 for i in 0..32 {
136 let mut sum = self.b1_data[i];
137
138 for j in 0..128 {
140 sum += self.w1_data[i * 128 + j] * input[j];
141 }
142
143 hidden[i] = if sum > 0.0 { sum } else { 0.0 };
145 }
146
147 let mut output = [0.0f32; 4];
149
150 for i in 0..4 {
151 let mut sum = self.b2_data[i];
152
153 for j in 0..32 {
154 sum += self.w2_data[i * 32 + j] * hidden[j];
155 }
156
157 output[i] = sum;
158 }
159
160 output
161 }
162
163 pub fn predict_timed(&self, input: &[f32; 128]) -> ([f32; 4], Duration) {
164 let start = Instant::now();
165 let output = self.forward(input);
166 let duration = start.elapsed();
167 (output, duration)
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn test_numpy_style_network() {
177 let network = NumpyStyleNetwork::new_standard();
178 let input = Array1::from_vec(vec![0.1; 128]);
179 let (output, duration) = network.predict_timed(&input);
180
181 assert_eq!(output.len(), 4);
182 println!("NumPy-style latency: {:?}", duration);
183 }
184
185 #[test]
186 fn test_numpy_batch_processing() {
187 let network = NumpyStyleNetwork::new_standard();
188 let inputs = Array2::from_shape_vec((10, 128), vec![0.1; 10 * 128]).unwrap();
189 let (outputs, duration) = network.predict_batch_timed(&inputs);
190
191 assert_eq!(outputs.shape(), &[10, 4]);
192 println!("NumPy batch latency: {:?}", duration);
193 }
194
195 #[test]
196 fn test_optimized_numpy_style() {
197 let network = OptimizedNumpyStyle::new_standard();
198 let input = [0.1f32; 128];
199 let (output, duration) = network.predict_timed(&input);
200
201 assert_eq!(output.len(), 4);
202 println!("Optimized NumPy-style latency: {:?}", duration);
203 }
204}