grx-tensor 0.1.0 → 0.2.1

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.
data/README.md CHANGED
@@ -2,470 +2,1213 @@
2
2
 
3
3
  **Ruby speaks. C computes.**
4
4
 
5
- A tensor framework for Ruby with automatic differentiation, a C+SIMD compute core, and neural network primitives — all behind a clean, expressive Ruby API.
5
+ A high-performance scientific computing, multidimensional tensor, and deep learning framework for Ruby. Features automatic differentiation (Autograd), dynamic multi-target SIMD hardware acceleration (AVX2+FMA, SSE, and portable scalar C), and complete neural network primitives behind a clean, expressive Ruby API.
6
6
 
7
7
  [![Ruby](https://img.shields.io/badge/ruby-%3E%3D%203.0-CC342D?logo=ruby)](https://www.ruby-lang.org)
8
8
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE.txt)
9
- [![Platform](https://img.shields.io/badge/platform-Linux%20%7C%20macOS%20%7C%20Windows-lightgrey)]()
9
+ [![Platform](https://img.shields.io/badge/platform-Linux%20%7C%20macOS%20%7C%20Windows-lightgrey)](https://github.com/Gabo-Razo/grx-tensor)
10
10
 
11
11
  ---
12
12
 
13
- ## What is GRX?
13
+ ## Architectural Flow
14
+
15
+ ```mermaid
16
+ flowchart TD
17
+ subgraph UserApp["User Application"]
18
+ A["require 'grx'"] --> B["GRX.tensor(data, shape)"]
19
+ B --> C["Scientific Computing / Machine Learning Models"]
20
+ C --> D["Training Loops, Simulations & Production Inference"]
21
+ end
22
+
23
+ subgraph RubyLayer["Ruby High-Level Engine"]
24
+ E["GRX::Tensor (Multidimensional Shape, Strides, DAG Nodes)"]
25
+ F["GRX::NN (Linear, Embedding, LayerNorm, Dropout, BatchNorm1d)"]
26
+ G["GRX::Loss (CrossEntropy, MSE, BCE, Huber, MAE)"]
27
+ H["GRX::Optim (Adam with FMA, SGD with Momentum)"]
28
+ I["GRX::Data (TensorDataset, DataLoader)"]
29
+ J["GRX::Serialization (High-Speed .grx Binary Engine)"]
30
+ end
31
+
32
+ subgraph FFISubsystem["FFI Bridge & Memory Management (Fiddle)"]
33
+ K["GRX::Storage (Aligned Pointer Buffers / GC Finalizer)"]
34
+ L["GRX::CAPI (Dynamic Multi-Library Symbol Dispatch)"]
35
+ end
36
+
37
+ subgraph NativeCore["Native C Core (ext/grx/grx_core.c)"]
38
+ M{"grx_simd_level() Hardware Detection"}
39
+ N["AVX2 + FMA Engine (4 doubles/cycle, Fused Multiply-Add)"]
40
+ O["SSE Engine (2 doubles/cycle, Vectorized Math)"]
41
+ P["Universal Scalar C (Portable IEEE 754 Math)"]
42
+ end
43
+
44
+ subgraph Hardware["Hardware Layer"]
45
+ Q["32-Byte Aligned Heap Memory"]
46
+ R["L1/L2 Cache Tiling Engine (64-Byte Cache Lines)"]
47
+ end
48
+
49
+ UserApp --> RubyLayer
50
+ RubyLayer --> FFISubsystem
51
+ FFISubsystem --> NativeCore
52
+ M -- "AVX2+FMA CPU" --> N
53
+ M -- "SSE2/SSE4 CPU" --> O
54
+ M -- "Generic / ARM / VM" --> P
55
+ N --> Hardware
56
+ O --> Hardware
57
+ P --> Hardware
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Universal Build & Dynamic Hardware Dispatch
63
+
64
+ ```mermaid
65
+ flowchart TD
66
+ A["gem install grx-tensor"] --> B["RubyGems Package Manager"]
67
+ B --> C["ext/grx/extconf.rb (mkmf)"]
14
68
 
15
- GRX is a tensor computation library for Ruby. The numeric core is written in C and compiled with **AVX2 + FMA** SIMD instructions — processing 4 doubles per CPU cycle with fused multiply-add. Ruby handles the high-level API: shape validation, computation graph construction, and orchestration. C handles everything else.
69
+ C --> D1["Linux (GCC / Clang)"]
70
+ C --> D2["macOS (Clang / Apple LLVM)"]
71
+ C --> D3["Windows (RubyInstaller DevKit / MinGW-w64)"]
16
72
 
17
- ### Key features
73
+ D1 --> E1["libgrx_core.so"]
74
+ D2 --> E2["libgrx_core.dylib"]
75
+ D3 --> E3["grx_core.dll"]
18
76
 
19
- | Feature | Details |
77
+ E1 --> F["Universal Multi-Target Binary"]
78
+ E2 --> F
79
+ E3 --> F
80
+
81
+ F --> G{"Host CPU Feature Detection at Runtime"}
82
+ G -- "AVX2 + FMA Detected" --> H["Runs AVX2+FMA SIMD (Max Speed)"]
83
+ G -- "SSE2 Detected" --> I["Runs SSE Vectorized Kernels"]
84
+ G -- "ARM / VM / Legacy CPU" --> J["Runs Portable Scalar C (Zero Crashes)"]
85
+ ```
86
+
87
+ ---
88
+
89
+ ## Table of Contents
90
+
91
+ 1. [Key Features](#key-features)
92
+ 2. [Installation](#installation)
93
+ 3. [Beginner-Friendly Quickstart Tutorials](#beginner-friendly-quickstart-tutorials)
94
+ - [1. Celsius to Fahrenheit Converter](#1-celsius-to-fahrenheit-converter-1-neuron-learns-f--18c--32)
95
+ - [2. Logic Gates (AND & XOR)](#2-logic-gates-and-linear-vs-non-linear-xor-with-relu--sigmoid)
96
+ - [3. Everyday Tensor Math](#3-everyday-tensor-math-in-4-lines)
97
+ 4. [Tensor API Reference & Core Operations](#tensor-api-reference--core-operations)
98
+ 5. [Autograd & Gradient Engine](#autograd--gradient-engine)
99
+ 6. [Scientific & Numerical Computing (Beyond Deep Learning)](#scientific--numerical-computing-beyond-deep-learning)
100
+ - [A. Computer Vision & Image Filtering (Sobel Kernel Convolution)](#a-computer-vision--image-filtering-sobel-kernel-convolution)
101
+ - [B. Quantitative Finance & Portfolio Covariance](#b-quantitative-finance--portfolio-covariance)
102
+ - [C. Particle Physics & N-Body Simulation](#c-particle-physics--n-body-simulation)
103
+ - [D. Pure Mathematical Optimization with Autograd (Rosenbrock Function)](#d-pure-mathematical-optimization-with-autograd-rosenbrock-function)
104
+ 7. [Deep Learning Cookbook (10 Neural Network Architectures)](#deep-learning-cookbook-10-neural-network-architectures)
105
+ - [Architecture 1: Deep Vision & Character Classifier (BatchNorm + Dropout)](#architecture-1-deep-vision--character-classifier-batchnorm--dropout)
106
+ - [Architecture 2: NLP & Intent Chatbot (Embedding + LayerNorm)](#architecture-2-nlp--intent-chatbot-embedding--layernorm)
107
+ - [Architecture 3: Reinforcement Learning (Deep Q-Network Agent)](#architecture-3-reinforcement-learning-deep-q-network-agent)
108
+ - [Architecture 4: Nonlinear Multi-Variable Time Series Forecasting](#architecture-4-nonlinear-multi-variable-time-series-forecasting)
109
+ - [Architecture 5: Deep Autoencoder for Dimensionality Reduction & Anomaly Detection](#architecture-5-deep-autoencoder-for-dimensionality-reduction--anomaly-detection)
110
+ - [Architecture 6: Autoregressive Next-Token Character Language Model & Generator](#architecture-6-autoregressive-next-token-character-language-model--generator)
111
+ - [Architecture 7: Sentiment Analysis & Review Classification (BCELoss)](#architecture-7-sentiment-analysis--review-classification-bceloss)
112
+ - [Architecture 8: Siamese Neural Network for Similarity & Verification](#architecture-8-siamese-neural-network-for-similarity--verification)
113
+ - [Architecture 9: Deep Residual Network (ResNet MLP Block with Skip Connection)](#architecture-9-deep-residual-network-resnet-mlp-block-with-skip-connection)
114
+ - [Architecture 10: Neural Collaborative Filtering & Recommendation System](#architecture-10-neural-collaborative-filtering--recommendation-system)
115
+ 8. [Loss Functions (GRX::Loss)](#loss-functions-grxloss)
116
+ 9. [Scientific Notation & Hyperparameter Reference](#scientific-notation--hyperparameter-reference)
117
+ - [1. Scientific Notation in Machine Learning (1e-1 to 1e-8)](#1-scientific-notation-in-machine-learning-1e-1-to-1e-8)
118
+ - [2. Neural Layer Parameters (GRX::NN)](#2-neural-layer-parameters-grxnn)
119
+ 10. [Optimizers & Hyperparameter Reference (GRX::Optim)](#optimizers--hyperparameter-reference-grxoptim)
120
+ 11. [Data Pipelines & DataLoader (GRX::Data)](#data-pipelines--dataloader-grxdata)
121
+ 12. [Model Persistence & Brain Serialization (.grx Format)](#model-persistence--brain-serialization-grx-format)
122
+ - [Binary Format Layout (GRX1 Specification)](#binary-format-layout-grx1-specification)
123
+ - [Production Inference Deployment Workflow](#production-inference-deployment-workflow)
124
+ 13. [Gradient Management & Utilities (GRX::Utils)](#gradient-management--utilities-grxutils)
125
+ 14. [Windows Support & Toolchain Guide](#windows-support--toolchain-guide)
126
+ 15. [License](#license)
127
+
128
+ ---
129
+
130
+ ## Key Features
131
+
132
+ | Feature | Specification |
20
133
  |---|---|
21
- | **C+SIMD kernel** | AVX2+FMA, SSE2 fallback, scalar fallback auto-detected at compile time |
22
- | **Autograd** | Automatic differentiation via a topological computation graph |
23
- | **Optimizers** | SGD (momentum, weight decay) and Adam (inner loop in C with FMA) |
24
- | **NN layers** | Linear, Sequential, Dropout, BatchNorm1d |
25
- | **Activations** | ReLU, Leaky ReLU, Tanh, Sigmoid, Softmax |
26
- | **Loss functions** | MSE, MAE, BCE, CrossEntropy, Huber |
27
- | **Weight init** | Xavier uniform, He normal (Box-Muller in C) |
28
- | **Cross-platform** | `.so` on Linux, `.dylib` on macOS, `.dll` on Windows |
29
- | **Pure Ruby fallback** | Works without compilation slower but always correct |
134
+ | **Multi-Target SIMD Engine** | Dynamic runtime CPU detection routes ops to AVX2+FMA, SSE, or portable scalar C |
135
+ | **Aligned Heap Memory** | 32-byte aligned native heap buffers (`posix_memalign` / `_aligned_malloc`) |
136
+ | **Zero-Copy Views** | Strided geometric transformations (`reshape`, `transpose`, `flatten`, `t`) |
137
+ | **Autograd Engine** | Reverse-mode automatic differentiation with dynamic DAG backpropagation |
138
+ | **Neural Layers** | `Linear`, `Sequential`, `Embedding`, `LayerNorm`, `BatchNorm1d`, `Dropout` |
139
+ | **Activation Functions** | `ReLU`, `LeakyReLU`, `Sigmoid`, `Tanh`, `Softmax` (fully differentiable) |
140
+ | **Loss Functions** | `MSELoss`, `MAELoss`, `BCELoss`, `CrossEntropyLoss`, `HuberLoss` |
141
+ | **Optimizers** | `Adam` (with FMA acceleration and bias correction), `SGD` (with momentum and weight decay) |
142
+ | **Binary Persistence** | Fast `.grx` binary serialization format for instant weight saving and loading |
143
+ | **Data Pipelines** | `TensorDataset` and `DataLoader` with batching, slicing, and shuffling |
144
+ | **Weight Initializers** | Xavier uniform and He normal (xorshift64* and Box-Muller in C) |
145
+ | **Cross-Platform** | Linux (`.so`), macOS (`.dylib`), Windows (`.dll` via DevKit) |
146
+ | **Pure Ruby Fallback** | Runs seamlessly in pure Ruby mode when native compiler tools are unavailable |
147
+
148
+ ---
30
149
 
31
150
  ---
32
151
 
33
152
  ## Installation
34
153
 
154
+ ### Standard RubyGems Installation
155
+
35
156
  ```bash
36
157
  gem install grx-tensor
37
158
  ```
38
159
 
160
+ Or add it to your project's `Gemfile`:
161
+
39
162
  ```ruby
40
- # Gemfile
41
163
  gem "grx-tensor"
42
164
  ```
43
165
 
44
- The C extension compiles automatically on `gem install`. No extra steps needed.
166
+ The native C extension is automatically compiled and linked in the background during installation.
45
167
 
46
168
  ---
47
169
 
48
- ## Quick start
170
+ ## Beginner-Friendly Quickstart Tutorials
171
+
172
+ ### 1. Celsius to Fahrenheit Converter (1 Neuron Learns $F = 1.8C + 32$)
173
+
174
+ The absolute "Hello World" of Machine Learning. A single linear neuron ($y = w \cdot x + b$) discovers the physical laws of temperature on its own:
49
175
 
50
176
  ```ruby
51
177
  require "grx"
52
178
 
53
- a = GRX.tensor([1.0, 2.0, 3.0], [3], requires_grad: true)
54
- b = GRX.tensor([4.0, 5.0, 6.0], [3], requires_grad: true)
179
+ # 1. Training data (Celsius and Fahrenheit pairs)
180
+ celsius = GRX.tensor([18.0, 25.0, 14.0, 21.0, 9.0, 16.0, 4.0, 32.0], [8, 1])
181
+ fahrenheit = GRX.tensor([64.4, 77.0, 57.2, 69.8, 48.2, 60.8, 39.2, 89.6], [8, 1])
55
182
 
56
- c = a + b # [5.0, 7.0, 9.0] — computed in C with AVX2
57
- c.backward # propagates gradients through the graph
183
+ # 2. Build 1-neuron model
184
+ model = GRX::NN::Sequential.new(GRX::NN::Linear.new(1, 1))
185
+ optimizer = GRX::Optim::Adam.new(model.parameters, lr: 0.8)
186
+ loss_fn = GRX::Loss::MSELoss.new
58
187
 
59
- a.grad.to_a # [1.0, 1.0, 1.0]
60
- b.grad.to_a # [1.0, 1.0, 1.0]
188
+ # 3. Train in 5 lines
189
+ 1500.times do
190
+ optimizer.zero_grad
191
+ prediction = model.call(celsius)
192
+ loss = loss_fn.call(prediction, fahrenheit)
193
+ loss.backward
194
+ optimizer.step
195
+ end
196
+
197
+ # 4. Predict unseen temperatures
198
+ test_temps = GRX.tensor([[100.0], [0.0], [37.0]], [3, 1])
199
+ predictions = model.call(test_temps).to_a
200
+
201
+ puts "100.0 C -> #{predictions[0].round(1)} F (Expected: 212.0 F)"
202
+ puts " 0.0 C -> #{predictions[1].round(1)} F (Expected: 32.0 F)"
203
+ puts " 37.0 C -> #{predictions[2].round(1)} F (Expected: 98.6 F)"
61
204
  ```
62
205
 
63
206
  ---
64
207
 
65
- ## Tensors
208
+ ### 2. Logic Gates (AND Linear vs Non-Linear XOR with ReLU & Sigmoid)
209
+
210
+ Solving the classic non-linear XOR problem using a 2-layer neural network:
66
211
 
67
212
  ```ruby
68
- # From array + shape
69
- t = GRX.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [2, 3])
70
- t.shape # [2, 3]
71
- t.numel # 6
72
- t.to_a # [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
73
- t.item # only for single-element tensors → Float
213
+ require "grx"
214
+
215
+ # XOR Truth Table: (0,0)->0, (0,1)->1, (1,0)->1, (1,1)->0
216
+ x = GRX.tensor([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]], [4, 2])
217
+ y = GRX.tensor([[0.0], [1.0], [1.0], [0.0]], [4, 1])
218
+
219
+ # Multi-layer network: 2 inputs -> 4 hidden (ReLU) -> 1 output (Sigmoid)
220
+ xor_net = GRX::NN::Sequential.new(
221
+ GRX::NN::Linear.new(2, 4),
222
+ GRX::NN::ReLU.new,
223
+ GRX::NN::Linear.new(4, 1),
224
+ GRX::NN::Sigmoid.new
225
+ )
226
+
227
+ opt = GRX::Optim::Adam.new(xor_net.parameters, lr: 0.05)
228
+ loss_fn = GRX::Loss::BCELoss.new
229
+
230
+ 400.times do
231
+ opt.zero_grad
232
+ pred = xor_net.call(x)
233
+ loss = loss_fn.call(pred, y)
234
+ loss.backward
235
+ opt.step
236
+ end
74
237
 
75
- # Factories
76
- GRX.zeros([3]) # [0.0, 0.0, 0.0]
77
- GRX.ones([2, 2]) # [1.0, 1.0, 1.0, 1.0]
78
- GRX.rand([4]) # uniform [0, 1)
79
- GRX.randn([4]) # normal N(0, 1)
238
+ puts "XOR Predictions: #{xor_net.call(x).to_a.map { |v| v.round(3) }}"
239
+ ```
80
240
 
81
- GRX::Tensor.zeros_like(t) # same shape, all zeros
82
- GRX::Tensor.ones_like(t) # same shape, all ones
241
+ ---
242
+
243
+ ### 3. Everyday Tensor Math in 4 Lines
244
+
245
+ ```ruby
246
+ require "grx"
247
+
248
+ prices = GRX.tensor([19.99, 45.50, 120.00, 5.25], [4])
249
+ discounted = (prices * 0.85).round(2) rescue (prices * 0.85) # 15% discount applied
250
+
251
+ puts "Total revenue: #{prices.sum.item.round(2)}"
252
+ puts "Average price: #{prices.mean.item.round(2)}"
253
+ puts "Most expensive item index: #{prices.argmax}"
83
254
  ```
84
255
 
85
256
  ---
86
257
 
87
- ## Arithmetic
258
+ ## Tensor API Reference & Core Operations
259
+
260
+ Tensors in GRX represent multidimensional arrays of 64-bit IEEE 754 floating-point numbers stored in contiguous native memory buffers.
261
+
262
+ ### 1. Creation & Factory Methods
263
+
264
+ ```ruby
265
+ require "grx"
266
+
267
+ # 1. From flat or nested Ruby arrays (integers and floats)
268
+ t1 = GRX.tensor([1.0, 2.0, 3.0, 4.0], [2, 2])
269
+ t2 = GRX.tensor([[1.0, 2.0], [3.0, 4.0]], [2, 2], requires_grad: true)
88
270
 
89
- All operations run in C. Scalar operands are supported on both sides.
271
+ # 2. Zeros and Ones
272
+ zeros = GRX.zeros([3, 4])
273
+ ones = GRX.ones([2, 5], requires_grad: true)
274
+
275
+ # 3. Random initialization
276
+ uniform_rand = GRX.rand([4, 4]) # Uniform distribution U[0, 1)
277
+ normal_rand = GRX.randn([4, 4]) # Standard normal distribution N(0, 1)
278
+
279
+ # 4. Weight initialization factories
280
+ xavier = GRX::Tensor.xavier_uniform([64, 32], requires_grad: true)
281
+ he = GRX::Tensor.he_normal([64, 32], requires_grad: true)
282
+
283
+ # 5. Like factories
284
+ z_like = GRX::Tensor.zeros_like(t1)
285
+ o_like = GRX::Tensor.ones_like(t1)
286
+ ```
287
+
288
+ ### 2. Inspection & Element Access
90
289
 
91
290
  ```ruby
92
- a = GRX.tensor([1.0, 2.0, 3.0, 4.0], [4])
93
- b = GRX.tensor([4.0, 3.0, 2.0, 1.0], [4])
291
+ t = GRX.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [2, 3])
292
+
293
+ puts t.shape # [2, 3] (Dimensions)
294
+ puts t.strides # [3, 1] (Memory strides)
295
+ puts t.numel # 6 (Total elements count)
296
+ puts t.rank # 2 (Number of dimensions)
297
+ puts t.item # Returns scalar float if numel == 1
298
+ puts t.to_a # [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
299
+ puts t.get(1, 2) # 6.0 (Element at row 1, col 2)
300
+ t.set(1, 2, 9.9) # Updates element at (1, 2)
301
+ ```
94
302
 
95
- (a + b).to_a # [5.0, 5.0, 5.0, 5.0]
96
- (a - b).to_a # [-3.0, -1.0, 1.0, 3.0]
97
- (a * b).to_a # [4.0, 6.0, 6.0, 4.0]
98
- (a / b).to_a # [0.25, 0.666, 1.5, 4.0]
99
- (-a).to_a # [-1.0, -2.0, -3.0, -4.0]
303
+ ### 3. Arithmetic Operations (SIMD Accelerated)
100
304
 
101
- # Tensor OP scalar
102
- (a + 10.0).to_a # [11.0, 12.0, 13.0, 14.0]
103
- (a * 3.0).to_a # [3.0, 6.0, 9.0, 12.0]
104
- (a / 2.0).to_a # [0.5, 1.0, 1.5, 2.0]
105
- (a - 1.0).to_a # [0.0, 1.0, 2.0, 3.0]
305
+ ```ruby
306
+ a = GRX.tensor([1.0, 2.0, 3.0], [3])
307
+ b = GRX.tensor([4.0, 5.0, 6.0], [3])
308
+
309
+ c_add = a + b # [5.0, 7.0, 9.0]
310
+ c_sub = a - b # [-3.0, -3.0, -3.0]
311
+ c_mul = a * b # [4.0, 10.0, 18.0]
312
+ c_div = b / a # [4.0, 2.5, 2.0]
313
+ c_neg = -a # [-1.0, -2.0, -3.0]
314
+
315
+ # Scalar operations
316
+ s_add = a + 10.0 # [11.0, 12.0, 13.0]
317
+ s_mul = a * 2.0 # [2.0, 4.0, 6.0]
318
+ s_div = a / 2.0 # [0.5, 1.0, 1.5]
319
+ ```
320
+
321
+ ### 4. Element-Wise Math Functions
322
+
323
+ ```ruby
324
+ t = GRX.tensor([1.0, 4.0, 9.0, 16.0], [4])
325
+
326
+ puts t.sqrt.to_a # [1.0, 2.0, 3.0, 4.0]
327
+ puts t.square.to_a # [1.0, 16.0, 81.0, 256.0]
328
+ puts t.abs.to_a # [1.0, 4.0, 9.0, 16.0]
329
+ puts t.pow(3.0).to_a # [1.0, 64.0, 729.0, 4096.0]
330
+ puts t.log.to_a # Natural logarithm ln(x)
331
+ puts t.exp.to_a # Exponential e^x
332
+ puts t.clip(2.0, 10.0) # Clamps values to [2.0, 10.0]
333
+ ```
334
+
335
+ ### 5. Matrix Multiplication & Linear Algebra
336
+
337
+ ```ruby
338
+ # Matrix multiplication (Cache Tiling SIMD)
339
+ m1 = GRX.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [2, 3])
340
+ m2 = GRX.tensor([7.0, 8.0, 9.0, 10.0, 11.0, 12.0], [3, 2])
341
+
342
+ result = m1 @ m2 # Equivalent to m1.matmul(m2), returns Shape [2, 2]
343
+
344
+ # Dot product (1D vectors)
345
+ v1 = GRX.tensor([1.0, 2.0, 3.0], [3])
346
+ v2 = GRX.tensor([4.0, 5.0, 6.0], [3])
347
+ dot_val = v1.dot(v2) # 32.0 (Double precision scalar)
348
+ ```
349
+
350
+ ### 6. Geometric Transformations (Zero-Copy Views)
351
+
352
+ ```ruby
353
+ matrix = GRX.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [2, 3])
354
+
355
+ # Reshape & Flatten
356
+ reshaped = matrix.reshape([3, 2]) # Shape [3, 2]
357
+ flat = matrix.flatten # Shape [6]
358
+
359
+ # Transpose
360
+ transposed = matrix.transpose(0, 1) # Shape [3, 2]
361
+ t_view = matrix.t # Shorthand 2D transpose
362
+ ```
363
+
364
+ ### 7. Reductions & Statistics
365
+
366
+ ```ruby
367
+ t = GRX.tensor([2.0, 4.0, 6.0, 8.0], [4], requires_grad: true)
368
+
369
+ s = t.sum # Scalar tensor [20.0], autograd node
370
+ m = t.mean # Scalar tensor [5.0], autograd node
371
+ max_val = t.max # 8.0 (Scalar float)
372
+ min_val = t.min # 2.0 (Scalar float)
373
+ best_idx = t.argmax # 3 (Index of maximum value)
374
+ worst_idx = t.argmin # 0 (Index of minimum value)
375
+ ```
376
+
377
+ ---
378
+
379
+ ## Autograd & Gradient Engine
380
+
381
+ GRX features a dynamic Directed Acyclic Graph (DAG) reverse-mode automatic differentiation engine. Calling `backward` propagates derivatives according to the chain rule.
382
+
383
+ ```mermaid
384
+ flowchart LR
385
+ A["Tensor a (requires_grad: true)"] --> C["Multiplication (a * b)"]
386
+ B["Tensor b (requires_grad: true)"] --> C
387
+ C --> D["Addition (+ 2.0)"]
388
+ D --> E["Reduction (.sum)"]
389
+ E --> F["Scalar Loss"]
390
+ F -. "loss.backward" .-> E
391
+ E -. "dLoss/dD" .-> D
392
+ D -. "dLoss/dC" .-> C
393
+ C -. "a.grad = dLoss/da" .-> A
394
+ C -. "b.grad = dLoss/db" .-> B
395
+ ```
396
+
397
+ ```ruby
398
+ require "grx"
399
+
400
+ x = GRX.tensor([2.0, 3.0], [2], requires_grad: true)
401
+ w = GRX.tensor([4.0, 5.0], [2], requires_grad: true)
402
+ b = GRX.tensor([1.0, 1.0], [2], requires_grad: true)
403
+
404
+ y = (x * w) + b
405
+ loss = y.sum
406
+ loss.backward
407
+
408
+ puts "x.grad: #{x.grad.to_a}" # [4.0, 5.0]
409
+ puts "w.grad: #{w.grad.to_a}" # [2.0, 3.0]
410
+ puts "b.grad: #{b.grad.to_a}" # [1.0, 1.0]
106
411
  ```
107
412
 
108
413
  ---
109
414
 
110
- ## Math operations
415
+ ## Scientific & Numerical Computing (Beyond Deep Learning)
416
+
417
+ Tensors are general-purpose multidimensional mathematical engines suitable for image processing, numerical physics, quantitative finance, and pure mathematical optimization.
418
+
419
+ ### A. Computer Vision & Image Filtering (Sobel Kernel Convolution)
420
+
421
+ Apply spatial filtering and edge detection directly on 2D image pixel grids:
111
422
 
112
423
  ```ruby
