1use 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#[derive(Debug, Clone)]
18pub struct TransformerConfig<T: FloatBounds> {
19 pub d_model: usize,
21 pub num_heads: usize,
23 pub d_ff: usize,
25 pub num_encoder_layers: usize,
27 pub num_decoder_layers: usize,
29 pub max_seq_len: usize,
31 pub vocab_size: usize,
33 pub dropout_rate: T,
35 pub label_smoothing: T,
37 pub pre_norm: bool,
39 pub activation: String,
41 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#[derive(Debug, Clone)]
66pub struct FeedForwardNetwork<T: FloatBounds> {
67 linear1: Array2<T>,
69 bias1: Array1<T>,
71 linear2: Array2<T>,
73 bias2: Array1<T>,
75 activation: String,
77 dropout_rate: T,
79}
80
81impl<T: FloatBounds + scirs2_core::ndarray::ScalarOperand> FeedForwardNetwork<T> {
82 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 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 let hidden = x.dot(&self.linear1) + &self.bias1;
118
119 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, };
129
130 let dropout_applied = if training && self.dropout_rate > T::zero() {
132 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 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#[derive(Debug, Clone)]
157pub struct LayerNorm<T: FloatBounds> {
158 weight: Array1<T>,
160 bias: Array1<T>,
162 eps: T,
164}
165
166impl<T: FloatBounds + scirs2_core::ndarray::ScalarOperand> LayerNorm<T> {
167 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 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#[derive(Debug, Clone)]
204#[allow(dead_code)] pub struct TransformerEncoderLayer<T: FloatBounds> {
206 self_attention: MultiHeadAttention<T>,
208 ffn: FeedForwardNetwork<T>,
210 norm1: LayerNorm<T>,
212 norm2: LayerNorm<T>,
214 dropout_rate: T,
216 pre_norm: bool,
218}
219
220impl<T: FloatBounds + scirs2_core::ndarray::ScalarOperand> TransformerEncoderLayer<T> {
221 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 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 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 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#[derive(Debug, Clone)]
285#[allow(dead_code)] pub struct TransformerDecoderLayer<T: FloatBounds> {
287 self_attention: MultiHeadAttention<T>,
289 cross_attention: MultiHeadAttention<T>,
291 ffn: FeedForwardNetwork<T>,
293 norm1: LayerNorm<T>,
295 norm2: LayerNorm<T>,
296 norm3: LayerNorm<T>,
297 dropout_rate: T,
299 pre_norm: bool,
301}
302
303impl<T: FloatBounds + scirs2_core::ndarray::ScalarOperand> TransformerDecoderLayer<T> {
304 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 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 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 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#[derive(Debug, Clone)]
400pub struct TransformerEncoder<T: FloatBounds> {
401 layers: Vec<TransformerEncoderLayer<T>>,
403 embeddings: Array2<T>,
405 positional_encoding: PositionalEncoding<T>,
407 final_norm: Option<LayerNorm<T>>,
409}
410
411impl<T: FloatBounds + scirs2_core::ndarray::ScalarOperand + From<f64>> TransformerEncoder<T> {
412 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 let mut layers = Vec::new();
419 for _ in 0..config.num_encoder_layers {
420 layers.push(TransformerEncoderLayer::new(config)?);
421 }
422
423 let embeddings =
425 initializer.initialize_2d(&mut rng, (config.vocab_size, config.d_model))?;
426
427 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 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 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 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 let mut x = self.positional_encoding.encode(&embedded)?;
476
477 for layer in &mut self.layers {
479 x = layer.forward(&x, mask, training)?;
480 }
481
482 if let Some(ref final_norm) = self.final_norm {
484 x = final_norm.forward(&x);
485 }
486
487 Ok(x)
488 }
489}
490
491#[derive(Debug, Clone)]
493pub struct TransformerDecoder<T: FloatBounds> {
494 layers: Vec<TransformerDecoderLayer<T>>,
496 embeddings: Array2<T>,
498 positional_encoding: PositionalEncoding<T>,
500 final_norm: Option<LayerNorm<T>>,
502 output_projection: Array2<T>,
504}
505
506impl<T: FloatBounds + scirs2_core::ndarray::ScalarOperand + From<f64>> TransformerDecoder<T> {
507 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 let mut layers = Vec::new();
514 for _ in 0..config.num_decoder_layers {
515 layers.push(TransformerDecoderLayer::new(config)?);
516 }
517
518 let embeddings =
520 initializer.initialize_2d(&mut rng, (config.vocab_size, config.d_model))?;
521
522 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 let final_norm = if config.pre_norm {
533 Some(LayerNorm::new(config.d_model))
534 } else {
535 None
536 };
537
538 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 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 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 let mut x = self.positional_encoding.encode(&embedded)?;
578
579 for layer in &mut self.layers {
581 x = layer.forward(&x, encoder_output, self_mask, cross_mask, training)?;
582 }
583
584 if let Some(ref final_norm) = self.final_norm {
586 x = final_norm.forward(&x);
587 }
588
589 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#[derive(Debug, Clone)]
607pub struct EncoderDecoderTransformer<T: FloatBounds> {
608 encoder: TransformerEncoder<T>,
610 decoder: TransformerDecoder<T>,
612 config: TransformerConfig<T>,
614}
615
616impl<T: FloatBounds + scirs2_core::ndarray::ScalarOperand + From<f64>>
617 EncoderDecoderTransformer<T>
618{
619 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 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 let encoder_output = self.encoder.forward(src_ids, src_mask, training)?;
643
644 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 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 let encoder_output = self.encoder.forward(src_ids, src_mask, false)?;
665
666 let mut generated = Array2::from_elem((batch_size, 1), start_token);
668
669 for _ in 1..max_length {
670 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 let logits =
681 self.decoder
682 .forward(&generated, &encoder_output, Some(&tgt_mask), None, false)?;
683
684 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 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 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 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); let tgt_ids = Array2::from_elem((2, 8), 2); 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)); }
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); }
868}