collenchyma_blas/frameworks/
native.rs

1//! Provides BLAS for a Native backend.
2
3use ::operation::*;
4use ::plugin::*;
5use ::transpose::*;
6use collenchyma::backend::Backend;
7use collenchyma::memory::MemoryType;
8use collenchyma::frameworks::native::Native;
9use collenchyma::plugin::Error;
10use rblas::math::mat::Mat;
11use rblas::matrix::Matrix;
12use rblas;
13
14macro_rules! impl_asum_for {
15    ($t:ident, $b:ty) => (
16        impl IOperationAsum<$t> for $b {
17            fn compute(&self, x: &MemoryType, result: &mut MemoryType) -> Result<(), Error> {
18                let x_slice = try!(x.as_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `x`."))).as_slice::<$t>();
19                let mut r_slice = try!(result.as_mut_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `result`."))).as_mut_slice::<$t>();
20                r_slice[0] = rblas::Asum::asum(x_slice);
21                Ok(())
22            }
23        }
24    );
25}
26
27macro_rules! impl_axpy_for {
28    ($t:ident, $b:ty) => (
29        impl IOperationAxpy<$t> for $b {
30            fn compute(&self, a: &MemoryType, x: &MemoryType, y: &mut MemoryType) -> Result<(), Error> {
31                let a_slice = try!(a.as_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `a`."))).as_slice::<$t>();
32                let x_slice = try!(x.as_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `x`."))).as_slice::<$t>();
33                let y_slice = try!(y.as_mut_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `y`."))).as_mut_slice::<$t>();
34                rblas::Axpy::axpy(&a_slice[0], x_slice, y_slice);
35                Ok(())
36            }
37        }
38    );
39}
40
41macro_rules! impl_copy_for {
42    ($t:ident, $b:ty) => (
43        impl IOperationCopy<$t> for $b {
44            fn compute(&self, x: &MemoryType, y: &mut MemoryType) -> Result<(), Error> {
45                let x_slice = try!(x.as_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `x`."))).as_slice::<$t>();
46                let y_slice = try!(y.as_mut_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `y`."))).as_mut_slice::<$t>();
47                rblas::Copy::copy(x_slice, y_slice);
48                Ok(())
49            }
50        }
51    );
52}
53
54macro_rules! impl_dot_for {
55    ($t:ident, $b:ty) => (
56        impl IOperationDot<$t> for $b {
57            fn compute(&self, x: &MemoryType, y: &MemoryType, result: &mut MemoryType) -> Result<(), Error> {
58                let x_slice = try!(x.as_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `x`."))).as_slice::<$t>();
59                let y_slice = try!(y.as_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `y`."))).as_slice::<$t>();
60                let mut r_slice = try!(result.as_mut_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `result`."))).as_mut_slice::<$t>();
61                r_slice[0] = rblas::Dot::dot(x_slice, y_slice);
62                Ok(())
63            }
64        }
65    );
66}
67
68macro_rules! impl_nrm2_for {
69    ($t:ident, $b:ty) => (
70        impl IOperationNrm2<$t> for $b {
71            fn compute(&self, x: &MemoryType, result: &mut MemoryType) -> Result<(), Error> {
72                let x_slice = try!(x.as_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `x`."))).as_slice::<$t>();
73                let mut r_slice = try!(result.as_mut_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `result`."))).as_mut_slice::<$t>();
74                r_slice[0] = rblas::Nrm2::nrm2(x_slice);
75                Ok(())
76            }
77        }
78    );
79}
80
81macro_rules! impl_scale_for {
82    ($t:ident, $b:ty) => (
83        impl IOperationScale<$t> for $b {
84            fn compute(&self, a: &MemoryType, x: &mut MemoryType) -> Result<(), Error> {
85                let a_slice = try!(a.as_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `a`."))).as_slice::<$t>();
86                let mut x_slice = try!(x.as_mut_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `x`."))).as_mut_slice::<$t>();
87                rblas::Scal::scal(&a_slice[0], x_slice);
88                Ok(())
89            }
90        }
91    );
92}
93
94macro_rules! impl_swap_for {
95    ($t:ident, $b:ty) => (
96        impl IOperationSwap<$t> for $b {
97            fn compute(&self, x: &mut MemoryType, y: &mut MemoryType) -> Result<(), Error> {
98                let mut x_slice = try!(x.as_mut_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `x`."))).as_mut_slice::<$t>();
99                let mut y_slice = try!(y.as_mut_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `y`."))).as_mut_slice::<$t>();
100                rblas::Swap::swap(x_slice, y_slice);
101                Ok(())
102            }
103        }
104    );
105}
106
107macro_rules! impl_gemm_for {
108    ($t:ident, $b:ty) => (
109        impl IOperationGemm<$t> for $b {
110            fn compute(&self, alpha: &MemoryType, at: Transpose, a_dims: &[usize], a: &MemoryType, bt: Transpose, b_dims: &[usize], b: &MemoryType, beta: &MemoryType, c_dims: &[usize], c: &mut MemoryType) -> Result<(), ::collenchyma::error::Error> {
111                let alpha_slice = try!(alpha.as_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `alpha`."))).as_slice::<$t>();
112                let a_slice = try!(a.as_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `a`."))).as_slice::<$t>();
113                let beta_slice = try!(beta.as_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `beta`."))).as_slice::<$t>();
114                let b_slice = try!(b.as_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `b`."))).as_slice::<$t>();
115                let mut c_slice = try!(c.as_mut_native().ok_or(Error::MissingMemoryForDevice("Unable to receive native memory for `c`."))).as_mut_slice::<$t>();
116
117                let a_matrix = as_matrix(a_slice, a_dims);
118                let b_matrix = as_matrix(b_slice, b_dims);
119                let mut c_matrix = as_matrix(c_slice, c_dims);
120                rblas::Gemm::gemm(&alpha_slice[0], at.to_rblas(), &a_matrix, bt.to_rblas(), &b_matrix, &beta_slice[0], &mut c_matrix);
121                read_from_matrix(&c_matrix, c_slice);
122                Ok(())
123            }
124        }
125    );
126}
127
128macro_rules! impl_iblas_for {
129    ($t:ident, $b:ty) => (
130        impl_asum_for!($t, $b);
131        impl_axpy_for!($t, $b);
132        impl_copy_for!($t, $b);
133        impl_dot_for!($t, $b);
134        impl_nrm2_for!($t, $b);
135        impl_scale_for!($t, $b);
136        impl_swap_for!($t, $b);
137
138        impl_gemm_for!($t, $b);
139
140        impl IBlas<$t> for $b { }
141
142        // Level 1
143
144        impl Asum<$t> for $b {
145            iblas_asum_for!($t, $b);
146        }
147
148        impl Axpy<$t> for $b {
149            iblas_axpy_for!($t, $b);
150        }
151
152        impl Copy<$t> for $b {
153            iblas_copy_for!($t, $b);
154        }
155
156        impl Dot<$t> for $b {
157            iblas_dot_for!($t, $b);
158        }
159
160        impl Nrm2<$t> for $b {
161            iblas_nrm2_for!($t, $b);
162        }
163
164        impl Scal<$t> for $b {
165            iblas_scale_for!($t, $b);
166        }
167
168        impl Swap<$t> for $b {
169            iblas_swap_for!($t, $b);
170        }
171
172        impl Gemm<$t> for $b {
173            iblas_gemm_for!($t, $b);
174        }
175    );
176}
177
178impl_iblas_for!(f32, Backend<Native>);
179impl_iblas_for!(f64, Backend<Native>);
180
181/// Create a rblas-Matrix from a slice and dimensions.
182fn as_matrix<T: Clone + ::std::fmt::Debug>(slice: &[T], dims: &[usize]) -> Mat<T> {
183    let n = dims[0];
184    let m = dims.iter().skip(1).fold(1, |prod, i| prod * i);
185    let mut mat: Mat<T> = Mat::new(n, m);
186    for i in 0..n {
187        for j in 0..m {
188            let index = m * i + j;
189            unsafe {
190                *mat.as_mut_ptr().offset(index as isize) = slice[index].clone();
191            }
192        }
193    }
194
195    mat
196}
197
198fn read_from_matrix<T: Clone>(mat: &Mat<T>, slice: &mut [T]) {
199    let n = mat.rows();
200    let m = mat.cols();
201    for i in 0..n {
202        for j in 0..m {
203            let index = m * i + j;
204            slice[index] = mat[i][j].clone();
205        }
206    }
207}
208
209#[cfg(test)]
210mod test {
211    use collenchyma::backend::{Backend, BackendConfig};
212    use collenchyma::framework::IFramework;
213    use collenchyma::frameworks::Native;
214    use collenchyma::tensor::SharedTensor;
215    use collenchyma::memory::MemoryType;
216    use super::as_matrix;
217
218    fn get_native_backend() -> Backend<Native> {
219        let framework = Native::new();
220        let hardwares = framework.hardwares().to_vec();
221        let backend_config = BackendConfig::new(framework, &hardwares);
222        Backend::new(backend_config).unwrap()
223    }
224
225    pub fn write_to_memory<T: Copy>(mem: &mut MemoryType, data: &[T]) {
226        match mem {
227            &mut MemoryType::Native(ref mut mem) => {
228                let mut mem_buffer = mem.as_mut_slice::<T>();
229                for (index, datum) in data.iter().enumerate() {
230                    mem_buffer[index] = *datum;
231                }
232            },
233            #[cfg(any(feature = "cuda", feature = "opencl"))]
234            _ => assert!(false)
235        }
236    }
237
238    /// UTIL: as_matrix and read_from_matrix
239    #[test]
240    fn it_converts_correctly_to_and_from_matrix() {
241        let backend = get_native_backend();
242        let mut a = SharedTensor::<f32>::new(backend.device(), &vec![3, 2]).unwrap();
243        write_to_memory(a.get_mut(backend.device()).unwrap(),
244            &[2f32, 5f32,
245              2f32, 5f32,
246              2f32, 5f32]);
247
248        {
249            let a_slice_in = a.get(backend.device()).unwrap().as_native().unwrap().as_slice::<f32>();
250            let a_mat = as_matrix(a_slice_in, &[3, 2]);
251            // right
252            assert_eq!(a_mat[0][0], 2f32);
253            assert_eq!(a_mat[0][1], 5f32);
254            assert_eq!(a_mat[1][0], 2f32);
255            assert_eq!(a_mat[1][1], 5f32);
256            assert_eq!(a_mat[2][0], 2f32);
257            assert_eq!(a_mat[2][1], 5f32);
258        }
259    }
260}