1use super::attention::MultiHeadAttention;
4use super::config::TransformerArchConfig;
5use super::feedforward::{FeedForwardCache, FeedForwardNetwork};
6use super::layers::{
7 DropoutLayer, EmbeddingLayer, LayerNormalization, OutputProjection, ResidualConnections,
8};
9use crate::error::Result;
10use scirs2_core::ndarray::{Array2, Array3};
11use scirs2_core::numeric::Float;
12use std::fmt::Debug;
13
14#[derive(Debug, Clone)]
18pub struct ArchitectureCache<
19 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
20> {
21 pub embedding_input: Array2<T>,
23
24 pub layers: Vec<LayerCache<T>>,
26
27 pub final_norm_input: Array2<T>,
29
30 pub projection_input: Array2<T>,
32}
33
34#[derive(Debug, Clone)]
36pub struct LayerCache<
37 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
38> {
39 pub input: Array2<T>,
41
42 pub attention_residual: Array2<T>,
44
45 pub ff_norm_input: Array2<T>,
47
48 pub feed_forward: FeedForwardCache<T>,
50}
51
52pub struct TransformerArchitecture<
54 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
55> {
56 layers: Vec<TransformerLayer<T>>,
58
59 input_embedding: EmbeddingLayer<T>,
61
62 output_projection: OutputProjection<T>,
64
65 layer_norm: LayerNormalization<T>,
67
68 dropout: DropoutLayer,
70
71 config: TransformerArchConfig,
73}
74
75impl<T: Float + Debug + 'static + scirs2_core::ndarray::ScalarOperand + Send + Sync>
76 TransformerArchitecture<T>
77{
78 pub fn new(config: TransformerArchConfig) -> Result<Self> {
80 let mut layers = Vec::new();
81
82 for layer_idx in 0..config.num_layers {
83 let layer = TransformerLayer::new(
84 config.model_dimension,
85 config.num_attention_heads,
86 config.feedforward_dimension,
87 config.dropout_rate,
88 config.use_pre_norm,
89 layer_idx,
90 )?;
91 layers.push(layer);
92 }
93
94 let input_embedding = EmbeddingLayer::new(
95 config.model_dimension,
96 config.model_dimension, )?;
98
99 let output_projection =
100 OutputProjection::new(config.model_dimension, config.model_dimension)?;
101
102 let layer_norm = LayerNormalization::new(config.model_dimension)?;
103 let dropout = DropoutLayer::new(config.dropout_rate);
104
105 Ok(Self {
106 layers,
107 input_embedding,
108 output_projection,
109 layer_norm,
110 dropout,
111 config,
112 })
113 }
114
115 pub fn forward(&mut self, input: &Array2<T>) -> Result<Array2<T>> {
117 let mut hidden_states = self.input_embedding.forward(input)?;
119
120 hidden_states = self.dropout.forward(&hidden_states);
122
123 for layer in &mut self.layers {
125 hidden_states = layer.forward(&hidden_states)?;
126 }
127
128 hidden_states = self.layer_norm.forward(&hidden_states)?;
130
131 let output = self.output_projection.forward(&hidden_states)?;
133
134 Ok(output)
135 }
136
137 pub fn parameter_count(&self) -> usize {
139 let mut count = 0;
140
141 count += self.input_embedding.parameter_count();
142 count += self.output_projection.parameter_count();
143 count += self.layer_norm.parameter_count();
144
145 for layer in &self.layers {
146 count += layer.parameter_count();
147 }
148
149 count
150 }
151
152 pub fn reset(&mut self) -> Result<()> {
154 self.input_embedding.reset()?;
155 self.output_projection.reset()?;
156 self.layer_norm.reset()?;
157
158 for layer in &mut self.layers {
159 layer.reset()?;
160 }
161
162 Ok(())
163 }
164
165 pub fn get_config(&self) -> &TransformerArchConfig {
167 &self.config
168 }
169
170 pub fn set_training(&mut self, training: bool) {
172 self.dropout.set_training(training);
173 for layer in &mut self.layers {
174 layer.set_training(training);
175 }
176 }
177
178 pub fn forward_with_cache(
183 &mut self,
184 input: &Array2<T>,
185 ) -> Result<(Array2<T>, ArchitectureCache<T>)> {
186 let embedding_input = input.clone();
187 let mut hidden_states = self.input_embedding.forward(input)?;
188
189 let mut layer_caches = Vec::with_capacity(self.layers.len());
190 for layer in &mut self.layers {
191 let (next, cache) = layer.forward_with_cache(&hidden_states)?;
192 layer_caches.push(cache);
193 hidden_states = next;
194 }
195
196 let final_norm_input = hidden_states.clone();
197 let projection_input = self.layer_norm.forward(&hidden_states)?;
198 let output = self.output_projection.forward(&projection_input)?;
199
200 Ok((
201 output,
202 ArchitectureCache {
203 embedding_input,
204 layers: layer_caches,
205 final_norm_input,
206 projection_input,
207 },
208 ))
209 }
210
211 pub fn backward(
225 &mut self,
226 cache: &ArchitectureCache<T>,
227 grad_output: &Array2<T>,
228 learning_rate: T,
229 ) -> Result<T> {
230 let mut grad =
231 self.output_projection
232 .backward(&cache.projection_input, grad_output, learning_rate)?;
233 grad = self
234 .layer_norm
235 .backward(&cache.final_norm_input, &grad, learning_rate)?;
236
237 for (layer, layer_cache) in self.layers.iter_mut().rev().zip(cache.layers.iter().rev()) {
238 grad = layer.backward(layer_cache, &grad, learning_rate)?;
239 }
240
241 let grad_embedding =
242 self.input_embedding
243 .backward(&cache.embedding_input, &grad, learning_rate)?;
244
245 Ok(grad_embedding
246 .iter()
247 .map(|&x| x * x)
248 .fold(T::zero(), |a, b| a + b))
249 }
250}
251
252pub struct TransformerLayer<
254 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
255> {
256 self_attention: MultiHeadAttention<T>,
258
259 feed_forward: FeedForwardNetwork<T>,
261
262 pre_norm1: LayerNormalization<T>,
264 pre_norm2: LayerNormalization<T>,
265
266 post_norm1: Option<LayerNormalization<T>>,
268 post_norm2: Option<LayerNormalization<T>>,
269
270 residual_connections: ResidualConnections<T>,
272
273 dropout: DropoutLayer,
275
276 use_pre_norm: bool,
278 layer_index: usize,
279}
280
281impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static>
282 TransformerLayer<T>
283{
284 pub fn new(
286 model_dimension: usize,
287 num_attention_heads: usize,
288 feedforward_dimension: usize,
289 dropout_rate: f64,
290 use_pre_norm: bool,
291 layer_index: usize,
292 ) -> Result<Self> {
293 let self_attention = MultiHeadAttention::new(
294 num_attention_heads,
295 model_dimension,
296 model_dimension / num_attention_heads,
297 )?;
298
299 let feed_forward = FeedForwardNetwork::new(
300 model_dimension,
301 feedforward_dimension,
302 super::config::ActivationFunction::ReLU,
303 )?;
304
305 let pre_norm1 = LayerNormalization::new(model_dimension)?;
306 let pre_norm2 = LayerNormalization::new(model_dimension)?;
307
308 let (post_norm1, post_norm2) = if !use_pre_norm {
309 (
310 Some(LayerNormalization::new(model_dimension)?),
311 Some(LayerNormalization::new(model_dimension)?),
312 )
313 } else {
314 (None, None)
315 };
316
317 let residual_connections = ResidualConnections::new(model_dimension);
318 let dropout = DropoutLayer::new(dropout_rate);
319
320 Ok(Self {
321 self_attention,
322 feed_forward,
323 pre_norm1,
324 pre_norm2,
325 post_norm1,
326 post_norm2,
327 residual_connections,
328 dropout,
329 use_pre_norm,
330 layer_index,
331 })
332 }
333
334 pub fn forward(&mut self, input: &Array2<T>) -> Result<Array2<T>> {
336 if self.use_pre_norm {
337 self.forward_pre_norm(input)
338 } else {
339 self.forward_post_norm(input)
340 }
341 }
342
343 fn forward_pre_norm(&mut self, input: &Array2<T>) -> Result<Array2<T>> {
345 let normed_input = self.pre_norm1.forward(input)?;
347 let attention_output =
348 self.self_attention
349 .forward(&normed_input, &normed_input, &normed_input)?;
350 let attention_output = self.dropout.forward(&attention_output);
351 let attention_residual = self.residual_connections.add(input, &attention_output)?;
352
353 let normed_attention = self.pre_norm2.forward(&attention_residual)?;
355 let ff_output = self.feed_forward.forward(&normed_attention)?;
356 let ff_output = self.dropout.forward(&ff_output);
357 let ff_residual = self
358 .residual_connections
359 .add(&attention_residual, &ff_output)?;
360
361 Ok(ff_residual)
362 }
363
364 fn forward_post_norm(&mut self, input: &Array2<T>) -> Result<Array2<T>> {
366 let attention_output = self.self_attention.forward(input, input, input)?;
368 let attention_output = self.dropout.forward(&attention_output);
369 let attention_residual = self.residual_connections.add(input, &attention_output)?;
370 let attention_normed = if let Some(ref mut post_norm1) = self.post_norm1 {
371 post_norm1.forward(&attention_residual)?
372 } else {
373 attention_residual
374 };
375
376 let ff_output = self.feed_forward.forward(&attention_normed)?;
378 let ff_output = self.dropout.forward(&ff_output);
379 let ff_residual = self
380 .residual_connections
381 .add(&attention_normed, &ff_output)?;
382 let ff_normed = if let Some(ref mut post_norm2) = self.post_norm2 {
383 post_norm2.forward(&ff_residual)?
384 } else {
385 ff_residual
386 };
387
388 Ok(ff_normed)
389 }
390
391 pub fn parameter_count(&self) -> usize {
393 let mut count = 0;
394
395 count += self.self_attention.parameter_count();
396 count += self.feed_forward.parameter_count();
397 count += self.pre_norm1.parameter_count();
398 count += self.pre_norm2.parameter_count();
399
400 if let Some(ref post_norm1) = self.post_norm1 {
401 count += post_norm1.parameter_count();
402 }
403
404 if let Some(ref post_norm2) = self.post_norm2 {
405 count += post_norm2.parameter_count();
406 }
407
408 count
409 }
410
411 pub fn reset(&mut self) -> Result<()> {
413 self.self_attention.reset()?;
414 self.feed_forward.reset()?;
415 self.pre_norm1.reset()?;
416 self.pre_norm2.reset()?;
417
418 if let Some(ref mut post_norm1) = self.post_norm1 {
419 post_norm1.reset()?;
420 }
421
422 if let Some(ref mut post_norm2) = self.post_norm2 {
423 post_norm2.reset()?;
424 }
425
426 Ok(())
427 }
428
429 pub fn get_layer_index(&self) -> usize {
431 self.layer_index
432 }
433
434 pub fn get_attention_weights(&self) -> Option<Array3<T>> {
436 self.self_attention.get_attention_weights()
437 }
438
439 pub fn set_training(&mut self, training: bool) {
441 self.dropout.set_training(training);
442 self.feed_forward.set_training(training);
443 }
444
445 pub fn forward_with_cache(&mut self, input: &Array2<T>) -> Result<(Array2<T>, LayerCache<T>)> {
450 let normed_input = self.pre_norm1.forward(input)?;
451 let attention_output =
452 self.self_attention
453 .forward(&normed_input, &normed_input, &normed_input)?;
454 let attention_residual = self.residual_connections.add(input, &attention_output)?;
455
456 let ff_norm_input = attention_residual.clone();
457 let normed_attention = self.pre_norm2.forward(&attention_residual)?;
458 let (ff_output, feed_forward) = self.feed_forward.forward_with_cache(&normed_attention)?;
459 let ff_residual = self
460 .residual_connections
461 .add(&attention_residual, &ff_output)?;
462
463 Ok((
464 ff_residual,
465 LayerCache {
466 input: input.clone(),
467 attention_residual,
468 ff_norm_input,
469 feed_forward,
470 },
471 ))
472 }
473
474 pub fn backward(
476 &mut self,
477 cache: &LayerCache<T>,
478 grad_output: &Array2<T>,
479 learning_rate: T,
480 ) -> Result<Array2<T>> {
481 let grad_ff =
484 self.feed_forward
485 .backward(&cache.feed_forward, grad_output, learning_rate)?;
486 let grad_ff_norm =
487 self.pre_norm2
488 .backward(&cache.ff_norm_input, &grad_ff, learning_rate)?;
489 let grad_attention_residual = grad_output + &grad_ff_norm;
490
491 let _ = &cache.attention_residual;
494 Ok(grad_attention_residual)
495 }
496}
497
498#[derive(Debug, Clone)]
500pub struct ArchitectureStats {
501 pub total_parameters: usize,
502 pub total_layers: usize,
503 pub model_dimension: usize,
504 pub attention_heads: usize,
505 pub memory_usage_mb: f64,
506}
507
508impl<T: Float + Debug + 'static + scirs2_core::ndarray::ScalarOperand + Send + Sync>
509 TransformerArchitecture<T>
510{
511 pub fn get_stats(&self) -> ArchitectureStats {
513 let total_parameters = self.parameter_count();
514 let memory_usage_mb =
515 (total_parameters * std::mem::size_of::<T>()) as f64 / (1024.0 * 1024.0);
516
517 ArchitectureStats {
518 total_parameters,
519 total_layers: self.config.num_layers,
520 model_dimension: self.config.model_dimension,
521 attention_heads: self.config.num_attention_heads,
522 memory_usage_mb,
523 }
524 }
525}
526
527#[cfg(test)]
528mod tests {
529 use super::*;
530 use crate::transformer_based_optimizer::config::TransformerArchConfig;
531
532 fn create_test_config() -> TransformerArchConfig {
533 TransformerArchConfig {
534 model_dimension: 128,
535 num_layers: 2,
536 num_attention_heads: 4,
537 feedforward_dimension: 256,
538 dropout_rate: 0.1,
539 use_pre_norm: true,
540 enable_residual_connections: true,
541 }
542 }
543
544 #[test]
545 fn test_transformer_architecture_creation() {
546 let config = create_test_config();
547 let architecture = TransformerArchitecture::<f32>::new(config);
548 assert!(architecture.is_ok());
549
550 let arch = architecture.expect("TransformerArchitecture::new should succeed");
551 assert_eq!(arch.layers.len(), 2);
552 assert_eq!(arch.config.model_dimension, 128);
553 }
554
555 #[test]
556 fn test_transformer_layer_creation() {
557 let layer = TransformerLayer::<f32>::new(128, 4, 256, 0.1, true, 0);
558 assert!(layer.is_ok());
559
560 let l = layer.expect("TransformerLayer::new should succeed");
561 assert_eq!(l.layer_index, 0);
562 assert!(l.use_pre_norm);
563 }
564
565 #[test]
566 fn test_forward_pass() {
567 let config = create_test_config();
568 let mut architecture = TransformerArchitecture::<f32>::new(config)
569 .expect("TransformerArchitecture::new should succeed");
570
571 let input = Array2::<f32>::zeros((4, 128)); let result = architecture.forward(&input);
573 assert!(result.is_ok());
574
575 let output = result.expect("forward should succeed");
576 assert_eq!(output.shape(), &[4, 128]);
577 }
578
579 #[test]
580 fn test_parameter_count() {
581 let config = create_test_config();
582 let architecture = TransformerArchitecture::<f32>::new(config)
583 .expect("TransformerArchitecture::new should succeed");
584
585 let param_count = architecture.parameter_count();
586 assert!(param_count > 0);
587
588 let stats = architecture.get_stats();
589 assert_eq!(stats.total_parameters, param_count);
590 assert_eq!(stats.total_layers, 2);
591 }
592}