grx-tensor 0.2.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,7 +2,7 @@
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 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)
@@ -10,265 +10,1205 @@ A tensor framework for Ruby with automatic differentiation, a C+SIMD compute cor
10
10
 
11
11
  ---
12
12
 
13
- ## What is GRX?
13
+ ## Architectural Flow
14
14
 
15
- GRX is a high-performance tensor computation library for Ruby. The numeric core is written in C and compiled with **AVX2 + FMA** SIMD instructions — processing 4 double-precision floats per CPU cycle with fused multiply-add.
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)"]
68
+
69
+ C --> D1["Linux (GCC / Clang)"]
70
+ C --> D2["macOS (Clang / Apple LLVM)"]
71
+ C --> D3["Windows (RubyInstaller DevKit / MinGW-w64)"]
72
+
73
+ D1 --> E1["libgrx_core.so"]
74
+ D2 --> E2["libgrx_core.dylib"]
75
+ D3 --> E3["grx_core.dll"]
76
+
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)
16
127
 
17
- Ruby handles the high-level API: shape validation, computation graph construction, and orchestration. C handles all numerical memory buffers and heavy arithmetic.
128
+ ---
18
129
 
19
- ### Key features
130
+ ## Key Features
20
131
 
21
- | Feature | Details |
132
+ | Feature | Specification |
22
133
  |---|---|
23
- | **C+SIMD Kernel** | AVX2+FMA, SSE2 fallback, scalar fallback auto-detected at compile time |
24
- | **Aligned Memory** | 32-byte aligned heap allocation (`posix_memalign` / `_aligned_malloc`) |
25
- | **Autograd** | Automatic differentiation with differentiable loss functions and DAG traversal |
26
- | **Optimizers** | SGD (with momentum and weight decay) and Adam (vectorized in C with FMA) |
27
- | **NN Layers** | Linear, Sequential, Embedding, LayerNorm, Dropout, BatchNorm1d |
28
- | **Activations** | ReLU, Leaky ReLU, Tanh, Sigmoid, Softmax (all fully differentiable) |
29
- | **Loss Functions** | MSE, MAE, BCE, CrossEntropy, Huber (fully differentiable) |
30
- | **Model Persistence** | Native high-speed binary `.grx` format (`save_weights` / `load_weights`) |
31
- | **Data Pipelines** | `TensorDataset` and `DataLoader` for mini-batching and shuffling |
32
- | **Weight Init** | Xavier uniform, He normal (Box-Muller in C) |
33
- | **Cross-Platform** | Linux (`.so`), macOS (`.dylib`), Windows (`.dll`) |
34
- | **Pure Ruby Fallback** | Runs without a C compiler when native binaries are unavailable |
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
+ ---
35
149
 
36
150
  ---
37
151
 
38
152
  ## Installation
39
153
 
40
- ### Via RubyGems
154
+ ### Standard RubyGems Installation
41
155
 
42
156
  ```bash
43
157
  gem install grx-tensor
44
158
  ```
45
159
 
46
- Or add it to your `Gemfile`:
160
+ Or add it to your project's `Gemfile`:
47
161
 
48
162
  ```ruby
49
163
  gem "grx-tensor"
50
164
  ```
51
165
 
52
- ### Manual Compilation from Source
166
+ The native C extension is automatically compiled and linked in the background during installation.
53
167
 
54
- If you clone the repository directly, compile the native extension:
168
+ ---
55
169
 
56
- **Linux / macOS:**
57
- ```bash
58
- make -C ext/unix
59
- ```
170
+ ## Beginner-Friendly Quickstart Tutorials
60
171
 
61
- **Windows (MinGW-w64):**
62
- ```bash
63
- make -C ext/windows -f Makefile.mingw
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:
175
+
176
+ ```ruby
177
+ require "grx"
178
+
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])
182
+
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
187
+
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)"
64
204
  ```
65
205
 
66
206
  ---
67
207
 
68
- ## Quick Start
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:
69
211
 
70
212
  ```ruby
71
213
  require "grx"
72
214
 
73
- # 1. Create tensors with gradient tracking
74
- a = GRX.tensor([1.0, 2.0, 3.0], [3], requires_grad: true)
75
- b = GRX.tensor([4.0, 5.0, 6.0], [3], requires_grad: true)
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])
76
218
 
