use core::ops::Range; #[allow(unused_imports)] use core_maths::*; use std::vec; use std::vec::Vec; use zerovec::ule::AsULE; use zerovec::ZeroSlice;
/// A `D`-dimensional, heap-allocated matrix. /// /// This matrix implementation supports slicing matrices into tightly-packed /// submatrices. For example, indexing into a matrix of size 5x4x3 returns a /// matrix of size 4x3. For more information, see [`MatrixOwned::submatrix`]. #[derive(Debug, Clone)] pub(super) struct MatrixOwned<const D: usize> {
data: Vec<f32>,
dims: [usize; D],
}
/// Returns the tightly packed submatrix at _index_, or `None` if _index_ is out of range. /// /// For example, if the matrix is 5x4x3, this function returns a matrix sized 4x3. If the /// matrix is 4x3, then this function returns a linear matrix of length 3. /// /// The type parameter `M` should be `D - 1`. #[inline] pub(super) fn submatrix<const M: usize>(&self, index: usize) -> Option<MatrixBorrowed<'_, M>> { // This assertion is based on const generics; it should always succeed and be elided.
assert_eq!(M, D - 1); let (range, dims) = self.as_borrowed().submatrix_range(index); let data = &self.data.get(range)?;
Some(MatrixBorrowed { data, dims })
}
/// A mutable version of [`Self::submatrix`]. #[inline] pub(super) fn submatrix_mut<const M: usize>(
&mutself,
index: usize,
) -> Option<MatrixBorrowedMut<'_, M>> { // This assertion is based on const generics; it should always succeed and be elided.
assert_eq!(M, D - 1); let (range, dims) = self.as_borrowed().submatrix_range(index); let data = self.data.get_mut(range)?;
Some(MatrixBorrowedMut { data, dims })
}
}
/// See [`MatrixOwned::submatrix`]. #[inline] pub(super) fn submatrix<const M: usize>(&self, index: usize) -> Option<MatrixBorrowed<'a, M>> { // This assertion is based on const generics; it should always succeed and be elided.
assert_eq!(M, D - 1); let (range, dims) = self.submatrix_range(index); let data = &self.data.get(range)?;
Some(MatrixBorrowed { data, dims })
}
#[inline] fn submatrix_range<const M: usize>(&self, index: usize) -> (Range<usize>, [usize; M]) { // This assertion is based on const generics; it should always succeed and be elided.
assert_eq!(M, D - 1); // The above assertion guarantees that the following line will succeed #[expect(clippy::unwrap_used)] let sub_dims: [usize; M] = self.dims[1..].try_into().unwrap(); let n = sub_dims.iter().product::<usize>();
(n * index..n * (index + 1), sub_dims)
}
}
#[allow(dead_code)] pub(super) fn copy_submatrix<const M: usize>(&mutself, from: usize, to: usize) { let (range_from, _) = self.as_borrowed().submatrix_range::<M>(from); let (range_to, _) = self.as_borrowed().submatrix_range::<M>(to); iflet (Some(_), Some(_)) = ( self.data.get(range_from.clone()), self.data.get(range_to.clone()),
) { // This function is panicky, but we just validated the ranges self.data.copy_within(range_from, range_to.start);
}
}
#[allow(dead_code)] // maybe needed for more complicated bies calculations /// Mutates this matrix by applying a softmax transformation. pub(super) fn softmax_transform(&mutself) { for v inself.data.iter_mut() {
*v = v.exp();
} let sm = 1.0 / self.data.iter().sum::<f32>(); for v inself.data.iter_mut() {
*v *= sm;
}
}
#[allow(dead_code)] pub(super) fn sigmoid_transform(&mutself) { for x in &mutself.data.iter_mut() {
*x = 1.0 / (1.0 + (-*x).exp());
}
}
#[allow(dead_code)] pub(super) fn tanh_transform(&mutself) { for x in &mutself.data.iter_mut() {
*x = x.tanh();
}
}
#[allow(dead_code)] pub(super) fn convolve(
&mutself,
i: MatrixBorrowed<'_, D>,
c: MatrixBorrowed<'_, D>,
f: MatrixBorrowed<'_, D>,
) { let i = i.as_slice(); let c = c.as_slice(); let f = f.as_slice(); let len = self.data.len(); if len != i.len() || len != c.len() || len != f.len() {
debug_assert!(false, "LSTM matrices not the correct dimensions"); return;
} for idx in0..len { // Safety: The lengths are all the same (checked above) unsafe {
*self.data.get_unchecked_mut(idx) = i.get_unchecked(idx) * c.get_unchecked(idx)
+ self.data.get_unchecked(idx) * f.get_unchecked(idx)
}
}
}
#[allow(dead_code)] pub(super) fn mul_tanh(&mutself, o: MatrixBorrowed<'_, D>, c: MatrixBorrowed<'_, D>) { let o = o.as_slice(); let c = c.as_slice(); let len = self.data.len(); if len != o.len() || len != c.len() {
debug_assert!(false, "LSTM matrices not the correct dimensions"); return;
} for idx in0..len { // Safety: The lengths are all the same (checked above) unsafe {
*self.data.get_unchecked_mut(idx) =
o.get_unchecked(idx) * c.get_unchecked(idx).tanh();
}
}
}
}
impl MatrixBorrowedMut<'_, 1> { /// Calculate the dot product of a and b, adding the result to self. /// /// Note: For better dot product efficiency, if `b` is MxN, then `a` should be N; /// this is the opposite of standard practice. #[allow(dead_code)] pub(super) fn add_dot_2d(&mutself, a: MatrixBorrowed<1>, b: MatrixZero<2>) { let m = a.dim(); let n = self.as_borrowed().dim();
debug_assert_eq!(
m,
b.dim().1, "dims: {:?}/{:?}/{:?}", self.as_borrowed().dim(),
a.dim(),
b.dim()
);
debug_assert_eq!(
n,
b.dim().0, "dims: {:?}/{:?}/{:?}", self.as_borrowed().dim(),
a.dim(),
b.dim()
); for i in0..n { iflet (Some(dest), Some(b_sub)) = (self.as_mut_slice().get_mut(i), b.submatrix::<1>(i))
{
*dest += unrolled_dot_1(a.data, b_sub.data);
} else {
debug_assert!(false, "unreachable: dims checked above");
}
}
}
}
impl MatrixBorrowedMut<'_, 2> { /// Calculate the dot product of a and b, adding the result to self. /// /// Self should be _MxN_; `a`, _O_; and `b`, _MxNxO_. #[allow(dead_code)] pub(super) fn add_dot_3d_1(&mutself, a: MatrixBorrowed<1>, b: MatrixZero<3>) { let m = a.dim(); let n = self.as_borrowed().dim().0 * self.as_borrowed().dim().1;
debug_assert_eq!(
m,
b.dim().2, "dims: {:?}/{:?}/{:?}", self.as_borrowed().dim(),
a.dim(),
b.dim()
);
debug_assert_eq!(
n,
b.dim().0 * b.dim().1, "dims: {:?}/{:?}/{:?}", self.as_borrowed().dim(),
a.dim(),
b.dim()
); // Note: The following two loops are equivalent, but the second has more opportunity for // vectorization since it allows the vectorization to span submatrices. // for i in 0..b.dim().0 { // self.submatrix_mut::<1>(i).add_dot_2d(a, b.submatrix(i)); // } let lhs = a.as_slice(); for i in0..n { iflet (Some(dest), Some(rhs)) = ( self.as_mut_slice().get_mut(i),
b.as_slice().get_subslice(i * m..(i + 1) * m),
) {
*dest += unrolled_dot_1(lhs, rhs);
} else {
debug_assert!(false, "unreachable: dims checked above");
}
}
}
/// Calculate the dot product of a and b, adding the result to self. /// /// Self should be _MxN_; `a`, _O_; and `b`, _MxNxO_. #[allow(dead_code)] pub(super) fn add_dot_3d_2(&mutself, a: MatrixZero<1>, b: MatrixZero<3>) { let m = a.dim(); let n = self.as_borrowed().dim().0 * self.as_borrowed().dim().1;
debug_assert_eq!(
m,
b.dim().2, "dims: {:?}/{:?}/{:?}", self.as_borrowed().dim(),
a.dim(),
b.dim()
);
debug_assert_eq!(
n,
b.dim().0 * b.dim().1, "dims: {:?}/{:?}/{:?}", self.as_borrowed().dim(),
a.dim(),
b.dim()
); // Note: The following two loops are equivalent, but the second has more opportunity for // vectorization since it allows the vectorization to span submatrices. // for i in 0..b.dim().0 { // self.submatrix_mut::<1>(i).add_dot_2d(a, b.submatrix(i)); // } let lhs = a.as_slice(); for i in0..n { iflet (Some(dest), Some(rhs)) = ( self.as_mut_slice().get_mut(i),
b.as_slice().get_subslice(i * m..(i + 1) * m),
) {
*dest += unrolled_dot_2(lhs, rhs);
} else {
debug_assert!(false, "unreachable: dims checked above");
}
}
}
}
/// A `D`-dimensional matrix borrowed from a [`ZeroSlice`]. #[derive(Debug, Clone, Copy)] pub(super) struct MatrixZero<'a, const D: usize> {
data: &'a ZeroSlice<f32>,
dims: [usize; D],
}
/// See [`MatrixOwned::submatrix`]. #[inline] pub(super) fn submatrix<const M: usize>(&self, index: usize) -> Option<MatrixZero<'a, M>> { // This assertion is based on const generics; it should always succeed and be elided.
assert_eq!(M, D - 1); let (range, dims) = self.submatrix_range(index); let data = &self.data.get_subslice(range)?;
Some(MatrixZero { data, dims })
}
#[inline] fn submatrix_range<const M: usize>(&self, index: usize) -> (Range<usize>, [usize; M]) { // This assertion is based on const generics; it should always succeed and be elided.
assert_eq!(M, D - 1); // The above assertion guarantees that the following line will succeed #[expect(clippy::unwrap_used)] let sub_dims: [usize; M] = self.dims[1..].try_into().unwrap(); let n = sub_dims.iter().product::<usize>();
(n * index..n * (index + 1), sub_dims)
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.