dfdx 0.13.0

Ergonomic auto differentiation in Rust, with pytorch like apis.
Documentation
use super::*;
use std::ops::{Bound, RangeBounds};

fn get_start_bound(bound: Bound<&usize>) -> usize {
    match bound {
        Bound::Included(x) => *x,
        Bound::Excluded(x) => x + 1,
        Bound::Unbounded => 0,
    }
}

fn get_end_bound(bound: Bound<&usize>, size: usize) -> usize {
    match bound {
        Bound::Excluded(x) => *x,
        Bound::Included(x) => x + 1,
        Bound::Unbounded => size,
    }
}

pub trait SliceDim<R: RangeBounds<usize>>: Dim {
    type Sliced: Dim;

    fn slice(&self, range: &R) -> Option<Self::Sliced> {
        let size = self.size();

        let start_bound = get_start_bound(range.start_bound());
        let end_bound = get_end_bound(range.end_bound(), size);

        (end_bound <= size && start_bound <= end_bound)
            .then_some(end_bound - start_bound)
            .and_then(Self::Sliced::from_size)
    }
}

macro_rules! slice_dim_to_usize {
    ($range:ty) => {
        impl<D: Dim> SliceDim<$range> for D {
            type Sliced = usize;
        }
    };
}

slice_dim_to_usize!(std::ops::Range<usize>);
slice_dim_to_usize!(std::ops::RangeTo<usize>);
slice_dim_to_usize!(std::ops::RangeFrom<usize>);
slice_dim_to_usize!(std::ops::RangeInclusive<usize>);
slice_dim_to_usize!(std::ops::RangeToInclusive<usize>);

impl<D: Dim> SliceDim<std::ops::RangeFull> for D {
    type Sliced = D;

    fn slice(&self, _: &std::ops::RangeFull) -> Option<D> {
        Some(*self)
    }
}

pub trait SliceShape<R>: Shape {
    type Sliced: Shape<Concrete = Self::Concrete>;

    fn slice(&self, range: &R) -> Option<Self::Sliced>;
    fn first_idx_in_slice(&self, range: &R) -> usize;
}

impl SliceShape<()> for () {
    type Sliced = Self;

    fn slice(&self, _range: &()) -> Option<Self> {
        Some(())
    }

    fn first_idx_in_slice(&self, _range: &()) -> usize {
        0
    }
}

use super::broadcasts::length;

macro_rules! slice_shape {
    ([$($dim:ident)*] [$($range:ident)*] [$($idx:tt)*]) => {
        impl<$($dim: Dim),*, $($range: RangeBounds<usize>),*> SliceShape<($($range,)*)> for ($($dim,)*)
        where
            $($dim: SliceDim<$range>),*
        {
            type Sliced = ($($dim::Sliced,)*);

            fn slice(&self, range: &($($range,)*)) -> Option<Self::Sliced> {
                Some(($(self.$idx.slice(&range.$idx)?,)*))
            }

            fn first_idx_in_slice(&self, range: &($($range,)*)) -> usize {
                let strides = self.strides();
                $(get_start_bound(range.$idx.start_bound()) * strides[$idx] + )* 0
            }
        }

        impl<$($range: RangeBounds<usize>),*> SliceShape<($($range,)*)> for [usize; {length!($($range)*)}]
        where
            $(usize: SliceDim<$range>),*
        {
            type Sliced = ($(<usize as SliceDim<$range>>::Sliced,)*);

            fn slice(&self, range: &($($range,)*)) -> Option<Self::Sliced> {
                Some(($(self[$idx].slice(&range.$idx)?,)*))
            }

            fn first_idx_in_slice(&self, range: &($($range,)*)) -> usize {
                let strides = self.strides();
                $(get_start_bound(range.$idx.start_bound()) * strides[$idx] + )* 0
            }
        }
    }
}

slice_shape!([D1][R1][0]);
slice_shape!([D1 D2] [R1 R2] [0 1]);
slice_shape!([D1 D2 D3] [R1 R2 R3] [0 1 2]);
slice_shape!([D1 D2 D3 D4] [R1 R2 R3 R4] [0 1 2 3]);
slice_shape!([D1 D2 D3 D4 D5] [R1 R2 R3 R4 R5] [0 1 2 3 4]);
slice_shape!([D1 D2 D3 D4 D5 D6] [R1 R2 R3 R4 R5 R6] [0 1 2 3 4 5]);