chela 0.0.2

High-performance Machine Learning, Auto-Differentiation and Tensor Algebra crate for Rust
Documentation
use crate::gradient_function::{GradientFunction};
use crate::ndarray::flags::NdArrayFlags;
use crate::ndarray::NdArray;
use crate::util::flatten::Flatten;
use crate::util::nested::Nested;
use crate::util::shape::Shape;
use crate::util::to_vec::ToVec;
use crate::{Tensor, TensorDataType};
use crate::none_backwards::NoneBackwards;

impl<'a, T: TensorDataType> Tensor<'a, T> {
    /// Constructs a new tensor from the given array, gradient function, and metadata
    ///
    /// # Parameters
    /// - `requires_grad`: If gradients need to be computed for this tensor
    /// - `grad_fn`: The gradient function used on the backwards pass
    pub(crate) unsafe fn from_raw_parts(array: NdArray<'a, T>,
                                        requires_grad: bool,
                                        grad_fn: GradientFunction<T>) -> Self {
        let mut flags = NdArrayFlags::empty();

        if requires_grad {
            flags |= NdArrayFlags::RequiresGrad;
        }

        Self {
            array,
            flags,
            grad_fn,
        }
    }

    /// Constructs a new tensor from the given array
    ///
    /// # Parameters
    /// - `requires_grad`: If gradients need to be computed for this tensor
    ///
    /// # Safety
    /// - `user_created` must be set only if the Tensor was generated by the user outside this crate
    pub(crate) unsafe fn from_array_and_flags(array: NdArray<'a, T>,
                                              requires_grad: bool,
                                              user_created: bool) -> Self {
        let mut flags = NdArrayFlags::empty();

        if requires_grad {
            flags |= NdArrayFlags::RequiresGrad;
        }

        if user_created {
            flags |= NdArrayFlags::UserCreated;
        }

        Self {
            array,
            flags,
            grad_fn: NoneBackwards::new(),
        }
    }

    /// Constructs an n-dimensional `Tensor` from input data such as a vector or array.
    ///
    /// # Parameters
    /// - `data`: a nested array or vector of valid data types (floats, integers, bools)
    ///
    /// # Panics
    ///   - If the input data has inhomogeneous dimensions, i.e., nested arrays do not have consistent sizes.
    ///   - If the input data is empty (cannot create a zero-length tensor)
    ///
    /// # Example
    /// ```
    /// # use chela::*;
    ///
    /// let tensor = Tensor::from([[1.0, 2.0], [3.0, 4.0]]);
    /// assert_eq!(tensor.shape(), &[2, 2]);
    ///
    /// let tensor = Tensor::from(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
    /// assert_eq!(tensor.ndims(), 1);
    /// ```
    pub fn from<const D: usize>(data: impl Flatten<T> + Shape + Nested<{ D }>) -> Self {
        let array = NdArray::from(data);
        unsafe { Tensor::from_array_and_flags(array, false, true) }
    }

    /// Creates a tensor filled with a specified value and given shape.
    ///
    /// # Parameters
    ///
    /// * `n` - The value to fill the tensor with (can be any valid data type like float, integer, or bool).
    /// * `shape` - An array or vector representing the shape of the tensor (e.g. `[2, 3, 5]`).
    ///
    /// # Panics
    /// This function panics if the provided shape is empty.
    ///
    /// # Examples
    ///
    /// ```
    /// # use chela::*;
    ///
    /// let tensor = Tensor::full(5.0, [2, 3]); // creates a 2x3 tensor filled with the value 5.
    /// ```
    pub fn full(n: T, shape: impl ToVec<usize>) -> Self {
        let array = NdArray::full(n, shape);
        unsafe { Tensor::from_array_and_flags(array, false, true) }
    }

    /// Creates a new tensor filled with zeros with the given shape.
    ///
    /// # Parameters
    /// - `shape`: An array or vector representing the shape of the tensor (e.g. `[2, 3, 5]`).
    ///
    /// # Panics
    /// This function panics if the provided shape is empty.
    ///
    /// # Examples
    /// ```
    /// # use chela::*;
    ///
    /// let tensor = Tensor::<f32>::zeros([2, 3]);
    /// ```
    pub fn zeros(shape: impl ToVec<usize>) -> Self {
        let array = NdArray::zeros(shape);
        unsafe { Tensor::from_array_and_flags(array, false, true) }
    }

