matrix_assign.hpp
Go to the documentation of this file.
1/*!
2 * \brief Kernels for matrix-expression assignments
3 *
4 * \author O. Krause
5 * \date 2013
6 *
7 *
8 * \par Copyright 1995-2015 Shark Development Team
9 *
10 * <BR><HR>
11 * This file is part of Shark.
12 * <http://image.diku.dk/shark/>
13 *
14 * Shark is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License as published
16 * by the Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * Shark is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License for more details.
23 *
24 * You should have received a copy of the GNU Lesser General Public License
25 * along with Shark. If not, see <http://www.gnu.org/licenses/>.
26 *
27 */
28#ifndef REMORA_KERNELS_DEFAULT_MATRIX_ASSIGN_HPP
29#define REMORA_KERNELS_DEFAULT_MATRIX_ASSIGN_HPP
30
31#include "../vector_assign.hpp"
32#include "../../proxy_expressions.hpp"
33#include "../../detail/traits.hpp"
34#include <algorithm>
35#include <vector>
36#include <iostream>
37namespace remora{namespace bindings{
38
39// Explicitly iterating row major
40template<class F, class M>
41void matrix_apply(
42 matrix_expression<M, cpu_tag>& m,
43 F const& f,
44 row_major
45){
46 for(std::size_t i = 0; i != m().size1(); ++i){
47 auto rowM = row(m,i);
48 kernels::apply(rowM,f);
49 }
50}
51// Explicitly iterating column major
52template<class F, class M>
53void matrix_apply(
54 matrix_expression<M, cpu_tag>& m,
55 F const& f,
56 column_major
57){
58 for(std::size_t j = 0; j != m().size2(); ++j){
59 auto columnM = column(m,j);
60 kernels::apply(columnM,f);
61 }
62}
63// Spcial case triangular packed - just calls the first two implementations.
64template<class F, class M, class Orientation, class Triangular>
65void matrix_apply(
66 matrix_expression<M, cpu_tag>& m,
67 F const& f,
68 triangular<Orientation,Triangular>
69){
70 matrix_apply(m,f,Orientation());
71}
72
73//////////////////////////////////////////////////////
74////Scalar Assignment to Matrix
75/////////////////////////////////////////////////////
76
77
78// Explicitly iterating row major
79template<class F, class M, class Orientation>
80void matrix_assign(
81 matrix_expression<M, cpu_tag>& m,
82 typename M::value_type t,
83 Orientation
84){
85 F f;
86 matrix_apply(m, [=](typename M::value_type x){return f(x,t);},Orientation());
87
88}
89
90
91
92/////////////////////////////////////////////////////////////////
93//////Matrix Assignment implementing op=
94////////////////////////////////////////////////////////////////
95
96// direct assignment without functor
97// the cases were both arguments have the same orientation and the left hand side
98// is dense can be implemented using assign.
99template<class M, class E,class TagE>
100void matrix_assign(
101 matrix_expression<M, cpu_tag>& m,
102 matrix_expression<E, cpu_tag> const& e,
103 row_major, row_major,dense_tag, TagE
104) {
105 for(std::size_t i = 0; i != m().size1(); ++i){
106 auto rowM = row(m,i);
107 kernels::assign(rowM,row(e,i));
108 }
109}
110
111// direct assignment for sparse matrices with the same orientation
112template<class M, class E>
113void matrix_assign(
114 matrix_expression<M, cpu_tag>& m,
115 matrix_expression<E, cpu_tag> const& e,
116 row_major, row_major,sparse_tag, sparse_tag
117) {
118 m().clear();
119 for(std::size_t i = 0; i != m().size1(); ++i){
120 auto m_pos = m().major_begin(i);
121 auto end = e().major_end(i);
122 for(auto it = e().major_begin(i); it != end; ++it){
123 m_pos = m().set_element(m_pos,it.index(),*it);
124 }
125 }
126}
127
128//remain the versions where both arguments do not have the same orientation
129
130//dense-dense case
131template<class M, class E>
132void matrix_assign(
133 matrix_expression<M, cpu_tag>& m,
134 matrix_expression<E, cpu_tag> const& e,
135 row_major, column_major,dense_tag, dense_tag
136) {
137 //compute blockwise and wrelem the transposed block.
138 std::size_t const blockSize = 8;
139 typename M::value_type blockStorage[blockSize][blockSize];
140
141 typedef typename M::size_type size_type;
142 size_type size1 = m().size1();
143 size_type size2 = m().size2();
144 for (size_type iblock = 0; iblock < size1; iblock += blockSize){
145 for (size_type jblock = 0; jblock < size2; jblock += blockSize){
146 std::size_t blockSizei = std::min(blockSize,size1-iblock);
147 std::size_t blockSizej = std::min(blockSize,size2-jblock);
148
149 //compute block values
150 for (size_type j = 0; j < blockSizej; ++j){
151 for (size_type i = 0; i < blockSizei; ++i){
152 blockStorage[i][j] = e()(iblock+i,jblock+j);
153 }
154 }
155
156 //copy block in a different order to m
157 for (size_type i = 0; i < blockSizei; ++i){
158 for (size_type j = 0; j < blockSizej; ++j){
159 m()(iblock+i,jblock+j) = blockStorage[i][j];
160 }
161 }
162 }
163 }
164}
165
166// dense-sparse
167template<class M, class E>
168void matrix_assign(
169 matrix_expression<M, cpu_tag>& m,
170 matrix_expression<E, cpu_tag> const& e,
171 row_major, column_major,dense_tag, sparse_tag
172) {
173 for(std::size_t j = 0; j != m().size2(); ++j){
174 auto columnM = column(m,j);
175 kernels::assign(columnM,column(e,j));
176 }
177}
178
179//sparse-sparse
180template<class M, class E>
181void matrix_assign(
182 matrix_expression<M, cpu_tag>& m,
183 matrix_expression<E, cpu_tag> const& e,
184 row_major, column_major,sparse_tag,sparse_tag
185) {
186 //apply the transposition of e()
187 //first evaluate e and fill the values into a vector which is then sorted by row_major order
188 //this gives this algorithm a run time of O(eval(e)+k*log(k))
189 //where eval(e) is the time to evaluate and k*log(k) the number of non-zero elements
190 typedef typename M::value_type value_type;
191 typedef typename M::size_type size_type;
192 typedef row_major::sparse_element<value_type> Element;
193 std::vector<Element> elements;
194
195 size_type size2 = m().size2();
196 for(size_type j = 0; j != size2; ++j){
197 auto pos_e = e().major_begin(j);
198 auto end_e = e().major_end(j);
199 for(; pos_e != end_e; ++pos_e){
200 Element element;
201 element.i = pos_e.index();
202 element.j = j;
203 element.value = *pos_e;
204 elements.push_back(element);
205 }
206 }
207 std::sort(elements.begin(),elements.end());
208 //fill m with the contents
209 m().clear();
210 size_type num_elems = size_type(elements.size());
211 for(size_type current = 0; current != num_elems;){
212 //count elements in row and reserve enough space for it
213 size_type row = elements[current].i;
214 size_type row_end = current;
215 while(row_end != num_elems && elements[row_end].i == row)
216 ++ row_end;
217 m().major_reserve(row,row_end - current);
218
219 //copy elements
220 auto row_pos = m().major_begin(row);
221 for(; current != row_end; ++current){
222 row_pos = m().set_element(row_pos,elements[current].j,elements[current].value);
223 }
224 }
225}
226
227//triangular row_major,row_major
228template<class M, class E, bool Upper, bool Unit>
229void matrix_assign(
230 matrix_expression<M, cpu_tag>& m,
231 matrix_expression<E, cpu_tag> const& e,
232 triangular<row_major,triangular_tag<Upper, false> >, triangular<row_major,triangular_tag<Upper, Unit> >,
233 packed_tag, packed_tag
234) {
235 for(std::size_t i = 0; i != m().size1(); ++i){
236 auto mpos = m().major_begin(i);
237 auto epos = e().major_begin(i);
238 auto eend = e().major_end(i);
239 if(Unit && Upper){
240 *mpos = 1;
241 ++mpos;
242 }
243 REMORA_SIZE_CHECK(mpos.index() == epos.index());
244 for(; epos != eend; ++epos,++mpos){
245 *mpos = *epos;
246 }
247 if(Unit && Upper){
248 *mpos = 1;
249 }
250 }
251}
252
253////triangular row_major,column_major
254//todo: this is suboptimal as we do strided access!!!!
255template<class M, class E,class Triangular>
256void matrix_assign(
257 matrix_expression<M, cpu_tag>& m,
258 matrix_expression<E, cpu_tag> const& e,
259 triangular<row_major,Triangular>, triangular<column_major,Triangular>,
260 packed_tag, packed_tag
261) {
262 for(std::size_t i = 0; i != m().size1(); ++i){
263 auto mpos = m().major_begin(i);
264 auto mend = m().major_end(i);
265 for(; mpos!=mend; ++mpos){
266 *mpos = e()(i,mpos.index());
267 }
268 }
269}
270
271///////////////////////////////////////////////////////////////////////////////////////////
272//////Matrix Assignment With Functor implementing +=,-=...
273///////////////////////////////////////////////////////////////////////////////////////////
274
275//when both are row-major and target is dense we can map to vector case
276template<class F, class M, class E, class TagE>
277void matrix_assign_functor(
278 matrix_expression<M, cpu_tag>& m,
279 matrix_expression<E, cpu_tag> const& e,
280 F f,
281 row_major, row_major, dense_tag, TagE
282) {
283 for(std::size_t i = 0; i != m().size1(); ++i){
284 auto rowM = row(m,i);
285 kernels::assign(rowM,row(e,i),f);
286 }
287}
288template<class F, class M, class E>
289void matrix_assign_functor(
290 matrix_expression<M, cpu_tag>& m,
291 matrix_expression<E, cpu_tag> const& e,
292 F f,
293 row_major, row_major, sparse_tag, sparse_tag
294) {
295 typedef typename M::value_type value_type;
296 value_type zero = value_type();
297 typedef row_major::sparse_element<value_type> Element;
298 std::vector<Element> elements;
299
300 for(std::size_t i = 0; i != major_size(m); ++i){
301 //first merge the two rows in elements using the functor
302
303 elements.clear();
304 auto m_pos = m().major_begin(i);
305 auto m_end = m().major_end(i);
306 auto e_pos = e().major_begin(i);
307 auto e_end = e().major_end(i);
308
309 while(m_pos != m_end && e_pos != e_end){
310 if(m_pos.index() < e_pos.index()){
311 elements.push_back({i,m_pos.index(), f(*m_pos, zero)});
312 ++m_pos;
313 }else if( m_pos.index() == e_pos.index()){
314 elements.push_back({i,m_pos.index(), f(*m_pos ,*e_pos)});
315 ++m_pos;
316 ++e_pos;
317 }
318 else{ //m_pos.index() > e_pos.index()
319 elements.push_back({i,e_pos.index(), f(zero,*e_pos)});
320 ++e_pos;
321 }
322 }
323 for(;m_pos != m_end;++m_pos){
324 elements.push_back({i,m_pos.index(), f(*m_pos, zero)});
325 }
326 for(;e_pos != e_end; ++e_pos){
327 elements.push_back({i,e_pos.index(), f(zero, *e_pos)});
328 }
329
330 //clear contents of m and fill with elements
331 m().clear_range(m().major_begin(i),m().major_end(i));
332 m().major_reserve(i,elements.size());
333 m_pos = m().major_begin(i);
334 for(auto elem: elements){
335 m_pos = m().set_element(m_pos, elem.j, elem.value);
336 }
337 }
338}
339
340
341//we only need to implement the remaining versions for column major second argument
342
343//dense-dense case
344template<class F,class M, class E>
345void matrix_assign_functor(
346 matrix_expression<M, cpu_tag>& m,
347 matrix_expression<E, cpu_tag> const& e,
348 F f,
349 row_major, column_major,dense_tag, dense_tag
350) {
351 //compute blockwise and wrelem the transposed block.
352 std::size_t const blockSize = 16;
353 typename M::value_type blockStorage[blockSize][blockSize];
354
355 typedef typename M::size_type size_type;
356 size_type size1 = m().size1();
357 size_type size2 = m().size2();
358 for (size_type iblock = 0; iblock < size1; iblock += blockSize){
359 for (size_type jblock = 0; jblock < size2; jblock += blockSize){
360 std::size_t blockSizei = std::min(blockSize,size1-iblock);
361 std::size_t blockSizej = std::min(blockSize,size2-jblock);
362
363 //fill the block with the values of e
364 for (size_type j = 0; j < blockSizej; ++j){
365 for (size_type i = 0; i < blockSizei; ++i){
366 blockStorage[i][j] = e()(iblock+i,jblock+j);
367 }
368 }
369
370 //compute block values and store in m
371 for (size_type i = 0; i < blockSizei; ++i){
372 for (size_type j = 0; j < blockSizej; ++j){
373 m()(iblock+i,jblock+j) = f(m()(iblock+i,jblock+j), blockStorage[i][j]);
374 }
375 }
376 }
377 }
378}
379
380//dense-sparse case
381template<class F,class M, class E>
382void matrix_assign_functor(
383 matrix_expression<M, cpu_tag>& m,
384 matrix_expression<E, cpu_tag> const& e,
385 F f,
386 row_major, column_major,dense_tag, sparse_tag
387) {
388 for(std::size_t j = 0; j != m().size2(); ++j){
389 auto columnM = column(m,j);
390 kernels::assign(columnM,column(e,j),f);
391 }
392}
393
394//sparse-sparse
395template<class F,class M, class E>
396void matrix_assign_functor(
397 matrix_expression<M, cpu_tag>& m,
398 matrix_expression<E, cpu_tag> const& e,
399 F f,
400 row_major, column_major,sparse_tag t,sparse_tag
401) {
402 typename matrix_temporary<M>::type eTrans = e;//explicit calculation of the transpose for now
403 matrix_assign_functor(m,eTrans,f,row_major(),row_major(),t,t);
404}
405
406
407//kernels for triangular
408template<class F, class M, class E, class Triangular, class Tag1, class Tag2>
409void matrix_assign_functor(
410 matrix_expression<M, cpu_tag>& m,
411 matrix_expression<E, cpu_tag> const& e,
412 F f,
413 triangular<row_major,Triangular>, triangular<row_major,Triangular>,
414 Tag1, Tag2
415) {
416 //there is nothing we can do if F does not leave the non-stored elements 0
417 //this is the case for all current assignment functors, but you never know :)
418
419 for(std::size_t i = 0; i != m().size1(); ++i){
420 auto mpos = m().major_begin(i);
421 auto epos = e().major_begin(i);
422 auto mend = m().major_end(i);
423 REMORA_SIZE_CHECK(mpos.index() == epos.index());
424 for(; mpos!=mend; ++mpos,++epos){
425 *mpos = f(*mpos,*epos);
426 }
427 }
428}
429
430//todo: this is suboptimal as we do strided access!!!!
431template<class F, class M, class E, class Triangular, class Tag1, class Tag2>
432void matrix_assign_functor(
433 matrix_expression<M, cpu_tag>& m,
434 matrix_expression<E, cpu_tag> const& e,
435 F f,
436 triangular<row_major,Triangular>, triangular<column_major,Triangular>,
437 Tag1, Tag2
438) {
439 //there is nothing we can do, if F does not leave the non-stored elements 0
440
441 for(std::size_t i = 0; i != m().size1(); ++i){
442 auto mpos = m().major_begin(i);
443 auto mend = m().major_end(i);
444 for(; mpos!=mend; ++mpos){
445 *mpos = f(*mpos,e()(i,mpos.index()));
446 }
447 }
448}
449
450
451}}
452
453#endif