77
- # 2. Perform arithmetic operations (executed in C with SIMD)
78
- c = (a * b) + 2.0
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
+ )
79
226
 
80
- # 3. Compute gradients via backpropagation
81
- c.sum.backward
227
+ opt = GRX::Optim::Adam.new(xor_net.parameters, lr: 0.05)
228
+ loss_fn = GRX::Loss::BCELoss.new
82
229
 
83
- # 4. Inspect calculated gradients
84
- puts a.grad.to_a # [4.0, 5.0, 6.0]
85
- puts b.grad.to_a # [1.0, 2.0, 3.0]
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
237
+
238
+ puts "XOR Predictions: #{xor_net.call(x).to_a.map { |v| v.round(3) }}"
239
+ ```
240
+
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}"
86
254
  ```
87
255
 
88
256
  ---
89
257
 
90
- ## Advanced Architecture and Layers
258
+ ## Tensor API Reference & Core Operations
91
259
 
92
- ### Natural Language Processing with `Embedding` & `LayerNorm`
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
93
263
 
94
264
  ```ruby
95
265
  require "grx"
96
266
 
97
- # 1. Vocabulary of 1000 tokens, 32-dimensional embedding space
98
- vocab_size = 1000
99
- embedding_dim = 32
100
- embedding = GRX::NN::Embedding.new(vocab_size, embedding_dim)
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)
270
+
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
289
+
290
+ ```ruby
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
+ ```
302
+
303
+ ### 3. Arithmetic Operations (SIMD Accelerated)
304
+
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
101
365
 
102
- # 2. Tokenized sentence IDs
103
- token_ids = GRX.tensor([42, 108, 999], [3])
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
+ ```
104
396
 
105
- # 3. Dense vector lookup
106
- vectors = embedding.call(token_ids)
107
- puts vectors.shape # [3, 32]
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)
108
403
 
109
- # 4. Layer normalization for numerical stability
110
- norm = GRX::NN::LayerNorm.new(32)
111
- normalized = norm.call(vectors)
112
- puts normalized.shape # [3, 32]
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]
113
411
  ```
114
412
 
115
413
  ---
116
414
 
117
- ## Complete Real-World Examples
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.
118
418
 
119
- ### Example 1: NLP Intent & Sentiment Classifier (CrossEntropyLoss)
419
+ ### A. Computer Vision & Image Filtering (Sobel Kernel Convolution)
120
420
 
121
- Trains an end-to-end NLP classifier that categorizes customer inquiries into Complaints (0), Praise (1), and Questions (2):
421
+ Apply spatial filtering and edge detection directly on 2D image pixel grids:
122
422
 
123
423
  ```ruby
124
424
  require "grx"
125
425
 
126
- data = [
127
- ["el servicio es pesimo y muy malo", 0],
128
- ["la atencion fue horrible nunca vuelvo", 0],
129
- ["no me gusto para nada es lento", 0],
130
- ["excelente servicio muy rapido y bueno", 1],
131
- ["me encanto la atencion fantastica gracias", 1],
132
- ["todo perfecto muy feliz con la compra", 1],
133
- ["como puedo hacer una devolucion de compra", 2],
134
- ["cual es el horario de atencion hoy", 2],
135
- ["donde puedo consultar el estado del pedido", 2]
136
- ]
137
-
138
- vocab = data.flat_map { |text, _| text.split }.uniq
139
- token_to_id = vocab.each_with_index.to_h
140
- vocab_size = vocab.size
141
- embedding_dim = 16
142
- num_classes = 3
143
- seq_len = 7
144
-
145
- batch_size = data.size
146
- x_indices = data.map { |text, _| (text.split.map { |w| token_to_id[w] } + [0]*seq_len).first(seq_len) }
147
- train_x = GRX.tensor(x_indices.flatten.map(&:to_f), [batch_size, seq_len])
148
-
149
- onehot = Array.new(batch_size * num_classes, 0.0)
150
- data.each_with_index { |(_, label), i| onehot[i * num_classes + label] = 1.0 }
151
- train_y = GRX.tensor(onehot, [batch_size, num_classes])
152
-
153
- emb = GRX::NN::Embedding.new(vocab_size, embedding_dim)
154
- ln = GRX::NN::LayerNorm.new(embedding_dim)
155
- fc1 = GRX::NN::Linear.new(embedding_dim, 16)
156
- act = GRX::NN::ReLU.new
157
- fc2 = GRX::NN::Linear.new(16, num_classes)
158
-
159
- params = emb.parameters + ln.parameters + fc1.parameters + fc2.parameters
160
- opt = GRX::Optim::Adam.new(params, lr: 0.05)
161
- loss_fn = GRX::Loss::CrossEntropyLoss.new
162
-
163
- 120.times do
164
- opt.zero_grad
165
- flat_tokens = train_x.flatten
166
- embedded = emb.call(flat_tokens)
167
-
168
- emb_data = embedded.to_a
169
- pooled_data = Array.new(batch_size * embedding_dim, 0.0)
170
- batch_size.times do |b|
171
- embedding_dim.times do |d|
172
- sum_v = 0.0
173
- seq_len.times { |t| sum_v += emb_data[(b * seq_len + t) * embedding_dim + d] }
174
- pooled_data[b * embedding_dim + d] = sum_v / seq_len.to_f
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
175
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
176
459
  end
