easy_ml/
using_custom_types.rs

1/*!
2Using custom numeric types examples.
3
4# Using a custom numeric type
5
6The following example shows how to make a type defined outside this crate implement Numeric
7so it can be used by this library for matrix and tensor operations.
8
9In this case the type is `BigInt` from [`num_bigint`](https://docs.rs/num-bigint/0.3.1/num_bigint/)
10but as the type is also outside this crate we need to wrap it in another struct first so we can
11implement traits on it due to Rust's orphan rules.
12
13```
14extern crate easy_ml;
15extern crate num_bigint;
16extern crate num_traits;
17
18use std::ops::{Add, Sub, Mul, Div, Neg};
19use std::iter::Sum;
20
21use easy_ml::numeric::{ZeroOne, FromUsize};
22use easy_ml::matrices::Matrix;
23use num_bigint::{BigInt, ToBigInt, Sign};
24use num_traits::{Zero, One};
25
26/**
27 * We can derive a number of traits, Clone and Debug are required to implement Numeric
28 */
29#[derive(Clone, Eq, PartialEq, PartialOrd, Debug)]
30struct BigIntWrapper(BigInt);
31
32/*
33 * First we define a utility function to obtain the &BigInt
34 * from a &BigIntWrapper as we don't want to have to clone
35 * the BigInt when we can avoid it.
36 */
37
38impl BigIntWrapper {
39    /**
40     * A utility function to get the &BigInt from a &BigIntWrapper
41     */
42    fn unwrap(&self) -> &BigInt {
43        let BigIntWrapper(ref x) = *self;
44        x
45    }
46}
47
48/*
49 * Then implement the two traits defined in easy_ml onto the wrapper.
50 */
51
52impl ZeroOne for BigIntWrapper {
53    #[inline]
54    fn zero() -> BigIntWrapper {
55        BigIntWrapper(Zero::zero())
56    }
57    #[inline]
58    fn one() -> BigIntWrapper {
59        BigIntWrapper(One::one())
60    }
61}
62
63impl FromUsize for BigIntWrapper {
64    #[inline]
65    fn from_usize(n: usize) -> Option<BigIntWrapper> {
66        let bigint = ToBigInt::to_bigint(&n)?;
67        Some(BigIntWrapper(bigint))
68    }
69}
70
71/*
72 * If we weren't wrapping BigInt and could have defined `ZeroOne`
73 * and `FromUsize` directly on it we would stop here. As we have
74 * to wrap it, all that's left is a lot of boilerplate to
75 * redefine most of the traits BigInt already implements on our
76 * wrapper.
77 */
78
79// Define 4 macros to implement the operations Add, Sub, Mul and Div for
80// all 4 combinations of by value and by reference BigIntWrappers
81
82// BigIntWrapper op BigIntWrapper
83macro_rules! operation_impl_value_value {
84    (impl $Trait:ident for BigIntWrapper { fn $method:ident }) => {
85        impl $Trait<BigIntWrapper> for BigIntWrapper {
86            type Output = BigIntWrapper;
87
88            #[inline]
89            fn $method(self, rhs: BigIntWrapper) -> Self::Output {
90                // unwrap, do operation unwrapped, then wrap output
91                BigIntWrapper((self.0).$method(rhs.0))
92            }
93        }
94    }
95}
96
97// BigIntWrapper op &BigIntWrapper
98macro_rules! operation_impl_value_reference {
99    (impl $Trait:ident for BigIntWrapper { fn $method:ident }) => {
100        impl <'a> $Trait<&'a BigIntWrapper> for BigIntWrapper {
101            type Output = BigIntWrapper;
102
103            #[inline]
104            fn $method(self, rhs: &BigIntWrapper) -> Self::Output {
105                // unwrap, do operation unwrapped, then wrap output
106                BigIntWrapper((self.0).$method(rhs.unwrap()))
107            }
108        }
109    }
110}
111
112// &BigIntWrapper op BigIntWrapper
113macro_rules! operation_impl_reference_value {
114    (impl $Trait:ident for BigIntWrapper { fn $method:ident }) => {
115        impl <'a> $Trait<BigIntWrapper> for &'a BigIntWrapper {
116            type Output = BigIntWrapper;
117
118            #[inline]
119            fn $method(self, rhs: BigIntWrapper) -> Self::Output {
120                // unwrap, do operation unwrapped, then wrap output
121                BigIntWrapper(self.unwrap().$method(rhs.0))
122            }
123        }
124    }
125}
126
127// &BigIntWrapper op &BigIntWrapper
128macro_rules! operation_impl_reference_reference {
129    (impl $Trait:ident for BigIntWrapper { fn $method:ident }) => {
130        impl <'a, 'b> $Trait<&'a BigIntWrapper> for &'b BigIntWrapper {
131            type Output = BigIntWrapper;
132
133            #[inline]
134            fn $method(self, rhs: &BigIntWrapper) -> Self::Output {
135                // unwrap, do operation unwrapped, then wrap output
136                BigIntWrapper(self.unwrap().$method(rhs.unwrap()))
137            }
138        }
139    }
140}
141
142// Now we can implement these operations for Add, Sub, Mul and Div in one go
143// instead of writing them out 4 times each.
144operation_impl_value_value! { impl Add for BigIntWrapper { fn add } }
145operation_impl_value_value! { impl Sub for BigIntWrapper { fn sub } }
146operation_impl_value_value! { impl Mul for BigIntWrapper { fn mul } }
147operation_impl_value_value! { impl Div for BigIntWrapper { fn div } }
148operation_impl_value_reference! { impl Add for BigIntWrapper { fn add } }
149operation_impl_value_reference! { impl Sub for BigIntWrapper { fn sub } }
150operation_impl_value_reference! { impl Mul for BigIntWrapper { fn mul } }
151operation_impl_value_reference! { impl Div for BigIntWrapper { fn div } }
152operation_impl_reference_value! { impl Add for BigIntWrapper { fn add } }
153operation_impl_reference_value! { impl Sub for BigIntWrapper { fn sub } }
154operation_impl_reference_value! { impl Mul for BigIntWrapper { fn mul } }
155operation_impl_reference_value! { impl Div for BigIntWrapper { fn div } }
156operation_impl_reference_reference! { impl Add for BigIntWrapper { fn add } }
157operation_impl_reference_reference! { impl Sub for BigIntWrapper { fn sub } }
158operation_impl_reference_reference! { impl Mul for BigIntWrapper { fn mul } }
159operation_impl_reference_reference! { impl Div for BigIntWrapper { fn div } }
160
161/*
162 * Because Neg is a unary operation there are only two combinations
163 * so the implementations are written out longhand for ease of understanding.
164 */
165
166// - BigIntWrapper
167impl Neg for BigIntWrapper {
168    type Output = BigIntWrapper;
169
170    #[inline]
171    fn neg(self) -> Self::Output {
172        // unwrap, do operation unwrapped, then wrap output
173        BigIntWrapper(-(self.0))
174    }
175}
176
177// - &BigIntWrapper
178// Like with the macro'd versions for Add, Sub, Mul and Div, we need to define
179// the Neg trait on a reference to a BigIntWrapper, which is itself a type.
180impl <'a> Neg for &'a BigIntWrapper {
181    type Output = BigIntWrapper;
182
183    #[inline]
184    fn neg(self) -> Self::Output {
185        // unwrap, do operation unwrapped, then wrap output
186        BigIntWrapper(-(self.unwrap()))
187    }
188}
189
190// Finally we need to implement Sum on just types of BigIntWrapper
191// to complete the steps to getting Numeric implemented on BigIntWrapper
192
193impl Sum<BigIntWrapper> for BigIntWrapper {
194    fn sum<I>(iter: I) -> BigIntWrapper
195    where I: Iterator<Item = BigIntWrapper> {
196        // unwrap every item in the iterator, do sum unwrapped, then wrap output
197        BigIntWrapper(iter.map(|wrapped| wrapped.0).sum())
198    }
199}
200
201// For convenience and demonstrations below ToString is also implemented on BigIntWrapper
202impl ToString for BigIntWrapper {
203    #[inline]
204    fn to_string(&self) -> String {
205        self.unwrap().to_string()
206    }
207}
208
209let one_million = ToBigInt::to_bigint(&1000000).unwrap();
210let wrapped = BigIntWrapper(one_million);
211
212let matrix = Matrix::from_scalar(wrapped);
213println!("1000000 x 1000000 = {:?}", (&matrix * &matrix).get_reference(0, 0).to_string());
214
215// Wrapping and unwrapping transformations can be done with map
216let unwrapped: Matrix<BigInt> = matrix.map(|wrapped| wrapped.0);
217println!("Unwrapped:\n{:?}", unwrapped);
218let matrix: Matrix<BigInt> = Matrix::from_scalar(ToBigInt::to_bigint(&-3).unwrap());
219let wrapped: Matrix<BigIntWrapper> = matrix.map(|unwrapped| BigIntWrapper(unwrapped));
220println!("Wrapped:\n{:?}", wrapped);
221
222// For completeness conversions between our wrapper type and BigInt are included
223// so they can be converted with .into()
224impl From<BigInt> for BigIntWrapper {
225    #[inline]
226    fn from(number: BigInt) -> Self {
227        BigIntWrapper(number)
228    }
229}
230
231impl From<BigIntWrapper> for BigInt {
232    #[inline]
233    fn from(number: BigIntWrapper) -> Self {
234        number.0
235    }
236}
237```
238*/