ilovetools 0.2.19__tar.gz → 0.2.20__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. {ilovetools-0.2.19/ilovetools.egg-info → ilovetools-0.2.20}/PKG-INFO +2 -2
  2. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/__init__.py +2 -2
  3. ilovetools-0.2.20/ilovetools/ml/rnn.py +498 -0
  4. {ilovetools-0.2.19 → ilovetools-0.2.20/ilovetools.egg-info}/PKG-INFO +2 -2
  5. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools.egg-info/SOURCES.txt +3 -1
  6. {ilovetools-0.2.19 → ilovetools-0.2.20}/pyproject.toml +2 -2
  7. {ilovetools-0.2.19 → ilovetools-0.2.20}/setup.py +2 -2
  8. ilovetools-0.2.20/tests/test_rnn.py +419 -0
  9. {ilovetools-0.2.19 → ilovetools-0.2.20}/LICENSE +0 -0
  10. {ilovetools-0.2.19 → ilovetools-0.2.20}/MANIFEST.in +0 -0
  11. {ilovetools-0.2.19 → ilovetools-0.2.20}/README.md +0 -0
  12. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ai/__init__.py +0 -0
  13. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ai/embeddings.py +0 -0
  14. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ai/inference.py +0 -0
  15. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ai/llm_helpers.py +0 -0
  16. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/audio/__init__.py +0 -0
  17. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/automation/__init__.py +0 -0
  18. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/automation/file_organizer.py +0 -0
  19. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/conversion/__init__.py +0 -0
  20. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/conversion/config_converter.py +0 -0
  21. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/conversion/config_converter_fixed_header.py +0 -0
  22. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/data/__init__.py +0 -0
  23. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/data/feature_engineering.py +0 -0
  24. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/data/preprocessing.py +0 -0
  25. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/database/__init__.py +0 -0
  26. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/datetime/__init__.py +0 -0
  27. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/email/__init__.py +0 -0
  28. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/email/template_engine.py +0 -0
  29. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/files/__init__.py +0 -0
  30. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/image/__init__.py +0 -0
  31. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/__init__.py +0 -0
  32. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/activations.py +0 -0
  33. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/anomaly_detection.py +0 -0
  34. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/attention.py +0 -0
  35. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/clustering.py +0 -0
  36. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/cnn.py +0 -0
  37. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/cross_validation.py +0 -0
  38. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/dimensionality.py +0 -0
  39. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/ensemble.py +0 -0
  40. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/feature_selection.py +0 -0
  41. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/gradient_descent.py +0 -0
  42. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/imbalanced.py +0 -0
  43. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/interpretation.py +0 -0
  44. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/loss_functions.py +0 -0
  45. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/metrics.py +0 -0
  46. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/neural_network.py +0 -0
  47. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/normalization.py +0 -0
  48. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/optimizers.py +0 -0
  49. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/pipeline.py +0 -0
  50. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/regularization.py +0 -0
  51. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/timeseries.py +0 -0
  52. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/ml/tuning.py +0 -0
  53. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/security/__init__.py +0 -0
  54. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/security/password_checker.py +0 -0
  55. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/text/__init__.py +0 -0
  56. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/utils/__init__.py +0 -0
  57. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/utils/cache_system.py +0 -0
  58. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/utils/logger.py +0 -0
  59. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/utils/rate_limiter.py +0 -0
  60. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/utils/retry.py +0 -0
  61. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/validation/__init__.py +0 -0
  62. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/validation/data_validator.py +0 -0
  63. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/web/__init__.py +0 -0
  64. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/web/scraper.py +0 -0
  65. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools/web/url_shortener.py +0 -0
  66. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools.egg-info/dependency_links.txt +0 -0
  67. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools.egg-info/requires.txt +0 -0
  68. {ilovetools-0.2.19 → ilovetools-0.2.20}/ilovetools.egg-info/top_level.txt +0 -0
  69. {ilovetools-0.2.19 → ilovetools-0.2.20}/requirements.txt +0 -0
  70. {ilovetools-0.2.19 → ilovetools-0.2.20}/setup.cfg +0 -0
  71. {ilovetools-0.2.19 → ilovetools-0.2.20}/tests/__init__.py +0 -0
  72. {ilovetools-0.2.19 → ilovetools-0.2.20}/tests/test_activations.py +0 -0
  73. {ilovetools-0.2.19 → ilovetools-0.2.20}/tests/test_attention.py +0 -0
  74. {ilovetools-0.2.19 → ilovetools-0.2.20}/tests/test_cnn.py +0 -0
  75. {ilovetools-0.2.19 → ilovetools-0.2.20}/tests/test_gradient_descent.py +0 -0
  76. {ilovetools-0.2.19 → ilovetools-0.2.20}/tests/test_loss_functions.py +0 -0
  77. {ilovetools-0.2.19 → ilovetools-0.2.20}/tests/test_neural_network.py +0 -0
  78. {ilovetools-0.2.19 → ilovetools-0.2.20}/tests/test_normalization.py +0 -0
  79. {ilovetools-0.2.19 → ilovetools-0.2.20}/tests/test_optimizers.py +0 -0
  80. {ilovetools-0.2.19 → ilovetools-0.2.20}/tests/test_regularization.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ilovetools
3
- Version: 0.2.19
3
+ Version: 0.2.20
4
4
  Summary: A comprehensive Python utility library with modular tools for AI/ML, data processing, and daily programming needs
5
5
  Home-page: https://github.com/AliMehdi512/ilovetools
6
6
  Author: Ali Mehdi
@@ -11,7 +11,7 @@ Project-URL: Repository, https://github.com/AliMehdi512/ilovetools
11
11
  Project-URL: Issues, https://github.com/AliMehdi512/ilovetools/issues
12
12
  Project-URL: Bug Reports, https://github.com/AliMehdi512/ilovetools/issues
13
13
  Project-URL: Source, https://github.com/AliMehdi512/ilovetools
14
- Keywords: utilities,tools,ai,ml,data-processing,automation,cnn,convolutional-neural-networks,conv2d,pooling,computer-vision,image-processing
14
+ Keywords: utilities,tools,ai,ml,data-processing,automation,rnn,lstm,gru,recurrent-neural-networks,sequence-modeling,nlp
15
15
  Classifier: Development Status :: 3 - Alpha