177
- pooled = GRX.tensor(pooled_data, [batch_size, embedding_dim], requires_grad: true)
460
+ end
178
461
 
179
- logits = fc2.call(act.call(fc1.call(ln.call(pooled))))
180
- loss = loss_fn.call(logits, train_y)
181
- loss.backward
462
+ edge_map = GRX.tensor(edge_map_data, [out_rows, out_cols])
463
+ puts "Detected Edges Shape: #{edge_map.shape}"
464
+ ```
182
465
 
183
- if pooled.grad
184
- grad_emb = Array.new(batch_size * seq_len * embedding_dim, 0.0)
185
- p_grad = pooled.grad.to_a
186
- batch_size.times do |b|
187
- embedding_dim.times do |d|
188
- val = p_grad[b * embedding_dim + d] / seq_len.to_f
189
- seq_len.times { |t| grad_emb[(b * seq_len + t) * embedding_dim + d] = val }
190
- end
191
- end
192
- embedded.backward(GRX.tensor(grad_emb, embedded.shape))
466
+ ---
467
+
468
+ ### B. Quantitative Finance & Portfolio Covariance
469
+
470
+ Calculate asset returns, annualized volatility, and portfolio risk matrix:
471
+
472
+ ```ruby
473
+ require "grx"
474
+
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)
193
490
  end
491
+ end
492
+ returns = GRX.tensor(returns_data, [4, 3])
194
493
 
195
- opt.step
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
499
+
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
196
505
  end
506
+ centered_returns = GRX.tensor(centered_data, [4, 3])
507
+
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)
197
515
 
198
- puts "Training finished with CrossEntropy Loss < 0.0001"
516
+ puts "Portfolio Daily Volatility: #{(port_volatility * 100).round(4)}%"
199
517
  ```
200
518
 
201
519
  ---
202
520
 
203
- ### Example 2: Large-Scale Dataset Training (5,000 Samples with DataLoader)
521
+ ### C. Particle Physics & N-Body Simulation
204
522
 
205
- Trains a deep regression network over 5,000 samples and 4 features using mini-batch gradient descent:
523
+ Simulate particle positions, velocities, and pairwise Euclidean distances in 3D:
206
524
 
207
525
  ```ruby
208
526
  require "grx"
209
527
 
210
- num_samples = 5000
211
- num_features = 4
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
559
+
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)}"
562
+ ```
563
+
564
+ ---
565
+
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$$
570
+
571
+ ```ruby
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)
212
583
 
213
- # Generate synthetic dataset: y = 2*x1 - 3*x2 + 1.5*x3 - 0.5*x4 + 4.0
214
- raw_x = Array.new(num_samples * num_features) { rand * 2.0 - 1.0 }
215
- raw_y = Array.new(num_samples) do |i|
216
- x1, x2, x3, x4 = raw_x.slice(i * 4, 4)
217
- 2.0 * x1 - 3.0 * x2 + 1.5 * x3 - 0.5 * x4 + 4.0 + (rand - 0.5) * 0.02
584
+ term1 = (GRX.tensor([1.0], [1]) - tx).square
585
+ term2 = (ty - tx.square).square * 100.0
586
+ loss = term1 + term2
587
+ loss.backward
588
+
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])
218
592
  end