113
- x = GRX.tensor([1.0, 4.0, 9.0, 16.0], [4])
424
+ require "grx"
114
425
 
115
- x.sqrt.to_a # [1.0, 2.0, 3.0, 4.0]
116
- x.square.to_a # [1.0, 16.0, 81.0, 256.0]
117
- x.abs.to_a # absolute value element-wise
118
- x.log.to_a # natural logarithm
119
- x.exp.to_a # e^x
120
- x.pow(3).to_a # [1.0, 64.0, 729.0, 4096.0]
121
- x.clip(2.0, 10.0).to_a # [2.0, 4.0, 9.0, 10.0]
426
+ # 1. Create a 6x6 grayscale synthetic image tensor
427
+ image = GRX.tensor([
428
+ 0.0, 0.0, 0.0, 255.0, 255.0, 255.0,
429
+ 0.0, 0.0, 0.0, 255.0, 255.0, 255.0,
430
+ 0.0, 0.0, 0.0, 255.0, 255.0, 255.0,
431
+ 0.0, 0.0, 0.0, 255.0, 255.0, 255.0,
432
+ 0.0, 0.0, 0.0, 255.0, 255.0, 255.0,
433
+ 0.0, 0.0, 0.0, 255.0, 255.0, 255.0
434
+ ], [6, 6])
435
+
436
+ # 2. Define horizontal Sobel edge detection kernel (3x3)
437
+ sobel_h = GRX.tensor([
438
+ -1.0, 0.0, 1.0,
439
+ -2.0, 0.0, 2.0,
440
+ -1.0, 0.0, 1.0
441
+ ], [3, 3])
442
+
443
+ # 3. 2D Spatial Convolution
444
+ out_rows = image.shape[0] - sobel_h.shape[0] + 1
445
+ out_cols = image.shape[1] - sobel_h.shape[1] + 1
446
+ edge_map_data = []
447
+
448
+ out_rows.times do |r|
449
+ out_cols.times do |c|
450
+ patch_values = []
451
+ 3.times do |kr|
452
+ 3.times do |kc|
453
+ patch_values << image.get(r + kr, c + kc)
454
+ end
455
+ end
456
+ patch_tensor = GRX.tensor(patch_values, [3, 3])
457
+ conv_val = (patch_tensor * sobel_h).sum.item
458
+ edge_map_data << conv_val.abs
459
+ end
460
+ end
122
461
 
