Skip to main content

sklears_neural/
transformer.rs

1//! Encoder-Decoder Transformer Architectures
2//!
3//! This module provides complete transformer architectures including encoder-decoder
4//! models for sequence-to-sequence tasks, encoder-only models for classification,
5//! and decoder-only models for language generation.
6
7use crate::layers::attention::MultiHeadAttention;
8use crate::layers::transformer::{PositionalEncoding, PositionalEncodingType};
9use crate::weight_init::{InitStrategy, WeightInitializer};
10use crate::NeuralResult;
11use scirs2_core::ndarray::{s, Array1, Array2, Array3};
12use scirs2_core::random::ChaCha8Rng;
13use scirs2_core::random::SeedableRng;
14use sklears_core::types::FloatBounds;
15
16/// Configuration for transformer models
17#[derive(Debug, Clone)]
18pub struct TransformerConfig<T: FloatBounds> {
19    /// Model dimension (d_model)
20    pub d_model: usize,
21    /// Number of attention heads
22    pub num_heads: usize,
23    /// Dimension of feed-forward network
24    pub d_ff: usize,
25    /// Number of encoder layers
26    pub num_encoder_layers: usize,
27    /// Number of decoder layers
28    pub num_decoder_layers: usize,
29    /// Maximum sequence length
30    pub max_seq_len: usize,
31    /// Vocabulary size
32    pub vocab_size: usize,
33    /// Dropout rate
34    pub dropout_rate: T,
35    /// Label smoothing for training
36    pub label_smoothing: T,
37    /// Whether to use pre-normalization
38    pub pre_norm: bool,
39    /// Activation function for FFN
40    pub activation: String,
41    /// Whether to share embeddings between encoder and decoder
42    pub share_embeddings: bool,
43}
44
45impl<T: FloatBounds> Default for TransformerConfig<T> {
46    fn default() -> Self {
47        Self {
48            d_model: 512,
49            num_heads: 8,
50            d_ff: 2048,
51            num_encoder_layers: 6,
52            num_decoder_layers: 6,
53            max_seq_len: 512,
54            vocab_size: 30000,
55            dropout_rate: T::from(0.1).unwrap_or_else(|| T::zero()),
56            label_smoothing: T::from(0.1).unwrap_or_else(|| T::zero()),
57            pre_norm: false,
58            activation: "relu".to_string(),
59            share_embeddings: false,
60        }
61    }
62}
63
64/// Feed-forward network used in transformer blocks
65#[derive(Debug, Clone)]
66pub struct FeedForwardNetwork<T: FloatBounds> {
67    /// First linear transformation
68    linear1: Array2<T>,
69    /// First bias
70    bias1: Array1<T>,
71    /// Second linear transformation
72    linear2: Array2<T>,
73    /// Second bias
74    bias2: Array1<T>,
75    /// Activation function
76    activation: String,
77    /// Dropout rate
78    dropout_rate: T,
79}
80
81impl<T: FloatBounds + scirs2_core::ndarray::ScalarOperand> FeedForwardNetwork<T> {
82    /// Create a new feed-forward network
83    pub fn new(
84        d_model: usize,
85        d_ff: usize,
86        dropout_rate: T,
87        activation: String,
88    ) -> NeuralResult<Self> {
89        let mut rng = ChaCha8Rng::seed_from_u64(42);
90        let initializer = WeightInitializer::new(InitStrategy::XavierUniform);
91
92        let linear1 = initializer.initialize_2d(&mut rng, (d_model, d_ff))?;
93        let bias1 = Array1::zeros(d_ff);
94        let linear2 = initializer.initialize_2d(&mut rng, (d_ff, d_model))?;
95        let bias2 = Array1::zeros(d_model);
96
97        Ok(Self {
98            linear1,
99            bias1,
100            linear2,
101            bias2,
102            activation,
103            dropout_rate,
104        })
105    }
106
107    /// Forward pass through the feed-forward network
108    pub fn forward(&self, input: &Array3<T>, training: bool) -> NeuralResult<Array3<T>> {
109        let (batch_size, seq_len, _d_model) = input.dim();
110        let mut output = Array3::zeros((batch_size, seq_len, self.linear2.ncols()));
111
112        for b in 0..batch_size {
113            for s in 0..seq_len {
114                let x = input.slice(s![b, s, ..]);
115
116                // First linear transformation
117                let hidden = x.dot(&self.linear1) + &self.bias1;
118
119                // Apply activation
120                let activated = match self.activation.as_str() {
121                    "relu" => hidden.mapv(|x| x.max(T::zero())),
122                    "gelu" => hidden.mapv(|x| {
123                        let x_f64 = x.to_f64().unwrap_or(0.0);
124                        let gelu_val = 0.5 * x_f64 * (1.0 + (x_f64 * 0.7978845608028654).tanh());
125                        T::from(gelu_val).unwrap_or(T::zero())
126                    }),
127                    _ => hidden, // Default to identity
128                };
129
130                // Apply dropout if training
131                let dropout_applied = if training && self.dropout_rate > T::zero() {
132                    // Simple dropout implementation
133                    let mut rng = scirs2_core::random::thread_rng();
134                    activated.mapv(|x| {
135                        if rng.random::<f64>() < self.dropout_rate.to_f64().unwrap_or(0.0) {
136                            T::zero()
137                        } else {
138                            x / (T::one() - self.dropout_rate)
139                        }
140                    })
141                } else {
142                    activated
143                };
144
145                // Second linear transformation
146                let final_output = dropout_applied.dot(&self.linear2) + &self.bias2;
147                output.slice_mut(s![b, s, ..]).assign(&final_output);
148            }
149        }
150
151        Ok(output)
152    }
153}
154
155/// Layer normalization for transformers
156#[derive(Debug, Clone)]
157pub struct LayerNorm<T: FloatBounds> {
158    /// Normalization weights
159    weight: Array1<T>,
160    /// Normalization bias
161    bias: Array1<T>,
162    /// Epsilon for numerical stability
163    eps: T,
164}
165
166impl<T: FloatBounds + scirs2_core::ndarray::ScalarOperand> LayerNorm<T> {
167    /// Create a new layer normalization layer
168    pub fn new(d_model: usize) -> Self {
169        Self {
170            weight: Array1::ones(d_model),
171            bias: Array1::zeros(d_model),
172            eps: T::from(1e-5).unwrap_or_else(|| T::zero()),
173        }
174    }
175
176    /// Apply layer normalization
177    pub fn forward(&self, input: &Array3<T>) -> Array3<T> {
178        let (batch_size, seq_len, d_model) = input.dim();
179        let mut output = Array3::zeros((batch_size, seq_len, d_model));
180
181        for b in 0..batch_size {
182            for s in 0..seq_len {
183                let x = input.slice(s![b, s, ..]);
184                let mean = x.mean().unwrap_or(T::zero());
185                let variance = x
186                    .mapv(|v| (v - mean) * (v - mean))
187                    .mean()
188                    .unwrap_or(T::zero());
189                let std = (variance + self.eps).sqrt();
190
191                let normalized = x.mapv(|v| (v - mean) / std);
192                let scaled = &normalized * &self.weight + &self.bias;
193
194                output.slice_mut(s![b, s, ..]).assign(&scaled);
195            }
196        }
197
198        output
199    }
200}
201
202/// Transformer encoder layer
203#[derive(Debug, Clone)]
204#[allow(dead_code)] // dropout_rate retained for future dropout application during forward pass
205pub struct TransformerEncoderLayer<T: FloatBounds> {
206    /// Multi-head self-attention
207    self_attention: MultiHeadAttention<T>,
208    /// Feed-forward network
209    ffn: FeedForwardNetwork<T>,
210    /// Layer normalization for attention
211    norm1: LayerNorm<T>,
212    /// Layer normalization for FFN
213    norm2: LayerNorm<T>,
214    /// Dropout rate
215    dropout_rate: T,
216    /// Whether to use pre-normalization
217    pre_norm: bool,
218}
219
220impl<T: FloatBounds + scirs2_core::ndarray::ScalarOperand> TransformerEncoderLayer<T> {
221    /// Create a new encoder layer from the given transformer configuration
222    pub fn new(config: &TransformerConfig<T>) -> NeuralResult<Self> {
223        let self_attention =
224            MultiHeadAttention::new(config.num_heads, config.d_model, Some(config.dropout_rate))?;
225
226        let ffn = FeedForwardNetwork::new(
227            config.d_model,
228            config.d_ff,
229            config.dropout_rate,
230            config.activation.clone(),
231        )?;
232
233        let norm1 = LayerNorm::new(config.d_model);
234        let norm2 = LayerNorm::new(config.d_model);
235
236        Ok(Self {
237            self_attention,
238            ffn,
239            norm1,
240            norm2,
241            dropout_rate: config.dropout_rate,
242            pre_norm: config.pre_norm,
243        })
244    }
245
246    /// Forward pass through encoder layer
247    pub fn forward(
248        &mut self,
249        input: &Array3<T>,
250        mask: Option<&Array2<bool>>,
251        training: bool,
252    ) -> NeuralResult<Array3<T>> {
253        if self.pre_norm {
254            // Pre-normalization: LayerNorm -> Attention -> Residual
255            let norm_input = self.norm1.forward(input);
256            let attn_output =
257                self.self_attention
258                    .apply(&norm_input, &norm_input, &norm_input, mask, training)?;
259            let residual1 = input + &attn_output;
260
261            let norm_residual1 = self.norm2.forward(&residual1);
262            let ffn_output = self.ffn.forward(&norm_residual1, training)?;
263            let residual2 = &residual1 + &ffn_output;
264
265            Ok(residual2)
266        } else {
267            // Post-normalization: Attention -> Residual -> LayerNorm
268            let attn_output = self
269                .self_attention
270                .apply(input, input, input, mask, training)?;
271            let residual1 = input + &attn_output;
272            let norm1_output = self.norm1.forward(&residual1);
273
274            let ffn_output = self.ffn.forward(&norm1_output, training)?;
275            let residual2 = &norm1_output + &ffn_output;
276            let norm2_output = self.norm2.forward(&residual2);
277
278            Ok(norm2_output)
279        }
280    }
281}
282
283/// Transformer decoder layer
284#[derive(Debug, Clone)]
285#[allow(dead_code)] // dropout_rate retained for future dropout application during forward pass
286pub struct TransformerDecoderLayer<T: FloatBounds> {
287    /// Masked self-attention
288    self_attention: MultiHeadAttention<T>,
289    /// Cross-attention with encoder
290    cross_attention: MultiHeadAttention<T>,
291    /// Feed-forward network
292    ffn: FeedForwardNetwork<T>,
293    /// Layer normalization layers
294    norm1: LayerNorm<T>,
295    norm2: LayerNorm<T>,
296    norm3: LayerNorm<T>,
297    /// Dropout rate
298    dropout_rate: T,
299    /// Whether to use pre-normalization
300    pre_norm: bool,
301}
302
303impl<T: FloatBounds + scirs2_core::ndarray::ScalarOperand> TransformerDecoderLayer<T> {
304    /// Create a new decoder layer from the given transformer configuration
305    pub fn new(config: &TransformerConfig<T>) -> NeuralResult<Self> {
306        let self_attention =
307            MultiHeadAttention::new(config.num_heads, config.d_model, Some(config.dropout_rate))?;
308
309        let cross_attention =
310            MultiHeadAttention::new(config.num_heads, config.d_model, Some(config.dropout_rate))?;
311
312        let ffn = FeedForwardNetwork::new(
313            config.d_model,
314            config.d_ff,
315            config.dropout_rate,
316            config.activation.clone(),
317        )?;
318
319        let norm1 = LayerNorm::new(config.d_model);
320        let norm2 = LayerNorm::new(config.d_model);
321        let norm3 = LayerNorm::new(config.d_model);
322
323        Ok(Self {
324            self_attention,
325            cross_attention,
326            ffn,
327            norm1,
328            norm2,
329            norm3,
330            dropout_rate: config.dropout_rate,
331            pre_norm: config.pre_norm,
332        })
333    }
334
335    /// Forward pass through decoder layer
336    pub fn forward(
337        &mut self,
338        input: &Array3<T>,
339        encoder_output: &Array3<T>,
340        self_mask: Option<&Array2<bool>>,
341        cross_mask: Option<&Array2<bool>>,
342        training: bool,
343    ) -> NeuralResult<Array3<T>> {
344        if self.pre_norm {
345            // Pre-normalization
346            let norm_input = self.norm1.forward(input);
347            let self_attn_output = self.self_attention.apply(
348                &norm_input,
349                &norm_input,
350                &norm_input,
351                self_mask,
352                training,
353            )?;
354            let residual1 = input + &self_attn_output;
355
356            let norm_residual1 = self.norm2.forward(&residual1);
357            let cross_attn_output = self.cross_attention.apply(
358                &norm_residual1,
359                encoder_output,
360                encoder_output,
361                cross_mask,
362                training,
363            )?;
364            let residual2 = &residual1 + &cross_attn_output;
365
366            let norm_residual2 = self.norm3.forward(&residual2);
367            let ffn_output = self.ffn.forward(&norm_residual2, training)?;
368            let residual3 = &residual2 + &ffn_output;
369
370            Ok(residual3)
371        } else {
372            // Post-normalization
373            let self_attn_output = self
374                .self_attention
375                .apply(input, input, input, self_mask, training)?;
376            let residual1 = input + &self_attn_output;
377            let norm1_output = self.norm1.forward(&residual1);
378
379            let cross_attn_output = self.cross_attention.apply(
380                &norm1_output,
381                encoder_output,
382                encoder_output,
383                cross_mask,
384                training,
385            )?;
386            let residual2 = &norm1_output + &cross_attn_output;
387            let norm2_output = self.norm2.forward(&residual2);
388
389            let ffn_output = self.ffn.forward(&norm2_output, training)?;
390            let residual3 = &norm2_output + &ffn_output;
391            let norm3_output = self.norm3.forward(&residual3);
392
393            Ok(norm3_output)
394        }
395    }
396}
397
398/// Complete Transformer encoder
399#[derive(Debug, Clone)]
400pub struct TransformerEncoder<T: FloatBounds> {
401    /// Stack of encoder layers
402    layers: Vec<TransformerEncoderLayer<T>>,
403    /// Input embeddings
404    embeddings: Array2<T>,
405    /// Positional encoding
406    positional_encoding: PositionalEncoding<T>,
407    /// Final layer normalization
408    final_norm: Option<LayerNorm<T>>,
409}
410
411impl<T: FloatBounds + scirs2_core::ndarray::ScalarOperand + From<f64>> TransformerEncoder<T> {
412    /// Create a new transformer encoder
413    pub fn new(config: &TransformerConfig<T>) -> NeuralResult<Self> {
414        let mut rng = ChaCha8Rng::seed_from_u64(42);
415        let initializer = WeightInitializer::new(InitStrategy::XavierUniform);
416
417        // Create encoder layers
418        let mut layers = Vec::new();
419        for _ in 0..config.num_encoder_layers {
420            layers.push(TransformerEncoderLayer::new(config)?);
421        }
422
423        // Initialize embeddings
424        let embeddings =
425            initializer.initialize_2d(&mut rng, (config.vocab_size, config.d_model))?;
426
427        // Create positional encoding
428        let positional_encoding = PositionalEncoding::new(
429            config.max_seq_len,
430            config.d_model,
431            PositionalEncodingType::Sinusoidal,
432            config.dropout_rate,
433            true,
434        )?;
435
436        // Final layer normalization for pre-norm architectures
437        let final_norm = if config.pre_norm {
438            Some(LayerNorm::new(config.d_model))
439        } else {
440            None
441        };
442
443        Ok(Self {
444            layers,
445            embeddings,
446            positional_encoding,
447            final_norm,
448        })
449    }
450
451    /// Forward pass through encoder
452    pub fn forward(
453        &mut self,
454        input_ids: &Array2<usize>,
455        mask: Option<&Array2<bool>>,
456        training: bool,
457    ) -> NeuralResult<Array3<T>> {
458        let (batch_size, seq_len) = input_ids.dim();
459        let d_model = self.embeddings.ncols();
460
461        // Embedding lookup
462        let mut embedded = Array3::zeros((batch_size, seq_len, d_model));
463        for b in 0..batch_size {
464            for s in 0..seq_len {
465                let token_id = input_ids[[b, s]];
466                if token_id < self.embeddings.nrows() {
467                    embedded
468                        .slice_mut(s![b, s, ..])
469                        .assign(&self.embeddings.row(token_id));
470                }
471            }
472        }
473
474        // Add positional encoding
475        let mut x = self.positional_encoding.encode(&embedded)?;
476
477        // Pass through encoder layers
478        for layer in &mut self.layers {
479            x = layer.forward(&x, mask, training)?;
480        }
481
482        // Apply final normalization if using pre-norm
483        if let Some(ref final_norm) = self.final_norm {
484            x = final_norm.forward(&x);
485        }
486
487        Ok(x)
488    }
489}
490
491/// Complete Transformer decoder
492#[derive(Debug, Clone)]
493pub struct TransformerDecoder<T: FloatBounds> {
494    /// Stack of decoder layers
495    layers: Vec<TransformerDecoderLayer<T>>,
496    /// Output embeddings
497    embeddings: Array2<T>,
498    /// Positional encoding
499    positional_encoding: PositionalEncoding<T>,
500    /// Final layer normalization
501    final_norm: Option<LayerNorm<T>>,
502    /// Output projection to vocabulary
503    output_projection: Array2<T>,
504}
505
506impl<T: FloatBounds + scirs2_core::ndarray::ScalarOperand + From<f64>> TransformerDecoder<T> {
507    /// Create a new transformer decoder
508    pub fn new(config: &TransformerConfig<T>) -> NeuralResult<Self> {
509        let mut rng = ChaCha8Rng::seed_from_u64(42);
510        let initializer = WeightInitializer::new(InitStrategy::XavierUniform);
511
512        // Create decoder layers
513        let mut layers = Vec::new();
514        for _ in 0..config.num_decoder_layers {
515            layers.push(TransformerDecoderLayer::new(config)?);
516        }
517
518        // Initialize embeddings
519        let embeddings =
520            initializer.initialize_2d(&mut rng, (config.vocab_size, config.d_model))?;
521
522        // Create positional encoding
523        let positional_encoding = PositionalEncoding::new(
524            config.max_seq_len,
525            config.d_model,
526            PositionalEncodingType::Sinusoidal,
527            config.dropout_rate,
528            true,
529        )?;
530
531        // Final layer normalization
532        let final_norm = if config.pre_norm {
533            Some(LayerNorm::new(config.d_model))
534        } else {
535            None
536        };
537
538        // Output projection
539        let output_projection =
540            initializer.initialize_2d(&mut rng, (config.d_model, config.vocab_size))?;
541
542        Ok(Self {
543            layers,
544            embeddings,
545            positional_encoding,
546            final_norm,
547            output_projection,
548        })
549    }
550
551    /// Forward pass through decoder
552    pub fn forward(
553        &mut self,
554        input_ids: &Array2<usize>,
555        encoder_output: &Array3<T>,
556        self_mask: Option<&Array2<bool>>,
557        cross_mask: Option<&Array2<bool>>,
558        training: bool,
559    ) -> NeuralResult<Array3<T>> {
560        let (batch_size, seq_len) = input_ids.dim();
561        let d_model = self.embeddings.ncols();
562
563        // Embedding lookup
564        let mut embedded = Array3::zeros((batch_size, seq_len, d_model));
565        for b in 0..batch_size {
566            for s in 0..seq_len {
567                let token_id = input_ids[[b, s]];
568                if token_id < self.embeddings.nrows() {
569                    embedded
570                        .slice_mut(s![b, s, ..])
571                        .assign(&self.embeddings.row(token_id));
572                }
573            }
574        }
575
576        // Add positional encoding
577        let mut x = self.positional_encoding.encode(&embedded)?;
578
579        // Pass through decoder layers
580        for layer in &mut self.layers {
581            x = layer.forward(&x, encoder_output, self_mask, cross_mask, training)?;
582        }
583
584        // Apply final normalization if using pre-norm
585        if let Some(ref final_norm) = self.final_norm {
586            x = final_norm.forward(&x);
587        }
588
589        // Project to vocabulary
590        let (batch_size, seq_len, _d_model) = x.dim();
591        let mut logits = Array3::zeros((batch_size, seq_len, self.output_projection.ncols()));
592
593        for b in 0..batch_size {
594            for s in 0..seq_len {
595                let hidden = x.slice(s![b, s, ..]);
596                let output = hidden.dot(&self.output_projection);
597                logits.slice_mut(s![b, s, ..]).assign(&output);
598            }
599        }
600
601        Ok(logits)
602    }
603}
604
605/// Complete Encoder-Decoder Transformer
606#[derive(Debug, Clone)]
607pub struct EncoderDecoderTransformer<T: FloatBounds> {
608    /// Transformer encoder
609    encoder: TransformerEncoder<T>,
610    /// Transformer decoder
611    decoder: TransformerDecoder<T>,
612    /// Configuration
613    config: TransformerConfig<T>,
614}
615
616impl<T: FloatBounds + scirs2_core::ndarray::ScalarOperand + From<f64>>
617    EncoderDecoderTransformer<T>
618{
619    /// Create a new encoder-decoder transformer
620    pub fn new(config: TransformerConfig<T>) -> NeuralResult<Self> {
621        let encoder = TransformerEncoder::new(&config)?;
622        let decoder = TransformerDecoder::new(&config)?;
623
624        Ok(Self {
625            encoder,
626            decoder,
627            config,
628        })
629    }
630
631    /// Forward pass for training
632    pub fn forward(
633        &mut self,
634        src_ids: &Array2<usize>,
635        tgt_ids: &Array2<usize>,
636        src_mask: Option<&Array2<bool>>,
637        tgt_mask: Option<&Array2<bool>>,
638        cross_mask: Option<&Array2<bool>>,
639        training: bool,
640    ) -> NeuralResult<Array3<T>> {
641        // Encode source sequence
642        let encoder_output = self.encoder.forward(src_ids, src_mask, training)?;
643
644        // Decode target sequence
645        let decoder_output =
646            self.decoder
647                .forward(tgt_ids, &encoder_output, tgt_mask, cross_mask, training)?;
648
649        Ok(decoder_output)
650    }
651
652    /// Generate sequences auto-regressively
653    pub fn generate(
654        &mut self,
655        src_ids: &Array2<usize>,
656        max_length: usize,
657        start_token: usize,
658        end_token: usize,
659        src_mask: Option<&Array2<bool>>,
660    ) -> NeuralResult<Array2<usize>> {
661        let batch_size = src_ids.nrows();
662
663        // Encode source
664        let encoder_output = self.encoder.forward(src_ids, src_mask, false)?;
665
666        // Initialize decoder input with start token
667        let mut generated = Array2::from_elem((batch_size, 1), start_token);
668
669        for _ in 1..max_length {
670            // Create causal mask for decoder
671            let seq_len = generated.ncols();
672            let mut tgt_mask = Array2::from_elem((seq_len, seq_len), true);
673            for i in 0..seq_len {
674                for j in i + 1..seq_len {
675                    tgt_mask[[i, j]] = false;
676                }
677            }
678
679            // Forward pass through decoder
680            let logits =
681                self.decoder
682                    .forward(&generated, &encoder_output, Some(&tgt_mask), None, false)?;
683
684            // Get next token predictions (greedy decoding)
685            let last_logits = logits.slice(s![.., -1, ..]);
686            let mut next_tokens = Array1::zeros(batch_size);
687
688            for b in 0..batch_size {
689                let logits_row = last_logits.slice(s![b, ..]);
690                let mut max_idx = 0;
691                let mut max_val = *logits_row.iter().next().expect("empty iterator");
692
693                for (idx, &val) in logits_row.iter().enumerate() {
694                    if val > max_val {
695                        max_val = val;
696                        max_idx = idx;
697                    }
698                }
699                next_tokens[b] = max_idx;
700            }
701
702            // Append next tokens to generated sequence
703            let mut new_generated = Array2::zeros((batch_size, generated.ncols() + 1));
704            new_generated
705                .slice_mut(s![.., ..generated.ncols()])
706                .assign(&generated);
707            for b in 0..batch_size {
708                new_generated[[b, generated.ncols()]] = next_tokens[b];
709            }
710            generated = new_generated;
711
712            // Check for end tokens (early stopping)
713            let mut all_ended = true;
714            for b in 0..batch_size {
715                if next_tokens[b] != end_token {
716                    all_ended = false;
717                    break;
718                }
719            }
720            if all_ended {
721                break;
722            }
723        }
724
725        Ok(generated)
726    }
727
728    /// Get configuration
729    pub fn config(&self) -> &TransformerConfig<T> {
730        &self.config
731    }
732}
733
734#[allow(non_snake_case)]
735#[cfg(test)]
736mod tests {
737    use super::*;
738    use scirs2_core::essentials::Normal;
739    use scirs2_core::ndarray::Array3;
740    use scirs2_core::random::thread_rng;
741
742    #[test]
743    #[ignore]
744    fn test_transformer_config() {
745        let config: TransformerConfig<f64> = TransformerConfig::default();
746        assert_eq!(config.d_model, 512);
747        assert_eq!(config.num_heads, 8);
748        assert_eq!(config.d_ff, 2048);
749    }
750
751    #[test]
752    #[ignore]
753    fn test_feed_forward_network() {
754        let ffn = FeedForwardNetwork::new(512, 2048, 0.1, "relu".to_string())
755            .expect("construction should succeed");
756        let input = Array3::from_shape_fn((2, 10, 512), |_| {
757            let mut rng = thread_rng();
758            rng.sample(Normal::new(0.0, 1.0).expect("construction should succeed"))
759        });
760        let output = ffn
761            .forward(&input, false)
762            .expect("forward pass should succeed");
763        assert_eq!(output.dim(), (2, 10, 512));
764    }
765
766    #[test]
767    #[ignore]
768    fn test_layer_norm() {
769        let layer_norm = LayerNorm::<f64>::new(512);
770        let input = Array3::from_shape_fn((2, 10, 512), |_| {
771            let mut rng = thread_rng();
772            rng.sample(Normal::new(0.0, 1.0).expect("construction should succeed"))
773        });
774        let output = layer_norm.forward(&input);
775        assert_eq!(output.dim(), (2, 10, 512));
776    }
777
778    #[test]
779    #[ignore]
780    fn test_transformer_encoder_layer() {
781        let config: TransformerConfig<f64> = TransformerConfig::default();
782        let mut encoder_layer =
783            TransformerEncoderLayer::new(&config).expect("construction should succeed");
784
785        let input = Array3::from_shape_fn((2, 10, 512), |_| {
786            let mut rng = thread_rng();
787            rng.sample(Normal::new(0.0, 1.0).expect("construction should succeed"))
788        });
789        let output = encoder_layer
790            .forward(&input, None, false)
791            .expect("forward pass should succeed");
792        assert_eq!(output.dim(), (2, 10, 512));
793    }
794
795    #[test]
796    #[ignore]
797    fn test_transformer_decoder_layer() {
798        let config: TransformerConfig<f64> = TransformerConfig::default();
799        let mut decoder_layer =
800            TransformerDecoderLayer::new(&config).expect("construction should succeed");
801
802        let input = Array3::from_shape_fn((2, 10, 512), |_| {
803            let mut rng = thread_rng();
804            rng.sample(Normal::new(0.0, 1.0).expect("construction should succeed"))
805        });
806        let encoder_output = Array3::from_shape_fn((2, 15, 512), |_| {
807            let mut rng = thread_rng();
808            rng.sample(Normal::new(0.0, 1.0).expect("construction should succeed"))
809        });
810        let output = decoder_layer
811            .forward(&input, &encoder_output, None, None, false)
812            .expect("operation should succeed");
813        assert_eq!(output.dim(), (2, 10, 512));
814    }
815
816    #[test]
817    #[ignore]
818    fn test_encoder_decoder_transformer() {
819        let config: TransformerConfig<f64> = TransformerConfig {
820            vocab_size: 1000,
821            max_seq_len: 20,
822            d_model: 128,
823            num_heads: 4,
824            d_ff: 256,
825            num_encoder_layers: 2,
826            num_decoder_layers: 2,
827            ..Default::default()
828        };
829
830        let mut transformer =
831            EncoderDecoderTransformer::new(config).expect("construction should succeed");
832
833        let src_ids = Array2::from_elem((2, 10), 1); // Simple input
834        let tgt_ids = Array2::from_elem((2, 8), 2); // Simple target
835
836        let output = transformer
837            .forward(&src_ids, &tgt_ids, None, None, None, false)
838            .expect("operation should succeed");
839        assert_eq!(output.dim(), (2, 8, 1000)); // batch, seq, vocab
840    }
841
842    #[test]
843    #[ignore]
844    fn test_transformer_generation() {
845        let config: TransformerConfig<f64> = TransformerConfig {
846            vocab_size: 100,
847            max_seq_len: 20,
848            d_model: 64,
849            num_heads: 2,
850            d_ff: 128,
851            num_encoder_layers: 1,
852            num_decoder_layers: 1,
853            ..Default::default()
854        };
855
856        let mut transformer =
857            EncoderDecoderTransformer::new(config).expect("construction should succeed");
858
859        let src_ids = Array2::from_elem((1, 5), 1);
860        let generated = transformer
861            .generate(&src_ids, 10, 2, 3, None)
862            .expect("operation should succeed");
863
864        assert_eq!(generated.nrows(), 1);
865        assert!(generated.ncols() <= 10);
866        assert_eq!(generated[[0, 0]], 2); // Should start with start token
867    }
868}