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/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
|
@@ -5,14 +5,15 @@ require_relative "lib/grx/version"
|
|
|
5
5
|
Gem::Specification.new do |spec|
|
|
6
6
|
spec.name = "grx-tensor"
|
|
7
7
|
spec.version = GRX::VERSION
|
|
8
|
-
spec.authors = ["
|
|
8
|
+
spec.authors = ["Razo"]
|
|
9
9
|
spec.email = ["garabatoangelopolis@gmail.com"]
|
|
10
10
|
|
|
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.
|
|
@@ -37,47 +38,52 @@ Gem::Specification.new do |spec|
|
|
|
37
38
|
"ext/windows/Makefile.mingw",
|
|
38
39
|
"*.gemspec",
|
|
39
40
|
"README.md",
|
|
41
|
+
"README.es.md",
|
|
42
|
+
"GUIA_PRINCIPIANTES.md",
|
|
40
43
|
"LICENSE.txt",
|
|
41
44
|
"CHANGELOG.md"
|
|
42
|
-
]
|
|
45
|
+
].reject { |f| f.match?(/\.(so|dll|dylib|bundle|a)$/) }
|
|
43
46
|
|
|
44
47
|
spec.require_paths = ["lib"]
|
|
45
48
|
|
|
46
|
-
# rake-compiler compiles ext/grx/extconf.rb on `gem install`
|
|
49
|
+
# rake-compiler / rubygems compiles ext/grx/extconf.rb on `gem install`
|
|
47
50
|
spec.extensions = ["ext/grx/extconf.rb"]
|
|
48
51
|
|
|
49
52
|
spec.post_install_message = <<~MSG
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
GRX-Tensor #{GRX::VERSION}
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
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
|
+
============================================================
|
|
81
87
|
|
|
82
88
|
MSG
|
|
83
89
|
|
data/lib/grx/c_api.rb
CHANGED
|
@@ -2,54 +2,78 @@
|
|
|
2
2
|
|
|
3
3
|
require "fiddle"
|
|
4
4
|
require "fiddle/import"
|
|
5
|
+
require "rbconfig"
|
|
5
6
|
|
|
6
7
|
module GRX
|
|
7
8
|
module CAPI
|
|
8
9
|
extend Fiddle::Importer
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
#
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
11
|
+
CANDIDATE_NAMES = case RUBY_PLATFORM
|
|
12
|
+
when /mingw|mswin|windows/i
|
|
13
|
+
["grx_core.dll", "libgrx_core.dll", "grx_core.so", "libgrx_core.so"]
|
|
14
|
+
when /darwin/i
|
|
15
|
+
["libgrx_core.dylib", "grx_core.bundle", "libgrx_core.so", "grx_core.so"]
|
|
16
|
+
else
|
|
17
|
+
["libgrx_core.so", "grx_core.so", "libgrx_core.dylib", "grx_core.dll"]
|
|
18
|
+
end
|
|
19
|
+
|
|
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
|
|
43
|
+
|
|
44
|
+
LIB_PATHS = SEARCH_DIRS.flat_map do |dir|
|
|
45
|
+
CANDIDATE_NAMES.flat_map do |name|
|
|
46
|
+
[
|
|
47
|
+
File.join(dir, name),
|
|
48
|
+
File.join(dir, name).tr("/", "\\")
|
|
49
|
+
]
|
|
50
|
+
end
|
|
51
|
+
end.uniq.freeze
|
|
52
|
+
|
|
53
|
+
LOADED_PATH = LIB_PATHS.find { |p| File.file?(p) && File.exist?(p) }
|
|
34
54
|
|
|
35
55
|
LOADED = begin
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
56
|
+
if LOADED_PATH
|
|
57
|
+
dlload LOADED_PATH
|
|
58
|
+
true
|
|
59
|
+
else
|
|
60
|
+
false
|
|
61
|
+
end
|
|
40
62
|
rescue Fiddle::DLError => e
|
|
41
|
-
warn "[GRX]
|
|
42
|
-
"
|
|
43
|
-
" → Corriendo en modo Ruby puro (sin SIMD)."
|
|
63
|
+
warn "[GRX] C extension load error: #{e.message}\n" \
|
|
64
|
+
" -> Running in pure Ruby fallback mode."
|
|
44
65
|
false
|
|
45
66
|
end
|
|
46
67
|
|
|
47
68
|
if LOADED
|
|
48
|
-
#
|
|
69
|
+
# CPU SIMD Level
|
|
70
|
+
extern "int grx_simd_level(void)"
|
|
71
|
+
|
|
72
|
+
# Memory management
|
|
49
73
|
extern "double* grx_alloc(size_t)"
|
|
50
74
|
extern "void grx_free(double*)"
|
|
51
75
|
|
|
52
|
-
#
|
|
76
|
+
# Element-wise arithmetic
|
|
53
77
|
extern "void grx_add (double*, double*, double*, size_t)"
|
|
54
78
|
extern "void grx_sub (double*, double*, double*, size_t)"
|
|
55
79
|
extern "void grx_mul (double*, double*, double*, size_t)"
|
|
@@ -58,7 +82,7 @@ module GRX
|
|
|
58
82
|
extern "void grx_add_scalar(double*, double, double*, size_t)"
|
|
59
83
|
extern "void grx_negate (double*, double*, size_t)"
|
|
60
84
|
|
|
61
|
-
#
|
|
85
|
+
# Element-wise math
|
|
62
86
|
extern "void grx_abs (double*, double*, size_t)"
|
|
63
87
|
extern "void grx_sqrt (double*, double*, size_t)"
|
|
64
88
|
extern "void grx_square(double*, double*, size_t)"
|
|
@@ -67,30 +91,42 @@ module GRX
|
|
|
67
91
|
extern "void grx_pow (double*, double, double*, size_t)"
|
|
68
92
|
extern "void grx_clip (double*, double, double, double*, size_t)"
|
|
69
93
|
|
|
70
|
-
#
|
|
94
|
+
# Reductions
|
|
71
95
|
extern "double grx_sum (double*, size_t)"
|
|
72
96
|
extern "double grx_mean(double*, size_t)"
|
|
73
97
|
extern "double grx_max (double*, size_t)"
|
|
74
98
|
extern "double grx_min (double*, size_t)"
|
|
75
99
|
|
|
76
|
-
#
|
|
100
|
+
# Linear algebra
|
|
77
101
|
extern "double grx_dot (double*, double*, size_t)"
|
|
78
102
|
extern "void grx_matmul (double*, double*, double*, size_t, size_t, size_t)"
|
|
79
103
|
|
|
80
|
-
#
|
|
104
|
+
# Activations
|
|
81
105
|
extern "void grx_relu (double*, double*, size_t)"
|
|
82
106
|
extern "void grx_leaky_relu (double*, double, double*, size_t)"
|
|
83
107
|
extern "void grx_tanh_act (double*, double*, size_t)"
|
|
84
108
|
extern "void grx_sigmoid (double*, double*, size_t)"
|
|
85
109
|
extern "void grx_softmax (double*, double*, size_t)"
|
|
86
110
|
|
|
87
|
-
#
|
|
111
|
+
# Optimizers
|
|
88
112
|
extern "void grx_sgd_step (double*, double*, double, size_t)"
|
|
89
113
|
extern "void grx_adam_step(double*, double*, double*, double*, double, double, double, double, double, double, size_t)"
|
|
90
114
|
|
|
91
|
-
#
|
|
115
|
+
# Weight initialization
|
|
92
116
|
extern "void grx_init_xavier_uniform(double*, size_t, size_t, size_t)"
|
|
93
117
|
extern "void grx_init_he_normal (double*, size_t, size_t)"
|
|
94
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
|
|
95
131
|
end
|
|
96
132
|
end
|
data/lib/grx/data.rb
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GRX
|
|
4
|
+
module Data
|
|
5
|
+
# ================================================================
|
|
6
|
+
# Dataset — Base class for data collections
|
|
7
|
+
# ================================================================
|
|
8
|
+
class Dataset
|
|
9
|
+
def size
|
|
10
|
+
raise NotImplementedError, "#{self.class}#size must be implemented"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def [](index)
|
|
14
|
+
raise NotImplementedError, "#{self.class}#[] must be implemented"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# ================================================================
|
|
19
|
+
# TensorDataset — Dataset wrapping parallel tensors (e.g. X and Y)
|
|
20
|
+
# ================================================================
|
|
21
|
+
class TensorDataset < Dataset
|
|
22
|
+
attr_reader :tensors, :size
|
|
23
|
+
|
|
24
|
+
def initialize(*tensors)
|
|
25
|
+
raise ArgumentError, "Must provide at least one tensor" if tensors.empty?
|
|
26
|
+
first_dim = tensors.first.shape[0]
|
|
27
|
+
unless tensors.all? { |t| t.shape[0] == first_dim }
|
|
28
|
+
raise ArgumentError, "All tensors must have the same size in batch dimension (dimension 0)"
|
|
29
|
+
end
|
|
30
|
+
@tensors = tensors
|
|
31
|
+
@size = first_dim
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def [](index)
|
|
35
|
+
@tensors.map do |t|
|
|
36
|
+
cols = t.numel / @size
|
|
37
|
+
offset = index * cols
|
|
38
|
+
data = t.to_a.slice(offset, cols)
|
|
39
|
+
new_shape = t.shape.size == 1 ? [1] : [1] + t.shape[1..]
|
|
40
|
+
Tensor.create(data, new_shape)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# ================================================================
|
|
46
|
+
# DataLoader — Mini-batch iterator with optional shuffling
|
|
47
|
+
# ================================================================
|
|
48
|
+
class DataLoader
|
|
49
|
+
include Enumerable
|
|
50
|
+
|
|
51
|
+
attr_reader :dataset, :batch_size, :shuffle
|
|
52
|
+
|
|
53
|
+
def initialize(dataset, batch_size: 32, shuffle: true)
|
|
54
|
+
@dataset = dataset
|
|
55
|
+
@batch_size = batch_size
|
|
56
|
+
@shuffle = shuffle
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def each
|
|
60
|
+
return to_enum(:each) unless block_given?
|
|
61
|
+
|
|
62
|
+
indices = (0...@dataset.size).to_a
|
|
63
|
+
indices.shuffle! if @shuffle
|
|
64
|
+
|
|
65
|
+
indices.each_slice(@batch_size) do |batch_indices|
|
|
66
|
+
batch_samples = batch_indices.map { |i| @dataset[i] }
|
|
67
|
+
num_tensors = batch_samples.first.size
|
|
68
|
+
|
|
69
|
+
batched = (0...num_tensors).map do |t_idx|
|
|
70
|
+
slices = batch_samples.map { |sample| sample[t_idx].to_a }
|
|
71
|
+
flat_data = slices.flatten
|
|
72
|
+
sample_shape = batch_samples.first[t_idx].shape
|
|
73
|
+
batch_dim = batch_indices.size
|
|
74
|
+
target_shape = [batch_dim] + sample_shape[1..]
|
|
75
|
+
Tensor.create(flat_data, target_shape)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
yield(*batched)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def size
|
|
83
|
+
(@dataset.size.to_f / @batch_size).ceil
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|