123
- # Reductions Float
124
- x.sum # 30.0
125
- x.mean # 7.5
126
- x.max # 16.0
127
- x.min # 1.0
462
+ edge_map = GRX.tensor(edge_map_data, [out_rows, out_cols])
463
+ puts "Detected Edges Shape: #{edge_map.shape}"
128
464
  ```
129
465
 
130
466
  ---
131
467
 
132
- ## Linear algebra
468
+ ### B. Quantitative Finance & Portfolio Covariance
469
+
470
+ Calculate asset returns, annualized volatility, and portfolio risk matrix:
133
471
 
134
472
  ```ruby
135
- u = GRX.tensor([1.0, 2.0, 3.0], [3])
136
- v = GRX.tensor([4.0, 5.0, 6.0], [3])
473
+ require "grx"
137
474
 
138
- u.dot(v) # 32.0 → 1×4 + 2×5 + 3×6
475
+ prices = GRX.tensor([
476
+ 100.0, 50.0, 200.0,
477
+ 102.0, 49.0, 205.0,
478
+ 101.0, 51.0, 210.0,
479
+ 105.0, 52.0, 208.0,
480
+ 108.0, 53.0, 215.0
481
+ ], [5, 3])
482
+
483
+ # 1. Compute Daily Simple Returns: (P_t - P_{t-1}) / P_{t-1}
484
+ returns_data = []
485
+ 4.times do |t|
486
+ 3.times do |asset|
487
+ p_prev = prices.get(t, asset)
488
+ p_curr = prices.get(t + 1, asset)
489
+ returns_data << ((p_curr - p_prev) / p_prev)
490
+ end
491
+ end
492
+ returns = GRX.tensor(returns_data, [4, 3])
493
+
494
+ # 2. Mean centering
495
+ mean_returns = Array.new(3) do |asset|
496
+ asset_col = 4.times.map { |d| returns.get(d, asset) }
497
+ asset_col.sum / 4.0
498
+ end
139
499
 
