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*/