acyclib 0.3.0

ML library for directed acyclic tensor graphs.
Documentation
use crate::device::{
    Device, OperationError,
    function::DeviceOperation,
    operation::{DiffableFromOutput, SparseAffineOps},
    tensor::{Shape, TensorRef},
};

#[derive(Clone)]
pub struct SparseAffineActivate<D: Device> {
    pub weights: TensorRef<D>,
    pub weights_shape: Shape,
    pub biases: Option<TensorRef<D>>,
    pub input_shape: Shape,
    pub indices: TensorRef<D>,
    pub values: Option<TensorRef<D>>,
    pub activation: DiffableFromOutput,
    pub output: TensorRef<D>,
}

impl<D: SparseAffineOps> DeviceOperation<D> for SparseAffineActivate<D> {
    fn opname(&self) -> String {
        "SparseAffineActivate".to_string()
    }

    fn execute(&self) -> Result<(), OperationError<D::DeviceError>> {
        let SparseAffineActivate { weights, weights_shape, biases, input_shape, indices, values, activation, output } =
            self;

        let weights = weights.dense();
        let indices = indices.sparse();
        let mut output = output.dense_mut();

        let biases = biases.as_ref().map(|b| b.dense());
        let biases = biases.as_deref();
        let values = values.as_ref().map(|v| v.dense());
        let values = values.as_deref();

        let batch_size = indices.batch_size();

        if batch_size != output.batch_size() {
            return Err(OperationError::MismatchedBatchSizes);
        }

        D::sparse_affine_activate(
            batch_size.unwrap_or(1),
            *activation,
            &weights.buf,
            *weights_shape,
            &indices.buf,
            values.map(|v| &v.buf),
            *input_shape,
            indices.nnz,
            biases.map(|b| &b.buf),
            biases.map(|b| b.batch_size.is_some()).unwrap_or(false),
            &mut output.buf,
        )
    }
}

#[derive(Clone)]
pub struct BackpropSparseAffineActivate<D: Device> {
    pub weights_grads: TensorRef<D>,
    pub weights_shape: Shape,
    pub biases_grads: Option<TensorRef<D>>,
    pub input_shape: Shape,
    pub indices: TensorRef<D>,
    pub values: Option<TensorRef<D>>,
    pub activation: DiffableFromOutput,
    pub output: TensorRef<D>,
    pub output_grads: TensorRef<D>,
}

impl<D: SparseAffineOps> DeviceOperation<D> for BackpropSparseAffineActivate<D> {
    fn opname(&self) -> String {
        "BackpropSparseAffineActivate".to_string()
    }

    fn execute(&self) -> Result<(), OperationError<<D as Device>::DeviceError>> {
        let BackpropSparseAffineActivate {
            weights_grads,
            weights_shape,
            biases_grads,
            input_shape,
            indices,
            values,
            activation,
            output,
            output_grads,
        } = self;

        let mut weights_grads = weights_grads.dense_mut();
        let indices = indices.sparse();
        let output = output.dense();
        let output_grads = output_grads.dense();

        let mut biases_grads = biases_grads.as_ref().map(|b| b.dense_mut());
        let biases_grads = biases_grads.as_deref_mut();
        let values = values.as_ref().map(|v| v.dense_mut());
        let values = values.as_deref();

        let biases_batched = biases_grads.as_ref().map(|b| b.batch_size.is_some()).unwrap_or(false);

        let batch_size = indices.batch_size();

        if batch_size != output.batch_size() || batch_size != output_grads.batch_size() {
            return Err(OperationError::MismatchedBatchSizes);
        }

        D::backprop_sparse_affine_activate(
            batch_size.unwrap_or(1),
            *activation,
            &mut weights_grads.buf,
            *weights_shape,
            &indices.buf,
            values.map(|v| &v.buf),
            *input_shape,
            indices.nnz,
            biases_grads.map(|b| &mut b.buf),
            biases_batched,
            &output.buf,
            &output_grads.buf,
        )
    }
}