140
- # Matrix multiplication — tiled for cache efficiency
141
- a = GRX.tensor([1.0, 2.0, 3.0, 4.0], [2, 2])
142
- b = GRX.tensor([5.0, 6.0, 7.0, 8.0], [2, 2])
143
- a.matmul(b).to_a # [19.0, 22.0, 43.0, 50.0]
500
+ centered_data = []
501
+ 4.times do |d|
502
+ 3.times do |asset|
503
+ centered_data << (returns.get(d, asset) - mean_returns[asset])
504
+ end
505
+ end
506
+ centered_returns = GRX.tensor(centered_data, [4, 3])
144
507
 
145
- # Non-square: [2×3] × [3×2] [2×2]
146
- a3 = GRX.tensor([1.0,2.0,3.0, 4.0,5.0,6.0], [2, 3])
147
- b3 = GRX.tensor([7.0,8.0, 9.0,10.0, 11.0,12.0], [3, 2])
148
- a3.matmul(b3).to_a # [58.0, 64.0, 139.0, 154.0]
508
+ # 3. Covariance Matrix: Sigma = (X^T @ X) / (N - 1)
509
+ cov_matrix = (centered_returns.t @ centered_returns) / 3.0
510
+
511
+ # 4. Portfolio Variance for weights w = [0.4, 0.3, 0.3]
512
+ weights = GRX.tensor([[0.4, 0.3, 0.3]], [1, 3])
513
+ port_variance = (weights @ cov_matrix @ weights.t).item
514
+ port_volatility = Math.sqrt(port_variance)
515
+
516
+ puts "Portfolio Daily Volatility: #{(port_volatility * 100).round(4)}%"
149
517
  ```
150
518
 
151
519
  ---
152
520
 
153
- ## Zero-copy geometry
521
+ ### C. Particle Physics & N-Body Simulation
154
522
 
155
- `reshape` and `transpose` return views over the same memory — no data is copied.
523
+ Simulate particle positions, velocities, and pairwise Euclidean distances in 3D:
156
524
 
157
525
  ```ruby
158
- m = GRX.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [2, 3])
526
+ require "grx"
159
527
 
160
- m.get(1, 2) # 6.0
161
- m.reshape([3, 2]) # new view, same data
162
- m.flatten # shape [6], same data
163
- m.transpose # shape [3, 2], same data
528
+ n_particles = 4
529
+ dt = 0.01
530
+
531
+ positions = GRX.tensor([
532
+ 0.0, 0.0, 0.0,
533
+ 1.0, 0.0, 0.0,
534
+ 0.0, 1.0, 0.0,
535
+ 0.0, 0.0, 1.0
536
+ ], [n_particles, 3])
537
+
538
+ velocities = GRX.tensor([
539
+ 0.1, 0.0, 0.0,
540
+ 0.0, 0.2, 0.0,
541
+ 0.0, 0.0, 0.1,
542
+ -0.1, 0.0, 0.0
543
+ ], [n_particles, 3])
544
+
545
+ gravity = GRX.tensor(Array.new(n_particles * 3) { |i| (i % 3 == 1) ? -9.81 : 0.0 }, [n_particles, 3])
546
+
547
+ velocities = velocities + (gravity * dt)
548
+ positions = positions + (velocities * dt)
549
+
550
+ dist_matrix_data = []
551
+ n_particles.times do |i|
552
+ n_particles.times do |j|
553
+ dx = positions.get(i, 0) - positions.get(j, 0)
554
+ dy = positions.get(i, 1) - positions.get(j, 1)
555
+ dz = positions.get(i, 2) - positions.get(j, 2)
556
+ dist_matrix_data << Math.sqrt(dx*dx + dy*dy + dz*dz)
557
+ end
558
+ end
164
559
 
165
- # Transpose is a true view
166
- sq = GRX.tensor([1.0, 2.0, 3.0, 4.0], [2, 2])
167
- tr = sq.transpose
168
- tr.get(0, 1) # 3.0 (was sq[1, 0])
169
- tr.get(1, 0) # 2.0 (was sq[0, 1])
170
- tr.to_a # [1.0, 3.0, 2.0, 4.0]
560
+ dist_matrix = GRX.tensor(dist_matrix_data, [n_particles, n_particles])
561
+ puts "Pairwise Distances (P0 to P1): #{dist_matrix.get(0, 1).round(4)}"
171
562
  ```
172
563
 
173
564
  ---
174
565
 
175
- ## Activations
566
+ ### D. Pure Mathematical Optimization with Autograd (Rosenbrock Function)
567
+
568
+ Find the global minimum of the non-convex Rosenbrock Banana Function:
569
+ $$f(x, y) = (a - x)^2 + b(y - x^2)^2 \quad \text{with } a=1, b=100$$
176
570
 
177
571
  ```ruby
178
- x = GRX.tensor([-3.0, -1.0, 0.0, 1.0, 3.0], [5])
572
+ require "grx"
573
+
574
+ point = GRX.tensor([-1.5, 2.0], [2], requires_grad: true)
575
+ lr = 0.002
576
+
577
+ 500.times do |step|
578
+ x = point.get(0)
579
+ y = point.get(1)
580
+
581
+ tx = GRX.tensor([x], [1], requires_grad: true)
582
+ ty = GRX.tensor([y], [1], requires_grad: true)
179
583
 
180
- x.relu.to_a # [0.0, 0.0, 0.0, 1.0, 3.0]
181
- x.leaky_relu(0.1).to_a # [-0.3, -0.1, 0.0, 1.0, 3.0]
182
- x.sigmoid.to_a # [0.047, 0.268, 0.5, 0.731, 0.952]
183
- x.tanh.to_a # [-0.995, -0.761, 0.0, 0.761, 0.995]
584
+ term1 = (GRX.tensor([1.0], [1]) - tx).square
585
+ term2 = (ty - tx.square).square * 100.0
586
+ loss = term1 + term2
587
+ loss.backward
184
588
 
185
- GRX.tensor([1.0, 2.0, 3.0, 4.0], [4]).softmax.to_a
186
- # [0.032, 0.087, 0.236, 0.643] — always sums to 1.0
589
+ new_x = x - lr * tx.grad.item
590
+ new_y = y - lr * ty.grad.item
591
+ point = GRX.tensor([new_x, new_y], [2])
592
+ end
593
+
594
+ puts "Converged Minimum: x = #{point.get(0).round(3)}, y = #{point.get(1).round(3)}"
187
595
  ```
188
596
 
189
597
  ---
190
598
 
191
- ## Autograd
599
+ ## Deep Learning Cookbook (10 Neural Network Architectures)
192
600
 
193
- Every operation builds a computation graph automatically. Call `.backward` to propagate gradients back through the graph.
601
+ ### Architecture 1: Deep Vision & Character Classifier (BatchNorm + Dropout)
194
602
 
195
603
  ```ruby
196
- # --- Simple gradient ---
197
- a = GRX.tensor([2.0, 3.0], [2], requires_grad: true)
198
- b = GRX.tensor([4.0, 5.0], [2], requires_grad: true)
604
+ require "grx"
199
605
 
200
- c = a + b
201
- c.backward
606
+ vision_net = GRX::NN::Sequential.new(
607
+ GRX::NN::Linear.new(784, 256),
608
+ GRX::NN::BatchNorm1d.new(256),
609
+ GRX::NN::ReLU.new,
610
+ GRX::NN::Dropout.new(0.25),
611
+ GRX::NN::Linear.new(256, 64),
612
+ GRX::NN::LayerNorm.new(64),
613
+ GRX::NN::LeakyReLU.new(alpha: 0.01),
614
+ GRX::NN::Linear.new(64, 10),
615
+ GRX::NN::Softmax.new
616
+ )
202
617
 
203
- a.grad.to_a # [1.0, 1.0] — d(a+b)/da = 1
204
- b.grad.to_a # [1.0, 1.0] — d(a+b)/db = 1
618
+ batch_images = GRX.randn([16, 784])
619
+ predictions = vision_net.forward(batch_images) # Shape [16, 10]
620
+ puts "Vision Predictions Shape: #{predictions.shape}"
621
+ ```
205
622
 
206
- # --- Chained operations ---
207
- x = GRX.tensor([1.0, 2.0], [2], requires_grad: true)
208
- y = GRX.tensor([3.0, 4.0], [2], requires_grad: true)
623
+ ---
209
624
 
210
- z = (x + y) * y # z = xy +
211
- z.backward
625
+ ### Architecture 2: NLP & Intent Chatbot (Embedding + LayerNorm)
212
626
 
213
- x.grad.to_a # [3.0, 4.0] — dz/dx = y
214
- y.grad.to_a # [7.0, 10.0] — dz/dy = x + 2y
627
+ ```ruby
628
+ require "grx"
629
+
630
+ vocab_size = 500
631
+ embedding_dim = 32
632
+ num_classes = 4
633
+
634
+ embedding = GRX::NN::Embedding.new(vocab_size, embedding_dim)
635
+ classifier = GRX::NN::Sequential.new(
636
+ GRX::NN::LayerNorm.new(embedding_dim),
637
+ GRX::NN::Linear.new(embedding_dim, 16),
638
+ GRX::NN::Tanh.new,
639
+ GRX::NN::Linear.new(16, num_classes)
640
+ )
641
+
642
+ token_ids = GRX.tensor([14, 2, 88, 412], [4])
643
+ dense_words = embedding.forward(token_ids)
644
+
645
+ seq_mean = Array.new(embedding_dim) do |d|
646
+ 4.times.sum { |t| dense_words.get(t, d) } / 4.0
647
+ end
648
+ sentence_vector = GRX.tensor(seq_mean, [1, embedding_dim])
215
649
 