    /// Creates a new tensor filled with ones with the given shape.
    ///
    /// # Parameters
    /// - `shape`: An array or vector representing the shape of the tensor (e.g. `[2, 3, 5]`).
    ///
    /// # Panics
    /// This function panics if the provided shape is empty.
    ///
    /// # Examples
    /// ```
    /// # use chela::*;
    ///
    /// let tensor = Tensor::<f32>::ones([2, 3]);
    /// ```
    pub fn ones(shape: impl ToVec<usize>) -> Self {
        let array = NdArray::ones(shape);
        unsafe { Tensor::from_array_and_flags(array, false, true) }
    }

    /// Creates a 0-dimensional (shapeless) tensor containing a single value.
    ///
    /// # Parameters
    /// - `n`: The value to be stored in the scalar tensor.
    ///
    /// # Example
    /// ```rust
    /// # use chela::*;
    ///
    /// let scalar_array = Tensor::scalar(42.0);
    /// assert_eq!(scalar_array.shape(), []);
    /// assert_eq!(scalar_array.value(), 42.0);
    /// ```
    pub fn scalar(n: T) -> Self {
        let array = NdArray::scalar(n);
        unsafe { Tensor::from_array_and_flags(array, false, true) }
    }

    /// Generates a 1D tensor with evenly spaced values within a specified range.
    ///
    /// # Arguments
    ///
    /// * `start` - The starting value of the sequence, inclusive.
    /// * `stop` - The ending value of the sequence, exclusive.
    ///
    /// # Returns
    ///
    /// An `Tensor` containing values starting from `start` and ending before `stop`,
    /// with a step-size of 1.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use chela::*;
    /// let tensor = Tensor::arange(0.0, 5.0); // [0, 1, 2, 3, 4].
    /// ```
    pub fn arange(start: T, stop: T) -> Tensor<'static, T> {
        let array = NdArray::arange(start, stop);
        unsafe { Tensor::from_array_and_flags(array, false, true) }
    }

    /// Generates a 1D tensor with evenly spaced values within a specified range.
    ///
    /// # Arguments
    ///
    /// * `start` - The starting value of the sequence, inclusive.
    /// * `stop` - The ending value of the sequence, exclusive.
    /// * `step` - The interval between each consecutive value
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use chela::*;
    /// let tensor = Tensor::arange_with_step(0.0, 5.0, 2.0); // [0, 2, 4].
    /// ```
    pub fn arange_with_step(start: T, stop: T, step: T) -> Tensor<'static, T> {
        let array = NdArray::arange_with_step(start, stop, step);
        unsafe { Tensor::from_array_and_flags(array, false, true) }
    }

    /// Generates a 1-dimensional tensor with `num `evenly spaced values between `start` and `stop`
    /// (inclusive).
    ///
    /// # Arguments
    ///
    /// * `start` - The starting value of the sequence.
    /// * `stop` - The ending value of the sequence. The value is inclusive in the range.
    /// * `num` - The number of evenly spaced values to generate. Must be greater than 0.
    ///
    /// # Panic
    ///
    /// Panics if `num` is 0.
    ///
    /// # Example
    ///
    /// ```
    /// # use chela::*;
    /// let result = Tensor::linspace(0f32, 1.0, 5);  // [0.0, 0.25, 0.5, 0.75, 1.0]
    /// assert_eq!(result, Tensor::from([0f32, 0.25, 0.5, 0.75, 1.0]));
    /// ```
    pub fn linspace(start: T, stop: T, num: usize) -> Tensor<'static, T> {
        let array = NdArray::linspace(start, stop, num);
        unsafe { Tensor::from_array_and_flags(array, false, true) }
    }

    /// Generates a 1-dimensional tensor with `num `evenly spaced values between `start` and `stop`
    /// (exclusive).
    ///
    /// # Arguments
    ///
    /// * `start` - The starting value of the sequence.
    /// * `stop` - The ending value of the sequence. The value is exclusive in the range.
    /// * `num` - The number of evenly spaced values to generate. Must be greater than 0.
    ///
    /// # Panic
    ///
    /// Panics if `num` is 0.
    ///
    /// # Example
    ///
    /// ```
    /// # use chela::*;
    /// let result = Tensor::linspace_exclusive(0.0f32, 1.0, 5);
    /// assert_eq!(result, Tensor::from([0f32, 0.2, 0.4, 0.6, 0.8]));
    /// ```
    pub fn linspace_exclusive(start: T, stop: T, num: usize) -> Tensor<'static, T> {
        let array = NdArray::linspace_exclusive(start, stop, num);
        unsafe { Tensor::from_array_and_flags(array, false, true) }
    }
}