1 // Copyright (c) 2017-2021, Lawrence Livermore National Security, LLC and
2 // other Axom Project Developers. See the top-level LICENSE file for details.
3 //
4 // SPDX-License-Identifier: (BSD-3-Clause)
5
6 #include "gtest/gtest.h"
7
8 #include "axom/core/numerics/Matrix.hpp"
9
10 // C/C++ includes
11 #include <sstream> // for std::ostringstream
12
13 using IndexType = axom::IndexType;
14
15 //-----------------------------------------------------------------------------
16 // HELPER ROUTINES
17 //-----------------------------------------------------------------------------
18 namespace
19 {
testConstAccess(const axom::numerics::Matrix<double> & A)20 void testConstAccess(const axom::numerics::Matrix<double>& A)
21 {
22 const int MROWS = 10;
23 const int NCOLS = 10;
24 EXPECT_EQ(MROWS, A.getNumRows());
25 EXPECT_EQ(NCOLS, A.getNumColumns());
26
27 for(int irow = 0; irow < MROWS; ++irow)
28 {
29 for(int jcol = 0; jcol < NCOLS; ++jcol)
30 {
31 double expval = static_cast<double>(irow * NCOLS + jcol);
32 EXPECT_EQ(expval, A(irow, jcol));
33 } // END for all columns
34 } // END for all rows
35 }
36
37 //------------------------------------------------------------------------------
testCopyConstructor(axom::numerics::Matrix<double> A)38 void testCopyConstructor(axom::numerics::Matrix<double> A)
39 {
40 const int MROWS = 10;
41 const int NCOLS = 10;
42 EXPECT_EQ(MROWS, A.getNumRows());
43 EXPECT_EQ(NCOLS, A.getNumColumns());
44
45 for(int irow = 0; irow < MROWS; ++irow)
46 {
47 for(int jcol = 0; jcol < NCOLS; ++jcol)
48 {
49 double expval = static_cast<double>(irow * NCOLS + jcol);
50 EXPECT_EQ(expval, A(irow, jcol));
51 } // END for all columns
52 } // END for all rows
53 }
54
55 //------------------------------------------------------------------------------
testExternalBufferPassByValue(axom::numerics::Matrix<int> A)56 void testExternalBufferPassByValue(axom::numerics::Matrix<int> A)
57 {
58 const int FILL_VAL = 42;
59
60 EXPECT_FALSE(A.usesExternalBuffer());
61 A.fill(FILL_VAL);
62
63 const int nrows = A.getNumRows();
64 const int ncols = A.getNumColumns();
65
66 for(int i = 0; i < nrows; ++i)
67 {
68 for(int j = 0; j < ncols; ++j)
69 {
70 EXPECT_EQ(FILL_VAL, A(i, j));
71 } // END for all columns
72 } // END for all rows
73 }
74
75 } /* end unnamed namespace */
76
77 //-----------------------------------------------------------------------------
78 // TEST ROUTINES
79 //------------------------------------------------------------------------------
TEST(numerics_matrix,basic_constructor)80 TEST(numerics_matrix, basic_constructor)
81 {
82 const int MROWS = 5;
83 const int NCOLS = 10;
84
85 axom::numerics::Matrix<double> A(MROWS, NCOLS);
86 EXPECT_EQ(MROWS, A.getNumRows());
87 EXPECT_EQ(NCOLS, A.getNumColumns());
88 EXPECT_FALSE(A.isSquare());
89
90 for(int irow = 0; irow < MROWS; ++irow)
91 {
92 for(int jcol = 0; jcol < NCOLS; ++jcol)
93 {
94 EXPECT_DOUBLE_EQ(0.0, A(irow, jcol));
95 }
96 }
97 }
98
99 //------------------------------------------------------------------------------
TEST(numerics_matrix,basic_constructor_with_value)100 TEST(numerics_matrix, basic_constructor_with_value)
101 {
102 const int MROWS = 5;
103 const int NCOLS = 10;
104 const double FILL_VAL = 2.5;
105
106 axom::numerics::Matrix<double> A(MROWS, NCOLS, FILL_VAL);
107 EXPECT_EQ(MROWS, A.getNumRows());
108 EXPECT_EQ(NCOLS, A.getNumColumns());
109 EXPECT_FALSE(A.isSquare());
110
111 for(int irow = 0; irow < MROWS; ++irow)
112 {
113 for(int jcol = 0; jcol < NCOLS; ++jcol)
114 {
115 EXPECT_DOUBLE_EQ(FILL_VAL, A(irow, jcol));
116 }
117 }
118 }
119
120 //------------------------------------------------------------------------------
TEST(numerics_matrix,array_constructor)121 TEST(numerics_matrix, array_constructor)
122 {
123 const int MROWS = 2;
124 const int NCOLS = 2;
125 int data[4] = {1, 2, 3, 4};
126
127 axom::numerics::Matrix<int> A(MROWS, NCOLS, data);
128 EXPECT_EQ(MROWS, A.getNumRows());
129 EXPECT_EQ(NCOLS, A.getNumColumns());
130
131 int idx = 0;
132 for(int j = 0; j < NCOLS; ++j)
133 {
134 for(int i = 0; i < MROWS; ++i)
135 {
136 ++idx;
137 EXPECT_EQ(idx, A(i, j));
138 }
139 }
140 }
141
142 //------------------------------------------------------------------------------
TEST(numerics_matrix,array_constructor_with_external_buffer)143 TEST(numerics_matrix, array_constructor_with_external_buffer)
144 {
145 const int MROWS = 4;
146 const int NCOLS = 2;
147 int data[8] = {1, 1, 1, 1, 2, 2, 2, 2};
148 // BEGIN SCOPE
149 {
150 axom::numerics::Matrix<int> A(MROWS, NCOLS, data, true);
151 EXPECT_TRUE(A.usesExternalBuffer());
152
153 for(int i = 0; i < NCOLS; ++i)
154 {
155 int* col = A.getColumn(i);
156 for(int j = 0; j < MROWS; ++j)
157 {
158 EXPECT_EQ(i + 1, col[j]);
159 } // END for all rows
160 } // END for all cols
161
162 A.swapColumns(0, 1);
163 }
164 // END SCOPE
165
166 // ensure the data is still there when the matrix goes out-of-scope
167 for(int i = 0; i < 8; ++i)
168 {
169 int expected = (i < 4) ? 2 : 1;
170 EXPECT_EQ(expected, data[i]);
171 }
172
173 // test assignment
174 // BEGIN SCOPE
175 {
176 axom::numerics::Matrix<int> A(MROWS, NCOLS, data, true);
177 EXPECT_TRUE(A.usesExternalBuffer());
178 A.swapColumns(0, 1);
179
180 // deep copy A into B
181 axom::numerics::Matrix<int> B = A;
182 EXPECT_FALSE(B.usesExternalBuffer());
183
184 const int nrows = B.getNumRows();
185 const int ncols = B.getNumColumns();
186 EXPECT_EQ(nrows, A.getNumRows());
187 EXPECT_EQ(ncols, A.getNumColumns());
188
189 for(int i = 0; i < nrows; ++i)
190 {
191 for(int j = 0; j < ncols; ++j)
192 {
193 EXPECT_EQ(A(i, j), B(i, j));
194 }
195 }
196
197 const int FILL_VAL = 3;
198 B.fill(FILL_VAL);
199 for(int i = 0; i < nrows; ++i)
200 {
201 for(int j = 0; j < ncols; ++j)
202 {
203 EXPECT_EQ(FILL_VAL, B(i, j));
204 EXPECT_FALSE(A(i, j) == B(i, j));
205 }
206 }
207
208 for(int i = 0; i < NCOLS; ++i)
209 {
210 int* col = A.getColumn(i);
211 for(int j = 0; j < MROWS; ++j)
212 {
213 EXPECT_EQ(i + 1, col[j]);
214 } // END for all rows
215 } // END for all cols
216 }
217 // END SCOPE
218
219 // test copy constructor
220 // BEGIN SCOPE
221 {
222 axom::numerics::Matrix<int> A(MROWS, NCOLS, data, true);
223 EXPECT_TRUE(A.usesExternalBuffer());
224
225 testExternalBufferPassByValue(A);
226
227 for(int i = 0; i < NCOLS; ++i)
228 {
229 int* col = A.getColumn(i);
230 for(int j = 0; j < MROWS; ++j)
231 {
232 EXPECT_EQ(i + 1, col[j]);
233 } // END for all rows
234 } // END for all cols
235 }
236 // END SCOPE
237 }
238
239 //------------------------------------------------------------------------------
TEST(numerics_matrix,is_square)240 TEST(numerics_matrix, is_square)
241 {
242 const int MROWS = 10;
243 const int NCOLS = 10;
244
245 axom::numerics::Matrix<double> A(MROWS, NCOLS);
246 EXPECT_TRUE(A.isSquare());
247 }
248
249 //------------------------------------------------------------------------------
TEST(numerics_matrix,random_access_operators)250 TEST(numerics_matrix, random_access_operators)
251 {
252 const int MROWS = 10;
253 const int NCOLS = 10;
254
255 axom::numerics::Matrix<double> A(MROWS, NCOLS);
256
257 for(int irow = 0; irow < MROWS; ++irow)
258 {
259 for(int jcol = 0; jcol < NCOLS; ++jcol)
260 {
261 double val = static_cast<double>(irow * NCOLS + jcol);
262 A(irow, jcol) = val;
263 EXPECT_EQ(val, A(irow, jcol));
264 } // END for all columns
265 } // END for all rows
266
267 testConstAccess(A);
268 }
269
270 //------------------------------------------------------------------------------
TEST(numerics_matrix,copy_constructor)271 TEST(numerics_matrix, copy_constructor)
272 {
273 const int MROWS = 10;
274 const int NCOLS = 10;
275
276 axom::numerics::Matrix<double> A(MROWS, NCOLS);
277
278 for(int irow = 0; irow < MROWS; ++irow)
279 {
280 for(int jcol = 0; jcol < NCOLS; ++jcol)
281 {
282 double val = static_cast<double>(irow * NCOLS + jcol);
283 A(irow, jcol) = val;
284 EXPECT_EQ(val, A(irow, jcol));
285 } // END for all columns
286 } // END for all rows
287
288 testCopyConstructor(A);
289 }
290
291 //------------------------------------------------------------------------------
TEST(numerics_matrix,assignment)292 TEST(numerics_matrix, assignment)
293 {
294 const int MROWS = 3;
295 const int NCOLS = 3;
296
297 axom::numerics::Matrix<int> A(MROWS, NCOLS);
298 A.fill(3);
299
300 axom::numerics::Matrix<int> B(2, 2);
301 B.fill(1);
302
303 B = A;
304
305 EXPECT_EQ(MROWS, B.getNumRows());
306 EXPECT_EQ(NCOLS, B.getNumColumns());
307
308 for(int i = 0; i < MROWS; ++i)
309 {
310 for(int j = 0; j < NCOLS; ++j)
311 {
312 EXPECT_EQ(A(i, j), B(i, j));
313 }
314 }
315 }
316
317 //------------------------------------------------------------------------------
TEST(numerics_matrix,getColumn)318 TEST(numerics_matrix, getColumn)
319 {
320 const int N = 3;
321 axom::numerics::Matrix<int> M = axom::numerics::Matrix<int>::identity(N);
322 EXPECT_EQ(N, M.getNumRows());
323 EXPECT_EQ(N, M.getNumColumns());
324
325 int expected[] = {1, 0, 0, 0, 1, 0, 0, 0, 1};
326
327 for(int j = 0; j < N; ++j)
328 {
329 const int* column = M.getColumn(j);
330 for(int i = 0; i < N; ++i)
331 {
332 EXPECT_EQ(expected[j * N + i], column[i]);
333 } // END for all i
334 } // END for all j
335 }
336
337 //------------------------------------------------------------------------------
TEST(numerics_matrix,getRow)338 TEST(numerics_matrix, getRow)
339 {
340 const int N = 3;
341 axom::numerics::Matrix<int> A(N, N);
342
343 int row_sums[] = {0, 0, 0};
344
345 for(IndexType i = 0; i < N; ++i)
346 {
347 A.fillRow(i, i + 1);
348 row_sums[i] = N * (i + 1);
349 }
350
351 IndexType p = 0;
352 IndexType size = 0;
353 for(IndexType i = 0; i < N; ++i)
354 {
355 int* row = A.getRow(i, p, size);
356 for(IndexType j = 0; j < size; j += p)
357 {
358 EXPECT_EQ(i + 1, row[j]);
359 }
360
361 double sum = 0.0;
362 for(IndexType j = 0; j < size; j += p)
363 {
364 sum += row[j];
365 }
366
367 EXPECT_EQ(row_sums[i], sum);
368 }
369 }
370
371 //------------------------------------------------------------------------------
TEST(numerics_matrix,getDiagonal)372 TEST(numerics_matrix, getDiagonal)
373 {
374 const int N = 3;
375 axom::numerics::Matrix<int> M = axom::numerics::Matrix<int>::identity(N);
376 EXPECT_EQ(N, M.getNumRows());
377 EXPECT_EQ(N, M.getNumColumns());
378 EXPECT_EQ(N, M.getDiagonalSize());
379
380 const int EXPECTED_SUM = N;
381
382 int* diagonal = new int[N];
383 M.getDiagonal(diagonal);
384
385 int sum = 0;
386 for(int i = 0; i < N; ++i)
387 {
388 EXPECT_EQ(1, diagonal[i]);
389 sum += diagonal[i];
390 }
391
392 EXPECT_EQ(EXPECTED_SUM, sum);
393 delete[] diagonal;
394
395 IndexType p = 0;
396 IndexType size = 0;
397 const int* diag = M.getDiagonal(p, size);
398 EXPECT_EQ(N + 1, p);
399 EXPECT_EQ(M.getDiagonalSize() * N, size);
400
401 int sum2 = 0;
402 for(IndexType i = 0; i < size; i += p)
403 {
404 EXPECT_EQ(1, diag[i]);
405 sum2 += diag[i];
406 }
407 EXPECT_EQ(EXPECTED_SUM, sum2);
408 }
409
410 //------------------------------------------------------------------------------
TEST(numerics_matrix,fillDiagonal)411 TEST(numerics_matrix, fillDiagonal)
412 {
413 const int N = 3;
414 axom::numerics::Matrix<int> M = axom::numerics::Matrix<int>::identity(N);
415 EXPECT_EQ(N, M.getNumRows());
416 EXPECT_EQ(N, M.getNumColumns());
417 EXPECT_EQ(N, M.getDiagonalSize());
418
419 M.fillDiagonal(3);
420
421 int* diagonal = new int[N];
422 M.getDiagonal(diagonal);
423
424 for(int i = 0; i < N; ++i)
425 {
426 EXPECT_EQ(3, diagonal[i]);
427 }
428
429 delete[] diagonal;
430 }
431
432 //------------------------------------------------------------------------------
TEST(numerics_matrix,fillRow)433 TEST(numerics_matrix, fillRow)
434 {
435 const int FILL_VAL = 3;
436 const int TARGET_ROW = 1;
437
438 const int N = 3;
439 axom::numerics::Matrix<int> M = axom::numerics::Matrix<int>::identity(N);
440 EXPECT_EQ(N, M.getNumRows());
441 EXPECT_EQ(N, M.getNumColumns());
442
443 M.fillRow(TARGET_ROW, FILL_VAL);
444
445 for(int i = 0; i < N; ++i)
446 {
447 EXPECT_EQ(FILL_VAL, M(TARGET_ROW, i));
448 }
449 }
450
451 //------------------------------------------------------------------------------
TEST(numerics_matrix,fillColumn)452 TEST(numerics_matrix, fillColumn)
453 {
454 const int FILL_VAL = 3;
455 const int TARGET_COL = 1;
456
457 const int N = 3;
458 axom::numerics::Matrix<int> M = axom::numerics::Matrix<int>::identity(N);
459 EXPECT_EQ(N, M.getNumRows());
460 EXPECT_EQ(N, M.getNumColumns());
461
462 M.fillColumn(TARGET_COL, FILL_VAL);
463
464 for(int i = 0; i < N; ++i)
465 {
466 EXPECT_EQ(FILL_VAL, M(i, TARGET_COL));
467 }
468 }
469
470 //------------------------------------------------------------------------------
TEST(numerics_matrix,fill)471 TEST(numerics_matrix, fill)
472 {
473 const int FILL_VAL = 3;
474
475 const int N = 3;
476 axom::numerics::Matrix<int> M = axom::numerics::Matrix<int>::identity(N);
477 EXPECT_EQ(N, M.getNumRows());
478 EXPECT_EQ(N, M.getNumColumns());
479
480 M.fill(FILL_VAL);
481
482 for(int i = 0; i < N; ++i)
483 {
484 for(int j = 0; j < N; ++j)
485 {
486 EXPECT_EQ(FILL_VAL, M(i, j));
487 }
488 }
489 }
490
491 //------------------------------------------------------------------------------
TEST(numerics_matrix,swapRows)492 TEST(numerics_matrix, swapRows)
493 {
494 const int M = 2;
495 const int N = 3;
496 axom::numerics::Matrix<int> A = axom::numerics::Matrix<int>::zeros(M, N);
497
498 EXPECT_EQ(2, M);
499 int FILL_VAL[2] = {3, 9};
500
501 A.fillRow(0, FILL_VAL[0]);
502 A.fillRow(1, FILL_VAL[1]);
503
504 A.swapRows(0, 1);
505
506 for(IndexType i = 0; i < N; ++i)
507 {
508 int* column = A.getColumn(i);
509 for(IndexType j = 0; j < M; ++j)
510 {
511 EXPECT_EQ(FILL_VAL[(j + 1) % M], column[j]);
512 }
513 }
514 }
515
516 //------------------------------------------------------------------------------
TEST(numerics_matrix,swapColumns)517 TEST(numerics_matrix, swapColumns)
518 {
519 const int M = 3;
520 const int N = 4;
521
522 // setup a test matrix
523 axom::numerics::Matrix<int> A(M, N);
524 for(IndexType i = 0; i < N; ++i)
525 {
526 A.fillColumn(i, i + 1);
527 }
528
529 int first_column = 0;
530 int last_column = N - 1;
531 A.swapColumns(first_column, last_column);
532
533 int* first_column_data = A.getColumn(first_column);
534 int* last_column_data = A.getColumn(last_column);
535 for(IndexType i = 0; i < M; ++i)
536 {
537 EXPECT_EQ(last_column + 1, first_column_data[i]);
538 EXPECT_EQ(first_column + 1, last_column_data[i]);
539 }
540 }
541
542 //------------------------------------------------------------------------------
TEST(numerics_matrix,output_stream)543 TEST(numerics_matrix, output_stream)
544 {
545 axom::numerics::Matrix<int> A(2, 3);
546 A(0, 0) = 1;
547 A(0, 1) = 2;
548 A(0, 2) = 3;
549 A(1, 0) = 4;
550 A(1, 1) = 5;
551 A(1, 2) = 6;
552
553 std::ostringstream expected_stream;
554 expected_stream << "[ 1 2 3 ]\n"
555 << "[ 4 5 6 ]\n";
556
557 std::ostringstream actual_stream;
558 actual_stream << A;
559
560 EXPECT_EQ(expected_stream.str(), actual_stream.str());
561 }
562