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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/GUIA_PRINCIPIANTES.md +307 -24
- data/README.es.md +1090 -161
- data/README.md +1102 -162
- 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 +37 -33
- data/lib/grx/c_api.rb +47 -15
- data/lib/grx/nn.rb +9 -7
- data/lib/grx/optim.rb +12 -7
- data/lib/grx/storage.rb +6 -5
- data/lib/grx/tensor.rb +36 -0
- data/lib/grx/utils.rb +15 -0
- data/lib/grx/version.rb +1 -1
- data/lib/grx.rb +8 -3
- metadata +31 -27
data/ext/grx/grx_core.h
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* grx_core.h — API
|
|
2
|
+
* grx_core.h — API publica del nucleo C de GRX
|
|
3
|
+
* =============================================================
|
|
4
|
+
* Compatible con compilacion universal y despacho dinamico SIMD
|
|
5
|
+
* (AVX2 + FMA / SSE / Escalar C)
|
|
3
6
|
* =============================================================
|
|
4
7
|
*/
|
|
5
8
|
|
|
@@ -18,20 +21,24 @@
|
|
|
18
21
|
extern "C" {
|
|
19
22
|
#endif
|
|
20
23
|
|
|
24
|
+
/* ---- Diagnostico y nivel SIMD ----------------------------------- */
|
|
25
|
+
/* Retorna 2 para AVX2+FMA, 1 para SSE, 0 para Escalar portable C */
|
|
26
|
+
GRX_API int grx_simd_level(void);
|
|
27
|
+
|
|
21
28
|
/* ---- Memoria alineada -------------------------------------------- */
|
|
22
29
|
GRX_API double* grx_alloc(size_t n);
|
|
23
30
|
GRX_API void grx_free(double *ptr);
|
|
24
31
|
|
|
25
|
-
/* ---- Element-wise
|
|
26
|
-
GRX_API void grx_add
|
|
27
|
-
GRX_API void grx_sub
|
|
28
|
-
GRX_API void grx_mul
|
|
29
|
-
GRX_API void grx_div
|
|
30
|
-
GRX_API void grx_scale
|
|
31
|
-
GRX_API void grx_negate(const double *a, double *out, size_t n);
|
|
32
|
-
GRX_API void grx_add_scalar(const double *a, double s,
|
|
32
|
+
/* ---- Element-wise aritmetica ------------------------------------- */
|
|
33
|
+
GRX_API void grx_add (const double *a, const double *b, double *out, size_t n);
|
|
34
|
+
GRX_API void grx_sub (const double *a, const double *b, double *out, size_t n);
|
|
35
|
+
GRX_API void grx_mul (const double *a, const double *b, double *out, size_t n);
|
|
36
|
+
GRX_API void grx_div (const double *a, const double *b, double *out, size_t n);
|
|
37
|
+
GRX_API void grx_scale (const double *a, double s, double *out, size_t n);
|
|
38
|
+
GRX_API void grx_negate (const double *a, double *out, size_t n);
|
|
39
|
+
GRX_API void grx_add_scalar(const double *a, double s, double *out, size_t n);
|
|
33
40
|
|
|
34
|
-
/* ---- Element-wise
|
|
41
|
+
/* ---- Element-wise matematicas ------------------------------------ */
|
|
35
42
|
GRX_API void grx_abs (const double *a, double *out, size_t n);
|
|
36
43
|
GRX_API void grx_sqrt (const double *a, double *out, size_t n);
|
|
37
44
|
GRX_API void grx_log (const double *a, double *out, size_t n);
|
|
@@ -46,7 +53,7 @@ GRX_API double grx_mean(const double *a, size_t n);
|
|
|
46
53
|
GRX_API double grx_max (const double *a, size_t n);
|
|
47
54
|
GRX_API double grx_min (const double *a, size_t n);
|
|
48
55
|
|
|
49
|
-
/* ----
|
|
56
|
+
/* ---- Algebra lineal ---------------------------------------------- */
|
|
50
57
|
GRX_API double grx_dot (const double *a, const double *b, size_t n);
|
|
51
58
|
GRX_API void grx_matmul (const double *a, const double *b, double *out,
|
|
52
59
|
size_t M, size_t K, size_t N);
|
|
@@ -63,7 +70,7 @@ GRX_API void grx_softmax (const double *a, double *out, size_t n);
|
|
|
63
70
|
GRX_API void grx_sgd_step(double *param, const double *grad,
|
|
64
71
|
double lr, size_t n);
|
|
65
72
|
|
|
66
|
-
/* Adam: actualiza param, m, v in-place */
|
|
73
|
+
/* Adam: actualiza param, m, v in-place con aceleracion FMA */
|
|
67
74
|
GRX_API void grx_adam_step(double *param,
|
|
68
75
|
double *m, double *v,
|
|
69
76
|
const double *grad,
|
|
@@ -71,13 +78,16 @@ GRX_API void grx_adam_step(double *param,
|
|
|
71
78
|
double epsilon, double beta1t, double beta2t,
|
|
72
79
|
size_t n);
|
|
73
80
|
|
|
74
|
-
/* ----
|
|
81
|
+
/* ---- Inicializacion de pesos ------------------------------------- */
|
|
75
82
|
/* Xavier uniform: U(-limit, limit), limit = sqrt(6 / (fan_in + fan_out)) */
|
|
76
83
|
GRX_API void grx_init_xavier_uniform(double *out, size_t n,
|
|
77
84
|
size_t fan_in, size_t fan_out);
|
|
78
85
|
/* He normal: N(0, sqrt(2/fan_in)) */
|
|
79
86
|
GRX_API void grx_init_he_normal(double *out, size_t n, size_t fan_in);
|
|
80
87
|
|
|
88
|
+
/* Extension init for Ruby */
|
|
89
|
+
GRX_API void Init_grx_core(void);
|
|
90
|
+
|
|
81
91
|
#ifdef __cplusplus
|
|
82
92
|
}
|
|
83
93
|
#endif
|
data/ext/unix/Makefile
CHANGED
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
# =============================================================
|
|
2
2
|
# Makefile — Linux / macOS
|
|
3
|
-
#
|
|
4
|
-
# Compila grx_core.c DIRECTAMENTE en lib/grx/ (sin archivo intermedio).
|
|
5
|
-
# No hay .so en ext/unix/ — el único binario vive en lib/grx/.
|
|
6
|
-
#
|
|
7
|
-
# Uso:
|
|
8
|
-
# make → compila (detecta OS y SIMD automáticamente)
|
|
9
|
-
# make clean → elimina el binario de lib/grx/
|
|
10
|
-
# make bench → compila y corre el benchmark
|
|
11
3
|
# =============================================================
|
|
12
4
|
|
|
13
|
-
CC
|
|
14
|
-
CFLAGS = -O3 -
|
|
5
|
+
CC ?= gcc
|
|
6
|
+
CFLAGS = -O3 -ffast-math -funroll-loops \
|
|
15
7
|
-fPIC -fvisibility=hidden \
|
|
16
8
|
-Wall -Wextra -std=c11
|
|
17
9
|
LDFLAGS = -lm
|
|
@@ -19,7 +11,6 @@ SRC_DIR = ../grx
|
|
|
19
11
|
SRC = $(SRC_DIR)/grx_core.c
|
|
20
12
|
HEADER = $(SRC_DIR)/grx_core.h
|
|
21
13
|
|
|
22
|
-
# Destino final — directamente en lib/grx/
|
|
23
14
|
OUT_DIR = ../../lib/grx
|
|
24
15
|
|
|
25
16
|
UNAME := $(shell uname -s)
|
|
@@ -33,21 +24,6 @@ endif
|
|
|
33
24
|
|
|
34
25
|
TARGET = $(OUT_DIR)/$(LIB)
|
|
35
26
|
|
|
36
|
-
# Detección de SIMD
|
|
37
|
-
AVX2_TEST := $(shell echo 'int main(){}' | $(CC) -mavx2 -mfma -x c - -o /dev/null 2>&1)
|
|
38
|
-
ifeq ($(AVX2_TEST),)
|
|
39
|
-
CFLAGS += -mavx2 -mfma
|
|
40
|
-
$(info [GRX] AVX2 + FMA habilitados — máxima velocidad SIMD)
|
|
41
|
-
else
|
|
42
|
-
SSE2_TEST := $(shell echo 'int main(){}' | $(CC) -msse2 -x c - -o /dev/null 2>&1)
|
|
43
|
-
ifeq ($(SSE2_TEST),)
|
|
44
|
-
CFLAGS += -msse2
|
|
45
|
-
$(info [GRX] SSE2 habilitado)
|
|
46
|
-
else
|
|
47
|
-
$(info [GRX] Sin SIMD — modo escalar)
|
|
48
|
-
endif
|
|
49
|
-
endif
|
|
50
|
-
|
|
51
27
|
.PHONY: all clean bench
|
|
52
28
|
|
|
53
29
|
all: $(TARGET)
|
|
@@ -55,7 +31,7 @@ all: $(TARGET)
|
|
|
55
31
|
$(TARGET): $(SRC) $(HEADER)
|
|
56
32
|
@mkdir -p $(OUT_DIR)
|
|
57
33
|
$(CC) $(CFLAGS) $(SHARED) $(SRC) -o $(TARGET) $(LDFLAGS)
|
|
58
|
-
@echo "[GRX] Compilado → $(TARGET)"
|
|
34
|
+
@echo "[GRX] Compilado exitosamente → $(TARGET)"
|
|
59
35
|
|
|
60
36
|
bench: all
|
|
61
37
|
@echo "[GRX] Corriendo benchmark..."
|
data/ext/windows/Makefile.mingw
CHANGED
|
@@ -1,41 +1,21 @@
|
|
|
1
1
|
# =============================================================
|
|
2
2
|
# Makefile.mingw — Windows (MinGW-w64 / MSYS2)
|
|
3
|
-
#
|
|
4
|
-
# Compila grx_core.c DIRECTAMENTE en lib/grx/ (sin archivo intermedio).
|
|
5
|
-
# El único .dll vive en lib/grx/grx_core.dll
|
|
6
|
-
#
|
|
7
|
-
# Requisitos:
|
|
8
|
-
# MSYS2: pacman -S mingw-w64-x86_64-gcc
|
|
9
|
-
#
|
|
10
|
-
# Uso:
|
|
11
|
-
# make -f Makefile.mingw → compila
|
|
12
|
-
# make -f Makefile.mingw clean → elimina el .dll de lib/grx/
|
|
13
3
|
# =============================================================
|
|
14
4
|
|
|
15
|
-
CC
|
|
16
|
-
CFLAGS = -O3 -
|
|
5
|
+
CC ?= x86_64-w64-mingw32-gcc
|
|
6
|
+
CFLAGS = -O3 -ffast-math -funroll-loops \
|
|
17
7
|
-Wall -Wextra -std=c11
|
|
18
8
|
LDFLAGS = -lm
|
|
19
9
|
SRC_DIR = ../grx
|
|
20
10
|
SRC = $(SRC_DIR)/grx_core.c
|
|
21
11
|
HEADER = $(SRC_DIR)/grx_core.h
|
|
22
12
|
|
|
23
|
-
# Destino final — directamente en lib/grx/
|
|
24
13
|
OUT_DIR = ../../lib/grx
|
|
25
14
|
LIB = grx_core.dll
|
|
26
15
|
TARGET = $(OUT_DIR)/$(LIB)
|
|
27
16
|
|
|
28
|
-
# __declspec(dllexport) ya está en el header vía GRX_API
|
|
29
17
|
SHARED = -shared -Wl,--out-implib,$(OUT_DIR)/libgrx_core.a
|
|
30
18
|
|
|
31
|
-
AVX2_TEST := $(shell echo 'int main(){}' | $(CC) -mavx2 -mfma -x c - -o NUL 2>&1)
|
|
32
|
-
ifeq ($(AVX2_TEST),)
|
|
33
|
-
CFLAGS += -mavx2 -mfma
|
|
34
|
-
$(info [GRX] AVX2 + FMA habilitados)
|
|
35
|
-
else
|
|
36
|
-
$(info [GRX] Sin AVX2 — modo escalar)
|
|
37
|
-
endif
|
|
38
|
-
|
|
39
19
|
.PHONY: all clean
|
|
40
20
|
|
|
41
21
|
all: $(TARGET)
|
|
@@ -43,7 +23,7 @@ all: $(TARGET)
|
|
|
43
23
|
$(TARGET): $(SRC) $(HEADER)
|
|
44
24
|
mkdir -p $(OUT_DIR)
|
|
45
25
|
$(CC) $(CFLAGS) $(SHARED) $(SRC) -o $(TARGET) $(LDFLAGS)
|
|
46
|
-
@echo [GRX] Compilado → $(TARGET)
|
|
26
|
+
@echo [GRX] Compilado exitosamente → $(TARGET)
|
|
47
27
|
|
|
48
28
|
clean:
|
|
49
29
|
rm -f "$(TARGET)" "$(OUT_DIR)/libgrx_core.a" 2>/dev/null || true
|
data/grx-tensor.gemspec
CHANGED
|
@@ -11,8 +11,9 @@ Gem::Specification.new do |spec|
|
|
|
11
11
|
spec.summary = "Tensor framework for Ruby with autograd and a C+SIMD compute core"
|
|
12
12
|
spec.description = <<~DESC
|
|
13
13
|
GRX brings PyTorch-style tensor operations to Ruby. Every arithmetic op,
|
|
14
|
-
activation, and optimizer step runs through a native C library
|
|
15
|
-
|
|
14
|
+
activation, and optimizer step runs through a native C library with
|
|
15
|
+
dynamic multi-target SIMD dispatch (AVX2+FMA, SSE, and scalar fallback).
|
|
16
|
+
Ruby is the interface — C does the work.
|
|
16
17
|
|
|
17
18
|
Features: autograd, SGD/Adam optimizers, Linear/Sequential/Dropout/BatchNorm
|
|
18
19
|
layers, MSE/BCE/CrossEntropy loss functions, Xavier and He weight init.
|
|
@@ -45,41 +46,44 @@ Gem::Specification.new do |spec|
|
|
|
45
46
|
|
|
46
47
|
spec.require_paths = ["lib"]
|
|
47
48
|
|
|
48
|
-
# rake-compiler compiles ext/grx/extconf.rb on `gem install`
|
|
49
|
+
# rake-compiler / rubygems compiles ext/grx/extconf.rb on `gem install`
|
|
49
50
|
spec.extensions = ["ext/grx/extconf.rb"]
|
|
50
51
|
|
|
51
52
|
spec.post_install_message = <<~MSG
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
GRX-Tensor #{GRX::VERSION}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
54
|
+
============================================================
|
|
55
|
+
GRX-Tensor #{GRX::VERSION}
|
|
56
|
+
============================================================
|
|
57
|
+
|
|
58
|
+
[ENGLISH]
|
|
59
|
+
Thank you for choosing and using GRX-Tensor!
|
|
60
|
+
|
|
61
|
+
* Native C acceleration with dynamic SIMD dispatch
|
|
62
|
+
(AVX2+FMA, SSE, and portable scalar C) is built and
|
|
63
|
+
configured automatically.
|
|
64
|
+
* Windows Note: Native C acceleration is supported when
|
|
65
|
+
using RubyInstaller with DevKit (MSYS2 / MinGW-w64).
|
|
66
|
+
Standalone pre-compiled Windows binaries are currently
|
|
67
|
+
in active development.
|
|
68
|
+
* Documentation, guides, and tutorials:
|
|
69
|
+
https://github.com/Gabo-Razo/grx-tensor
|
|
70
|
+
|
|
71
|
+
----------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
[ESPAÑOL]
|
|
74
|
+
Muchas gracias por elegir y utilizar GRX-Tensor!
|
|
75
|
+
|
|
76
|
+
* La aceleracion nativa en C con despacho dinamico SIMD
|
|
77
|
+
(AVX2+FMA, SSE y C escalar portable) se compila y
|
|
78
|
+
configura de forma totalmente automatica.
|
|
79
|
+
* Nota para Windows: La aceleracion nativa esta soportada
|
|
80
|
+
al utilizar RubyInstaller con DevKit (MSYS2 / MinGW-w64).
|
|
81
|
+
Los binarios pre-compilados independientes para Windows
|
|
82
|
+
se encuentran actualmente en desarrollo activo.
|
|
83
|
+
* Documentacion, guias y tutoriales:
|
|
84
|
+
https://github.com/Gabo-Razo/grx-tensor
|
|
85
|
+
|
|
86
|
+
============================================================
|
|
83
87
|
|
|
84
88
|
MSG
|
|
85
89
|
|
data/lib/grx/c_api.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "fiddle"
|
|
4
4
|
require "fiddle/import"
|
|
5
|
+
require "rbconfig"
|
|
5
6
|
|
|
6
7
|
module GRX
|
|
7
8
|
module CAPI
|
|
@@ -9,20 +10,36 @@ module GRX
|
|
|
9
10
|
|
|
10
11
|
CANDIDATE_NAMES = case RUBY_PLATFORM
|
|
11
12
|
when /mingw|mswin|windows/i
|
|
12
|
-
["grx_core.dll", "libgrx_core.dll", "
|
|
13
|
+
["grx_core.dll", "libgrx_core.dll", "grx_core.so", "libgrx_core.so"]
|
|
13
14
|
when /darwin/i
|
|
14
15
|
["libgrx_core.dylib", "grx_core.bundle", "libgrx_core.so", "grx_core.so"]
|
|
15
16
|
else
|
|
16
17
|
["libgrx_core.so", "grx_core.so", "libgrx_core.dylib", "grx_core.dll"]
|
|
17
18
|
end
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
# Search directories including standard Gem extension build paths
|
|
21
|
+
SEARCH_DIRS = begin
|
|
22
|
+
dirs = [
|
|
23
|
+
File.expand_path(__dir__), # lib/grx/
|
|
24
|
+
File.expand_path("..", __dir__), # lib/
|
|
25
|
+
File.expand_path("../../ext/grx", __dir__), # ext/grx/
|
|
26
|
+
File.expand_path("../../ext/unix", __dir__), # ext/unix/
|
|
27
|
+
File.expand_path("../../ext/windows", __dir__) # ext/windows/
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
# Gem extensions build directory (RubyGems standard)
|
|
31
|
+
if defined?(Gem) && Gem.loaded_specs["grx-tensor"]
|
|
32
|
+
ext_dir = Gem.loaded_specs["grx-tensor"].extension_dir
|
|
33
|
+
dirs << ext_dir if ext_dir && File.directory?(ext_dir)
|
|
34
|
+
dirs << File.join(ext_dir, "grx") if ext_dir && File.directory?(File.join(ext_dir, "grx"))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Ruby sitearch / vendorarch directories
|
|
38
|
+
sitearch = RbConfig::CONFIG["sitearchdir"]
|
|
39
|
+
dirs << sitearch if sitearch && File.directory?(sitearch)
|
|
40
|
+
|
|
41
|
+
dirs.uniq.freeze
|
|
42
|
+
end
|
|
26
43
|
|
|
27
44
|
LIB_PATHS = SEARCH_DIRS.flat_map do |dir|
|
|
28
45
|
CANDIDATE_NAMES.flat_map do |name|
|
|
@@ -33,22 +50,25 @@ module GRX
|
|
|
33
50
|
end
|
|
34
51
|
end.uniq.freeze
|
|
35
52
|
|
|
53
|
+
LOADED_PATH = LIB_PATHS.find { |p| File.file?(p) && File.exist?(p) }
|
|
54
|
+
|
|
36
55
|
LOADED = begin
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
dlload path
|
|
56
|
+
if LOADED_PATH
|
|
57
|
+
dlload LOADED_PATH
|
|
40
58
|
true
|
|
41
59
|
else
|
|
42
|
-
|
|
60
|
+
false
|
|
43
61
|
end
|
|
44
62
|
rescue Fiddle::DLError => e
|
|
45
|
-
warn "[GRX] C extension
|
|
46
|
-
"
|
|
47
|
-
" → Running in pure Ruby fallback mode (without SIMD)."
|
|
63
|
+
warn "[GRX] C extension load error: #{e.message}\n" \
|
|
64
|
+
" -> Running in pure Ruby fallback mode."
|
|
48
65
|
false
|
|
49
66
|
end
|
|
50
67
|
|
|
51
68
|
if LOADED
|
|
69
|
+
# CPU SIMD Level
|
|
70
|
+
extern "int grx_simd_level(void)"
|
|
71
|
+
|
|
52
72
|
# Memory management
|
|
53
73
|
extern "double* grx_alloc(size_t)"
|
|
54
74
|
extern "void grx_free(double*)"
|
|
@@ -96,5 +116,17 @@ module GRX
|
|
|
96
116
|
extern "void grx_init_xavier_uniform(double*, size_t, size_t, size_t)"
|
|
97
117
|
extern "void grx_init_he_normal (double*, size_t, size_t)"
|
|
98
118
|
end
|
|
119
|
+
|
|
120
|
+
def self.simd_mode
|
|
121
|
+
return :ruby unless LOADED
|
|
122
|
+
|
|
123
|
+
case grx_simd_level
|
|
124
|
+
when 2 then :avx2
|
|
125
|
+
when 1 then :sse
|
|
126
|
+
else :scalar
|
|
127
|
+
end
|
|
128
|
+
rescue StandardError
|
|
129
|
+
:scalar
|
|
130
|
+
end
|
|
99
131
|
end
|
|
100
132
|
end
|
data/lib/grx/nn.rb
CHANGED
|
@@ -147,8 +147,10 @@ module GRX
|
|
|
147
147
|
end
|
|
148
148
|
|
|
149
149
|
class LeakyReLU < Module
|
|
150
|
-
|
|
151
|
-
|
|
150
|
+
attr_reader :alpha
|
|
151
|
+
|
|
152
|
+
def initialize(alpha_arg = nil, alpha: nil)
|
|
153
|
+
@alpha = (alpha || alpha_arg || 0.01).to_f
|
|
152
154
|
end
|
|
153
155
|
def forward(x) = x.leaky_relu(@alpha)
|
|
154
156
|
def to_s = "LeakyReLU(alpha=#{@alpha})"
|
|
@@ -242,10 +244,10 @@ module GRX
|
|
|
242
244
|
class LayerNorm < Module
|
|
243
245
|
attr_reader :gamma, :beta, :normalized_shape, :epsilon
|
|
244
246
|
|
|
245
|
-
def initialize(normalized_shape, epsilon: 1e-5)
|
|
247
|
+
def initialize(normalized_shape, eps: nil, epsilon: 1e-5)
|
|
246
248
|
@normalized_shape = normalized_shape.is_a?(Array) ? normalized_shape : [normalized_shape]
|
|
247
249
|
@dim = @normalized_shape.reduce(1, :*)
|
|
248
|
-
@epsilon = epsilon
|
|
250
|
+
@epsilon = (eps || epsilon).to_f
|
|
249
251
|
|
|
250
252
|
@gamma = Tensor.ones(@normalized_shape, requires_grad: true)
|
|
251
253
|
@beta = Tensor.zeros(@normalized_shape, requires_grad: true)
|
|
@@ -318,10 +320,10 @@ module GRX
|
|
|
318
320
|
# BatchNorm1d — Normalizacion por batch
|
|
319
321
|
# ================================================================
|
|
320
322
|
class BatchNorm1d < Module
|
|
321
|
-
def initialize(num_features, epsilon: 1e-5, momentum: 0.1)
|
|
323
|
+
def initialize(num_features, eps: nil, epsilon: 1e-5, momentum: 0.1)
|
|
322
324
|
@num_features = num_features
|
|
323
|
-
@epsilon = epsilon
|
|
324
|
-
@momentum = momentum
|
|
325
|
+
@epsilon = (eps || epsilon).to_f
|
|
326
|
+
@momentum = momentum.to_f
|
|
325
327
|
@training = true
|
|
326
328
|
|
|
327
329
|
@gamma = Tensor.ones([num_features], requires_grad: true)
|
data/lib/grx/optim.rb
CHANGED
|
@@ -54,14 +54,19 @@ module GRX
|
|
|
54
54
|
# The standard optimizer for deep neural networks.
|
|
55
55
|
# ================================================================
|
|
56
56
|
class Adam
|
|
57
|
-
def initialize(params, lr: 0.001, beta1: 0.9, beta2: 0.999,
|
|
58
|
-
epsilon: 1e-8, weight_decay: 0.0)
|
|
57
|
+
def initialize(params, lr: 0.001, betas: nil, beta1: 0.9, beta2: 0.999,
|
|
58
|
+
eps: nil, epsilon: 1e-8, weight_decay: 0.0)
|
|
59
59
|
@params = params
|
|
60
|
-
@lr = lr
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
@lr = lr.to_f
|
|
61
|
+
if betas
|
|
62
|
+
@beta1 = betas[0].to_f
|
|
63
|
+
@beta2 = betas[1].to_f
|
|
64
|
+
else
|
|
65
|
+
@beta1 = beta1.to_f
|
|
66
|
+
@beta2 = beta2.to_f
|
|
67
|
+
end
|
|
68
|
+
@epsilon = eps ? eps.to_f : epsilon.to_f
|
|
69
|
+
@weight_decay = weight_decay.to_f
|
|
65
70
|
@t = 0 # current step
|
|
66
71
|
|
|
67
72
|
# First and second order moment vectors (zero-initialized)
|
data/lib/grx/storage.rb
CHANGED
|
@@ -7,19 +7,20 @@ module GRX
|
|
|
7
7
|
# Storage — Native memory buffer
|
|
8
8
|
#
|
|
9
9
|
# When CAPI is loaded:
|
|
10
|
-
# @ptr
|
|
10
|
+
# @ptr -> Fiddle::Pointer to 32-byte aligned doubles block
|
|
11
11
|
# allocated via grx_alloc() (C posix_memalign / _aligned_malloc).
|
|
12
12
|
# Data lives in C heap, NOT managed by Ruby GC.
|
|
13
13
|
#
|
|
14
14
|
# When CAPI is NOT loaded (fallback):
|
|
15
|
-
# @data
|
|
15
|
+
# @data -> Standard Ruby Array (slow but correct).
|
|
16
16
|
# ===================================================================
|
|
17
17
|
class Storage
|
|
18
18
|
attr_reader :size
|
|
19
19
|
attr_reader :ptr
|
|
20
20
|
|
|
21
21
|
def initialize(array_plano)
|
|
22
|
-
|
|
22
|
+
flat = array_plano.is_a?(Array) ? array_plano.flatten : Array(array_plano)
|
|
23
|
+
@size = flat.size
|
|
23
24
|
|
|
24
25
|
if CAPI::LOADED
|
|
25
26
|
# Fast mode: aligned C memory
|
|
@@ -27,7 +28,7 @@ module GRX
|
|
|
27
28
|
raise StorageError, "grx_alloc failed (OOM)" if @ptr.null?
|
|
28
29
|
|
|
29
30
|
# Pack Ruby Array into C buffer as IEEE 754 doubles
|
|
30
|
-
bytes =
|
|
31
|
+
bytes = flat.map(&:to_f).pack("d*")
|
|
31
32
|
@ptr[0, bytes.bytesize] = bytes
|
|
32
33
|
|
|
33
34
|
# Finalizer releases C memory upon Ruby GC collection
|
|
@@ -35,7 +36,7 @@ module GRX
|
|
|
35
36
|
ObjectSpace.define_finalizer(self, self.class.make_finalizer(ptr_to_free))
|
|
36
37
|
else
|
|
37
38
|
# Fallback mode: Ruby Array
|
|
38
|
-
@data =
|
|
39
|
+
@data = flat.map(&:to_f)
|
|
39
40
|
@ptr = nil
|
|
40
41
|
end
|
|
41
42
|
end
|
data/lib/grx/tensor.rb
CHANGED
|
@@ -345,6 +345,34 @@ module GRX
|
|
|
345
345
|
end
|
|
346
346
|
end
|
|
347
347
|
|
|
348
|
+
def argmax
|
|
349
|
+
arr = to_a
|
|
350
|
+
return 0 if arr.empty?
|
|
351
|
+
max_idx = 0
|
|
352
|
+
max_val = arr[0]
|
|
353
|
+
(1...arr.size).each do |i|
|
|
354
|
+
if arr[i] > max_val
|
|
355
|
+
max_val = arr[i]
|
|
356
|
+
max_idx = i
|
|
357
|
+
end
|
|
358
|
+
end
|
|
359
|
+
max_idx
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def argmin
|
|
363
|
+
arr = to_a
|
|
364
|
+
return 0 if arr.empty?
|
|
365
|
+
min_idx = 0
|
|
366
|
+
min_val = arr[0]
|
|
367
|
+
(1...arr.size).each do |i|
|
|
368
|
+
if arr[i] < min_val
|
|
369
|
+
min_val = arr[i]
|
|
370
|
+
min_idx = i
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
min_idx
|
|
374
|
+
end
|
|
375
|
+
|
|
348
376
|
# ----------------------------------------------------------------
|
|
349
377
|
# LINEAR ALGEBRA
|
|
350
378
|
# ----------------------------------------------------------------
|
|
@@ -538,6 +566,10 @@ module GRX
|
|
|
538
566
|
@storage.read(_calc_flat_index(coords))
|
|
539
567
|
end
|
|
540
568
|
|
|
569
|
+
def set(*coords, val)
|
|
570
|
+
@storage.write(_calc_flat_index(coords), val.to_f)
|
|
571
|
+
end
|
|
572
|
+
|
|
541
573
|
def contiguous
|
|
542
574
|
return self if _contiguous?
|
|
543
575
|
c = Tensor.create(to_a, @shape, requires_grad: @requires_grad)
|
|
@@ -614,6 +646,10 @@ module GRX
|
|
|
614
646
|
@shape.reduce(1, :*)
|
|
615
647
|
end
|
|
616
648
|
|
|
649
|
+
def rank
|
|
650
|
+
@shape.size
|
|
651
|
+
end
|
|
652
|
+
|
|
617
653
|
def to_a
|
|
618
654
|
# If strides are contiguous (normal tensor, reshape), read buffer directly.
|
|
619
655
|
# Otherwise (transpose, strided views), traverse with custom strides.
|
data/lib/grx/utils.rb
CHANGED
|
@@ -24,5 +24,20 @@ module GRX
|
|
|
24
24
|
end
|
|
25
25
|
total_norm
|
|
26
26
|
end
|
|
27
|
+
|
|
28
|
+
# ================================================================
|
|
29
|
+
# one_hot — Generates a 2D One-Hot encoded Tensor from class indices
|
|
30
|
+
# ================================================================
|
|
31
|
+
def self.one_hot(indices, num_classes: nil, requires_grad: false)
|
|
32
|
+
ids = indices.is_a?(Tensor) ? indices.to_a.map(&:to_i) : Array(indices).map(&:to_i)
|
|
33
|
+
c = num_classes || (ids.empty? ? 0 : ids.max + 1)
|
|
34
|
+
n = ids.size
|
|
35
|
+
matrix_data = Array.new(n * c, 0.0)
|
|
36
|
+
ids.each_with_index do |class_id, row|
|
|
37
|
+
raise IndexError, "Class index #{class_id} out of bounds [0, #{c})" if class_id < 0 || class_id >= c
|
|
38
|
+
matrix_data[row * c + class_id] = 1.0
|
|
39
|
+
end
|
|
40
|
+
Tensor.create(matrix_data, [n, c], requires_grad: requires_grad)
|
|
41
|
+
end
|
|
27
42
|
end
|
|
28
43
|
end
|
data/lib/grx/version.rb
CHANGED
data/lib/grx.rb
CHANGED
|
@@ -41,11 +41,12 @@ module GRX
|
|
|
41
41
|
n = shape.reduce(1, :*)
|
|
42
42
|
data = []
|
|
43
43
|
(n / 2.0).ceil.times do
|
|
44
|
-
u1 = ::Kernel.rand
|
|
44
|
+
u1 = ::Kernel.rand
|
|
45
|
+
u1 = ::Kernel.rand while u1 < 1e-15
|
|
45
46
|
u2 = ::Kernel.rand
|
|
46
47
|
r = Math.sqrt(-2.0 * Math.log(u1))
|
|
47
|
-
data << r * Math.cos(2 * Math::PI * u2)
|
|
48
|
-
data << r * Math.sin(2 * Math::PI * u2)
|
|
48
|
+
data << (r * Math.cos(2 * Math::PI * u2))
|
|
49
|
+
data << (r * Math.sin(2 * Math::PI * u2))
|
|
49
50
|
end
|
|
50
51
|
Tensor.create(data.first(n), shape, requires_grad: requires_grad)
|
|
51
52
|
end
|
|
@@ -57,4 +58,8 @@ module GRX
|
|
|
57
58
|
def self.mode
|
|
58
59
|
CAPI::LOADED ? :c : :ruby
|
|
59
60
|
end
|
|
61
|
+
|
|
62
|
+
def self.simd_mode
|
|
63
|
+
CAPI.simd_mode
|
|
64
|
+
end
|
|
60
65
|
end
|