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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/GUIA_PRINCIPIANTES.md +1046 -0
- data/README.es.md +1199 -0
- data/README.md +1026 -283
- data/ext/grx/extconf.rb +4 -18
- data/ext/grx/grx_core.c +411 -331
- data/ext/grx/grx_core.h +23 -13
- data/ext/unix/Makefile +3 -27
- data/ext/windows/Makefile.mingw +3 -23
- data/grx-tensor.gemspec +41 -35
- data/lib/grx/c_api.rb +75 -39
- data/lib/grx/data.rb +87 -0
- data/lib/grx/loss.rb +55 -25
- data/lib/grx/nn.rb +160 -41
- data/lib/grx/optim.rb +21 -16
- data/lib/grx/serialization.rb +66 -0
- data/lib/grx/storage.rb +19 -27
- data/lib/grx/tensor.rb +234 -65
- data/lib/grx/utils.rb +43 -0
- data/lib/grx/version.rb +1 -1
- data/lib/grx.rb +22 -6
- metadata +37 -28
data/README.md
CHANGED
|
@@ -2,470 +2,1213 @@
|
|
|
2
2
|
|
|
3
3
|
**Ruby speaks. C computes.**
|
|
4
4
|
|
|
5
|
-
A tensor framework for Ruby
|
|
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
|
[](https://www.ruby-lang.org)
|
|
8
8
|
[](LICENSE.txt)
|
|
9
|
-
[]()
|
|
9
|
+
[](https://github.com/Gabo-Razo/grx-tensor)
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
##
|
|
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
|
-
|
|
69
|
+
C --> D1["Linux (GCC / Clang)"]
|
|
70
|
+
C --> D2["macOS (Clang / Apple LLVM)"]
|
|
71
|
+
C --> D3["Windows (RubyInstaller DevKit / MinGW-w64)"]
|
|
16
72
|
|
|
17
|
-
|
|
73
|
+
D1 --> E1["libgrx_core.so"]
|
|
74
|
+
D2 --> E2["libgrx_core.dylib"]
|
|
75
|
+
D3 --> E3["grx_core.dll"]
|
|
18
76
|
|
|
19
|
-
|
|
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
|
-
| **
|
|
22
|
-
| **
|
|
23
|
-
| **
|
|
24
|
-
| **
|
|
25
|
-
| **
|
|
26
|
-
| **
|
|
27
|
-
| **
|
|
28
|
-
| **
|
|
29
|
-
| **
|
|
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
|
|
166
|
+
The native C extension is automatically compiled and linked in the background during installation.
|
|
45
167
|
|
|
46
168
|
---
|
|
47
169
|
|
|
48
|
-
##
|
|
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
|
-
|
|
54
|
-
|
|
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
|
-
|
|
57
|
-
|
|
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
|
-
|
|
60
|
-
|
|
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
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
#
|
|
76
|
-
|
|
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
|
-
|
|
82
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
93
|
-
|
|
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
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
424
|
+
require "grx"
|
|
114
425
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
124
|
-
|
|
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
|
-
|
|
468
|
+
### B. Quantitative Finance & Portfolio Covariance
|
|
469
|
+
|
|
470
|
+
Calculate asset returns, annualized volatility, and portfolio risk matrix:
|
|
133
471
|
|
|
134
472
|
```ruby
|
|
135
|
-
|
|
136
|
-
v = GRX.tensor([4.0, 5.0, 6.0], [3])
|
|
473
|
+
require "grx"
|
|
137
474
|
|
|
138
|
-
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
#
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
521
|
+
### C. Particle Physics & N-Body Simulation
|
|
154
522
|
|
|
155
|
-
|
|
523
|
+
Simulate particle positions, velocities, and pairwise Euclidean distances in 3D:
|
|
156
524
|
|
|
157
525
|
```ruby
|
|
158
|
-
|
|
526
|
+
require "grx"
|
|
159
527
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
166
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
|
|
186
|
-
|
|
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
|
-
##
|
|
599
|
+
## Deep Learning Cookbook (10 Neural Network Architectures)
|
|
192
600
|
|
|
193
|
-
|
|
601
|
+
### Architecture 1: Deep Vision & Character Classifier (BatchNorm + Dropout)
|
|
194
602
|
|
|
195
603
|
```ruby
|
|
196
|
-
|
|
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
|
-
|
|
201
|
-
|
|
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
|
-
|
|
204
|
-
|
|
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
|
-
|
|
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
|
-
|
|
211
|
-
z.backward
|
|
625
|
+
### Architecture 2: NLP & Intent Chatbot (Embedding + LayerNorm)
|
|
212
626
|
|
|
213
|
-
|
|
214
|
-
|
|
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
|
-
|
|
217
|
-
|
|
218
|
-
y.zero_grad!
|
|
650
|
+
logits = classifier.forward(sentence_vector)
|
|
651
|
+
puts "Predicted Intent Class: #{logits.argmax}"
|
|
219
652
|
```
|
|
220
653
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
-
|
|
689
|
+
### Architecture 4: Nonlinear Multi-Variable Time Series Forecasting
|
|
228
690
|
|
|
229
691
|
```ruby
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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(
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
241
|
-
|
|
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
|
-
#
|
|
251
|
-
|
|
252
|
-
pred = net.call(x) # shape [8, 1]
|
|
746
|
+
# Training reconstruction loop
|
|
747
|
+
sample_batch = GRX.rand([8, 64])
|
|
253
748
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
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
|
-
|
|
267
|
-
|
|
268
|
-
|
|
768
|
+
vocab_size = 256 # ASCII alphabet
|
|
769
|
+
embed_dim = 16
|
|
770
|
+
ctx_len = 4 # 4-character context window
|
|
269
771
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
GRX::NN::Linear.new(
|
|
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(
|
|
777
|
+
GRX::NN::Linear.new(64, vocab_size)
|
|
275
778
|
)
|
|
276
779
|
|
|
277
|
-
|
|
278
|
-
|
|
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
|
-
|
|
281
|
-
|
|
785
|
+
logits = language_head.forward(flattened_context) # Shape [1, 256]
|
|
786
|
+
predicted_char_ascii = logits.argmax
|
|
282
787
|
|
|
283
|
-
|
|
284
|
-
|
|
788
|
+
puts "Context: 'hell' -> Predicted Next Char: '#{predicted_char_ascii.chr}' (ASCII #{predicted_char_ascii})"
|
|
789
|
+
```
|
|
285
790
|
|
|
286
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
-
|
|
831
|
+
### Architecture 8: Siamese Neural Network for Similarity & Verification
|
|
303
832
|
|
|
304
|
-
|
|
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
|
-
|
|
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
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
-
|
|
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
|
-
|
|
361
|
-
|
|
905
|
+
require "grx"
|
|
906
|
+
|
|
907
|
+
n_users = 100
|
|
908
|
+
n_items = 50
|
|
909
|
+
latent_dim = 16
|
|
362
910
|
|
|
363
|
-
|
|
364
|
-
GRX::
|
|
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
|
-
|
|
367
|
-
GRX::
|
|
368
|
-
GRX::
|
|
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
|
-
##
|
|
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
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
1149
|
+
### Production Inference Deployment Workflow
|
|
432
1150
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
1191
|
+
## Gradient Management & Utilities (`GRX::Utils`)
|
|
445
1192
|
|
|
446
|
-
|
|
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
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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
|
-
##
|
|
1203
|
+
## Windows Support & Toolchain Guide
|
|
458
1204
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
-
|
|
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
|
|
1214
|
+
MIT License. Copyright (c) 2026 Razo.
|