Skip to main content

torsh_ffi/
ruby.rs

1//! Ruby FFI bindings for ToRSh
2//!
3//! This module provides Ruby-compatible FFI bindings using the existing C API.
4//! Ruby can call C functions directly through the FFI gem.
5
6#![allow(dead_code)]
7
8use crate::c_api::*;
9use std::os::raw::{c_char, c_float, c_int, c_void};
10
11/// Ruby-specific wrapper for tensor creation
12#[no_mangle]
13pub unsafe extern "C" fn ruby_tensor_new(
14    data: *const c_void,
15    shape: *const usize,
16    ndim: usize,
17    dtype: TorshDType,
18) -> *mut TorshTensor {
19    // Delegate to the C API
20    torsh_tensor_new(data, shape, ndim, dtype)
21}
22
23/// Ruby-specific wrapper for tensor addition
24#[no_mangle]
25pub unsafe extern "C" fn ruby_tensor_add(
26    a: *mut TorshTensor,
27    b: *mut TorshTensor,
28    output: *mut TorshTensor,
29) -> TorshError {
30    torsh_tensor_add(a, b, output)
31}
32
33/// Ruby-specific wrapper for tensor multiplication
34#[no_mangle]
35pub unsafe extern "C" fn ruby_tensor_mul(
36    a: *mut TorshTensor,
37    b: *mut TorshTensor,
38    output: *mut TorshTensor,
39) -> TorshError {
40    torsh_tensor_mul(a, b, output)
41}
42
43/// Ruby-specific wrapper for matrix multiplication
44#[no_mangle]
45pub unsafe extern "C" fn ruby_tensor_matmul(
46    a: *mut TorshTensor,
47    b: *mut TorshTensor,
48    output: *mut TorshTensor,
49) -> TorshError {
50    torsh_tensor_matmul(a, b, output)
51}
52
53/// Ruby-specific wrapper for ReLU activation
54#[no_mangle]
55pub unsafe extern "C" fn ruby_tensor_relu(
56    input: *mut TorshTensor,
57    output: *mut TorshTensor,
58) -> TorshError {
59    torsh_tensor_relu(input, output)
60}
61
62/// Ruby-specific wrapper for getting tensor shape
63#[no_mangle]
64pub unsafe extern "C" fn ruby_tensor_shape(
65    tensor: *mut TorshTensor,
66    shape: *mut usize,
67    ndim: *mut usize,
68) -> TorshError {
69    torsh_tensor_shape(tensor, shape, ndim)
70}
71
72/// Ruby-specific wrapper for getting tensor data
73#[no_mangle]
74pub unsafe extern "C" fn ruby_tensor_data(tensor: *mut TorshTensor) -> *const c_void {
75    torsh_tensor_data(tensor)
76}
77
78/// Ruby-specific wrapper for tensor cleanup
79#[no_mangle]
80pub unsafe extern "C" fn ruby_tensor_free(tensor: *mut TorshTensor) {
81    torsh_tensor_free(tensor)
82}
83
84/// Ruby-specific wrapper for creating a linear layer
85#[no_mangle]
86pub unsafe extern "C" fn ruby_linear_new(
87    in_features: usize,
88    out_features: usize,
89    bias: c_int,
90) -> *mut TorshModule {
91    torsh_linear_new(in_features, out_features, bias != 0)
92}
93
94/// Ruby-specific wrapper for linear layer forward pass
95#[no_mangle]
96pub unsafe extern "C" fn ruby_linear_forward(
97    module: *mut TorshModule,
98    input: *mut TorshTensor,
99) -> *mut TorshTensor {
100    // For simplicity, use input tensor as output (in-place operation)
101    let error = torsh_linear_forward(module, input, input);
102    if error != TorshError::Success {
103        return std::ptr::null_mut();
104    }
105
106    input
107}
108
109/// Ruby-specific wrapper for module cleanup
110#[no_mangle]
111pub unsafe extern "C" fn ruby_module_free(module: *mut TorshModule) {
112    torsh_module_free(module)
113}
114
115/// Ruby-specific wrapper for SGD optimizer
116#[no_mangle]
117pub unsafe extern "C" fn ruby_sgd_new(
118    learning_rate: c_float,
119    momentum: c_float,
120) -> *mut TorshOptimizer {
121    torsh_sgd_new(learning_rate, momentum)
122}
123
124/// Ruby-specific wrapper for Adam optimizer
125#[no_mangle]
126pub unsafe extern "C" fn ruby_adam_new(
127    learning_rate: c_float,
128    beta1: c_float,
129    beta2: c_float,
130    epsilon: c_float,
131) -> *mut TorshOptimizer {
132    torsh_adam_new(learning_rate, beta1, beta2, epsilon)
133}
134
135/// Ruby-specific wrapper for optimizer step
136#[no_mangle]
137pub unsafe extern "C" fn ruby_optimizer_step(
138    optimizer: *mut TorshOptimizer,
139    _parameters: *mut *mut TorshTensor,
140    _gradients: *mut *mut TorshTensor,
141    _param_count: usize,
142) -> TorshError {
143    torsh_optimizer_step(optimizer)
144}
145
146/// Ruby-specific wrapper for optimizer cleanup
147#[no_mangle]
148pub unsafe extern "C" fn ruby_optimizer_free(optimizer: *mut TorshOptimizer) {
149    torsh_optimizer_free(optimizer)
150}
151
152/// Ruby-specific wrapper for getting last error
153#[no_mangle]
154pub unsafe extern "C" fn ruby_get_last_error() -> *const c_char {
155    torsh_get_last_error()
156}
157
158/// Ruby-specific wrapper for clearing last error
159#[no_mangle]
160pub unsafe extern "C" fn ruby_clear_last_error() {
161    torsh_clear_last_error()
162}
163
164/// Ruby-specific wrapper for version information
165#[no_mangle]
166pub unsafe extern "C" fn ruby_version() -> *const c_char {
167    torsh_version()
168}
169
170/// Ruby-specific wrapper for device information
171#[no_mangle]
172pub unsafe extern "C" fn ruby_cuda_is_available() -> c_int {
173    torsh_cuda_is_available()
174}
175
176/// Ruby-specific wrapper for CUDA device count
177#[no_mangle]
178pub unsafe extern "C" fn ruby_cuda_device_count() -> c_int {
179    torsh_cuda_device_count()
180}
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185
186    #[test]
187    fn test_ruby_tensor_operations() {
188        // Test basic tensor creation through Ruby API
189        let data = vec![1.0f32, 2.0, 3.0, 4.0];
190        let shape = vec![2, 2];
191
192        unsafe {
193            let tensor = ruby_tensor_new(
194                data.as_ptr() as *const c_void,
195                shape.as_ptr(),
196                shape.len(),
197                TorshDType::F32,
198            );
199
200            assert!(!tensor.is_null());
201
202            // Test tensor shape retrieval
203            let mut retrieved_shape = vec![0usize; 2];
204            let mut ndim = 0;
205            let result = ruby_tensor_shape(tensor, retrieved_shape.as_mut_ptr(), &mut ndim);
206            assert_eq!(result, TorshError::Success);
207            assert_eq!(ndim, 2);
208            assert_eq!(retrieved_shape, shape);
209
210            // Clean up
211            ruby_tensor_free(tensor);
212        }
213    }
214
215    #[test]
216    fn test_ruby_module_operations() {
217        unsafe {
218            // Test linear layer creation
219            let linear = ruby_linear_new(4, 2, 1);
220            assert!(!linear.is_null());
221
222            // Test forward pass with dummy input
223            let input_data = vec![1.0f32, 2.0, 3.0, 4.0];
224            let input_shape = vec![1, 4];
225
226            let input_tensor = ruby_tensor_new(
227                input_data.as_ptr() as *const c_void,
228                input_shape.as_ptr(),
229                input_shape.len(),
230                TorshDType::F32,
231            );
232
233            let output = ruby_linear_forward(linear, input_tensor);
234            assert!(!output.is_null());
235
236            // Clean up
237            ruby_tensor_free(input_tensor);
238            ruby_tensor_free(output);
239            ruby_module_free(linear);
240        }
241    }
242
243    #[test]
244    fn test_ruby_optimizer_operations() {
245        unsafe {
246            // Test SGD optimizer creation
247            let sgd = ruby_sgd_new(0.01, 0.9);
248            assert!(!sgd.is_null());
249
250            // Test Adam optimizer creation
251            let adam = ruby_adam_new(0.001, 0.9, 0.999, 1e-8);
252            assert!(!adam.is_null());
253
254            // Clean up
255            ruby_optimizer_free(sgd);
256            ruby_optimizer_free(adam);
257        }
258    }
259}