parent
920bca4a48
commit
6c1d6874d7
@ -1,2 +1,3 @@
|
||||
/target
|
||||
/Cargo.lock
|
||||
/data
|
||||
|
@ -0,0 +1,77 @@
|
||||
#![feature(generic_arg_infer)]
|
||||
// #![feature(generic_const_exprs)]
|
||||
|
||||
use neuramethyst::algebra::NeuraVector;
|
||||
use rust_mnist::Mnist;
|
||||
|
||||
use neuramethyst::derivable::activation::{Linear, Relu};
|
||||
use neuramethyst::derivable::loss::CrossEntropy;
|
||||
use neuramethyst::{cycle_shuffling, one_hot, prelude::*};
|
||||
|
||||
fn main() {
|
||||
const TRAIN_SIZE: usize = 100;
|
||||
|
||||
let Mnist {
|
||||
train_data: train_images,
|
||||
train_labels,
|
||||
test_data: test_images,
|
||||
test_labels,
|
||||
..
|
||||
} = Mnist::new("data/");
|
||||
|
||||
let train_images = train_images
|
||||
.into_iter()
|
||||
.map(|raw| {
|
||||
raw.into_iter()
|
||||
.map(|x| x as f64 / 255.0)
|
||||
.collect::<NeuraVector<{ 28 * 28 }, f64>>()
|
||||
})
|
||||
.take(TRAIN_SIZE);
|
||||
let train_labels = train_labels
|
||||
.into_iter()
|
||||
.map(|x| one_hot::<10>(x as usize))
|
||||
.take(TRAIN_SIZE);
|
||||
|
||||
let test_images = test_images
|
||||
.into_iter()
|
||||
.map(|raw| {
|
||||
raw.into_iter()
|
||||
.map(|x| x as f64 / 255.0)
|
||||
.collect::<NeuraVector<{ 28 * 28 }, f64>>()
|
||||
})
|
||||
.take(TRAIN_SIZE / 6);
|
||||
let test_labels = test_labels
|
||||
.into_iter()
|
||||
.map(|x| one_hot::<10>(x as usize))
|
||||
.take(TRAIN_SIZE / 6);
|
||||
|
||||
let train_iter = cycle_shuffling(
|
||||
train_images.zip(train_labels.into_iter()),
|
||||
rand::thread_rng(),
|
||||
);
|
||||
|
||||
let test_inputs: Vec<_> = test_images.zip(test_labels.into_iter()).collect();
|
||||
|
||||
let mut network = neura_sequential![
|
||||
neura_layer!("dense", { 28 * 28 }, 200; Relu),
|
||||
neura_layer!("dropout", 0.5),
|
||||
neura_layer!("dense", 100; Relu),
|
||||
neura_layer!("dropout", 0.5),
|
||||
neura_layer!("dense", 30; Relu),
|
||||
neura_layer!("dropout", 0.5),
|
||||
neura_layer!("dense", 10; Linear),
|
||||
neura_layer!("softmax")
|
||||
];
|
||||
|
||||
let mut trainer = NeuraBatchedTrainer::new(0.03, TRAIN_SIZE * 10);
|
||||
trainer.log_iterations = (TRAIN_SIZE / 128).max(1);
|
||||
trainer.batch_size = 128;
|
||||
trainer.learning_momentum = 0.001;
|
||||
|
||||
trainer.train(
|
||||
NeuraBackprop::new(CrossEntropy),
|
||||
&mut network,
|
||||
train_iter,
|
||||
&test_inputs,
|
||||
);
|
||||
}
|
@ -0,0 +1,292 @@
|
||||
use std::borrow::Borrow;
|
||||
|
||||
use super::*;
|
||||
use boxed_array::from_cloned;
|
||||
use num::Float;
|
||||
|
||||
/// A simple abstraction around `[[F; WIDTH]; HEIGHT]`,
|
||||
/// which ensures that all allocations that depend on `WIDTH` or `HEIGHT` are done on the heap,
|
||||
/// without losing the length information.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct NeuraMatrix<const WIDTH: usize, const HEIGHT: usize, F> {
|
||||
pub data: Box<[[F; WIDTH]; HEIGHT]>,
|
||||
}
|
||||
|
||||
impl<const WIDTH: usize, const HEIGHT: usize, F> NeuraMatrix<WIDTH, HEIGHT, F> {
|
||||
#[inline(always)]
|
||||
pub fn from_value(value: F) -> Self
|
||||
where
|
||||
F: Clone,
|
||||
{
|
||||
Self {
|
||||
data: from_cloned(&value),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get(&self, x: usize, y: usize) -> Option<&F> {
|
||||
if x >= WIDTH || y >= HEIGHT {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(&self.data[y][x])
|
||||
}
|
||||
}
|
||||
|
||||
impl<const WIDTH: usize, const HEIGHT: usize, F: Float> NeuraMatrix<WIDTH, HEIGHT, F> {
|
||||
/// Returns `self * vector`
|
||||
pub fn multiply_vector(&self, vector: impl Borrow<[F; WIDTH]>) -> NeuraVector<HEIGHT, F> {
|
||||
let mut result: NeuraVector<HEIGHT, F> = NeuraVector::from_value(F::zero());
|
||||
let vector = vector.borrow();
|
||||
|
||||
for i in 0..HEIGHT {
|
||||
let mut sum = F::zero();
|
||||
for k in 0..WIDTH {
|
||||
sum = sum + self.data[i][k] * vector[k];
|
||||
}
|
||||
result[i] = sum;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Returns `transpose(self) * vector`,
|
||||
/// without actually performing the transpose operation
|
||||
pub fn transpose_multiply_vector(
|
||||
&self,
|
||||
vector: impl AsRef<[F; HEIGHT]>,
|
||||
) -> NeuraVector<WIDTH, F> {
|
||||
let mut result: NeuraVector<WIDTH, F> = NeuraVector::from_value(F::zero());
|
||||
let vector = vector.as_ref();
|
||||
|
||||
for j in 0..WIDTH {
|
||||
let mut sum = F::zero();
|
||||
for k in 0..HEIGHT {
|
||||
sum = sum + self.data[k][j] * vector[k];
|
||||
}
|
||||
result[j] = sum;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LENGTH: usize, F: Default + Clone> NeuraMatrix<LENGTH, LENGTH, F> {
|
||||
pub fn from_diagonal(vector: impl AsRef<[F; LENGTH]>) -> Self {
|
||||
let mut result: NeuraMatrix<LENGTH, LENGTH, F> = NeuraMatrix::default();
|
||||
let vector = vector.as_ref();
|
||||
|
||||
for i in 0..LENGTH {
|
||||
result[i][i] = vector[i].clone();
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<const WIDTH: usize, const HEIGHT: usize, F: Float + From<f64> + Into<f64>> NeuraVectorSpace
|
||||
for NeuraMatrix<WIDTH, HEIGHT, F>
|
||||
{
|
||||
fn add_assign(&mut self, other: &Self) {
|
||||
for i in 0..HEIGHT {
|
||||
for j in 0..WIDTH {
|
||||
self.data[i][j] = self.data[i][j] + other.data[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mul_assign(&mut self, by: f64) {
|
||||
let by: F = by.into();
|
||||
for i in 0..HEIGHT {
|
||||
for j in 0..WIDTH {
|
||||
self.data[i][j] = self.data[i][j] * by;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn zero() -> Self {
|
||||
Self::from_value(F::zero())
|
||||
}
|
||||
|
||||
fn norm_squared(&self) -> f64 {
|
||||
let mut sum = F::zero();
|
||||
|
||||
for i in 0..HEIGHT {
|
||||
for j in 0..WIDTH {
|
||||
let x = self.data[i][j];
|
||||
sum = sum + x * x;
|
||||
}
|
||||
}
|
||||
|
||||
sum.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const WIDTH: usize, const HEIGHT: usize, F> From<Box<[[F; WIDTH]; HEIGHT]>>
|
||||
for NeuraMatrix<WIDTH, HEIGHT, F>
|
||||
{
|
||||
#[inline]
|
||||
fn from(data: Box<[[F; WIDTH]; HEIGHT]>) -> Self {
|
||||
Self { data }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const WIDTH: usize, const HEIGHT: usize, F> From<NeuraMatrix<WIDTH, HEIGHT, F>>
|
||||
for Box<[[F; WIDTH]; HEIGHT]>
|
||||
{
|
||||
#[inline]
|
||||
fn from(matrix: NeuraMatrix<WIDTH, HEIGHT, F>) -> Self {
|
||||
matrix.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<const WIDTH: usize, const HEIGHT: usize, F: Default + Clone> From<&[[F; WIDTH]; HEIGHT]>
|
||||
for NeuraMatrix<WIDTH, HEIGHT, F>
|
||||
{
|
||||
/// **Warning:** when using this function, make sure that the array is not allocated on the stack
|
||||
/// or that `WIDTH` and `HEIGHT` are bounded.
|
||||
#[inline]
|
||||
fn from(data: &[[F; WIDTH]; HEIGHT]) -> Self {
|
||||
let mut res = Self::default();
|
||||
|
||||
for i in 0..HEIGHT {
|
||||
for j in 0..WIDTH {
|
||||
res[i][j] = data[i][j].clone();
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<const WIDTH: usize, const HEIGHT: usize, F> From<[[F; WIDTH]; HEIGHT]>
|
||||
for NeuraMatrix<WIDTH, HEIGHT, F>
|
||||
{
|
||||
/// **Warning:** when using this function, make sure that `WIDTH` and `HEIGHT` are bounded.
|
||||
fn from(data: [[F; WIDTH]; HEIGHT]) -> Self {
|
||||
Self {
|
||||
data: Box::new(data),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const WIDTH: usize, const HEIGHT: usize, F> std::ops::Index<(usize, usize)>
|
||||
for NeuraMatrix<WIDTH, HEIGHT, F>
|
||||
{
|
||||
type Output = F;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: (usize, usize)) -> &Self::Output {
|
||||
if index.0 >= WIDTH || index.1 >= HEIGHT {
|
||||
panic!(
|
||||
"Index out of bound: tried indexing matrix element ({}, {}), which is outside of NeuraMatrix<{}, {}, _>",
|
||||
index.0, index.1, WIDTH, HEIGHT
|
||||
);
|
||||
}
|
||||
|
||||
&self.data[index.1][index.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<const WIDTH: usize, const HEIGHT: usize, F> std::ops::IndexMut<(usize, usize)>
|
||||
for NeuraMatrix<WIDTH, HEIGHT, F>
|
||||
{
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output {
|
||||
if index.0 >= WIDTH || index.1 >= HEIGHT {
|
||||
panic!(
|
||||
"Index out of bound: tried indexing matrix element ({}, {}), which is outside of NeuraMatrix<{}, {}, _>",
|
||||
index.0, index.1, WIDTH, HEIGHT
|
||||
);
|
||||
}
|
||||
|
||||
&mut self.data[index.1][index.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<const WIDTH: usize, const HEIGHT: usize, F> std::ops::Index<usize>
|
||||
for NeuraMatrix<WIDTH, HEIGHT, F>
|
||||
{
|
||||
type Output = [F; WIDTH];
|
||||
|
||||
#[inline(always)]
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
if index >= HEIGHT {
|
||||
panic!(
|
||||
"Index out of bound: tried indexing matrix row {}, which is outside of NeuraMatrix<{}, {}, _>",
|
||||
index, WIDTH, HEIGHT
|
||||
);
|
||||
}
|
||||
|
||||
&self.data[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<const WIDTH: usize, const HEIGHT: usize, F> std::ops::IndexMut<usize>
|
||||
for NeuraMatrix<WIDTH, HEIGHT, F>
|
||||
{
|
||||
#[inline(always)]
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
if index >= HEIGHT {
|
||||
panic!(
|
||||
"Index out of bound: tried indexing matrix row {}, which is outside of NeuraMatrix<{}, {}, _>",
|
||||
index, WIDTH, HEIGHT
|
||||
);
|
||||
}
|
||||
|
||||
&mut self.data[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<const WIDTH: usize, const HEIGHT: usize, F> AsRef<[[F; WIDTH]; HEIGHT]>
|
||||
for NeuraMatrix<WIDTH, HEIGHT, F>
|
||||
{
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[[F; WIDTH]; HEIGHT] {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<const WIDTH: usize, const HEIGHT: usize, F> Borrow<[[F; WIDTH]; HEIGHT]>
|
||||
for NeuraMatrix<WIDTH, HEIGHT, F>
|
||||
{
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &[[F; WIDTH]; HEIGHT] {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<const WIDTH: usize, const HEIGHT: usize, F: Default + Clone> Default
|
||||
for NeuraMatrix<WIDTH, HEIGHT, F>
|
||||
{
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
Self::from_value(F::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_index() {
|
||||
let mut matrix: NeuraMatrix<1000, 1000, f64> = NeuraMatrix::from_value(0.0);
|
||||
|
||||
matrix[100][200] = 0.3;
|
||||
assert_eq!(matrix[(200, 100)], 0.3);
|
||||
matrix[(999, 999)] = 0.5;
|
||||
assert_eq!(matrix[999][999], 0.5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(
|
||||
expected = "Index out of bound: tried indexing matrix row 100, which is outside of NeuraMatrix<100, 100, _>"
|
||||
)]
|
||||
fn test_index_oob() {
|
||||
let matrix: NeuraMatrix<100, 100, f64> = NeuraMatrix::from_value(0.0);
|
||||
|
||||
let _ = matrix[100];
|
||||
}
|
||||
}
|
@ -0,0 +1,278 @@
|
||||
use std::borrow::Borrow;
|
||||
|
||||
use super::*;
|
||||
use boxed_array::from_cloned;
|
||||
use num::Float;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct NeuraVector<const LENGTH: usize, F> {
|
||||
pub data: Box<[F; LENGTH]>,
|
||||
}
|
||||
|
||||
impl<const LENGTH: usize, F> NeuraVector<LENGTH, F> {
|
||||
#[inline(always)]
|
||||
pub fn from_value(value: F) -> Self
|
||||
where
|
||||
F: Clone,
|
||||
{
|
||||
Self {
|
||||
data: from_cloned(&value),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get(&self, index: usize) -> Option<&F> {
|
||||
if index >= LENGTH {
|
||||
None
|
||||
} else {
|
||||
Some(&self.data[index])
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn len(&self) -> usize {
|
||||
LENGTH
|
||||
}
|
||||
|
||||
pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, F> {
|
||||
self.data.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LENGTH: usize, F: Float> NeuraVector<LENGTH, F> {
|
||||
pub fn dot(&self, other: impl AsRef<[F; LENGTH]>) -> F {
|
||||
let mut sum = F::zero();
|
||||
let other = other.as_ref();
|
||||
|
||||
for i in 0..LENGTH {
|
||||
sum = sum + self.data[i] * other[i];
|
||||
}
|
||||
|
||||
sum
|
||||
}
|
||||
|
||||
/// Returns $left^{\top} \cdot right$, ie. $\ket{left} \bra{right}$
|
||||
pub fn reverse_dot<const WIDTH: usize>(
|
||||
&self,
|
||||
other: impl Borrow<[F; WIDTH]>,
|
||||
) -> NeuraMatrix<WIDTH, LENGTH, F> {
|
||||
let mut result: NeuraMatrix<WIDTH, LENGTH, F> = NeuraMatrix::from_value(F::zero());
|
||||
let other = other.borrow();
|
||||
|
||||
for i in 0..LENGTH {
|
||||
for j in 0..WIDTH {
|
||||
result[i][j] = self.data[i] * other[j];
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn hadamard_product(&self, other: impl AsRef<[F; LENGTH]>) -> NeuraVector<LENGTH, F> {
|
||||
let mut result: NeuraVector<LENGTH, F> = NeuraVector::from_value(F::zero());
|
||||
let other = other.as_ref();
|
||||
|
||||
for i in 0..LENGTH {
|
||||
result[i] = self.data[i] * other[i];
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LENGTH: usize, F: Float + From<f64> + Into<f64>> NeuraVectorSpace
|
||||
for NeuraVector<LENGTH, F>
|
||||
{
|
||||
fn add_assign(&mut self, other: &Self) {
|
||||
for i in 0..LENGTH {
|
||||
self.data[i] = self.data[i] + other.data[i];
|
||||
}
|
||||
}
|
||||
|
||||
fn mul_assign(&mut self, by: f64) {
|
||||
for i in 0..LENGTH {
|
||||
self.data[i] = self.data[i] * by.into();
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn zero() -> Self {
|
||||
Self::from_value(F::zero())
|
||||
}
|
||||
|
||||
fn norm_squared(&self) -> f64 {
|
||||
let mut sum = F::zero();
|
||||
|
||||
for i in 0..LENGTH {
|
||||
sum = sum + self.data[i] * self.data[i];
|
||||
}
|
||||
|
||||
sum.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LENGTH: usize, F> std::ops::Index<usize> for NeuraVector<LENGTH, F> {
|
||||
type Output = F;
|
||||
|
||||
#[inline(always)]
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
if index >= LENGTH {
|
||||
panic!(
|
||||
"Tried indexing element {} of NeuraVector<{}, _>",
|
||||
index, LENGTH
|
||||
);
|
||||
}
|
||||
|
||||
&self.data[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LENGTH: usize, F> std::ops::IndexMut<usize> for NeuraVector<LENGTH, F> {
|
||||
#[inline(always)]
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
if index >= LENGTH {
|
||||
panic!(
|
||||
"Tried indexing element {} of NeuraVector<{}, _>",
|
||||
index, LENGTH
|
||||
);
|
||||
}
|
||||
|
||||
&mut self.data[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LENGTH: usize, F> AsRef<[F; LENGTH]> for NeuraVector<LENGTH, F> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[F; LENGTH] {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LENGTH: usize, F> AsRef<[F]> for NeuraVector<LENGTH, F> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[F] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LENGTH: usize, F> Borrow<[F; LENGTH]> for NeuraVector<LENGTH, F> {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &[F; LENGTH] {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LENGTH: usize, F> Borrow<[F; LENGTH]> for &NeuraVector<LENGTH, F> {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &[F; LENGTH] {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LENGTH: usize, F> From<Box<[F; LENGTH]>> for NeuraVector<LENGTH, F> {
|
||||
fn from(data: Box<[F; LENGTH]>) -> Self {
|
||||
Self { data }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LENGTH: usize, F> From<NeuraVector<LENGTH, F>> for Box<[F; LENGTH]> {
|
||||
fn from(vector: NeuraVector<LENGTH, F>) -> Self {
|
||||
vector.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LENGTH: usize, F: Default + Clone> From<&[F; LENGTH]> for NeuraVector<LENGTH, F> {
|
||||
/// **Warning:** when using this function, make sure that the array is not allocated on the stack,
|
||||
/// or that `LENGTH` is bounded.
|
||||
fn from(data: &[F; LENGTH]) -> Self {
|
||||
let mut res = Self::default();
|
||||
|
||||
for i in 0..LENGTH {
|
||||
res.data[i] = data[i].clone();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LENGTH: usize, F> From<[F; LENGTH]> for NeuraVector<LENGTH, F> {
|
||||
/// **Warning:** when using this function, make sure that `LENGTH` is bounded.
|
||||
fn from(data: [F; LENGTH]) -> Self {
|
||||
Self {
|
||||
data: Box::new(data),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LENGTH: usize, F: Default + Clone> Default for NeuraVector<LENGTH, F> {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
Self::from_value(F::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LENGTH: usize, F> IntoIterator for NeuraVector<LENGTH, F> {
|
||||
type Item = F;
|
||||
type IntoIter = std::array::IntoIter<F, LENGTH>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.data.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, const LENGTH: usize, F> IntoIterator for &'a NeuraVector<LENGTH, F> {
|
||||
type Item = &'a F;
|
||||
type IntoIter = std::slice::Iter<'a, F>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.data.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, const LENGTH: usize, F> IntoIterator for &'a mut NeuraVector<LENGTH, F> {
|
||||
type Item = &'a mut F;
|
||||
type IntoIter = std::slice::IterMut<'a, F>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.data.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, const LENGTH: usize, F: Default + Clone> FromIterator<F> for NeuraVector<LENGTH, F> {
|
||||
fn from_iter<T: IntoIterator<Item = F>>(iter: T) -> Self {
|
||||
let mut res = Self::default();
|
||||
let mut iter = iter.into_iter();
|
||||
|
||||
for i in 0..LENGTH {
|
||||
if let Some(next) = iter.next() {
|
||||
res[i] = next;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_reverse_dot() {
|
||||
let left: NeuraVector<_, f64> = [2.0, 3.0, 5.0].into();
|
||||
let right: NeuraVector<_, f64> = [7.0, 11.0, 13.0, 17.0].into();
|
||||
|
||||
let expected: NeuraMatrix<_, _, f64> = [
|
||||
[14.0, 22.0, 26.0, 34.0],
|
||||
[21.0, 33.0, 39.0, 51.0],
|
||||
[35.0, 55.0, 65.0, 85.0],
|
||||
]
|
||||
.into();
|
||||
|
||||
let actual = left.reverse_dot(right);
|
||||
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
//! This module is currently disabled, as it relies on `generic_const_exprs`, which is too unstable to use as of now
|
||||
|
||||
use super::{NeuraLayer, NeuraTrainableLayer};
|
||||
|
||||
/// Converts a `[[T; WIDTH]; HEIGHT]` into a `[T; WIDTH * HEIGHT]`.
|
||||
/// Requires the `#![feature(generic_const_exprs)]` feature to be enabled.
|
||||
pub struct NeuraFlattenLayer<const WIDTH: usize, const HEIGHT: usize, T> {
|
||||
phantom: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
/// Converts a `[T; WIDTH * HEIGHT]` into a `[[T; WIDTH]; HEIGHT]`.
|
||||
/// Requires the `#![feature(generic_const_exprs)]` feature to be enabled.
|
||||
pub struct NeuraReshapeLayer<const WIDTH: usize, const HEIGHT: usize, T> {
|
||||
phantom: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn flatten<const WIDTH: usize, const HEIGHT: usize, T: Copy + Default>(
|
||||
input: &[[T; WIDTH]; HEIGHT],
|
||||
) -> [T; WIDTH * HEIGHT]
|
||||
where
|
||||
[T; WIDTH * HEIGHT]: Sized,
|
||||
{
|
||||
let mut res = [T::default(); WIDTH * HEIGHT];
|
||||
|
||||
// Hopefully the optimizer realizes this can be all optimized away
|
||||
for i in 0..HEIGHT {
|
||||
for j in 0..WIDTH {
|
||||
res[i * WIDTH + j] = input[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn reshape<const WIDTH: usize, const HEIGHT: usize, T: Copy + Default>(
|
||||
input: &[T; WIDTH * HEIGHT],
|
||||
) -> [[T; WIDTH]; HEIGHT]
|
||||
where
|
||||
[T; WIDTH * HEIGHT]: Sized,
|
||||
{
|
||||
let mut res = [[T::default(); WIDTH]; HEIGHT];
|
||||
|
||||
// Hopefully the optimizer realizes this can be all optimized away
|
||||
for i in 0..HEIGHT {
|
||||
for j in 0..WIDTH {
|
||||
res[i][j] = input[i * WIDTH + j];
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
impl<const WIDTH: usize, const HEIGHT: usize, T> NeuraFlattenLayer<WIDTH, HEIGHT, T> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const WIDTH: usize, const HEIGHT: usize, T> NeuraReshapeLayer<WIDTH, HEIGHT, T> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const WIDTH: usize, const HEIGHT: usize, T: Copy + Default> NeuraLayer
|
||||
for NeuraFlattenLayer<WIDTH, HEIGHT, T>
|
||||
where
|
||||
[T; WIDTH * HEIGHT]: Sized,
|
||||
{
|
||||
type Input = [[T; WIDTH]; HEIGHT];
|
||||
|
||||
type Output = [T; WIDTH * HEIGHT];
|
||||
|
||||
#[inline(always)]
|
||||
fn eval(&self, input: &Self::Input) -> Self::Output {
|
||||
flatten(input)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const WIDTH: usize, const HEIGHT: usize, T: Copy + Default> NeuraLayer
|
||||
for NeuraReshapeLayer<WIDTH, HEIGHT, T>
|
||||
where
|
||||
[T; WIDTH * HEIGHT]: Sized,
|
||||
{
|
||||
type Input = [T; WIDTH * HEIGHT];
|
||||
|
||||
type Output = [[T; WIDTH]; HEIGHT];
|
||||
|
||||
#[inline(always)]
|
||||
fn eval(&self, input: &Self::Input) -> Self::Output {
|
||||
reshape(input)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const WIDTH: usize, const HEIGHT: usize, T: Copy + Default> NeuraTrainableLayer
|
||||
for NeuraFlattenLayer<WIDTH, HEIGHT, T>
|
||||
where
|
||||
[T; WIDTH * HEIGHT]: Sized,
|
||||
{
|
||||
type Delta = ();
|
||||
|
||||
fn backpropagate(
|
||||
&self,
|
||||
_input: &Self::Input,
|
||||
epsilon: Self::Output,
|
||||
) -> (Self::Input, Self::Delta) {
|
||||
(reshape(&epsilon), ())
|
||||
}
|
||||
|
||||
fn regularize(&self) -> Self::Delta {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn apply_gradient(&mut self, _gradient: &Self::Delta) {
|
||||
// Noop
|
||||
}
|
||||
}
|
||||
|
||||
impl<const WIDTH: usize, const HEIGHT: usize, T: Copy + Default> NeuraTrainableLayer
|
||||
for NeuraReshapeLayer<WIDTH, HEIGHT, T>
|
||||
where
|
||||
[T; WIDTH * HEIGHT]: Sized,
|
||||
{
|
||||
type Delta = ();
|
||||
|
||||
fn backpropagate(
|
||||
&self,
|
||||
_input: &Self::Input,
|
||||
epsilon: Self::Output,
|
||||
) -> (Self::Input, Self::Delta) {
|
||||
(flatten(&epsilon), ())
|
||||
}
|
||||
|
||||
fn regularize(&self) -> Self::Delta {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn apply_gradient(&mut self, _gradient: &Self::Delta) {
|
||||
// Noop
|
||||
}
|
||||
}
|
Loading…
Reference in new issue