219
593
 
220
- train_dataset = GRX::Data::TensorDataset.new(
221
- GRX.tensor(raw_x[0...(4000*4)], [4000, 4]),
222
- GRX.tensor(raw_y[0...4000], [4000, 1])
594
+ puts "Converged Minimum: x = #{point.get(0).round(3)}, y = #{point.get(1).round(3)}"
595
+ ```
596
+
597
+ ---
598
+
599
+ ## Deep Learning Cookbook (10 Neural Network Architectures)
600
+
601
+ ### Architecture 1: Deep Vision & Character Classifier (BatchNorm + Dropout)
602
+
603
+ ```ruby
604
+ require "grx"
605
+
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
223
616
  )
224
- val_x = GRX.tensor(raw_x[(4000*4)..], [1000, 4])
225
- val_y = GRX.tensor(raw_y[4000..], [1000, 1])
226
617
 
227
- train_loader = GRX::Data::DataLoader.new(train_dataset, batch_size: 64, shuffle: true)
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
+ ```
228
622
 
229
- model = GRX::NN::Sequential.new(
623
+ ---
624
+
625
+ ### Architecture 2: NLP & Intent Chatbot (Embedding + LayerNorm)
626
+
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])
649
+
650
+ logits = classifier.forward(sentence_vector)
651
+ puts "Predicted Intent Class: #{logits.argmax}"
652
+ ```
653
+
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
+ ```
686
+
687
+ ---
688
+
689
+ ### Architecture 4: Nonlinear Multi-Variable Time Series Forecasting
690
+
691
+ ```ruby
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),
698
+ GRX::NN::ReLU.new,
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(
230
736
  GRX::NN::Linear.new(4, 16),
231
737
  GRX::NN::LayerNorm.new(16),
738
+ GRX::NN::ReLU.new,
739
+ GRX::NN::Linear.new(16, 64),
740
+ GRX::NN::Sigmoid.new
741
+ )
742
+
743
+ optimizer = GRX::Optim::Adam.new(encoder.parameters + decoder.parameters, lr: 0.01)
744
+ recon_loss_fn = GRX::Loss::MSELoss.new
745
+
746
+ # Training reconstruction loop
747
+ sample_batch = GRX.rand([8, 64])
748
+
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)}"
757
+ ```
758
+
759
+ ---
760
+
761
+ ### Architecture 6: Autoregressive Next-Token Character Language Model & Generator
762
+
763
+ Generates text character-by-character using embedding lookups and dense prediction heads:
764
+
765
+ ```ruby
766
+ require "grx"
767
+
768
+ vocab_size = 256 # ASCII alphabet
769
+ embed_dim = 16
770
+ ctx_len = 4 # 4-character context window
771
+
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),
232
776
  GRX::NN::Tanh.new,
233
- GRX::NN::Linear.new(16, 1)
777
+ GRX::NN::Linear.new(64, vocab_size)
234
778
  )
235
779
 
236
- opt = GRX::Optim::Adam.new(model.parameters, lr: 0.03)
237
- 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])
784
+
785
+ logits = language_head.forward(flattened_context) # Shape [1, 256]
786
+ predicted_char_ascii = logits.argmax
787
+
788
+ puts "Context: 'hell' -> Predicted Next Char: '#{predicted_char_ascii.chr}' (ASCII #{predicted_char_ascii})"
789
+ ```
790
+
791
+ ---
792
+
793
+ ### Architecture 7: Sentiment Analysis & Review Classification (BCELoss)
794
+
795
+ Evaluates text sentiment polarity (Positive / Negative) from word token streams:
238
796
 
239
- 20.times do |epoch|
240
- train_loader.each do |bx, by|
241
- opt.zero_grad
242
- pred = model.call(bx)
243
- loss = loss_fn.call(pred, by)
244
- loss.backward
245
- opt.step
797
+ ```ruby
798
+ require "grx"
799
+
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
820
+ end
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}"
827
+ ```
828
+
829
+ ---
830
+
831
+ ### Architecture 8: Siamese Neural Network for Similarity & Verification
832
+
833
+ Twin branches sharing weights for signature, face, or document similarity verification:
834
+
835
+ ```ruby
836
+ require "grx"
837
+
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
+ )
845
+
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
+ ```
861
+
862
+ ---
863
+
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)$:
867
+
868
+ ```ruby
869
+ require "grx"
870
+
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
246
888
  end
