grx-tensor 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/ext/grx/grx_core.h CHANGED
@@ -1,5 +1,8 @@
1
1
  /*
2
- * grx_core.h — API pública del núcleo C de GRX
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 aritmética ------------------------------------- */
26
- GRX_API void grx_add (const double *a, const double *b, double *out, size_t n);
27
- GRX_API void grx_sub (const double *a, const double *b, double *out, size_t n);
28
- GRX_API void grx_mul (const double *a, const double *b, double *out, size_t n);
29
- GRX_API void grx_div (const double *a, const double *b, double *out, size_t n);
30
- GRX_API void grx_scale (const double *a, double s, double *out, size_t n);
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, double *out, size_t n);
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 matemáticas ------------------------------------ */
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
- /* ---- Álgebra lineal ---------------------------------------------- */
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
- /* ---- Inicialización de pesos ------------------------------------- */
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 = gcc
14
- CFLAGS = -O3 -march=native -ffast-math -funroll-loops \
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..."
@@ -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 = x86_64-w64-mingw32-gcc
16
- CFLAGS = -O3 -march=native -ffast-math -funroll-loops \
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 compiled
15
- with AVX2+FMA SIMD. Ruby is the interface C does the work.
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} installed
55
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
56
-
57
- Compile the C extension to enable AVX2+FMA SIMD:
58
-
59
- Linux / macOS: make -C ext/unix
60
- Windows: make -C ext/windows -f Makefile.mingw
61
-
62
- Without it, GRX runs in pure Ruby fallback mode (slower but correct).
63
-
64
- Quick start:
65
-
66
- require "grx"
67
-
68
- a = GRX.tensor([1.0, 2.0, 3.0], [3], requires_grad: true)
69
- b = GRX.tensor([4.0, 5.0, 6.0], [3], requires_grad: true)
70
- c = a + b
71
- c.backward
72
- puts a.grad.to_a # [1.0, 1.0, 1.0]
73
-
74
- net = GRX::NN::Sequential.new(
75
- GRX::NN::Linear.new(4, 16),
76
- GRX::NN::ReLU.new,
77
- GRX::NN::Linear.new(16, 1)
78
- )
79
- opt = GRX::Optim::Adam.new(net.parameters, lr: 0.001)
80
-
81
- Docs: https://github.com/Gabo-Razo/grx-tensor
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", "libgrx_core.so", "grx_core.so"]
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
- SEARCH_DIRS = [
20
- File.expand_path(__dir__), # lib/grx/
21
- File.expand_path("..", __dir__), # lib/
22
- File.expand_path("../../ext/grx", __dir__), # ext/grx/
23
- File.expand_path("../../ext/unix", __dir__), # ext/unix/
24
- File.expand_path("../../ext/windows", __dir__) # ext/windows/
25
- ].freeze
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
- path = LIB_PATHS.find { |p| File.file?(p) && File.exist?(p) }
38
- if path
39
- dlload path
56
+ if LOADED_PATH
57
+ dlload LOADED_PATH
40
58
  true
41
59
  else
42
- raise Fiddle::DLError, "Binary library not found (#{CANDIDATE_NAMES.join(', ')}) in #{SEARCH_DIRS.inspect}"
60
+ false
43
61
  end
44
62
  rescue Fiddle::DLError => e
45
- warn "[GRX] C extension unavailable: #{e.message}\n" \
46
- " Run: make -C ext/unix all (Linux/macOS) or make -C ext/windows -f Makefile.mingw all (Windows)\n" \
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
- def initialize(alpha = 0.01)
151
- @alpha = alpha
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
- @beta1 = beta1
62
- @beta2 = beta2
63
- @epsilon = epsilon
64
- @weight_decay = weight_decay
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 Fiddle::Pointer to 32-byte aligned doubles block
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 Standard Ruby Array (slow but correct).
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
- @size = array_plano.size
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 = array_plano.pack("d*")
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 = array_plano.map(&:to_f)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GRX
4
- VERSION = "0.2.0"
4
+ VERSION = "0.2.1"
5
5
  end
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; u1 = ::Kernel.rand while u1 < 1e-15
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