16
16
  Classifier: Intended Audience :: Developers
17
17
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
@@ -2,8 +2,8 @@
2
2
  ilovetools - A comprehensive Python utility library
3
3
  """
4
4
 
5
- __version__ = "0.2.18"
6
- # release marker: 0.2.18
5
+ __version__ = "0.2.19"
6
+ # release marker: 0.2.19
7
7
  __author__ = "Ali Mehdi"
8
8
  __email__ = "ali.mehdi.dev579@gmail.com"
9
9
 
@@ -0,0 +1,498 @@
1
+ """
2
+ Recurrent Neural Network Operations
3
+
4
+ This module provides RNN architectures and operations:
5
+ - Basic RNN (Vanilla RNN)
6
+ - LSTM (Long Short-Term Memory)
7
+ - GRU (Gated Recurrent Unit)
8
+ - Bidirectional RNNs
9
+ - Stacked RNNs
10
+ - Sequence-to-Sequence utilities
11
+
12
+ All operations support batched inputs and are optimized for sequence processing.
13
+ """
14
+
15
+ import numpy as np
16
+ from typing import Tuple, Optional, List
17
+
18
+
19
+ # ============================================================================
20
+ # BASIC RNN (VANILLA RNN)
21
+ # ============================================================================
22
+
23
+ def rnn_cell_forward(
24
+ x_t: np.ndarray,
25
+ h_prev: np.ndarray,
26
+ W_xh: np.ndarray,
27
+ W_hh: np.ndarray,
28
+ b_h: np.ndarray
29
+ ) -> np.ndarray:
30
+ """
31
+ Single timestep of basic RNN cell
32
+
33
+ Formula: h_t = tanh(W_xh @ x_t + W_hh @ h_prev + b_h)
34
+
35
+ Args:
36
+ x_t: Input at timestep t, shape (batch, input_size)
37
+ h_prev: Previous hidden state, shape (batch, hidden_size)
38
+ W_xh: Input-to-hidden weights, shape (input_size, hidden_size)
39
+ W_hh: Hidden-to-hidden weights, shape (hidden_size, hidden_size)
40
+ b_h: Hidden bias, shape (hidden_size,)
41
+
42
+ Returns:
43
+ h_t: New hidden state, shape (batch, hidden_size)
44
+
45
+ Example:
46
+ >>> x_t = np.random.randn(32, 128) # (batch, input_size)
47
+ >>> h_prev = np.random.randn(32, 256) # (batch, hidden_size)
48
+ >>> W_xh = np.random.randn(128, 256)
49
+ >>> W_hh = np.random.randn(256, 256)
50
+ >>> b_h = np.zeros(256)
51
+ >>> h_t = rnn_cell_forward(x_t, h_prev, W_xh, W_hh, b_h)
52
+ >>> print(h_t.shape) # (32, 256)
53
+ """
54
+ h_t = np.tanh(np.dot(x_t, W_xh) + np.dot(h_prev, W_hh) + b_h)
55
+ return h_t
56
+
57
+
58
+ def rnn_forward(
59
+ x: np.ndarray,
60
+ h_0: np.ndarray,
61
+ W_xh: np.ndarray,
62
+ W_hh: np.ndarray,
63
+ b_h: np.ndarray
64
+ ) -> Tuple[np.ndarray, np.ndarray]:
65
+ """
66
+ Forward pass through entire RNN sequence
67
+
68
+ Args:
69
+ x: Input sequence, shape (batch, seq_len, input_size)
70
+ h_0: Initial hidden state, shape (batch, hidden_size)
71
+ W_xh: Input-to-hidden weights
72
+ W_hh: Hidden-to-hidden weights
73
+ b_h: Hidden bias
74
+
75
+ Returns:
76
+ Tuple of (outputs, hidden_states)
77
+ - outputs: shape (batch, seq_len, hidden_size)
78
+ - hidden_states: shape (batch, seq_len, hidden_size)
79
+
80
+ Example:
81
+ >>> x = np.random.randn(32, 10, 128) # (batch, seq_len, input_size)
82
+ >>> h_0 = np.zeros((32, 256))
83
+ >>> W_xh = np.random.randn(128, 256)
84
+ >>> W_hh = np.random.randn(256, 256)
85
+ >>> b_h = np.zeros(256)
86
+ >>> outputs, hidden_states = rnn_forward(x, h_0, W_xh, W_hh, b_h)
87
+ >>> print(outputs.shape) # (32, 10, 256)
88
+ """
89
+ batch_size, seq_len, input_size = x.shape
90
+ hidden_size = h_0.shape[1]
91
+
92
+ # Initialize outputs
93
+ outputs = np.zeros((batch_size, seq_len, hidden_size))
94
+ hidden_states = np.zeros((batch_size, seq_len, hidden_size))
95
+
96
+ h_t = h_0
97
+
98
+ # Process sequence
99
+ for t in range(seq_len):
100
+ h_t = rnn_cell_forward(x[:, t, :], h_t, W_xh, W_hh, b_h)
101
+ outputs[:, t, :] = h_t
102
+ hidden_states[:, t, :] = h_t
103
+
104
+ return outputs, hidden_states
105
+
106
+
107
+ # ============================================================================
108
+ # LSTM (LONG SHORT-TERM MEMORY)
109
+ # ============================================================================
110
+
111
+ def lstm_cell_forward(
112
+ x_t: np.ndarray,
113
+ h_prev: np.ndarray,
114
+ c_prev: np.ndarray,
115
+ W_f: np.ndarray,
116
+ W_i: np.ndarray,
117
+ W_c: np.ndarray,
118
+ W_o: np.ndarray,
119
+ b_f: np.ndarray,
120
+ b_i: np.ndarray,
121
+ b_c: np.ndarray,
122
+ b_o: np.ndarray
123
+ ) -> Tuple[np.ndarray, np.ndarray]:
124
+ """
125
+ Single timestep of LSTM cell
126
+
127
+ LSTM has three gates:
128
+ - Forget gate: decides what to forget from cell state
129
+ - Input gate: decides what new information to store
130
+ - Output gate: decides what to output
131
+
132
+ Args:
133
+ x_t: Input at timestep t, shape (batch, input_size)
134
+ h_prev: Previous hidden state, shape (batch, hidden_size)
135
+ c_prev: Previous cell state, shape (batch, hidden_size)
136
+ W_f, W_i, W_c, W_o: Weight matrices for gates
137
+ b_f, b_i, b_c, b_o: Bias vectors for gates
138
+
139
+ Returns:
140
+ Tuple of (h_t, c_t)
141
+ - h_t: New hidden state, shape (batch, hidden_size)
142
+ - c_t: New cell state, shape (batch, hidden_size)
143
+
144
+ Example:
145
+ >>> x_t = np.random.randn(32, 128)
146
+ >>> h_prev = np.random.randn(32, 256)
147
+ >>> c_prev = np.random.randn(32, 256)
148
+ >>> # Initialize weights...
149
+ >>> h_t, c_t = lstm_cell_forward(x_t, h_prev, c_prev, W_f, W_i, W_c, W_o, b_f, b_i, b_c, b_o)
150
+ >>> print(h_t.shape, c_t.shape) # (32, 256), (32, 256)
151
+ """
152
+ # Concatenate input and previous hidden state
153
+ concat = np.concatenate([h_prev, x_t], axis=1)
154
+
155
+ # Forget gate
156
+ f_t = sigmoid(np.dot(concat, W_f) + b_f)
157
+
158
+ # Input gate
159
+ i_t = sigmoid(np.dot(concat, W_i) + b_i)
160
+
161
+ # Candidate cell state
162
+ c_tilde = np.tanh(np.dot(concat, W_c) + b_c)
163
+
164
+ # New cell state
165
+ c_t = f_t * c_prev + i_t * c_tilde
166
+
167
+ # Output gate
168
+ o_t = sigmoid(np.dot(concat, W_o) + b_o)
169
+
170
+ # New hidden state
171
+ h_t = o_t * np.tanh(c_t)
172
+
173
+ return h_t, c_t
174
+
175
+
176
+ def lstm_forward(
177
+ x: np.ndarray,
178
+ h_0: np.ndarray,
179
+ c_0: np.ndarray,
180
+ W_f: np.ndarray,
181
+ W_i: np.ndarray,
182
+ W_c: np.ndarray,
183
+ W_o: np.ndarray,
184
+ b_f: np.ndarray,
185
+ b_i: np.ndarray,
186
+ b_c: np.ndarray,
187
+ b_o: np.ndarray
188
+ ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
189
+ """
190
+ Forward pass through entire LSTM sequence
191
+
192
+ Args:
193
+ x: Input sequence, shape (batch, seq_len, input_size)
194
+ h_0: Initial hidden state, shape (batch, hidden_size)
195
+ c_0: Initial cell state, shape (batch, hidden_size)
196
+ W_f, W_i, W_c, W_o: Weight matrices
197
+ b_f, b_i, b_c, b_o: Bias vectors
198
+
199
+ Returns:
200
+ Tuple of (outputs, hidden_states, cell_states)
201
+ - outputs: shape (batch, seq_len, hidden_size)
202
+ - hidden_states: shape (batch, seq_len, hidden_size)
203
+ - cell_states: shape (batch, seq_len, hidden_size)
204
+
205
+ Example:
206
+ >>> x = np.random.randn(32, 10, 128)
207
+ >>> h_0 = np.zeros((32, 256))
208
+ >>> c_0 = np.zeros((32, 256))
209
+ >>> # Initialize weights...
210
+ >>> outputs, h_states, c_states = lstm_forward(x, h_0, c_0, W_f, W_i, W_c, W_o, b_f, b_i, b_c, b_o)
211
+ >>> print(outputs.shape) # (32, 10, 256)
212
+ """
213
+ batch_size, seq_len, input_size = x.shape
214
+ hidden_size = h_0.shape[1]
215
+
216
+ # Initialize outputs
217
+ outputs = np.zeros((batch_size, seq_len, hidden_size))
218
+ hidden_states = np.zeros((batch_size, seq_len, hidden_size))
219
+ cell_states = np.zeros((batch_size, seq_len, hidden_size))
220
+
221
+ h_t = h_0
222
+ c_t = c_0
223
+
224
+ # Process sequence
225
+ for t in range(seq_len):
226
+ h_t, c_t = lstm_cell_forward(
227
+ x[:, t, :], h_t, c_t,
228
+ W_f, W_i, W_c, W_o,
229
+ b_f, b_i, b_c, b_o
230
+ )
231
+ outputs[:, t, :] = h_t
232
+ hidden_states[:, t, :] = h_t
233
+ cell_states[:, t, :] = c_t
234
+
235
+ return outputs, hidden_states, cell_states
236
+
237
+
238
+ # ============================================================================
239
+ # GRU (GATED RECURRENT UNIT)
240
+ # ============================================================================
241
+
242
+ def gru_cell_forward(
243
+ x_t: np.ndarray,
244
+ h_prev: np.ndarray,
245
+ W_z: np.ndarray,
246
+ W_r: np.ndarray,
247
+ W_h: np.ndarray,
248
+ b_z: np.ndarray,
249
+ b_r: np.ndarray,
250
+ b_h: np.ndarray
251
+ ) -> np.ndarray:
252
+ """
253
+ Single timestep of GRU cell
254
+
255
+ GRU has two gates:
256
+ - Update gate: decides how much to update
257
+ - Reset gate: decides how much to forget
258
+
259
+ Args:
260
+ x_t: Input at timestep t, shape (batch, input_size)
261
+ h_prev: Previous hidden state, shape (batch, hidden_size)
262
+ W_z, W_r, W_h: Weight matrices for gates
263
+ b_z, b_r, b_h: Bias vectors for gates
264
+
265
+ Returns:
266
+ h_t: New hidden state, shape (batch, hidden_size)
267
+
268
+ Example:
269
+ >>> x_t = np.random.randn(32, 128)
270
+ >>> h_prev = np.random.randn(32, 256)
271
+ >>> # Initialize weights...
272
+ >>> h_t = gru_cell_forward(x_t, h_prev, W_z, W_r, W_h, b_z, b_r, b_h)
273
+ >>> print(h_t.shape) # (32, 256)
274
+ """
275
+ # Concatenate input and previous hidden state
276
+ concat = np.concatenate([h_prev, x_t], axis=1)
277
+
278
+ # Update gate
279
+ z_t = sigmoid(np.dot(concat, W_z) + b_z)
280
+
281
+ # Reset gate
282
+ r_t = sigmoid(np.dot(concat, W_r) + b_r)
283
+
284
+ # Candidate hidden state
285
+ concat_reset = np.concatenate([r_t * h_prev, x_t], axis=1)
286
+ h_tilde = np.tanh(np.dot(concat_reset, W_h) + b_h)
287
+
288
+ # New hidden state
289
+ h_t = (1 - z_t) * h_prev + z_t * h_tilde
290
+
291
+ return h_t
292
+
293
+
294
+ def gru_forward(
295
+ x: np.ndarray,
296
+ h_0: np.ndarray,
297
+ W_z: np.ndarray,
298
+ W_r: np.ndarray,
299
+ W_h: np.ndarray,
300
+ b_z: np.ndarray,
301
+ b_r: np.ndarray,
302
+ b_h: np.ndarray
303
+ ) -> Tuple[np.ndarray, np.ndarray]:
304
+ """
305
+ Forward pass through entire GRU sequence
306
+
307
+ Args:
308
+ x: Input sequence, shape (batch, seq_len, input_size)
309
+ h_0: Initial hidden state, shape (batch, hidden_size)
310
+ W_z, W_r, W_h: Weight matrices
311
+ b_z, b_r, b_h: Bias vectors
312
+
313
+ Returns:
314
+ Tuple of (outputs, hidden_states)
315
+ - outputs: shape (batch, seq_len, hidden_size)
316
+ - hidden_states: shape (batch, seq_len, hidden_size)
317
+
318
+ Example:
319
+ >>> x = np.random.randn(32, 10, 128)
320
+ >>> h_0 = np.zeros((32, 256))
321
+ >>> # Initialize weights...
322
+ >>> outputs, hidden_states = gru_forward(x, h_0, W_z, W_r, W_h, b_z, b_r, b_h)
323
+ >>> print(outputs.shape) # (32, 10, 256)
324
+ """
325
+ batch_size, seq_len, input_size = x.shape
326
+ hidden_size = h_0.shape[1]
327
+
328
+ # Initialize outputs
329
+ outputs = np.zeros((batch_size, seq_len, hidden_size))
330
+ hidden_states = np.zeros((batch_size, seq_len, hidden_size))
331
+
332
+ h_t = h_0
333
+
334
+ # Process sequence
335
+ for t in range(seq_len):
336
+ h_t = gru_cell_forward(
337
+ x[:, t, :], h_t,
338
+ W_z, W_r, W_h,
339
+ b_z, b_r, b_h
340
+ )
341
+ outputs[:, t, :] = h_t
342
+ hidden_states[:, t, :] = h_t
343
+
344
+ return outputs, hidden_states
345
+
346
+
347
+ # ============================================================================
348
+ # BIDIRECTIONAL RNN
349
+ # ============================================================================
350
+
351
+ def bidirectional_rnn_forward(
352
+ x: np.ndarray,
353
+ h_0_forward: np.ndarray,
354
+ h_0_backward: np.ndarray,
355
+ W_xh_f: np.ndarray,
356
+ W_hh_f: np.ndarray,
357
+ b_h_f: np.ndarray,
358
+ W_xh_b: np.ndarray,
359
+ W_hh_b: np.ndarray,
360
+ b_h_b: np.ndarray
361
+ ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
362
+ """
363
+ Bidirectional RNN forward pass
364
+
365
+ Processes sequence in both forward and backward directions.
366
+
367
+ Args:
368
+ x: Input sequence, shape (batch, seq_len, input_size)
369
+ h_0_forward: Initial forward hidden state
370
+ h_0_backward: Initial backward hidden state
371
+ W_xh_f, W_hh_f, b_h_f: Forward RNN parameters
372
+ W_xh_b, W_hh_b, b_h_b: Backward RNN parameters
373
+
374
+ Returns:
375
+ Tuple of (outputs, forward_states, backward_states)
376
+ - outputs: Concatenated forward and backward, shape (batch, seq_len, 2*hidden_size)
377
+
378
+ Example:
379
+ >>> x = np.random.randn(32, 10, 128)
380
+ >>> h_0_f = np.zeros((32, 256))
381
+ >>> h_0_b = np.zeros((32, 256))
382
+ >>> # Initialize weights...
383
+ >>> outputs, h_f, h_b = bidirectional_rnn_forward(x, h_0_f, h_0_b, W_xh_f, W_hh_f, b_h_f, W_xh_b, W_hh_b, b_h_b)
384
+ >>> print(outputs.shape) # (32, 10, 512)
385
+ """
386
+ # Forward pass
387
+ forward_outputs, forward_states = rnn_forward(
388
+ x, h_0_forward, W_xh_f, W_hh_f, b_h_f
389
+ )
390
+
391
+ # Backward pass (reverse sequence)
392
+ x_reversed = np.flip(x, axis=1)
393
+ backward_outputs, backward_states = rnn_forward(
394
+ x_reversed, h_0_backward, W_xh_b, W_hh_b, b_h_b
395
+ )
396
+ backward_outputs = np.flip(backward_outputs, axis=1)
397
+ backward_states = np.flip(backward_states, axis=1)
398
+
399
+ # Concatenate forward and backward
400
+ outputs = np.concatenate([forward_outputs, backward_outputs], axis=2)
401
+
402
+ return outputs, forward_states, backward_states
403
+
404
+
405
+ # ============================================================================
406
+ # UTILITY FUNCTIONS
407
+ # ============================================================================
408
+
409
+ def sigmoid(x: np.ndarray) -> np.ndarray:
410
+ """
411
+ Sigmoid activation function
412
+
413
+ Args:
414
+ x: Input array
415
+
416
+ Returns:
417
+ Sigmoid of input
418
+ """
419
+ return 1 / (1 + np.exp(-np.clip(x, -500, 500)))
420
+
421
+
422
+ def initialize_rnn_weights(
423
+ input_size: int,
424
+ hidden_size: int,
425
+ cell_type: str = 'rnn'
426
+ ) -> dict:
427
+ """
428
+ Initialize RNN weights
429
+
430
+ Args:
431
+ input_size: Size of input features
432
+ hidden_size: Size of hidden state
433
+ cell_type: Type of RNN cell ('rnn', 'lstm', 'gru')
434
+
435
+ Returns:
436
+ Dictionary of initialized weights
437
+
438
+ Example:
439
+ >>> weights = initialize_rnn_weights(128, 256, cell_type='lstm')
440
+ >>> print(weights.keys())
441
+ """
442
+ weights = {}
443
+
444
+ if cell_type == 'rnn':
445
+ weights['W_xh'] = np.random.randn(input_size, hidden_size) * 0.01
446
+ weights['W_hh'] = np.random.randn(hidden_size, hidden_size) * 0.01
447
+ weights['b_h'] = np.zeros(hidden_size)
448
+
449
+ elif cell_type == 'lstm':
450
+ concat_size = hidden_size + input_size
451
+ weights['W_f'] = np.random.randn(concat_size, hidden_size) * 0.01
452
+ weights['W_i'] = np.random.randn(concat_size, hidden_size) * 0.01
453
+ weights['W_c'] = np.random.randn(concat_size, hidden_size) * 0.01
454
+ weights['W_o'] = np.random.randn(concat_size, hidden_size) * 0.01
455
+ weights['b_f'] = np.zeros(hidden_size)
456
+ weights['b_i'] = np.zeros(hidden_size)
457
+ weights['b_c'] = np.zeros(hidden_size)
458
+ weights['b_o'] = np.zeros(hidden_size)
459
+
460
+ elif cell_type == 'gru':
461
+ concat_size = hidden_size + input_size
462
+ weights['W_z'] = np.random.randn(concat_size, hidden_size) * 0.01
463
+ weights['W_r'] = np.random.randn(concat_size, hidden_size) * 0.01
464
+ weights['W_h'] = np.random.randn(concat_size, hidden_size) * 0.01
465
+ weights['b_z'] = np.zeros(hidden_size)
466
+ weights['b_r'] = np.zeros(hidden_size)
467
+ weights['b_h'] = np.zeros(hidden_size)
468
+
469
+ return weights
470
+
471
+
472
+ def clip_gradients(gradients: np.ndarray, max_norm: float = 5.0) -> np.ndarray:
473
+ """
474
+ Clip gradients to prevent exploding gradients
475
+
476
+ Args:
477
+ gradients: Gradient array
478
+ max_norm: Maximum gradient norm
479
+
480
+ Returns:
481
+ Clipped gradients
482
+
483
+ Example:
484
+ >>> grads = np.random.randn(100, 100) * 10
485
+ >>> clipped = clip_gradients(grads, max_norm=5.0)
486
+ >>> print(np.linalg.norm(clipped)) # <= 5.0
487
+ """
488
+ norm = np.linalg.norm(gradients)
489
+ if norm > max_norm:
490
+ gradients = gradients * (max_norm / norm)
491
+ return gradients
492
+
493
+
494
+ # Aliases for convenience
495
+ vanilla_rnn = rnn_forward
496
+ lstm = lstm_forward
497
+ gru = gru_forward
498
+ bidirectional_rnn = bidirectional_rnn_forward
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ilovetools
3
- Version: 0.2.19
3
+ Version: 0.2.20
4
4
  Summary: A comprehensive Python utility library with modular tools for AI/ML, data processing, and daily programming needs
5
5
  Home-page: https://github.com/AliMehdi512/ilovetools
6
6
  Author: Ali Mehdi
@@ -11,7 +11,7 @@ Project-URL: Repository, https://github.com/AliMehdi512/ilovetools
11
11
  Project-URL: Issues, https://github.com/AliMehdi512/ilovetools/issues
12
12
  Project-URL: Bug Reports, https://github.com/AliMehdi512/ilovetools/issues
13
13
  Project-URL: Source, https://github.com/AliMehdi512/ilovetools
14
- Keywords: utilities,tools,ai,ml,data-processing,automation,cnn,convolutional-neural-networks,conv2d,pooling,computer-vision,image-processing
14
+ Keywords: utilities,tools,ai,ml,data-processing,automation,rnn,lstm,gru,recurrent-neural-networks,sequence-modeling,nlp
15
15
  Classifier: Development Status :: 3 - Alpha
16
16
  Classifier: Intended Audience :: Developers
17
17
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
@@ -49,6 +49,7 @@ ilovetools/ml/normalization.py
49
49
  ilovetools/ml/optimizers.py
50
50
  ilovetools/ml/pipeline.py
51
51
  ilovetools/ml/regularization.py
52
+ ilovetools/ml/rnn.py
52
53
  ilovetools/ml/timeseries.py
53
54
  ilovetools/ml/tuning.py
54
55
  ilovetools/security/__init__.py
@@ -73,4 +74,5 @@ tests/test_loss_functions.py
73
74
  tests/test_neural_network.py
74
75
  tests/test_normalization.py
75
76
  tests/test_optimizers.py
76
- tests/test_regularization.py
77
+ tests/test_regularization.py
78
+ tests/test_rnn.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ilovetools"
7
- version = "0.2.19"
7
+ version = "0.2.20"
8
8
  description = "A comprehensive Python utility library with modular tools for AI/ML, data processing, and daily programming needs"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -12,7 +12,7 @@ license = "MIT"
12
12
  authors = [
13
13
  {name = "Ali Mehdi", email = "ali.mehdi.dev579@gmail.com"}
14
14
  ]
15
- keywords = ["utilities", "tools", "ai", "ml", "data-processing", "automation", "cnn", "convolutional-neural-networks", "conv2d", "pooling", "computer-vision", "image-processing"]
15
+ keywords = ["utilities", "tools", "ai", "ml", "data-processing", "automation", "rnn", "lstm", "gru", "recurrent-neural-networks", "sequence-modeling", "nlp"]
16
16
  classifiers = [
17
17
  "Development Status :: 3 - Alpha",
18
18
  "Intended Audience :: Developers",
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setup(
7
7
  name="ilovetools",
8
- version="0.2.19",
8
+ version="0.2.20",
9
9
  author="Ali Mehdi",
10
10
  author_email="ali.mehdi.dev579@gmail.com",
11
11
  description="A comprehensive Python utility library with modular tools for AI/ML, data processing, and daily programming needs",
@@ -57,7 +57,7 @@ setup(
57
57
  "soundfile>=0.12.0",
58
58
  ],
59
59
  },
60
- keywords="utilities, tools, ai, ml, data-processing, automation, python-library, neural-networks, cnn, convolutional-neural-networks, conv2d, pooling, computer-vision, image-processing",
60
+ keywords="utilities, tools, ai, ml, data-processing, automation, python-library, neural-networks, rnn, lstm, gru, recurrent-neural-networks, sequence-modeling, nlp",
61
61
  project_urls={
62
62
  "Bug Reports": "https://github.com/AliMehdi512/ilovetools/issues",
63
63
  "Source": "https://github.com/AliMehdi512/ilovetools",
@@ -0,0 +1,419 @@
1
+ """
2
+ Tests for RNN operations module
3
+ """
4
+
5
+ import numpy as np
6
+ import sys
7
+ import os
8
+
9
+ # Add parent directory to path
10
+ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
11
+
12
+ from ilovetools.ml.rnn import (
13
+ # Basic RNN
14
+ rnn_cell_forward,
15
+ rnn_forward,
16
+ # LSTM
17
+ lstm_cell_forward,
18
+ lstm_forward,
19
+ # GRU
20
+ gru_cell_forward,
21
+ gru_forward,
22
+ # Bidirectional
23
+ bidirectional_rnn_forward,
24
+ # Utilities
25
+ sigmoid,
26
+ initialize_rnn_weights,
27
+ clip_gradients,
28
+ # Aliases
29
+ vanilla_rnn,
30
+ lstm,
31
+ gru,
32
+ bidirectional_rnn,
33
+ )
34
+
35
+
36
+ def test_rnn_cell_forward():
37
+ """Test basic RNN cell forward pass"""
38
+ print("Testing rnn_cell_forward...")
39
+
40
+ batch_size = 32
41
+ input_size = 128
42
+ hidden_size = 256
43
+
44
+ x_t = np.random.randn(batch_size, input_size)
45
+ h_prev = np.random.randn(batch_size, hidden_size)
46
+ W_xh = np.random.randn(input_size, hidden_size) * 0.01
47
+ W_hh = np.random.randn(hidden_size, hidden_size) * 0.01
48
+ b_h = np.zeros(hidden_size)
49
+
50
+ h_t = rnn_cell_forward(x_t, h_prev, W_xh, W_hh, b_h)
51
+
52
+ assert h_t.shape == (batch_size, hidden_size), "Output shape incorrect"
53
+ assert np.all(np.abs(h_t) <= 1.0), "tanh output should be in [-1, 1]"
54
+
55
+ print("✓ rnn_cell_forward passed")
56
+
57
+
58
+ def test_rnn_forward():
59
+ """Test basic RNN forward pass"""
60
+ print("Testing rnn_forward...")
61
+
62
+ batch_size = 32
63
+ seq_len = 10
64
+ input_size = 128
65
+ hidden_size = 256
66
+
67
+ x = np.random.randn(batch_size, seq_len, input_size)
68
+ h_0 = np.zeros((batch_size, hidden_size))
69
+ W_xh = np.random.randn(input_size, hidden_size) * 0.01
70
+ W_hh = np.random.randn(hidden_size, hidden_size) * 0.01
71
+ b_h = np.zeros(hidden_size)
72
+
73
+ outputs, hidden_states = rnn_forward(x, h_0, W_xh, W_hh, b_h)
74
+
75
+ assert outputs.shape == (batch_size, seq_len, hidden_size), "Outputs shape incorrect"
76
+ assert hidden_states.shape == (batch_size, seq_len, hidden_size), "Hidden states shape incorrect"
77
+
78
+ print("✓ rnn_forward passed")
79
+
80
+
81
+ def test_lstm_cell_forward():
82
+ """Test LSTM cell forward pass"""
83
+ print("Testing lstm_cell_forward...")
84
+
85
+ batch_size = 32
86
+ input_size = 128
87
+ hidden_size = 256
88
+
89
+ x_t = np.random.randn(batch_size, input_size)
90
+ h_prev = np.random.randn(batch_size, hidden_size)
91
+ c_prev = np.random.randn(batch_size, hidden_size)
92
+
93
+ # Initialize weights
94
+ concat_size = hidden_size + input_size
95
+ W_f = np.random.randn(concat_size, hidden_size) * 0.01
96
+ W_i = np.random.randn(concat_size, hidden_size) * 0.01
97
+ W_c = np.random.randn(concat_size, hidden_size) * 0.01
98
+ W_o = np.random.randn(concat_size, hidden_size) * 0.01
99
+ b_f = np.zeros(hidden_size)
100
+ b_i = np.zeros(hidden_size)
101
+ b_c = np.zeros(hidden_size)
102
+ b_o = np.zeros(hidden_size)
103
+
104
+ h_t, c_t = lstm_cell_forward(x_t, h_prev, c_prev, W_f, W_i, W_c, W_o, b_f, b_i, b_c, b_o)
105
+
106
+ assert h_t.shape == (batch_size, hidden_size), "Hidden state shape incorrect"
107
+ assert c_t.shape == (batch_size, hidden_size), "Cell state shape incorrect"
108
+
109
+ print("✓ lstm_cell_forward passed")
110
+
111
+
112
+ def test_lstm_forward():
113
+ """Test LSTM forward pass"""
114
+ print("Testing lstm_forward...")
115
+
116
+ batch_size = 32
117
+ seq_len = 10
118
+ input_size = 128
119
+ hidden_size = 256
120
+
121
+ x = np.random.randn(batch_size, seq_len, input_size)
122
+ h_0 = np.zeros((batch_size, hidden_size))
123
+ c_0 = np.zeros((batch_size, hidden_size))
124
+
125
+ # Initialize weights
126
+ concat_size = hidden_size + input_size
127
+ W_f = np.random.randn(concat_size, hidden_size) * 0.01
128
+ W_i = np.random.randn(concat_size, hidden_size) * 0.01
129
+ W_c = np.random.randn(concat_size, hidden_size) * 0.01
130
+ W_o = np.random.randn(concat_size, hidden_size) * 0.01
131
+ b_f = np.zeros(hidden_size)
132
+ b_i = np.zeros(hidden_size)
133
+ b_c = np.zeros(hidden_size)
134
+ b_o = np.zeros(hidden_size)
135
+
136
+ outputs, hidden_states, cell_states = lstm_forward(
137
+ x, h_0, c_0, W_f, W_i, W_c, W_o, b_f, b_i, b_c, b_o
138
+ )
139
+
140
+ assert outputs.shape == (batch_size, seq_len, hidden_size), "Outputs shape incorrect"
141
+ assert hidden_states.shape == (batch_size, seq_len, hidden_size), "Hidden states shape incorrect"
142
+ assert cell_states.shape == (batch_size, seq_len, hidden_size), "Cell states shape incorrect"
143
+
144
+ print("✓ lstm_forward passed")
145
+
146
+
147
+ def test_gru_cell_forward():
148
+ """Test GRU cell forward pass"""
149
+ print("Testing gru_cell_forward...")
150
+
151
+ batch_size = 32
152
+ input_size = 128
153
+ hidden_size = 256
154
+
155
+ x_t = np.random.randn(batch_size, input_size)
156
+ h_prev = np.random.randn(batch_size, hidden_size)
157
+
158
+ # Initialize weights
159
+ concat_size = hidden_size + input_size
160
+ W_z = np.random.randn(concat_size, hidden_size) * 0.01
161
+ W_r = np.random.randn(concat_size, hidden_size) * 0.01
162
+ W_h = np.random.randn(concat_size, hidden_size) * 0.01
163
+ b_z = np.zeros(hidden_size)
164
+ b_r = np.zeros(hidden_size)
165
+ b_h = np.zeros(hidden_size)
166
+
167
+ h_t = gru_cell_forward(x_t, h_prev, W_z, W_r, W_h, b_z, b_r, b_h)
168
+
169
+ assert h_t.shape == (batch_size, hidden_size), "Hidden state shape incorrect"
170
+
171
+ print("✓ gru_cell_forward passed")
172
+
173
+
174
+ def test_gru_forward():
175
+ """Test GRU forward pass"""
176
+ print("Testing gru_forward...")
177
+
178
+ batch_size = 32
179
+ seq_len = 10
180
+ input_size = 128
181
+ hidden_size = 256
182
+
183
+ x = np.random.randn(batch_size, seq_len, input_size)
184
+ h_0 = np.zeros((batch_size, hidden_size))
185
+
186
+ # Initialize weights
187
+ concat_size = hidden_size + input_size
188
+ W_z = np.random.randn(concat_size, hidden_size) * 0.01
189
+ W_r = np.random.randn(concat_size, hidden_size) * 0.01
190
+ W_h = np.random.randn(concat_size, hidden_size) * 0.01
191
+ b_z = np.zeros(hidden_size)
192
+ b_r = np.zeros(hidden_size)
193
+ b_h = np.zeros(hidden_size)
194
+
195
+ outputs, hidden_states = gru_forward(x, h_0, W_z, W_r, W_h, b_z, b_r, b_h)
196
+
197
+ assert outputs.shape == (batch_size, seq_len, hidden_size), "Outputs shape incorrect"
198
+ assert hidden_states.shape == (batch_size, seq_len, hidden_size), "Hidden states shape incorrect"
199
+
200
+ print("✓ gru_forward passed")
201
+
202
+
203
+ def test_bidirectional_rnn_forward():
204
+ """Test bidirectional RNN forward pass"""
205
+ print("Testing bidirectional_rnn_forward...")
206
+
207
+ batch_size = 32
208
+ seq_len = 10
209
+ input_size = 128
210
+ hidden_size = 256
211
+
212
+ x = np.random.randn(batch_size, seq_len, input_size)
213
+ h_0_f = np.zeros((batch_size, hidden_size))
214
+ h_0_b = np.zeros((batch_size, hidden_size))
215
+
216
+ # Initialize weights for forward and backward
217
+ W_xh_f = np.random.randn(input_size, hidden_size) * 0.01
218
+ W_hh_f = np.random.randn(hidden_size, hidden_size) * 0.01
219
+ b_h_f = np.zeros(hidden_size)
220
+
221
+ W_xh_b = np.random.randn(input_size, hidden_size) * 0.01
222
+ W_hh_b = np.random.randn(hidden_size, hidden_size) * 0.01
223
+ b_h_b = np.zeros(hidden_size)
224
+
225
+ outputs, forward_states, backward_states = bidirectional_rnn_forward(
226
+ x, h_0_f, h_0_b, W_xh_f, W_hh_f, b_h_f, W_xh_b, W_hh_b, b_h_b
227
+ )
228
+
229
+ # Output should be concatenation of forward and backward (2 * hidden_size)
230
+ assert outputs.shape == (batch_size, seq_len, 2 * hidden_size), "Outputs shape incorrect"
231
+ assert forward_states.shape == (batch_size, seq_len, hidden_size), "Forward states shape incorrect"
232
+ assert backward_states.shape == (batch_size, seq_len, hidden_size), "Backward states shape incorrect"
233
+
234
+ print("✓ bidirectional_rnn_forward passed")
235
+
236
+
237
+ def test_sigmoid():
238
+ """Test sigmoid function"""
239
+ print("Testing sigmoid...")
240
+
241
+ x = np.array([-1000, -1, 0, 1, 1000])
242
+ result = sigmoid(x)
243
+
244
+ assert np.all(result >= 0) and np.all(result <= 1), "Sigmoid should be in [0, 1]"
245
+ assert np.isclose(result[2], 0.5), "Sigmoid(0) should be 0.5"
246
+
247
+ print("✓ sigmoid passed")
248
+
249
+
250
+ def test_initialize_rnn_weights():
251
+ """Test RNN weight initialization"""
252
+ print("Testing initialize_rnn_weights...")
253
+
254
+ input_size = 128
255
+ hidden_size = 256
256
+
257
+ # Test RNN weights
258
+ weights_rnn = initialize_rnn_weights(input_size, hidden_size, cell_type='rnn')
259
+ assert 'W_xh' in weights_rnn, "RNN weights should have W_xh"
260
+ assert 'W_hh' in weights_rnn, "RNN weights should have W_hh"
261
+ assert 'b_h' in weights_rnn, "RNN weights should have b_h"
262
+
263
+ # Test LSTM weights
264
+ weights_lstm = initialize_rnn_weights(input_size, hidden_size, cell_type='lstm')
265
+ assert 'W_f' in weights_lstm, "LSTM weights should have W_f"
266
+ assert 'W_i' in weights_lstm, "LSTM weights should have W_i"
267
+ assert 'W_c' in weights_lstm, "LSTM weights should have W_c"
268
+ assert 'W_o' in weights_lstm, "LSTM weights should have W_o"
269
+
270
+ # Test GRU weights
271
+ weights_gru = initialize_rnn_weights(input_size, hidden_size, cell_type='gru')
272
+ assert 'W_z' in weights_gru, "GRU weights should have W_z"
273
+ assert 'W_r' in weights_gru, "GRU weights should have W_r"
274
+ assert 'W_h' in weights_gru, "GRU weights should have W_h"
275
+
276
+ print("✓ initialize_rnn_weights passed")
277
+
278
+
279
+ def test_clip_gradients():
280
+ """Test gradient clipping"""
281
+ print("Testing clip_gradients...")
282
+
283
+ # Create large gradients
284
+ grads = np.random.randn(100, 100) * 10
285
+
286
+ # Clip to max norm 5.0
287
+ clipped = clip_gradients(grads, max_norm=5.0)
288
+
289
+ norm = np.linalg.norm(clipped)
290
+ assert norm <= 5.0, "Clipped gradient norm should be <= max_norm"
291
+
292
+ # Test with small gradients (should not clip)
293
+ small_grads = np.random.randn(10, 10) * 0.1
294
+ clipped_small = clip_gradients(small_grads, max_norm=5.0)
295
+ assert np.allclose(small_grads, clipped_small), "Small gradients should not be clipped"
296
+
297
+ print("✓ clip_gradients passed")
298
+
299
+
300
+ def test_aliases():
301
+ """Test function aliases"""
302
+ print("Testing aliases...")
303
+
304
+ batch_size = 8
305
+ seq_len = 5
306
+ input_size = 64
307
+ hidden_size = 128
308
+
309
+ x = np.random.randn(batch_size, seq_len, input_size)
310
+ h_0 = np.zeros((batch_size, hidden_size))
311
+
312
+ # Test vanilla_rnn alias
313
+ W_xh = np.random.randn(input_size, hidden_size) * 0.01
314
+ W_hh = np.random.randn(hidden_size, hidden_size) * 0.01
315
+ b_h = np.zeros(hidden_size)
316
+
317
+ out1, h1 = vanilla_rnn(x, h_0, W_xh, W_hh, b_h)
318
+ out2, h2 = rnn_forward(x, h_0, W_xh, W_hh, b_h)
319
+ assert np.allclose(out1, out2), "vanilla_rnn alias should work"
320
+
321
+ print("✓ aliases passed")
322
+
323
+
324
+ def test_rnn_sequence_processing():
325
+ """Test that RNN processes sequences correctly"""
326
+ print("Testing RNN sequence processing...")
327
+
328
+ batch_size = 4
329
+ seq_len = 5
330
+ input_size = 32
331
+ hidden_size = 64
332
+
333
+ x = np.random.randn(batch_size, seq_len, input_size)
334
+ h_0 = np.zeros((batch_size, hidden_size))
335
+ W_xh = np.random.randn(input_size, hidden_size) * 0.01
336
+ W_hh = np.random.randn(hidden_size, hidden_size) * 0.01
337
+ b_h = np.zeros(hidden_size)
338
+
339
+ outputs, hidden_states = rnn_forward(x, h_0, W_xh, W_hh, b_h)
340
+
341
+ # Check that each timestep depends on previous
342
+ # (outputs should be different at each timestep)
343
+ assert not np.allclose(outputs[:, 0, :], outputs[:, 1, :]), "Outputs should differ across timesteps"
344
+
345
+ print("✓ RNN sequence processing passed")
346
+
347
+
348
+ def test_lstm_gates():
349
+ """Test that LSTM gates work correctly"""
350
+ print("Testing LSTM gates...")
351
+
352
+ batch_size = 4
353
+ input_size = 32
354
+ hidden_size = 64
355
+
356
+ x_t = np.random.randn(batch_size, input_size)
357
+ h_prev = np.random.randn(batch_size, hidden_size)
358
+ c_prev = np.random.randn(batch_size, hidden_size)
359
+
360
+ # Initialize weights
361
+ concat_size = hidden_size + input_size
362
+ W_f = np.random.randn(concat_size, hidden_size) * 0.01
363
+ W_i = np.random.randn(concat_size, hidden_size) * 0.01
364
+ W_c = np.random.randn(concat_size, hidden_size) * 0.01
365
+ W_o = np.random.randn(concat_size, hidden_size) * 0.01
366
+ b_f = np.zeros(hidden_size)
367
+ b_i = np.zeros(hidden_size)
368
+ b_c = np.zeros(hidden_size)
369
+ b_o = np.zeros(hidden_size)
370
+
371
+ h_t, c_t = lstm_cell_forward(x_t, h_prev, c_prev, W_f, W_i, W_c, W_o, b_f, b_i, b_c, b_o)
372
+
373
+ # Cell state should be updated
374
+ assert not np.allclose(c_t, c_prev), "Cell state should be updated"
375
+
376
+ # Hidden state should be different from previous
377
+ assert not np.allclose(h_t, h_prev), "Hidden state should be updated"
378
+
379
+ print("✓ LSTM gates passed")
380
+
381
+
382
+ def run_all_tests():
383
+ """Run all tests"""
384
+ print("\n" + "="*60)
385
+ print("RNN OPERATIONS MODULE TESTS")
386
+ print("="*60 + "\n")
387
+
388
+ # Basic RNN tests
389
+ test_rnn_cell_forward()
390
+ test_rnn_forward()
391
+ test_rnn_sequence_processing()
392
+
393
+ # LSTM tests
394
+ test_lstm_cell_forward()
395
+ test_lstm_forward()
396
+ test_lstm_gates()
397
+
398
+ # GRU tests
399
+ test_gru_cell_forward()
400
+ test_gru_forward()
401
+
402
+ # Bidirectional tests
403
+ test_bidirectional_rnn_forward()
404
+
405
+ # Utility tests
406
+ test_sigmoid()
407
+ test_initialize_rnn_weights()
408
+ test_clip_gradients()
409
+
410
+ # Aliases
411
+ test_aliases()
412
+
413
+ print("\n" + "="*60)
414
+ print("ALL TESTS PASSED! ✓")
415
+ print("="*60 + "\n")
416
+
417
+
418
+ if __name__ == "__main__":
419
+ run_all_tests()
File without changes
File without changes
File without changes
File without changes