1 /* Siconos is a program dedicated to modeling, simulation and control
2  * of non smooth dynamical systems.
3  *
4  * Copyright 2021 INRIA.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 #include "SiconosMatrixSetBlock.hpp"
20 #include <boost/numeric/ublas/matrix_proxy.hpp>
21 #include <boost/numeric/ublas/triangular.hpp>
22 #include <boost/numeric/ublas/symmetric.hpp>
23 #include <boost/numeric/ublas/banded.hpp>
24 #include <boost/numeric/ublas/matrix_sparse.hpp>
25 #include "SiconosMatrix.hpp"
26 #include "SiconosException.hpp"
27 
setBlock(SPC::SiconosMatrix input_matrix,SP::SiconosMatrix output_matrix,const Index & dim,const Index & start)28 void setBlock(SPC::SiconosMatrix  input_matrix, SP::SiconosMatrix output_matrix, const Index& dim, const Index& start)
29 {
30   // To copy a subBlock of input_matrix into a subBlock of output_matrix.
31   // dim[0], dim[1]: number of rows and columns of the sub-block
32   // start[0], start[1]: position (row, column) of the first element of the subBlock in input_matrix
33   // start[2], start[3]: position (row, column) of the first element of the subBlock in output_matrix
34 
35   if(input_matrix == output_matrix)  // useless op => nothing to be done.
36     return;
37 
38   Siconos::UBLAS_TYPE numIn = input_matrix->num();
39   Siconos::UBLAS_TYPE numOut = output_matrix->num();
40 
41   if(numOut == Siconos::ZERO || numOut == Siconos::IDENTITY)  // if output_matrix = 0 or Identity => read-only
42     THROW_EXCEPTION("output_matrix is read-only (zero or identity matrix?).");
43 
44   // Check dimension
45   Index MDim(4); // dim. of matrices input_matrix and output_matrix.
46   MDim[0] = input_matrix->size(0);
47   MDim[1] = input_matrix->size(1);
48   MDim[2] = output_matrix->size(0);
49   MDim[3] = output_matrix->size(1);
50 
51   for(unsigned int i = 0; i < 4 ; ++i)
52     if(start[i] >= MDim[i])
53       THROW_EXCEPTION("matrices, setBlock(input_matrix, ...): sub-block indices are out of range.");
54 
55   // index position of the last element in subBlock ...
56   Index end(4);
57   end[0] = dim[0] + start[0];
58   end[1] = dim[1] + start[1];
59   end[2] = dim[0] + start[2];
60   end[3] = dim[1] + start[3];
61 
62   for(unsigned int i = 0; i < 4 ; ++i)
63     if(end[i] > MDim[i])
64       THROW_EXCEPTION("sub-block last indices are out of range.");
65 
66   // Elements from row/col start[i] to row/col (end[i]-1) will be copied.
67 
68   // If both matrices input_matrix and output_matrix are block, exception.
69   if(numIn == Siconos::BLOCK && numOut == Siconos::BLOCK)
70     THROW_EXCEPTION("not yet implemented for input_matrix and output_matrix both BlockMatrix. Try to use setBlock on the sub-matrices?");
71 
72   if(numOut == Siconos::BLOCK)  // if output_matrix is a BlockMatrix.
73   {
74 
75     // Steps:
76     // A - Find the blocks of output_matrix that "own" indices start[2] and end[2] ie
77     //     the first and last sub-block to be set in a block-column
78     //         --> numbers blockStart0 and blockEnd0
79     // B - Find the  Block of output_matrix that "owns" index start[3] and end[3] ie
80     //     the first sub-block to be set in a block-row
81     //         --> numbers blockStart1 and blockEnd1
82     //
83     //        => The blocks concerned in output_matrix, are those between (block) rows blockStart0 and blockEnd0
84     //           and (block) columns blockStart1 and blockEnd1.
85     //
86     // C - Loop through the concerned blocks (name = currentBlock) of output_matrix and call setBlock(input_matrix, currentBlock, subSize, currentPos).
87     //     subSize: dim. of the considered sub-block of currentBlock to be set
88     //     currentPos: same as "start" vector but for currentBlock
89     //
90 
91     // A - Block-Row position: we look for the block of output_matrix that include index start[2] and end[2].
92     //
93     unsigned int blockStart0 = 0;
94     SPC::Index tab = output_matrix->tabRow();
95     while(start[2] >= (*tab)[blockStart0] && blockStart0 < tab->size())
96       blockStart0 ++;
97     // Relative position in the block blockStart0 of the first element to be set.
98     unsigned int posOut0 = start[2];
99     if(blockStart0 != 0)
100       posOut0 -= (*tab)[blockStart0 - 1];
101 
102     unsigned int blockEnd0 = blockStart0;
103     while(end[2] > (*tab)[blockEnd0] && blockEnd0 < tab->size())
104       blockEnd0 ++;
105 
106     // Size of the last sub-block in the column of block
107     unsigned int lastBlockSize0 = end[2];
108     if(blockEnd0 != 0)
109       lastBlockSize0 -= (*tab)[blockEnd0 - 1];
110 
111     // B - Block-Col position: we look for the block of output_matrix that include index start[3] and end[3].
112     unsigned int blockStart1 = 0;
113     tab = output_matrix->tabCol();
114     while(start[3] >= (*tab)[blockStart1] && blockStart1 < tab->size())
115       blockStart1 ++;
116     // Relative position in the block blockStart1 of the first element to be set.
117     unsigned int posOut1 = start[3];
118     if(blockStart1 != 0)
119       posOut1 -= (*tab)[blockStart1 - 1];
120 
121     unsigned int blockEnd1 = blockStart1;
122     while(end[3] > (*tab)[blockEnd1] && blockEnd1 < tab->size())
123       blockEnd1 ++;
124 
125     // Size of the last sub-block in the row of block
126     unsigned int lastBlockSize1 = end[3];
127     if(blockEnd1 != 0)
128       lastBlockSize1 -= (*tab)[blockEnd1 - 1];
129 
130     //C - Next, 3 steps for each row:
131     // - set first sub-block in the row (number blockStart1)
132     // - set all other blocks in the row except the last one
133     // - set last block (number blockEnd1)
134     // Same process for other rows ...
135 
136     // The current considered block
137     SP::SiconosMatrix   currentBlock = output_matrix->block(blockStart0, blockStart1);
138 
139     // dim of the subBlock of currentBlock to be set.
140     Index subSize(2);
141     // indices of the first element of input_matrix (resp. currentBlock) to be read (resp. set)  (same as start for input_matrix and output_matrix).
142     Index currentPos(4);
143 
144     // currentBlock position in output_matrix.
145     unsigned int numRow = blockStart0;
146     unsigned int numCol = blockStart1;
147 
148     // Init currentPos
149     // row and col position for first element to be read in input_matrix,
150     currentPos[0] = start[0];
151     currentPos[1] = start[1];
152     // row and col position for first element in sub-block of Mout (namely currentBlock).
153     currentPos[2] = posOut0;
154     currentPos[3] = posOut1;
155 
156     while(numRow != blockEnd0 + 1)
157     {
158 
159       while(numCol != blockEnd1 + 1)
160       {
161         // Get the block of output_matrix from which a sub-block will be set ...
162         currentBlock = output_matrix->block(numRow, numCol);
163 
164         // Set subSize[0], dim (rows) and subSize[1], dim (columns) of the sub-block.
165         // subSize[0] is only required for the first block in the row, after it remains constant.
166         subSize[1] = currentBlock->size(1);
167 
168         // Warning: test "a" must be done before test "b"
169         if(numCol == blockEnd1)  // if last column of blocks -> test "a"
170           subSize[1] = lastBlockSize1;
171 
172         if(numCol == blockStart1)  // -> test "b"
173         {
174           subSize[1] -= posOut1;
175           subSize[0] = currentBlock->size(0);
176           if(numRow == blockEnd0)  // if last row of blocks
177             subSize[0] = lastBlockSize0;
178           if(numRow == blockStart0)  // if first row of blocks
179             subSize[0] -= posOut0;
180         }
181 
182         // Set sub-block
183         setBlock(input_matrix, currentBlock, subSize, currentPos);
184 
185         // Update currentPos:
186         // col position for first element to be read in input_matrix,
187         currentPos[1] += subSize[1] ;
188         // col position for first element to be set in sub-block.
189         currentPos[3] = 0;
190         numCol++;
191       }
192 
193       numCol = blockStart1;
194       numRow++;
195 
196       // Update currentPos:
197       // row position for first element to be read in input_matrix,
198       currentPos[0] += subSize[0] ;
199       // col position for first element to be read in input_matrix,
200       currentPos[1] = start[1] ;
201       // row position for first element to be set in sub-block.
202       currentPos[2] = 0;
203       // col position for first element to be set in sub-block.
204       currentPos[3] = posOut1;
205 
206     }
207 
208   }
209   else if(numIn == Siconos::BLOCK)  // If input_matrix is a BlockMatrix.
210   {
211 
212     // Same process as for numOut == 0
213 
214     unsigned int blockStart0 = 0;
215     SPC::Index tab = input_matrix->tabRow();
216     while(start[0] >= (*tab)[blockStart0] && blockStart0 < tab->size())
217       blockStart0 ++;
218     // Relative position in the block blockStart0 of the first element to be set.
219     unsigned int posOut0 = start[0];
220     if(blockStart0 != 0)
221       posOut0 -= (*tab)[blockStart0 - 1];
222 
223     unsigned int blockEnd0 = blockStart0;
224     while(end[0] > (*tab)[blockEnd0] && blockEnd0 < tab->size())
225       blockEnd0 ++;
226 
227     // Size of the last sub-block in the column of block
228     unsigned int lastBlockSize0 = end[0];
229     if(blockEnd0 != 0)
230       lastBlockSize0 -= (*tab)[blockEnd0 - 1];
231 
232     // B - Block-Col position: we look for the block of output_matrix that include index start[3] and end[3].
233     unsigned int blockStart1 = 0;
234     tab = input_matrix->tabCol();
235     while(start[1] >= (*tab)[blockStart1] && blockStart1 < tab->size())
236       blockStart1 ++;
237     // Relative position in the block blockStart1 of the first element to be set.
238     unsigned int posOut1 = start[1];
239     if(blockStart1 != 0)
240       posOut1 -= (*tab)[blockStart1 - 1];
241 
242     unsigned int blockEnd1 = blockStart1;
243     while(end[1] > (*tab)[blockEnd1] && blockEnd1 < tab->size())
244       blockEnd1 ++;
245 
246     // Size of the last sub-block in the row of block
247     unsigned int lastBlockSize1 = end[1];
248     if(blockEnd1 != 0)
249       lastBlockSize1 -= (*tab)[blockEnd1 - 1];
250 
251     //C - Next, 3 steps for each row:
252     // - set first sub-block in the row (number blockStart1)
253     // - set all other blocks in the row except the last one
254     // - set last block (number blockEnd1)
255     // Same process for other rows ...
256 
257     // The current considered block
258     SPC::SiconosMatrix  currentBlock = input_matrix->block(blockStart0, blockStart1);
259 
260     // dim of the subBlock of currentBlock to be set.
261     Index subSize(2);
262     // indices of the first element of input_matrix (resp. currentBlock) to be read (resp. set)  (same as start for input_matrix and output_matrix).
263     Index currentPos(4);
264 
265     // currentBlock position in output_matrix.
266     unsigned int numRow = blockStart0;
267     unsigned int numCol = blockStart1;
268 
269     // Init currentPos
270     // row and col position for first element to be read in input_matrix,
271     currentPos[0] = posOut0;
272     currentPos[1] = posOut1;
273     // row and col position for first element in sub-block of Mout (namely currentBlock).
274     currentPos[2] = start[2];
275     currentPos[3] = start[3];
276 
277     while(numRow != blockEnd0 + 1)
278     {
279 
280       while(numCol != blockEnd1 + 1)
281       {
282         // Get the block of output_matrix from which a sub-block will be set ...
283         currentBlock = input_matrix->block(numRow, numCol);
284 
285         // Set subSize[0], dim (rows) and subSize[1], dim (columns) of the sub-block.
286         // subSize[0] is only required for the first block in the row, after it remains constant.
287         subSize[1] = currentBlock->size(1);
288         // Warning: test "a" must be done before test "b"
289         if(numCol == blockEnd1)  // if last column of blocks -> test "a"
290           subSize[1] = lastBlockSize1;
291 
292         if(numCol == blockStart1)  // -> test "b"
293         {
294           subSize[1] -= posOut1;
295           subSize[0] = currentBlock->size(0);
296           if(numRow == blockEnd0)  // if last row of blocks
297             subSize[0] = lastBlockSize0;
298           if(numRow == blockStart0)  // if first row of blocks
299             subSize[0] -= posOut0;
300         }
301 
302         // Set sub-block
303         setBlock(currentBlock, output_matrix, subSize, currentPos);
304 
305         // Update currentPos:
306         // col position for first element to be read in input_matrix,
307         currentPos[1] = 0 ;
308         // col position for first element to be set in sub-block.
309         currentPos[3] += subSize[1];
310         numCol++;
311       }
312 
313       numCol = blockStart1;
314       numRow++;
315 
316       // Update currentPos:
317       // row position for first element to be read in input_matrix,
318       currentPos[0] = 0;
319       // col position for first element to be read in input_matrix,
320       currentPos[1] = posOut1;
321       // row position for first element to be set in sub-block.
322       currentPos[2] += subSize[0] ;
323       // col position for first element to be set in sub-block.
324       currentPos[3] = start[3];
325 
326     }
327     output_matrix->resetFactorizationFlags();
328 
329   }
330   else // neither input_matrix nor output_matrix is a BlockMatrix.
331   {
332     if(numOut == Siconos::DENSE)
333     {
334       ublas::matrix_range<DenseMat> out_range(*output_matrix->dense(),
335                                               ublas::range(start[2],end[2]),
336                                               ublas::range(start[3], end[3]));
337       if(numIn == Siconos::DENSE)
338       {
339         ublas::matrix_range<DenseMat> in_range(*input_matrix->dense(),
340                                                ublas::range(start[0],end[0]),
341                                                ublas::range(start[1], end[1]));
342         noalias(out_range) = in_range;
343       }
344       else if(numIn == Siconos::SYMMETRIC)
345       {
346         ublas::matrix_range<SymMat> in_range(*input_matrix->sym(),
347                                              ublas::range(start[0],end[0]),
348                                              ublas::range(start[1], end[1]));
349         noalias(out_range) = in_range;
350       }
351       else if(numIn == Siconos::SPARSE)
352       {
353         ublas::matrix_range<SparseMat> in_range(*input_matrix->sparse(),
354                                                 ublas::range(start[0],end[0]),
355                                                 ublas::range(start[1], end[1]));
356         noalias(out_range) = in_range;
357       }
358       else if(numIn == Siconos::IDENTITY)
359       {
360         ublas::matrix_range<IdentityMat> in_range(*input_matrix->identity(),
361                                                   ublas::range(start[0],end[0]),
362                                                   ublas::range(start[1], end[1]));
363         noalias(out_range) = in_range;
364       }
365       else if(numIn == Siconos::ZERO)
366         out_range *= 0.;
367       else
368         THROW_EXCEPTION("unconsistent types between input_matrix and output_matrix.");
369     }
370     else if(numOut == Siconos::SPARSE)
371     {
372       ublas::matrix_range<SparseMat> out_range(*output_matrix->sparse(),
373                                                ublas::range(start[2],end[2]),
374                                                ublas::range(start[3], end[3]));
375       if(numIn == Siconos::DENSE)
376       {
377         ublas::matrix_range<DenseMat> in_range(*input_matrix->dense(),
378                                                ublas::range(start[0],end[0]),
379                                                ublas::range(start[1], end[1]));
380         noalias(out_range) = in_range;
381       }
382       else if(numIn == Siconos::SYMMETRIC)
383       {
384         ublas::matrix_range<SymMat> in_range(*input_matrix->sym(),
385                                              ublas::range(start[0],end[0]),
386                                              ublas::range(start[1], end[1]));
387         noalias(out_range) = in_range;
388       }
389       else if(numIn == Siconos::SPARSE)
390       {
391         ublas::matrix_range<SparseMat> in_range(*input_matrix->sparse(),
392                                                 ublas::range(start[0],end[0]),
393                                                 ublas::range(start[1], end[1]));
394         noalias(out_range) = in_range;
395       }
396       else if(numIn == Siconos::IDENTITY)
397       {
398         ublas::matrix_range<IdentityMat> in_range(*input_matrix->identity(),
399                                                   ublas::range(start[0],end[0]),
400                                                   ublas::range(start[1], end[1]));
401         noalias(out_range) = in_range;
402       }
403       else if(numIn == Siconos::ZERO)
404         out_range *= 0.;
405       else
406         THROW_EXCEPTION("unconsistent types between input_matrix and output_matrix.");
407     }
408 
409     output_matrix->resetFactorizationFlags();
410   }
411 }
412