247
889
  end
248
890
 
249
- val_pred = model.call(val_x)
250
- val_loss = loss_fn.call(val_pred, val_y).item
251
- puts "Validation MSE: #{val_loss.round(6)}"
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}"
896
+ ```
897
+
898
+ ---
899
+
900
+ ### Architecture 10: Neural Collaborative Filtering & Recommendation System
901
+
902
+ Combines User and Item latent embeddings with interaction MLP layers to predict recommendation scores:
903
+
904
+ ```ruby
905
+ require "grx"
906
+
907
+ n_users = 100
908
+ n_items = 50
909
+ latent_dim = 16
910
+
911
+ user_embedding = GRX::NN::Embedding.new(n_users, latent_dim)
912
+ item_embedding = GRX::NN::Embedding.new(n_items, latent_dim)
913
+
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])
252
924
 
253
- # Save model weights to binary .grx file
254
- model.save_weights("model_5000_samples.grx")
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"
255
933
  ```
256
934
 
257
935
  ---
258
936
 
259
- ## Benchmarks
937
+ ## Loss Functions (`GRX::Loss`)
260
938
 
261
- Measured on Ruby 3.3, Linux x86_64 with AVX2+FMA enabled:
939
+ All loss functions return a differentiable `GRX::Tensor` ready for `loss.backward`.
262
940
 
263
- | Operation | Array Size (n) | Execution Time | SIMD Throughput |
941
+ | Loss Class | Constructor Signature & Parameters | Default | Mathematical Use Case & Description |
264
942
  |---|---|---|---|
265
- | `add` | 1,000,000 | ~4 ms / iter | ~250M doubles/sec |
266
- | `dot` | 1,000,000 | ~2 ms / iter | ~500M doubles/sec |
267
- | `relu` | 1,000,000 | ~4 ms / iter | ~250M doubles/sec |
268
- | `matmul` (256x256) | 65,536 | ~6 ms | Tiled cache reuse |
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:
1060
+
1061
+ ```ruby
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. |
1077
+
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
+ )
1088
+ ```
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
+
1096
+ ---
1097
+
1098
+ ## Data Pipelines & DataLoader (`GRX::Data`)
1099
+
1100
+ ### 1. `GRX::Data::TensorDataset`
1101
+ Encapsulates features and labels into an indexed dataset:
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
1109
+ ```
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
1124
+ ```
1125
+
1126
+ ---
1127
+
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
+ ```
1148
+
1149
+ ### Production Inference Deployment Workflow
1150
+
1151
+ #### Step 1: Train & Save Brain (`train.rb`)
1152
+ ```ruby
1153
+ require "grx"
1154
+
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
+ ```
1188
+
1189
+ ---
1190
+
1191
+ ## Gradient Management & Utilities (`GRX::Utils`)
1192
+
1193
+ ```ruby
1194
+ # 1. Gradient Norm Clipping (prevents exploding gradients)
1195
+ total_norm = GRX::Utils.clip_grad_norm!(model.parameters, max_norm: 1.0)
1196
+
1197
+ # 2. One-Hot Encoding
1198
+ one_hot_matrix = GRX::Utils.one_hot([0, 2, 1], num_classes: 3)
1199
+ ```
1200
+
1201
+ ---
1202
+
1203
+ ## Windows Support & Toolchain Guide
1204
+
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.
269
1209
 
270
1210
  ---
271
1211
 
272
1212
  ## License
273
1213
 
274
- MIT License. See [LICENSE.txt](LICENSE.txt) for full details.
1214
+ MIT License. Copyright (c) 2026 Razo.