Skip to main content

nerio/
lib.rs

1#![feature(portable_simd)]
2
3use std::array;
4use std::fmt::Debug;
5use std::marker::PhantomData;
6use std::ops::{Add, AddAssign, Div, Index, IndexMut, Mul};
7
8mod linalg;
9use linalg::{vmdot, vvadd};
10
11fn sigmoid<E: linalg::Number>(x: E) -> E {
12    E::one() / (E::get_exp(-x) + E::one())
13}
14
15fn sigmoid_derivative<E: linalg::Number>(x: E) -> E {
16    x * (E::one() - x)
17}
18
19trait List {}
20
21#[derive(Default, Debug, Clone)]
22struct Nil;
23#[derive(Default, Debug, Clone)]
24struct Cons<H, T>(H, T);
25
26impl List for Nil {}
27impl<H, T: List> List for Cons<H, T> {}
28
29#[derive(Default)]
30struct Zero;
31#[derive(Default)]
32struct Succ<N>(PhantomData<N>);
33
34type S1<N = Zero> = Succ<N>;
35type S2<N = Zero> = S1<S1<N>>;
36type S4<N = Zero> = S2<S2<N>>;
37type S8<N = Zero> = S4<S4<N>>;
38type S16<N = Zero> = S8<S8<N>>;
39type S32<N = Zero> = S16<S16<N>>;
40type S64<N = Zero> = S32<S32<N>>;
41
42trait Prec {
43    type Output;
44}
45
46impl<N> Prec for Succ<N> {
47    type Output = N;
48}
49
50trait Repeat<N> {
51    type Output;
52}
53
54impl<H, T: List> Repeat<Zero> for Cons<H, T> {
55    type Output = T;
56}
57
58impl<H, T: List, N> Repeat<Succ<N>> for Cons<H, T>
59where
60    Cons<H, T>: Repeat<N>,
61{
62    type Output = Cons<H, <Cons<H, T> as Repeat<N>>::Output>;
63}
64
65trait Length {
66    type Output;
67}
68
69impl Length for Nil {
70    type Output = Zero;
71}
72
73impl<H, T: Length> Length for Cons<H, T> {
74    type Output = Succ<<T as Length>::Output>;
75}
76
77trait Nth<Idx> {
78    type Output;
79
80    fn nth(&self) -> &Self::Output;
81    fn nth_mut(&mut self) -> &mut Self::Output;
82}
83
84impl<H, T> Nth<Zero> for Cons<H, T> {
85    type Output = H;
86
87    fn nth(&self) -> &Self::Output {
88        &self.0
89    }
90
91    fn nth_mut(&mut self) -> &mut Self::Output {
92        &mut self.0
93    }
94}
95
96impl<Idx, H, T> Nth<Succ<Idx>> for Cons<H, T>
97where
98    T: Nth<Idx>,
99{
100    type Output = T::Output;
101
102    fn nth(&self) -> &Self::Output {
103        Nth::<Idx>::nth(&self.1)
104    }
105
106    fn nth_mut(&mut self) -> &mut Self::Output {
107        Nth::<Idx>::nth_mut(&mut self.1)
108    }
109}
110
111#[derive(Debug)]
112struct Layer<E, const S: usize>([E; S]);
113
114impl<E: Default, const S: usize> Default for Layer<E, S> {
115		fn default() -> Self {
116				Layer(array::from_fn(|_| E::default()))
117		}
118}
119
120trait Builder: Sized {
121    fn before<F>(self) -> Cons<F, Self>
122    where
123        F: Default,
124    {
125        Cons(F::default(), self)
126    }
127
128    fn repeat<N>(self) -> <Self as Repeat<N>>::Output
129    where
130        Self: List + Repeat<N>,
131        <Self as Repeat<N>>::Output: Default,
132    {
133        <Self as Repeat<N>>::Output::default()
134    }
135}
136
137impl Builder for Nil {}
138impl<H, T> Builder for Cons<H, T> {}
139
140trait ActivationFn<E> {
141		fn activate(e: E) -> E;
142}
143
144#[derive(Default)]
145struct Sigmoid;
146
147impl<E: linalg::Number> ActivationFn<E> for Sigmoid {
148		fn activate(x: E) -> E {
149				E::one() / (E::get_exp(-x) + E::one())
150		}
151}
152
153#[derive(Default)]
154struct Relu;
155
156impl<E: linalg::Number> ActivationFn<E> for Relu {
157		fn activate(x: E) -> E {
158				E::get_max(x, E::default())
159		}
160}
161
162trait FeedforwardTimes<Idx, Times> {
163    fn feedforward_times(&mut self) -> &mut Self;
164}
165
166struct NeuralNetwork<W: List, B: List, A: List, E, F: List> {
167    w: W,
168    b: B,
169    a: A,
170
171		f: F,
172		e: PhantomData<E>,
173}
174
175impl<Idx, W: List, B: List, A: List, E, F: List> FeedforwardTimes<Idx, Zero> for NeuralNetwork<W, B, A, E, F> {
176    fn feedforward_times(&mut self) -> &mut Self {
177        self
178    }
179}
180
181impl<Idx, Times, W, B, A, E, Fi, F, const AS: usize, const BS: usize, const CS: usize>
182    FeedforwardTimes<Idx, Succ<Times>> for NeuralNetwork<W, B, A, E, F>
183where
184    E: linalg::Number,
185		Fi: ActivationFn<E>,
186    F: List + Nth<Idx, Output = Fi>,
187    W: List + Nth<Idx, Output = Layer<E, AS>>,
188    B: List + Nth<Idx, Output = Layer<E, CS>>,
189    A: List + Nth<Idx, Output = Layer<E, BS>> + Nth<Succ<Idx>, Output = Layer<E, CS>>,
190    Self: FeedforwardTimes<Succ<Idx>, Times>,
191{
192    fn feedforward_times(&mut self) -> &mut Self {
193				let activate = Nth::<Idx>::nth(&self.f);
194        let activations = &Nth::<Idx>::nth(&self.a).0;
195        let weights = &Nth::<Idx>::nth(&self.w).0;
196        let bias = &Nth::<Idx>::nth(&self.b).0;
197
198        let activations = vvadd(&vmdot(activations, weights), bias).map(|e| Fi::activate(e));
199        Nth::<Succ<Idx>>::nth_mut(&mut self.a).0 = activations;
200
201        FeedforwardTimes::<Succ<Idx>, Times>::feedforward_times(self)
202    }
203}
204
205impl<W: List + Length, B: List, A: List, E, F: List> NeuralNetwork<W, B, A, E, F>
206where
207    Self: FeedforwardTimes<Zero, <W as Length>::Output>,
208{
209    fn feedforward(&mut self) -> &mut Self {
210        FeedforwardTimes::<Zero, <W as Length>::Output>::feedforward_times(self)
211    }
212}
213
214impl<W: List, B: List, A: List, E, F: List> NeuralNetwork<W, B, A, E, F>
215{
216		fn new(wba: (W, B, A), f: F) -> Self {
217				Self {
218						w: wba.0,
219						b: wba.1,
220						a: wba.2,
221						f,
222						e: PhantomData::<E>,
223				}
224		}
225}
226
227macro_rules! layers {
228		(@wbuilder $rt:expr; $t:ty; {$s:expr} $(x $r:ty)?) => {
229				$rt
230		};
231		(@wbuilder $rt:expr; $t:ty; {$sp:expr} $(x $rp:ty)?, {$sn:expr} $(x $rn:ty)? $(, {$s:expr} $(x $r:ty)?)*) => {
232				layers!(
233						@wbuilder
234						$rt
235						    $(
236							      .before::<Layer<$t, {$sp*$sp}>>()
237							      .repeat::<<$rp as Prec>::Output>()
238							  )*
239							  .before::<Layer<$t, {$sp*$sn}>>();
240						$t;
241						{$sn} $(x $rn)* $(, {$s} $(x $r)*)*
242				)
243		};
244		(@bbuilder $rt:expr; $t:ty; {$s:expr} $(x $r:ty)?) => {
245				$rt
246						$(
247								.before::<Layer<$t, $s>>()
248								.repeat::<<$r as Prec>::Output>()
249						)*
250		};
251		(@bbuilder $rt:expr; $t:ty; {$sp:expr} $(x $rp:ty)? $(, {$s:expr} $(x $r:ty)?)*) => {
252				layers!(
253						@bbuilder
254						$rt
255						    .before::<Layer<$t, $sp>>()
256							  $(.repeat::<$rp>())*;
257						$t;
258						$({$s} $(x $r)*),*
259				)
260		};
261		($t:ty; $({$s:expr} $(x $r:ty)?),+) => {
262				(
263						layers!(@wbuilder Nil; $t; $({$s} $(x $r)*),*),
264						layers!(@bbuilder Nil; $t; $({$s} $(x $r)*),*),
265						Nil
266								$(
267										.before::<Layer<$t, $s>>()
268										$(.repeat::<$r>())*
269								)*,
270				)
271		};
272}
273
274#[cfg(test)]
275mod tests {
276    use super::*;
277
278    #[test]
279    fn create_some_layers() {
280        // currently layers are set in reverse order
281        let (w, b, a) = layers!(f32; {3}, {4} x S2<S1>, {7});
282
283				assert_eq!(w.0.0.len(), 28);
284        assert_eq!(w.1.0.0.len(), 16);
285        assert_eq!(w.1.1.0.0.len(), 16);
286        assert_eq!(w.1.1.1.0.0.len(), 12);
287
288				assert_eq!(b.0.0.len(), 4);
289        assert_eq!(b.1.0.0.len(), 4);
290        assert_eq!(b.1.1.0.0.len(), 4);
291        assert_eq!(b.1.1.1.0.0.len(), 3);
292
293				assert_eq!(a.0.0.len(), 7);
294        assert_eq!(a.1.0.0.len(), 4);
295        assert_eq!(a.1.1.0.0.len(), 4);
296        assert_eq!(a.1.1.1.0.0.len(), 4);
297        assert_eq!(a.1.1.1.1.0.0.len(), 3);
298    }
299		
300    #[test]
301    fn some_feedforward() {
302        let wab = layers!(f32; {3}, {8}, {4});
303				let f = Nil
304						.before::<Sigmoid>()
305						.repeat::<S2>();
306
307        let mut nn = NeuralNetwork::new(wab, f);
308				nn.w.0.0 = [1.; 32];
309        nn.a.0.0 = [1.; 4];
310        nn.w.1.0.0 = [1.; 24];
311        nn.b.1.0.0 = [1.; 3];
312
313        nn.feedforward();
314
315        println!("{:?}", nn.a.0);
316    }
317
318		#[test]
319		fn feedforward_sum() {
320				let wab = layers!(f32; {1}, {2});
321				let f = Nil.before::<Relu>();
322
323				let mut nn = NeuralNetwork::new(wab, f);
324				nn.w.0.0 = [1., 1.];
325				nn.a.0.0 = [2., 3.];
326
327				nn.feedforward();
328				assert!(f32::abs(nn.a.1.0.0[0] - 5.) < 0.000001);
329		}
330}