1+ use ndarray:: { Array2 , arr2} ;
2+ use rust_lstm:: layers:: bilstm_network:: { BiLSTMNetwork , CombineMode } ;
3+ use rust_lstm:: models:: lstm_network:: LSTMNetwork ;
4+
5+ /// Generate a simple sequence that benefits from bidirectional processing
6+ fn generate_bidirectional_data ( ) -> Vec < Array2 < f64 > > {
7+ let sequence_length = 10 ;
8+ let mut sequence = Vec :: new ( ) ;
9+
10+ for t in 0 ..sequence_length {
11+ let t_f = t as f64 * 0.5 ;
12+ let current = t_f. sin ( ) ;
13+ let future = if t < sequence_length - 1 { ( t_f + 0.5 ) . cos ( ) * 0.5 } else { 0.0 } ;
14+ let past = if t > 0 { ( t_f - 0.5 ) . sin ( ) * 0.3 } else { 0.0 } ;
15+
16+ let value = current + future + past;
17+ sequence. push ( arr2 ( & [ [ value] ] ) ) ;
18+ }
19+
20+ sequence
21+ }
22+
23+ /// Demonstrate basic BiLSTM functionality
24+ fn demo_basic_bilstm ( ) {
25+ println ! ( "=== Basic BiLSTM Demonstration ===" ) ;
26+
27+ let mut bilstm = BiLSTMNetwork :: new_concat ( 1 , 4 , 1 ) ;
28+ let sequence = generate_bidirectional_data ( ) ;
29+
30+ println ! ( "Input sequence length: {}" , sequence. len( ) ) ;
31+ println ! ( "BiLSTM hidden size: {}" , bilstm. hidden_size) ;
32+ println ! ( "BiLSTM output size: {}" , bilstm. output_size( ) ) ;
33+
34+ let outputs = bilstm. forward_sequence ( & sequence) ;
35+
36+ println ! ( "Output shapes:" ) ;
37+ for ( i, output) in outputs. iter ( ) . enumerate ( ) {
38+ println ! ( " Time step {}: {:?}" , i, output. shape( ) ) ;
39+ }
40+
41+ println ! ( "Sample output values (first 3 time steps):" ) ;
42+ for ( i, output) in outputs. iter ( ) . take ( 3 ) . enumerate ( ) {
43+ println ! ( " t={}: [{:.4}, {:.4}, {:.4}, ...]" ,
44+ i, output[ [ 0 , 0 ] ] , output[ [ 1 , 0 ] ] , output[ [ 2 , 0 ] ] ) ;
45+ }
46+ }
47+
48+ /// Compare different combine modes
49+ fn demo_combine_modes ( ) {
50+ println ! ( "\n === BiLSTM Combine Modes Comparison ===" ) ;
51+
52+ let sequence = generate_bidirectional_data ( ) ;
53+
54+ // Test different combine modes
55+ let modes = vec ! [
56+ ( "Concatenation" , CombineMode :: Concat ) ,
57+ ( "Sum" , CombineMode :: Sum ) ,
58+ ( "Average" , CombineMode :: Average ) ,
59+ ] ;
60+
61+ for ( name, mode) in modes {
62+ let mut bilstm = BiLSTMNetwork :: new ( 1 , 3 , 1 , mode) ;
63+ let outputs = bilstm. forward_sequence ( & sequence) ;
64+
65+ println ! ( "{} mode:" , name) ;
66+ println ! ( " Output size: {}" , bilstm. output_size( ) ) ;
67+ println ! ( " First output shape: {:?}" , outputs[ 0 ] . shape( ) ) ;
68+ println ! ( " Sample values: [{:.4}, {:.4}]" ,
69+ outputs[ 0 ] [ [ 0 , 0 ] ] ,
70+ if outputs[ 0 ] . nrows( ) > 1 { outputs[ 0 ] [ [ 1 , 0 ] ] } else { 0.0 } ) ;
71+ }
72+ }
73+
74+ /// Compare BiLSTM vs unidirectional LSTM performance
75+ fn demo_bilstm_vs_lstm ( ) {
76+ println ! ( "\n === BiLSTM vs Unidirectional LSTM Comparison ===" ) ;
77+
78+ let sequence = generate_bidirectional_data ( ) ;
79+
80+ // Unidirectional LSTM
81+ let mut lstm = LSTMNetwork :: new ( 1 , 4 , 1 ) ;
82+ let mut hx = Array2 :: zeros ( ( 4 , 1 ) ) ;
83+ let mut cx = Array2 :: zeros ( ( 4 , 1 ) ) ;
84+
85+ let mut lstm_outputs = Vec :: new ( ) ;
86+ for input in & sequence {
87+ let ( new_hx, new_cx) = lstm. forward ( input, & hx, & cx) ;
88+ lstm_outputs. push ( new_hx. clone ( ) ) ;
89+ hx = new_hx;
90+ cx = new_cx;
91+ }
92+
93+ // Bidirectional LSTM (with same total parameters approximately)
94+ let mut bilstm = BiLSTMNetwork :: new_concat ( 1 , 2 , 1 ) ; // 2*2=4 total hidden units
95+ let bilstm_outputs = bilstm. forward_sequence ( & sequence) ;
96+
97+ println ! ( "Unidirectional LSTM:" ) ;
98+ println ! ( " Hidden size: 4" ) ;
99+ println ! ( " Output size: 4" ) ;
100+ println ! ( " Sample output: [{:.4}, {:.4}, {:.4}, {:.4}]" ,
101+ lstm_outputs[ 0 ] [ [ 0 , 0 ] ] , lstm_outputs[ 0 ] [ [ 1 , 0 ] ] ,
102+ lstm_outputs[ 0 ] [ [ 2 , 0 ] ] , lstm_outputs[ 0 ] [ [ 3 , 0 ] ] ) ;
103+
104+ println ! ( "Bidirectional LSTM:" ) ;
105+ println ! ( " Hidden size per direction: 2" ) ;
106+ println ! ( " Total output size: 4" ) ;
107+ println ! ( " Sample output: [{:.4}, {:.4}, {:.4}, {:.4}]" ,
108+ bilstm_outputs[ 0 ] [ [ 0 , 0 ] ] , bilstm_outputs[ 0 ] [ [ 1 , 0 ] ] ,
109+ bilstm_outputs[ 0 ] [ [ 2 , 0 ] ] , bilstm_outputs[ 0 ] [ [ 3 , 0 ] ] ) ;
110+
111+ // Demonstrate that BiLSTM has access to future context
112+ println ! ( "\n Context Analysis:" ) ;
113+ println ! ( " LSTM processes left-to-right only" ) ;
114+ println ! ( " BiLSTM processes both directions and combines information" ) ;
115+ println ! ( " This allows BiLSTM to use future context for current predictions" ) ;
116+ }
117+
118+ /// Demonstrate multi-layer BiLSTM
119+ fn demo_multilayer_bilstm ( ) {
120+ println ! ( "\n === Multi-layer BiLSTM ===" ) ;
121+
122+ let sequence = generate_bidirectional_data ( ) ;
123+
124+ for num_layers in 1 ..=3 {
125+ let mut bilstm = BiLSTMNetwork :: new_concat ( 1 , 3 , num_layers) ;
126+ let outputs = bilstm. forward_sequence ( & sequence) ;
127+
128+ println ! ( "{}-layer BiLSTM:" , num_layers) ;
129+ println ! ( " Total parameters (approx): {}" ,
130+ num_layers * 2 * ( 3 * 4 * ( if num_layers == 1 { 1 } else { 6 } ) + 4 * 3 ) ) ;
131+ println ! ( " Output shape: {:?}" , outputs[ 0 ] . shape( ) ) ;
132+ println ! ( " Sample output magnitude: {:.4}" ,
133+ outputs[ 0 ] . iter( ) . map( |& x| x. abs( ) ) . sum:: <f64 >( ) / outputs[ 0 ] . len( ) as f64 ) ;
134+ }
135+ }
136+
137+ /// Demonstrate BiLSTM with dropout
138+ fn demo_bilstm_with_dropout ( ) {
139+ println ! ( "\n === BiLSTM with Dropout ===" ) ;
140+
141+ let sequence = generate_bidirectional_data ( ) ;
142+
143+ let mut bilstm = BiLSTMNetwork :: new_concat ( 1 , 4 , 2 )
144+ . with_input_dropout ( 0.2 , true ) // 20% variational input dropout
145+ . with_recurrent_dropout ( 0.3 , true ) // 30% variational recurrent dropout
146+ . with_output_dropout ( 0.1 ) ; // 10% output dropout
147+
148+ // Training mode (dropout active)
149+ bilstm. train ( ) ;
150+ let train_outputs = bilstm. forward_sequence ( & sequence) ;
151+
152+ // Evaluation mode (dropout inactive)
153+ bilstm. eval ( ) ;
154+ let eval_outputs = bilstm. forward_sequence ( & sequence) ;
155+
156+ println ! ( "Training mode (with dropout):" ) ;
157+ println ! ( " Sample output: [{:.4}, {:.4}, {:.4}]" ,
158+ train_outputs[ 0 ] [ [ 0 , 0 ] ] , train_outputs[ 0 ] [ [ 1 , 0 ] ] , train_outputs[ 0 ] [ [ 2 , 0 ] ] ) ;
159+
160+ println ! ( "Evaluation mode (no dropout):" ) ;
161+ println ! ( " Sample output: [{:.4}, {:.4}, {:.4}]" ,
162+ eval_outputs[ 0 ] [ [ 0 , 0 ] ] , eval_outputs[ 0 ] [ [ 1 , 0 ] ] , eval_outputs[ 0 ] [ [ 2 , 0 ] ] ) ;
163+
164+ println ! ( "Dropout correctly affects training vs evaluation outputs" ) ;
165+ }
166+
167+ /// Demonstrate sequence processing with caching
168+ fn demo_bilstm_with_caching ( ) {
169+ println ! ( "\n === BiLSTM with Caching (for Training) ===" ) ;
170+
171+ let sequence = generate_bidirectional_data ( ) ;
172+ let mut bilstm = BiLSTMNetwork :: new_concat ( 1 , 3 , 1 ) ;
173+
174+ let ( outputs, cache) = bilstm. forward_sequence_with_cache ( & sequence) ;
175+
176+ println ! ( "Forward pass with caching:" ) ;
177+ println ! ( " Sequence length: {}" , sequence. len( ) ) ;
178+ println ! ( " Number of outputs: {}" , outputs. len( ) ) ;
179+ println ! ( " Forward caches: {}" , cache. forward_caches. len( ) ) ;
180+ println ! ( " Backward caches: {}" , cache. backward_caches. len( ) ) ;
181+ println ! ( " Cache enables efficient backpropagation for training" ) ;
182+ }
183+
184+ fn main ( ) {
185+ println ! ( "🔄 Bidirectional LSTM Demonstration" ) ;
186+ println ! ( "=====================================" ) ;
187+
188+ demo_basic_bilstm ( ) ;
189+ demo_combine_modes ( ) ;
190+ demo_bilstm_vs_lstm ( ) ;
191+ demo_multilayer_bilstm ( ) ;
192+ demo_bilstm_with_dropout ( ) ;
193+ demo_bilstm_with_caching ( ) ;
194+
195+ println ! ( "\n ✅ BiLSTM demonstration completed!" ) ;
196+ println ! ( "\n Key Benefits of Bidirectional LSTM:" ) ;
197+ println ! ( "• Captures both past and future context" ) ;
198+ println ! ( "• Better for tasks where full sequence is available" ) ;
199+ println ! ( "• Improved performance on sequence labeling tasks" ) ;
200+ println ! ( "• Flexible output combination modes" ) ;
201+ println ! ( "• Compatible with existing dropout and training systems" ) ;
202+ }
0 commit comments