216
- # Reset gradients before next step
217
- x.zero_grad!
218
- y.zero_grad!
650
+ logits = classifier.forward(sentence_vector)
651
+ puts "Predicted Intent Class: #{logits.argmax}"
219
652
  ```
220
653
 
221
- **Operations with autograd support:**
222
- `+` `-` `*` `/` `negate` `scale` `square` `sqrt` `log` `exp` `pow`
223
- `relu` `leaky_relu` `tanh` `sigmoid` `matmul` `transpose`
654
+ ---
655
+
656
+ ### Architecture 3: Reinforcement Learning (Deep Q-Network Agent)
657
+
658
+ ```ruby
659
+ require "grx"
660
+
661
+ state_dim = 8
662
+ action_dim = 4
663
+
664
+ q_net = GRX::NN::Sequential.new(
665
+ GRX::NN::Linear.new(state_dim, 64),
666
+ GRX::NN::ReLU.new,
667
+ GRX::NN::Linear.new(64, 64),
668
+ GRX::NN::ReLU.new,
669
+ GRX::NN::Linear.new(64, action_dim)
670
+ )
671
+
672
+ optimizer = GRX::Optim::Adam.new(q_net.parameters, lr: 0.001)
673
+ huber_loss = GRX::Loss::HuberLoss.new(delta: 1.0)
674
+
675
+ current_state = GRX.randn([1, state_dim])
676
+ target_q_vals = GRX.tensor([[1.2, 0.5, -0.8, 3.4]], [1, 4])
677
+
678
+ optimizer.zero_grad
679
+ predicted_q_vals = q_net.forward(current_state)
680
+ loss = huber_loss.call(predicted_q_vals, target_q_vals)
681
+ loss.backward
682
+ optimizer.step
683
+
684
+ puts "Q-Loss: #{loss.item.round(6)}"
685
+ ```
224
686
 
225
687
  ---
226
688
 
227
- ## Neural networks
689
+ ### Architecture 4: Nonlinear Multi-Variable Time Series Forecasting
228
690
 
229
691
  ```ruby
230
- # Build a network with Sequential
231
- net = GRX::NN::Sequential.new(
232
- GRX::NN::Linear.new(4, 64),
692
+ require "grx"
693
+
694
+ forecaster = GRX::NN::Sequential.new(
695
+ GRX::NN::Linear.new(5, 32),
696
+ GRX::NN::Sigmoid.new,
697
+ GRX::NN::Linear.new(32, 16),
233
698
  GRX::NN::ReLU.new,
234
- GRX::NN::Linear.new(64, 32),
235
- GRX::NN::Tanh.new,
236
- GRX::NN::Linear.new(32, 1),
699
+ GRX::NN::Linear.new(16, 1)
700
+ )
701
+
702
+ optimizer = GRX::Optim::Adam.new(forecaster.parameters, lr: 0.01, weight_decay: 1e-4)
703
+ criterion = GRX::Loss::MSELoss.new
704
+
705
+ sensor_input = GRX.tensor([[22.5, 60.1, 1013.2, 5.4, 0.8]], [1, 5])
706
+ expected_temp = GRX.tensor([[23.1]], [1, 1])
707
+
708
+ optimizer.zero_grad
709
+ pred = forecaster.forward(sensor_input)
710
+ loss = criterion.call(pred, expected_temp)
711
+ loss.backward
712
+ optimizer.step
713
+
714
+ puts "Forecast Error: #{loss.item.round(6)}"
715
+ ```
716
+
717
+ ---
718
+
719
+ ### Architecture 5: Deep Autoencoder for Dimensionality Reduction & Anomaly Detection
720
+
721
+ Compresses high-dimensional feature vectors into a compressed latent bottleneck and reconstructs them:
722
+
723
+ ```ruby
724
+ require "grx"
725
+
726
+ # 1. Encoder network: 64 inputs -> 16 hidden -> 4 latent code
727
+ encoder = GRX::NN::Sequential.new(
728
+ GRX::NN::Linear.new(64, 16),
729
+ GRX::NN::LayerNorm.new(16),
730
+ GRX::NN::ReLU.new,
731
+ GRX::NN::Linear.new(16, 4)
732
+ )
733
+
734
+ # 2. Decoder network: 4 latent code -> 16 hidden -> 64 reconstructed outputs
735
+ decoder = GRX::NN::Sequential.new(
736
+ GRX::NN::Linear.new(4, 16),
737
+ GRX::NN::LayerNorm.new(16),
738
+ GRX::NN::ReLU.new,
739
+ GRX::NN::Linear.new(16, 64),
237
740
  GRX::NN::Sigmoid.new
238
741
  )
239
742
 
240
- puts net
241
- # Sequential(
242
- # (0): Linear(4 → 64, bias: true)
243
- # (1): ReLU()
244
- # (2): Linear(64 → 32, bias: true)
245
- # (3): Tanh()
246
- # (4): Linear(32 → 1, bias: true)
247
- # (5): Sigmoid()
248
- # )
743
+ optimizer = GRX::Optim::Adam.new(encoder.parameters + decoder.parameters, lr: 0.01)
744
+ recon_loss_fn = GRX::Loss::MSELoss.new
249
745
 
250
- # Forward pass — batch of 8 samples, 4 features each
251
- x = GRX.randn([8, 4])
252
- pred = net.call(x) # shape [8, 1]
746
+ # Training reconstruction loop
747
+ sample_batch = GRX.rand([8, 64])
253
748
 
254
- # Access all trainable parameters
255
- params = net.parameters # Array of Tensors with requires_grad: true
256
- params.size # 6 (3 weights + 3 biases)
749
+ optimizer.zero_grad
750
+ latent_code = encoder.forward(sample_batch) # Shape [8, 4]
751
+ reconstruction = decoder.forward(latent_code) # Shape [8, 64]
752
+ loss = recon_loss_fn.call(reconstruction, sample_batch)
753
+ loss.backward
754
+ optimizer.step
755
+
756
+ puts "Autoencoder Reconstruction Loss: #{loss.item.round(6)}"
257
757
  ```
258
758
 
259
759
  ---
260
760
 
261
- ## Training loop
761
+ ### Architecture 6: Autoregressive Next-Token Character Language Model & Generator
762
+
763
+ Generates text character-by-character using embedding lookups and dense prediction heads:
262
764
 
263
765
  ```ruby
264
766
  require "grx"
265
767
 
266
- # --- Dataset: learn y = 2x + 1 ---
267
- train_x = GRX.tensor((1..8).map(&:to_f), [8, 1])
268
- train_y = GRX.tensor((1..8).map { |x| 2.0 * x + 1.0 }, [8, 1])
768
+ vocab_size = 256 # ASCII alphabet
769
+ embed_dim = 16
770
+ ctx_len = 4 # 4-character context window
269
771
 
270
- # --- Network ---
271
- net = GRX::NN::Sequential.new(
272
- GRX::NN::Linear.new(1, 8),
772
+ char_embedding = GRX::NN::Embedding.new(vocab_size, embed_dim)
773
+ language_head = GRX::NN::Sequential.new(
774
+ GRX::NN::Linear.new(embed_dim * ctx_len, 64),
775
+ GRX::NN::LayerNorm.new(64),
273
776
  GRX::NN::Tanh.new,
274
- GRX::NN::Linear.new(8, 1)
777
+ GRX::NN::Linear.new(64, vocab_size)
275
778
  )
276
779
 
277
- opt = GRX::Optim::Adam.new(net.parameters, lr: 0.05)
278
- loss_fn = GRX::Loss::MSELoss.new
780
+ # Predict next character for context "hell" -> tokens [104, 101, 108, 108]
781
+ context_ids = GRX.tensor([104, 101, 108, 108], [4])
782
+ embedded = char_embedding.forward(context_ids) # Shape [4, 16]
783
+ flattened_context = embedded.flatten.reshape([1, embed_dim * ctx_len])
279
784
 
280
- 300.times do |epoch|
281
- opt.zero_grad
785
+ logits = language_head.forward(flattened_context) # Shape [1, 256]
786
+ predicted_char_ascii = logits.argmax
282
787
 
283
- pred = net.call(train_x)
284
- loss_val = loss_fn.call(pred, train_y)
788
+ puts "Context: 'hell' -> Predicted Next Char: '#{predicted_char_ascii.chr}' (ASCII #{predicted_char_ascii})"
789
+ ```
285
790
 
286
- # Compute and inject gradients
287
- grad = pred.to_a.zip(train_y.to_a).map { |p, t| 2.0 * (p - t) / pred.numel }
288
- pred.agregar_gradiente(GRX.tensor(grad, pred.shape))
289
- pred.backward
791
+ ---
290
792
 
291
- opt.step
793
+ ### Architecture 7: Sentiment Analysis & Review Classification (BCELoss)
794
+
795
+ Evaluates text sentiment polarity (Positive / Negative) from word token streams:
796
+
797
+ ```ruby
798
+ require "grx"
292
799
 
293
- puts "epoch #{epoch + 1} loss: #{loss_val.round(6)}" if (epoch + 1) % 100 == 0
800
+ vocab_size = 1000
801
+ embed_dim = 32
802
+
803
+ sentiment_embedding = GRX::NN::Embedding.new(vocab_size, embed_dim)
804
+ sentiment_head = GRX::NN::Sequential.new(
805
+ GRX::NN::LayerNorm.new(embed_dim),
806
+ GRX::NN::Linear.new(embed_dim, 16),
807
+ GRX::NN::ReLU.new,
808
+ GRX::NN::Dropout.new(0.2),
809
+ GRX::NN::Linear.new(16, 1),
810
+ GRX::NN::Sigmoid.new
811
+ )
812
+
813
+ # Review token stream: "excellent quality product fast delivery" -> [42, 189, 7, 85, 301]
814
+ review_tokens = GRX.tensor([42, 189, 7, 85, 301], [5])
815
+ vectors = sentiment_embedding.forward(review_tokens) # Shape [5, 32]
816
+
817
+ # Global average pooling across words
818
+ review_representation = Array.new(embed_dim) do |d|
819
+ 5.times.sum { |t| vectors.get(t, d) } / 5.0
294
820
  end
295
- # epoch 100 loss: 0.312...
296
- # epoch 200 loss: 0.041...
297
- # epoch 300 loss: 0.005...
821
+ review_tensor = GRX.tensor(review_representation, [1, embed_dim])
822
+
823
+ polarity_prob = sentiment_head.forward(review_tensor).item
824
+ sentiment_label = polarity_prob >= 0.5 ? "POSITIVE" : "NEGATIVE"
825
+
826
+ puts "Sentiment Score: #{(polarity_prob * 100).round(2)}% -> #{sentiment_label}"
298
827
  ```
299
828
 
300
829
  ---
301
830
 
302
- ## Layers
831
+ ### Architecture 8: Siamese Neural Network for Similarity & Verification
303
832
 
304
- | Class | Description |
305
- |---|---|
306
- | `GRX::NN::Linear` | Dense layer — `y = x @ Wᵀ + b`, Xavier uniform init |
307
- | `GRX::NN::Sequential` | Ordered chain of layers |
308
- | `GRX::NN::ReLU` | Rectified Linear Unit |
309
- | `GRX::NN::LeakyReLU` | Leaky ReLU with configurable alpha (default `0.01`) |
310
- | `GRX::NN::Tanh` | Hyperbolic tangent |
311
- | `GRX::NN::Sigmoid` | Logistic sigmoid |
312
- | `GRX::NN::Softmax` | Normalized exponential |
313
- | `GRX::NN::Dropout` | Inverted dropout — `train!` / `eval!` modes |
314
- | `GRX::NN::BatchNorm1d` | Batch normalization with running statistics |
833
+ Twin branches sharing weights for signature, face, or document similarity verification:
315
834
 
316
- ---
835
+ ```ruby
836
+ require "grx"
317
837
 
318
- ## Loss functions
838
+ # Shared feature extractor
839
+ feature_extractor = GRX::NN::Sequential.new(
840
+ GRX::NN::Linear.new(32, 16),
841
+ GRX::NN::LayerNorm.new(16),
842
+ GRX::NN::Tanh.new,
843
+ GRX::NN::Linear.new(16, 8)
844
+ )
319
845
 
320
- | Class | Formula | Use case |
321
- |---|---|---|
322
- | `GRX::Loss::MSELoss` | `mean((pred − target)²)` | Regression |
323
- | `GRX::Loss::MAELoss` | `mean(|pred − target|)` | Robust regression |
324
- | `GRX::Loss::BCELoss` | `-mean(t·log(p) + (1−t)·log(1−p))` | Binary classification |
325
- | `GRX::Loss::CrossEntropyLoss` | Softmax + NLL | Multi-class classification |
326
- | `GRX::Loss::HuberLoss` | Smooth L1 (configurable delta) | Regression with outliers |
846
+ # Two sample inputs (e.g. Signature A and Signature B)
847
+ sig_a = GRX.randn([1, 32])
848
+ sig_b = GRX.randn([1, 32])
849
+
850
+ # Compute dense embedding vectors using identical shared weights
851
+ embedding_a = feature_extractor.forward(sig_a) # Shape [1, 8]
852
+ embedding_b = feature_extractor.forward(sig_b) # Shape [1, 8]
853
+
854
+ # Compute Euclidean embedding distance
855
+ diff = embedding_a - embedding_b
856
+ distance = diff.square.sum.sqrt.item
857
+
858
+ match = distance < 1.0 ? "MATCH (Same Entity)" : "NO MATCH (Different Entities)"
859
+ puts "Embedding Distance: #{distance.round(4)} -> #{match}"
860
+ ```
327
861
 
328
862
  ---
329
863
 
330
- ## Optimizers
864
+ ### Architecture 9: Deep Residual Network (ResNet MLP Block with Skip Connection)
865
+
866
+ Enables training deep networks without gradient degradation using identity shortcut connections $y = x + F(x)$:
331
867
 
332
868
  ```ruby
333
- # SGD with momentum and weight decay
334
- opt = GRX::Optim::SGD.new(net.parameters,
335
- lr: 0.01,
336
- momentum: 0.9,
337
- weight_decay: 1e-4
338
- )
869
+ require "grx"
339
870
 
340
- # Adam the standard choice for deep networks
341
- opt = GRX::Optim::Adam.new(net.parameters,
342
- lr: 0.001,
343
- beta1: 0.9,
344
- beta2: 0.999,
345
- epsilon: 1e-8,
346
- weight_decay: 0.0
347
- )
871
+ class ResidualBlock < GRX::NN::Module
872
+ def initialize(dim)
873
+ @fc1 = GRX::NN::Linear.new(dim, dim)
874
+ @bn1 = GRX::NN::BatchNorm1d.new(dim)
875
+ @act = GRX::NN::ReLU.new
876
+ @fc2 = GRX::NN::Linear.new(dim, dim)
877
+ @bn2 = GRX::NN::BatchNorm1d.new(dim)
878
+ end
879
+
880
+ def forward(x)
881
+ residual = x
882
+ out = @fc1.forward(x)
883
+ out = @bn1.forward(out)
884
+ out = @act.forward(out)
885
+ out = @fc2.forward(out)
886
+ out = @bn2.forward(out)
887
+ out + residual # Identity Skip Connection
888
+ end
889
+ end
348
890
 
349
- # Training step
350
- opt.zero_grad # clear gradients
351
- # ... forward + backward ...
352
- opt.step # update parameters
891
+ res_block = ResidualBlock.new(16)
892
+ x_in = GRX.randn([4, 16])
893
+ res_out = res_block.forward(x_in)
894
+
895
+ puts "Residual Block Output Shape: #{res_out.shape}"
353
896
  ```
354
897
 
355
898
  ---
356
899
 
357
- ## Weight initialization
900
+ ### Architecture 10: Neural Collaborative Filtering & Recommendation System
901
+
902
+ Combines User and Item latent embeddings with interaction MLP layers to predict recommendation scores:
358
903
 
359
904
  ```ruby
360
- # Xavier uniform — recommended for tanh / sigmoid layers
361
- GRX::Tensor.xavier_uniform([64, 32], requires_grad: true)
905
+ require "grx"
906
+
907
+ n_users = 100
908
+ n_items = 50
909
+ latent_dim = 16
362
910
 
363
- # He normal — recommended for ReLU layers
364
- GRX::Tensor.he_normal([64, 32], requires_grad: true)
911
+ user_embedding = GRX::NN::Embedding.new(n_users, latent_dim)
912
+ item_embedding = GRX::NN::Embedding.new(n_items, latent_dim)
365
913
 
366
- # Manual
367
- GRX::Tensor.zeros([64], requires_grad: true)
368
- GRX::Tensor.ones([64], requires_grad: true)
914
+ rating_mlp = GRX::NN::Sequential.new(
915
+ GRX::NN::Linear.new(latent_dim * 2, 16),
916
+ GRX::NN::ReLU.new,
917
+ GRX::NN::Linear.new(16, 1),
918
+ GRX::NN::Sigmoid.new
919
+ )
920
+
921
+ # Predict recommendation score for User #7 and Movie #23
922
+ u_idx = GRX.tensor([7], [1])
923
+ i_idx = GRX.tensor([23], [1])
924
+
925
+ u_vec = user_embedding.forward(u_idx) # Shape [1, 16]
926
+ i_vec = item_embedding.forward(i_idx) # Shape [1, 16]
927
+
928
+ # Concatenate user and item embeddings -> Shape [1, 32]
929
+ interaction_vector = GRX.tensor(u_vec.to_a + i_vec.to_a, [1, latent_dim * 2])
930
+ predicted_rating = rating_mlp.forward(interaction_vector).item
931
+
932
+ puts "Predicted Affinity Score: #{(predicted_rating * 5.0).round(2)} / 5.0 Stars"
369
933
  ```
370
934
 
371
935
  ---
372
936
 
373
- ## Dropout & BatchNorm
937
+ ## Loss Functions (`GRX::Loss`)
938
+
939
+ All loss functions return a differentiable `GRX::Tensor` ready for `loss.backward`.
940
+
941
+ | Loss Class | Constructor Signature & Parameters | Default | Mathematical Use Case & Description |
942
+ |---|---|---|---|
943
+ | `GRX::Loss::MSELoss` | `new(reduction: :mean)` | `reduction: :mean` | Mean Squared Error: $\frac{1}{N}\sum(y_{pred} - y_{true})^2$. Standard for continuous regression. |
944
+ | `GRX::Loss::MAELoss` | `new(reduction: :mean)` | `reduction: :mean` | Mean Absolute Error: $\frac{1}{N}\sum \|y_{pred} - y_{true}\|$. Robust to large outliers. |
945
+ | `GRX::Loss::BCELoss` | `new(reduction: :mean, eps: 1e-7)` | `eps: 1e-7` | Binary Cross Entropy: $-[y \log(p) + (1-y) \log(1-p)]$. Clamped by `eps` to prevent $\log(0)$. |
946
+ | `GRX::Loss::CrossEntropyLoss` | `new(reduction: :mean)` | `reduction: :mean` | Multi-Class Cross Entropy. Combines Log-Sum-Exp softmax with negative log-likelihood. |
947
+ | `GRX::Loss::HuberLoss` | `new(delta: 1.0, reduction: :mean)` | `delta: 1.0` | Smooth L1 / Huber: Quadratic for error $< \delta$, linear for error $\ge \delta$. Outlier resistant. |
948
+
949
+ * `reduction` options: `:mean` (divides total loss by mini-batch size; recommended) or `:sum` (accumulates unscaled sum).
950
+
951
+ ---
952
+
953
+ ## Scientific Notation & Hyperparameter Reference
954
+
955
+ ### 1. Scientific Notation in Machine Learning (`1e-1` to `1e-8`)
956
+
957
+ Deep learning parameters often represent fractional quantities (learning rates, regularization terms, variance stabilizers). GRTensor fully supports standard Ruby scientific notation literals:
958
+
959
+ | Notation | Exact Decimal | Fraction | Common Name | Typical Use Case in GRTensor |
960
+ |---|---|---|---|---|
961
+ | `1e-1` | `0.1` | $1/10$ | One Tenth | Fast learning rate in simple models, `BatchNorm1d` momentum. |
962
+ | `1e-2` | `0.01` | $1/100$ | One Hundredth | Default `SGD` learning rate, `LeakyReLU` negative slope (`alpha`). |
963
+ | `1e-3` | `0.001` | $1/1,000$ | One Thousandth | Gold standard default learning rate (`lr`) for `Adam` optimizer. |
964
+ | `1e-4` | `0.0001` | $1/10,000$ | One Ten-Thousandth | Recommended L2 regularization penalty (`weight_decay`) to prevent overfitting. |
965
+ | `1e-5` | `0.00001` | $1/100,000$ | One Hundred-Thousandth | Variance stabilizer constant (`eps`) in `LayerNorm` and `BatchNorm1d`. |
966
+ | `1e-7` | `0.0000001` | $1/10,000,000$ | One Ten-Millionth | Numerical safety clamp (`eps`) in `BCELoss` preventing $\log(0) \to -\infty$. |
967
+ | `1e-8` | `0.00000001` | $1/100,000,000$ | One Hundred-Millionth | Denominator variance stabilizer (`eps`) in `Adam` optimizer. |
968
+
969
+ ---
970
+
971
+ ### 2. Neural Layer Parameters (`GRX::NN`)
972
+
973
+ | Layer Class | Parameter | Type | Default | Valid Range | Physical & Computational Meaning |
974
+ |---|---|---|---|---|---|
975
+ | `Linear` | `in_features` | Integer | (Required) | $\ge 1$ | Dimensionality of input feature vectors. |
976
+ | `Linear` | `out_features`| Integer | (Required) | $\ge 1$ | Number of neurons / output features produced. |
977
+ | `Linear` | `bias` | Boolean | `true` | `true` / `false` | When `true`, adds trainable bias vector $b$ ($y = Wx + b$). |
978
+ | `Embedding` | `num_embeddings`| Integer | (Required) | $\ge 1$ | Total vocabulary size or entity count. |
979
+ | `Embedding` | `embedding_dim` | Integer | (Required) | $\ge 1$ | Dimensionality of dense latent vector for each token. |
980
+ | `Dropout` | `p` | Float | `0.5` | `0.0` to `0.99` | Probability of zeroing out activations during training to prevent co-adaptation. |
981
+ | `LeakyReLU` | `alpha` | Float | `0.01` | `0.001` to `0.3` | Slope for negative inputs ($x < 0$). Prevents dead neurons. |
982
+ | `LayerNorm` | `normalized_shape` | Int/Array | (Required) | Dimensions | Feature dimensions over which mean and variance are normalized. |
983
+ | `LayerNorm` | `eps` / `epsilon` | Float | `1e-5` | `1e-8` to `1e-4` | Variance stabilizer added to denominator. |
984
+ | `BatchNorm1d` | `num_features` | Integer | (Required) | $\ge 1$ | Number of channels normalized across the mini-batch dimension. |
985
+ | `BatchNorm1d` | `eps` / `epsilon` | Float | `1e-5` | `1e-8` to `1e-4` | Variance stabilizer added to denominator. |
986
+ | `BatchNorm1d` | `momentum` | Float | `0.1` | `0.01` to `0.5` | Exponential moving average factor for running mean and variance. |
987
+
988
+ ### 3. Tensor Creation & Core Operations Parameters (`GRX::Tensor`)
989
+
990
+ | Factory / Method | Parameter | Type | Default | Description |
991
+ |---|---|---|---|---|
992
+ | `GRX.tensor` | `data` | Array / Storage | (Required) | Flat or nested Ruby numbers array (`[1.0, 2.0]` or `[[1, 2], [3, 4]]`). |
993
+ | `GRX.tensor` | `shape` | Array[Integer] | `nil` (Auto) | Explicit dimensions (e.g. `[2, 3]`). Auto-inferred if omitted. |
994
+ | `GRX.tensor` | `requires_grad`| Boolean | `false` | Enables reverse-mode autograd tracking in the computational DAG. |
995
+ | `GRX.zeros` / `GRX.ones` | `shape` | Array[Integer] | (Required) | Shape dimensions initialized to `0.0` or `1.0`. |
996
+ | `GRX.rand` | `shape` | Array[Integer] | (Required) | Uniformly distributed random tensor $U[0, 1)$. |
997
+ | `GRX.randn` | `shape` | Array[Integer] | (Required) | Standard normal random tensor $N(0, 1)$ via Box-Muller transformation. |
998
+ | `Tensor.xavier_uniform`| `shape` | Array[Integer] | (Required) | Xavier/Glorot uniform initialization ($U[-\sqrt{6/(f_{in}+f_{out})}, \sqrt{6/(f_{in}+f_{out})}]$). |
999
+ | `Tensor.he_normal` | `shape` | Array[Integer] | (Required) | He/Kaiming normal initialization ($N(0, \sqrt{2/f_{in}})$). Optimal for ReLU. |
1000
+ | `Tensor.zeros_like` | `other` | Tensor | (Required) | Creates zeros tensor matching `other.shape`. |
1001
+ | `Tensor.ones_like` | `other` | Tensor | (Required) | Creates ones tensor matching `other.shape`. |
1002
+ | `tensor.clip` | `lo`, `hi` | Numeric | (Required) | Clamps all values to the $[lo, hi]$ interval. |
1003
+ | `tensor.pow` | `exponent` | Numeric | (Required) | Computes $x^e$ element-wise. Autograd differentiable. |
1004
+ | `tensor.reshape` | `new_shape` | Array[Integer] | (Required) | Reshapes tensor preserving total element count. Zero-copy view. |
1005
+ | `tensor.transpose` | (none) | - | - | Transposes 2D matrix axes. Zero-copy strided view. |
1006
+ | `tensor.flatten` | (none) | - | - | Flattens tensor into 1D `[numel]` shape. Zero-copy view. |
1007
+ | `tensor.contiguous`| (none) | - | - | Re-packs non-contiguous memory views into a fresh contiguous buffer. |
1008
+ | `tensor.get` | `*coords` | Integers | (Required) | Returns float element at specified multidimensional coordinates. |
1009
+ | `tensor.set` | `*coords, val` | Integers, Float | (Required) | Modifies value at specified coordinates in native memory. |
1010
+ | `tensor.item` | (none) | - | - | Returns Ruby Float for 1-element scalar tensors. |
1011
+ | `tensor.argmax` | (none) | - | - | Returns flat index of the maximum value. |
1012
+ | `tensor.argmin` | (none) | - | - | Returns flat index of the minimum value. |
1013
+ | `tensor.backward` | `gradient` | Tensor | `nil` | Executes reverse-mode backpropagation across the computational DAG. |
1014
+
1015
+ ---
1016
+
1017
+ ### 4. Data Pipelines, Persistence & Utilities (`GRX::Data`, `GRX::Serialization`, `GRX::Utils`)
1018
+
1019
+ * `TensorDataset.new(*tensors)`:
1020
+ * `*tensors` (Required): Parallel feature and label tensors sharing the same batch dimension 0.
1021
+ * `DataLoader.new(dataset, batch_size: 32, shuffle: true)`:
1022
+ * `dataset` (`GRX::Data::Dataset`): Wrapped dataset.
1023
+ * `batch_size` (Integer, default: `32`): Number of samples per mini-batch.
1024
+ * `shuffle` (Boolean, default: `true`): Randomly permutes dataset indices at the start of each epoch.
1025
+ * `GRX::Serialization.save(model, path)` / `model.save_weights(path)`:
1026
+ * `model` (`GRX::NN::Module`): Neural network model instance.
1027
+ * `path` (String): Output filepath for binary `.grx` format.
1028
+ * `GRX::Serialization.load(model, path)` / `model.load_weights(path)`:
1029
+ * `model` (`GRX::NN::Module`): Initialized neural model with matching architecture.
1030
+ * `path` (String): Source `.grx` file.
1031
+ * `model.train!` and `model.eval!`:
1032
+ * `train!`: Switches layers to training mode (enables `Dropout`, dynamic batch statistics in `BatchNorm1d`).
1033
+ * `eval!`: Switches layers to evaluation/inference mode (disables `Dropout`, freezes running statistics in `BatchNorm1d`).
1034
+ * `GRX::Utils.clip_grad_norm!(parameters, max_norm: 1.0)`:
1035
+ * `parameters` (Array[Tensor]): Trainable parameter collection.
1036
+ * `max_norm` (Float, default: `1.0`): Maximum allowable combined L2 gradient norm to prevent explosions.
1037
+ * `GRX::Utils.one_hot(indices, num_classes: nil, requires_grad: false)`:
1038
+ * `indices` (Array[Integer] or Tensor): Categorical class IDs (e.g. `[0, 2, 1]`).
1039
+ * `num_classes` (Integer, optional): Total number of output classes. Auto-inferred if omitted.
1040
+ * `GRX.simd_mode`:
1041
+ * Queries host CPU vectorization level: `:avx2` (4 doubles/cycle with FMA), `:sse` (2 doubles/cycle), `:scalar` (portable C), or `:ruby`.
1042
+
1043
+ ---
1044
+
1045
+ ### 5. Exception Hierarchy
1046
+
1047
+ | Exception Class | Parent | Description |
1048
+ |---|---|---|
1049
+ | `GRX::Error` | `StandardError` | Base class for all GRTensor exceptions. |
1050
+ | `GRX::ShapeError` | `GRX::Error` | Incompatible tensor shapes during algebraic operations or matrix multiplications. |
1051
+ | `GRX::DimensionError` | `GRX::Error` | Invalid tensor rank (e.g. calling `transpose` or `matmul` on 1D vectors). |
1052
+ | `GRX::StorageError` | `GRX::Error` | Native C heap memory allocation failure (OOM) or corrupted `.grx` binary file. |
1053
+
1054
+ ---
1055
+
1056
+ ## Optimizers & Hyperparameter Reference (`GRX::Optim`)
1057
+
1058
+ ### 1. `GRX::Optim::Adam`
1059
+ Adaptive Moment Estimation (Kingma & Ba) with SIMD-vectorized FMA acceleration in native C:
374
1060
 
375
1061
  ```ruby
376
- # Dropout — different behavior in train vs eval
377
- drop = GRX::NN::Dropout.new(0.5)
378
- drop.train! # activates dropout
379
- drop.eval! # passes input through unchanged
1062
+ optimizer = GRX::Optim::Adam.new(
1063
+ model.parameters,
1064
+ lr: 0.001, # Learning rate (alpha). Recommended: 1e-3 (0.001) for deep nets
1065
+ betas: [0.9, 0.999], # [beta1, beta2] exponential decay rates for 1st/2nd moments
1066
+ eps: 1e-8, # Epsilon term added to denominator for numerical stability
1067
+ weight_decay: 1e-4 # L2 regularization factor (weight penalty to prevent overfitting)
1068
+ )
1069
+ ```
1070
+
1071
+ | Parameter | Type | Default | Recommended Range | Description |
1072
+ |---|---|---|---|---|
1073
+ | `lr` | Float | `0.001` | `1e-4` to `1e-2` | Step size scaling factor along the negative gradient. |
1074
+ | `betas` / `beta1, beta2` | Array / Floats | `[0.9, 0.999]` | `[0.9, 0.999]` | $\beta_1$ tracks momentum; $\beta_2$ tracks squared gradient variance. |
1075
+ | `eps` / `epsilon` | Float | `1e-8` | `1e-8` to `1e-6` | Small constant preventing division by zero. |
1076
+ | `weight_decay` | Float | `0.0` | `1e-5` to `1e-3` | L2 weight shrinkage penalty ($\lambda$) to combat memorization/overfitting. |
380
1077
 
381
- # BatchNorm1d — normalizes across the batch dimension
382
- bn = GRX::NN::BatchNorm1d.new(16)
383
- bn.train!
384
- bn.eval!
1078
+ ### 2. `GRX::Optim::SGD`
1079
+ Stochastic Gradient Descent with momentum and weight decay:
1080
+
1081
+ ```ruby
1082
+ optimizer = GRX::Optim::SGD.new(
1083
+ model.parameters,
1084
+ lr: 0.01, # Learning rate. Recommended: 0.01 to 0.1
1085
+ momentum: 0.9, # Momentum buffer coefficient (mu). Recommended: 0.9
1086
+ weight_decay: 1e-4 # L2 regularization factor
1087
+ )
385
1088
  ```
386
1089
 
1090
+ | Parameter | Type | Default | Recommended Range | Description |
1091
+ |---|---|---|---|---|
1092
+ | `lr` | Float | `0.01` | `0.001` to `0.1` | Step size for gradient descent. |
1093
+ | `momentum` | Float | `0.0` | `0.8` to `0.99` | Accumulates gradient velocity in the direction of persistent descent. |
1094
+ | `weight_decay` | Float | `0.0` | `1e-5` to `1e-3` | L2 weight shrinkage penalty. |
1095
+
387
1096
  ---
388
1097
 
389
- ## Architecture
1098
+ ## Data Pipelines & DataLoader (`GRX::Data`)
1099
+
1100
+ ### 1. `GRX::Data::TensorDataset`
1101
+ Encapsulates features and labels into an indexed dataset:
390
1102
 
1103
+ ```ruby
1104
+ x_data = GRX.randn([1000, 20])
1105
+ y_data = GRX.tensor(Array.new(1000) { rand(0..2) }, [1000])
1106
+
1107
+ dataset = GRX::Data::TensorDataset.new(x_data, y_data)
1108
+ puts dataset.size # 1000
391
1109
  ```
392
- grx-tensor/
393
- ├── ext/
394
- │ ├── grx/
395
- │ │ ├── grx_core.c # C kernel
396
- │ │ │ # AVX2+FMA element-wise ops (unroll ×2)
397
- │ │ │ # Cache-tiled matmul (TILE=8, 64-byte cache lines)
398
- │ │ │ # Adam optimizer inner loop with FMA
399
- │ │ │ # Xavier uniform + He normal (Box-Muller in C)
400
- │ │ │ # 32-byte aligned memory (posix_memalign / _aligned_malloc)
401
- │ │ ├── grx_core.h # Public C API with GRX_API export macro
402
- │ │ └── extconf.rb # mkmf config — auto-detects AVX2, SSE2, scalar
403
- │ ├── unix/
404
- │ │ └── Makefile # Manual build → lib/grx/libgrx_core.so / .dylib
405
- │ └── windows/
406
- │ └── Makefile.mingw # Manual build → lib/grx/grx_core.dll
407
-
408
- ├── lib/
409
- │ ├── grx.rb # require "grx" ← entry point
410
- │ └── grx/
411
- │ ├── c_api.rb # Fiddle bridge — finds and loads the binary
412
- │ │ # Searches: lib/grx/, lib/, ext/grx/ (all install methods)
413
- │ ├── storage.rb # Native memory buffer (Fiddle::Pointer, 32-byte aligned)
414
- │ ├── tensor.rb # Tensor: zero-copy views + autograd node
415
- │ ├── nn.rb # NN layers
416
- │ ├── optim.rb # Optimizers
417
- │ ├── loss.rb # Loss functions
418
- │ └── errors.rb # ShapeError, DimensionError, StorageError
419
-
420
- └── test/
421
- ├── test_full.rb # 104-test integration suite
422
- ├── test_tensor.rb
423
- ├── test_nn.rb
424
- └── benchmark.rb
1110
+
1111
+ ### 2. `GRX::Data::DataLoader`
1112
+ Mini-batch generator with automatic shuffling and slicing:
1113
+
1114
+ ```ruby
1115
+ loader = GRX::Data::DataLoader.new(dataset, batch_size: 32, shuffle: true)
1116
+
1117
+ loader.each_with_index do |(batch_x, batch_y), batch_idx|
1118
+ optimizer.zero_grad
1119
+ preds = model.forward(batch_x)
1120
+ loss = criterion.call(preds, batch_y)
1121
+ loss.backward
1122
+ optimizer.step
1123
+ end
425
1124
  ```
426
1125
 
427
1126
  ---
428
1127
 
429
- ## How the binary is found
1128
+ ## Model Persistence & Brain Serialization (`.grx` Format)
1129
+
1130
+ ### Binary Format Layout (`GRX1` Specification)
1131
+
1132
+ GRX includes a binary serialization format that dumps floating-point parameters directly from native C memory buffers:
1133
+
1134
+ ```text
1135
+ +-------------------+--------------------+---------------------------------------------+
1136
+ | Field | Size | Content |
1137
+ +-------------------+--------------------+---------------------------------------------+
1138
+ | Magic Header | 8 bytes | "GRX1\0\0\0\0" (ASCII + null padding) |
1139
+ | Parameter Count | 4 bytes | uint32 big-endian |
1140
+ +-------------------+--------------------+---------------------------------------------+
1141
+ | For each parameter tensor: |
1142
+ | - Rank | 2 bytes | uint16 big-endian |
1143
+ | - Shape | Rank * 4 bytes | uint32 big-endian array |
1144
+ | - Numel | 8 bytes | uint64 big-endian |
1145
+ | - Data Payload | Numel * 8 bytes | IEEE 754 64-bit Doubles (Direct byte copy) |
1146
+ +-------------------+--------------------+---------------------------------------------+
1147
+ ```
430
1148
 
431
- `c_api.rb` searches for the compiled binary in this order:
1149
+ ### Production Inference Deployment Workflow
432
1150
 
433
- | Priority | Path | When |
434
- |---|---|---|
435
- | 1 | `lib/grx/libgrx_core.so` | `make -C ext/unix` (manual) |
436
- | 2 | `lib/grx_core.so` | `gem install` via rake-compiler |
437
- | 3 | `lib/grx_core.bundle` | `gem install` on macOS |
438
- | 4 | `ext/grx/libgrx_core.so` | local development |
1151
+ #### Step 1: Train & Save Brain (`train.rb`)
1152
+ ```ruby
1153
+ require "grx"
439
1154
 
440
- If none is found, GRX falls back to pure Ruby automatically — no crash, no configuration needed.
1155
+ model = GRX::NN::Sequential.new(
1156
+ GRX::NN::Linear.new(4, 16),
1157
+ GRX::NN::ReLU.new,
1158
+ GRX::NN::Linear.new(16, 2)
1159
+ )
1160
+
1161
+ # ... (training loop) ...
1162
+
1163
+ # Save trained brain
1164
+ model.save_weights("cerebro_campeon.grx")
1165
+ puts "Brain weights saved to cerebro_campeon.grx"
1166
+ ```
1167
+
1168
+ #### Step 2: Load & Serve in Production API (`serve.rb`)
1169
+ ```ruby
1170
+ require "grx"
1171
+
1172
+ brain = GRX::NN::Sequential.new(
1173
+ GRX::NN::Linear.new(4, 16),
1174
+ GRX::NN::ReLU.new,
1175
+ GRX::NN::Linear.new(16, 2)
1176
+ )
1177
+
1178
+ # Load binary weights instantly without re-training
1179
+ brain.load_weights("cerebro_campeon.grx")
1180
+ brain.eval!
1181
+
1182
+ # Serve real-time request
1183
+ input_data = GRX.tensor([[0.5, -1.2, 3.4, 0.1]], [1, 4])
1184
+ decision = brain.forward(input_data).softmax.to_a
1185
+
1186
+ puts "Production Decision Probabilities: #{decision.map { |d| d.round(4) }}"
1187
+ ```
441
1188
 
442
1189
  ---
443
1190
 
444
- ## Benchmark
1191
+ ## Gradient Management & Utilities (`GRX::Utils`)
445
1192
 
446
- Measured on Ruby 3.3, Linux x86_64, AVX2+FMA active.
1193
+ ```ruby
1194
+ # 1. Gradient Norm Clipping (prevents exploding gradients)
1195
+ total_norm = GRX::Utils.clip_grad_norm!(model.parameters, max_norm: 1.0)
447
1196
 
448
- | Operation | n = 1M elements | Throughput |
449
- |---|---|---|
450
- | `add` | ~4ms / iter | ~250M doubles/s |
451
- | `dot` | ~2ms / iter | ~500M doubles/s |
452
- | `relu` | ~4ms / iter | ~250M doubles/s |
453
- | `matmul` 256×256 | ~6ms | — |
1197
+ # 2. One-Hot Encoding
1198
+ one_hot_matrix = GRX::Utils.one_hot([0, 2, 1], num_classes: 3)
1199
+ ```
454
1200
 
455
1201
  ---
456
1202
 
457
- ## Roadmap
1203
+ ## Windows Support & Toolchain Guide
458
1204
 
459
- - [ ] OpenMP — parallelize element-wise ops across all CPU cores
460
- - [ ] BLAS (`cblas_dgemm`) production-grade matmul
461
- - [ ] Broadcasting automatic shape expansion
462
- - [ ] `float32` support 8 values/cycle with AVX2
463
- - [ ] Move autograd graph to C — eliminate Ruby GC overhead for large networks
464
- - [ ] `Conv2d`, `LSTM`, `MultiheadAttention`
465
- - [ ] CUDA extension (`grx-tensor-cuda`)
1205
+ On Windows operating systems:
1206
+ * **Native C Acceleration (Recommended):** Active automatically when installing via **RubyInstaller with DevKit (MSYS2 / MinGW-w64)**. The native C extension is compiled transparently upon `gem install grx-tensor`.
1207
+ * **Pure Ruby Fallback:** If DevKit is not installed, GRX automatically switches to pure Ruby computation mode without throwing errors.
1208
+ * **Pre-Built Standalone Binaries:** Fat binary gems containing pre-compiled `.dll` files are currently in active development.
466
1209
 
467
1210
  ---
468
1211
 
469
1212
  ## License
470
1213
 
471
- MIT see [LICENSE.txt](LICENSE.txt)
1214
+ MIT License. Copyright (c) 2026 Razo.