A Discrete-Event Network Simulator
API
matrix-array-test-suite.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2022 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation;
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  * Author: Biljana Bojovic <bbojovic@cttc.es>
18  */
19 
20 #include "ns3/log.h"
21 #include "ns3/matrix-array.h"
22 #include "ns3/test.h"
23 
31 namespace ns3
32 {
33 
34 namespace tests
35 {
36 
37 NS_LOG_COMPONENT_DEFINE("MatrixArrayTest");
38 
43 template <class T>
45 {
46  public:
47  MatrixArrayTestCase() = default;
53  MatrixArrayTestCase(const std::string& name);
54 
56  ~MatrixArrayTestCase() override;
79 
80  protected:
81  private:
82  void DoRun() override;
83 };
84 
85 template <class T>
87  : TestCase(name)
88 {
89 }
90 
91 template <class T>
93 {
94 }
95 
96 template <class T>
97 void
99 {
100  // test multiplication of matrices (MatrixArray containing only 1 matrix)
102  MatrixArray<T> m2 = MatrixArray<T>(m1.GetNumCols(), m1.GetNumRows());
103  for (size_t i = 0; i < m1.GetNumRows(); ++i)
104  {
105  for (size_t j = 0; j < m1.GetNumCols(); ++j)
106  {
107  m1(i, j) = 1;
108  m2(j, i) = 1;
109  }
110  }
111  MatrixArray<T> m3 = m1 * m2;
112  NS_LOG_INFO("m1:" << m1);
113  NS_LOG_INFO("m2:" << m2);
114  NS_LOG_INFO("m3 = m1 * m2:" << m3);
116  m1.GetNumRows(),
117  "The number of rows in resulting matrix is not correct");
119  m2.GetNumCols(),
120  "The number of cols in resulting matrix is not correct");
122  m3.GetNumCols(),
123  "The number of rows and cols should be equal");
124  for (size_t i = 0; i < m3.GetNumCols(); ++i)
125  {
126  for (size_t j = 0; j < m3.GetNumRows(); ++j)
127  {
128  NS_TEST_ASSERT_MSG_EQ(std::real(m3(i, j)),
129  m1.GetNumCols(),
130  "The element value should be " << m1.GetNumCols());
131  }
132  }
133 
134  // multiplication with a scalar value
135  MatrixArray<T> m4 = m3 * (static_cast<T>(5.0));
136  for (size_t i = 0; i < m4.GetNumCols(); ++i)
137  {
138  for (size_t j = 0; j < m4.GetNumRows(); ++j)
139  {
140  NS_TEST_ASSERT_MSG_EQ(m3(i, j) * (static_cast<T>(5.0)),
141  m4(i, j),
142  "The values are not equal");
143  }
144  }
145  NS_LOG_INFO("m4 = m3 * 5:" << m4);
146 
147  // test multiplication of arrays of matrices
148  MatrixArray<T> m5 = MatrixArray<T>(2, 3, 2);
150  for (size_t p = 0; p < m5.GetNumPages(); ++p)
151  {
152  for (size_t i = 0; i < m5.GetNumRows(); ++i)
153  {
154  for (size_t j = 0; j < m5.GetNumCols(); ++j)
155  {
156  m5(i, j, p) = 1;
157  m6(j, i, p) = 1;
158  }
159  }
160  }
161  MatrixArray<T> m7 = m5 * m6;
163  m5.GetNumRows(),
164  "The number of rows in resulting matrix is not correct");
166  m6.GetNumCols(),
167  "The number of cols in resulting matrix is not correct");
169  m7.GetNumCols(),
170  "The number of rows and cols should be equal");
171 
172  for (size_t p = 0; p < m7.GetNumPages(); ++p)
173  {
174  for (size_t i = 0; i < m7.GetNumCols(); ++i)
175  {
176  for (size_t j = 0; j < m7.GetNumRows(); ++j)
177  {
178  NS_TEST_ASSERT_MSG_EQ(std::real(m7(i, j, p)),
179  m5.GetNumCols(),
180  "The element value should be " << m5.GetNumCols());
181  }
182  }
183  }
184  // test ostream operator
185  NS_LOG_INFO("m5:" << m5);
186  NS_LOG_INFO("m6:" << m6);
187  NS_LOG_INFO("m7 = m5 * m6:" << m7);
188 
189  // test transpose function
190  MatrixArray<T> m8 = m5.Transpose();
191  NS_TEST_ASSERT_MSG_EQ(m6, m8, "These two matrices should be equal");
192  NS_LOG_INFO("m8 = m5.Transpose ()" << m8);
193 
194  // test transpose using initialization arrays
195  std::valarray<int> a{0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4};
196  std::valarray<int> b{0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4};
197  std::valarray<T> aCasted(a.size());
198  std::valarray<T> bCasted(b.size());
199  for (size_t i = 0; i < a.size(); ++i)
200  {
201  aCasted[i] = static_cast<T>(a[i]);
202  }
203  for (size_t i = 0; i < b.size(); ++i)
204  {
205  bCasted[i] = static_cast<T>(b[i]);
206  }
207  m5 = MatrixArray<T>(3, 5, 1, aCasted);
208  m6 = MatrixArray<T>(5, 3, 1, bCasted);
209  m8 = m5.Transpose();
210  NS_TEST_ASSERT_MSG_EQ(m6, m8, "These two matrices should be equal");
211  NS_LOG_INFO("m5 (3, 5, 1):" << m5);
212  NS_LOG_INFO("m6 (5, 3, 1):" << m6);
213  NS_LOG_INFO("m8 (5, 3, 1) = m5.Transpose ()" << m8);
214 
215  // test 1D array creation, i.e. vector and transposing it
216  MatrixArray<T> m9 = MatrixArray<T>(std::vector<T>({0, 1, 2, 3, 4, 5, 6, 7}));
217  NS_TEST_ASSERT_MSG_EQ((m9.GetNumRows() == 8) && (m9.GetNumCols() == 1) &&
218  (m9.GetNumPages() == 1),
219  true,
220  "Creation of vector is not correct.");
221 
222  NS_LOG_INFO("Vector:" << m9);
223  NS_LOG_INFO("Vector after transposing:" << m9.Transpose());
224 
225  // Test basic operators
226  MatrixArray<T> m10 =
227  MatrixArray<T>(m9.GetNumRows(), m9.GetNumCols(), m9.GetNumPages(), m9.GetValues());
228  NS_TEST_ASSERT_MSG_EQ(m10, m9, "m10 and m9 should be equal");
229  m10 -= m9;
230  NS_TEST_ASSERT_MSG_NE(m10, m9, "m10 and m9 should not be equal");
231  m10 += m9;
232  NS_TEST_ASSERT_MSG_EQ(m10, m9, "m10 and m9 should be equal");
233  m10 = m9;
234  NS_TEST_ASSERT_MSG_EQ(m10, m9, "m10 and m9 should be equal");
235  m10 = m9 + m9;
236  NS_TEST_ASSERT_MSG_NE(m10, m9, "m10 and m9 should not be equal");
237  m10 = m10 - m9;
238  NS_TEST_ASSERT_MSG_EQ(m10, m9, "m10 and m9 should be equal");
239 
240  // test multiplication by using an initialization matrixArray
241  // matrix dimensions in each page are 2x3, 3x2, and the resulting matrix per page is a square
242  // matrix 2x2
243  a = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5};
244  b = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1};
245  std::valarray<int> c{2, 3, 4, 6, 2, 3, 4, 6};
246  aCasted = std::valarray<T>(a.size());
247  bCasted = std::valarray<T>(b.size());
248  std::valarray<T> cCasted = std::valarray<T>(c.size());
249 
250  for (size_t i = 0; i < a.size(); ++i)
251  {
252  aCasted[i] = static_cast<T>(a[i]);
253  }
254  for (size_t i = 0; i < b.size(); ++i)
255  {
256  bCasted[i] = static_cast<T>(b[i]);
257  }
258  for (size_t i = 0; i < c.size(); ++i)
259  {
260  cCasted[i] = static_cast<T>(c[i]);
261  }
262  MatrixArray<T> m11 = MatrixArray<T>(2, 3, 2, aCasted);
263  MatrixArray<T> m12 = MatrixArray<T>(3, 2, 2, bCasted);
264  MatrixArray<T> m13 = m11 * m12;
265  MatrixArray<T> m14 = MatrixArray<T>(2, 2, 2, cCasted);
267  m14.GetNumCols(),
268  "The number of columns is not as expected.");
270  m14.GetNumRows(),
271  "The number of rows is not as expected.");
272  NS_TEST_ASSERT_MSG_EQ(m13, m14, "The values are not equal.");
273  NS_LOG_INFO("m11 (2,3,2):" << m11);
274  NS_LOG_INFO("m12 (3,2,2):" << m12);
275  NS_LOG_INFO("m13 = m11 * m12:" << m13);
276 
277  // test multiplication by using an initialization matrixArray
278  // matrices have different number of elements per page
279  // matrix dimensions in each page are 4x3, 3x2, and the resulting matrix
280  // dimensions are 4x2
281  a = std::valarray<int>(
282  {0, 1, 0, 1, 2, 3, 2, 3, 4, 5, 4, 5, 0, 1, 0, 1, 2, 3, 2, 3, 4, 5, 4, 5});
283  b = std::valarray<int>({0, 1, 0, 1, 0, 1, 0, 10, 0, 10, 0, 10});
284  c = std::valarray<int>({2, 3, 2, 3, 4, 6, 4, 6, 20, 30, 20, 30, 40, 60, 40, 60});
285  aCasted = std::valarray<T>(a.size());
286  bCasted = std::valarray<T>(b.size());
287  cCasted = std::valarray<T>(c.size());
288 
289  for (size_t i = 0; i < a.size(); ++i)
290  {
291  aCasted[i] = static_cast<T>(a[i]);
292  }
293  for (size_t i = 0; i < b.size(); ++i)
294  {
295  bCasted[i] = static_cast<T>(b[i]);
296  }
297  for (size_t i = 0; i < c.size(); ++i)
298  {
299  cCasted[i] = static_cast<T>(c[i]);
300  }
301 
302  m11 = MatrixArray<T>(4, 3, 2, aCasted);
303  m12 = MatrixArray<T>(3, 2, 2, bCasted);
304  m13 = m11 * m12;
305  m14 = MatrixArray<T>(4, 2, 2, cCasted);
307  m14.GetNumCols(),
308  "The number of columns is not as expected.");
310  m14.GetNumRows(),
311  "The number of rows is not as expected.");
312  NS_TEST_ASSERT_MSG_EQ(m13, m14, "The values are not equal.");
313  NS_LOG_INFO("m11 (4,3,2):" << m11);
314  NS_LOG_INFO("m12 (3,2,2):" << m12);
315  NS_LOG_INFO("m13 = m11 * m12:" << m13);
316 
317  // test multiplication by using an initialization matrixArray
318  // matrices have different number of elements per page
319  // matrix dimensions in each page are 1x3, 3x2, and the resulting matrix has
320  // dimensions 1x2
321  a = std::valarray<int>({5, 4, 5, 5, 4, 5});
322  b = std::valarray<int>({0, 1, 0, 1, 0, 1, 1, 2, 3, 10, 100, 1000});
323  c = std::valarray<int>({4, 10, 28, 5450});
324  aCasted = std::valarray<T>(a.size());
325  bCasted = std::valarray<T>(b.size());
326  cCasted = std::valarray<T>(c.size());
327 
328  for (size_t i = 0; i < a.size(); ++i)
329  {
330  aCasted[i] = static_cast<T>(a[i]);
331  }
332  for (size_t i = 0; i < b.size(); ++i)
333  {
334  bCasted[i] = static_cast<T>(b[i]);
335  }
336  for (size_t i = 0; i < c.size(); ++i)
337  {
338  cCasted[i] = static_cast<T>(c[i]);
339  }
340 
341  m11 = MatrixArray<T>(1, 3, 2, aCasted);
342  m12 = MatrixArray<T>(3, 2, 2, bCasted);
343  m13 = m11 * m12;
344  m14 = MatrixArray<T>(1, 2, 2, cCasted);
346  m14.GetNumCols(),
347  "The number of columns is not as expected.");
349  m14.GetNumRows(),
350  "The number of rows is not as expected.");
351  NS_TEST_ASSERT_MSG_EQ(m13, m14, "The values are not equal.");
352  NS_LOG_INFO("m11 (1,3,2):" << m11);
353  NS_LOG_INFO("m12 (3,2,2):" << m12);
354  NS_LOG_INFO("m13 = m11 * m12:" << m13);
355 
356  // test MultiplyByLeftAndRightMatrix
357  std::valarray<int> d{1, 1, 1};
358  std::valarray<int> e{1, 1};
359  std::valarray<int> f{1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3};
360  std::valarray<int> g{12, 12};
361  std::valarray<T> dCasted(d.size());
362  std::valarray<T> eCasted(e.size());
363  std::valarray<T> fCasted(f.size());
364  std::valarray<T> gCasted(g.size());
365  for (size_t i = 0; i < d.size(); ++i)
366  {
367  dCasted[i] = static_cast<T>(d[i]);
368  }
369  for (size_t i = 0; i < e.size(); ++i)
370  {
371  eCasted[i] = static_cast<T>(e[i]);
372  }
373  for (size_t i = 0; i < f.size(); ++i)
374  {
375  fCasted[i] = static_cast<T>(f[i]);
376  }
377  for (size_t i = 0; i < g.size(); ++i)
378  {
379  gCasted[i] = static_cast<T>(g[i]);
380  }
381  MatrixArray<T> m15 = MatrixArray<T>(1, 3, dCasted);
382  MatrixArray<T> m16 = MatrixArray<T>(2, 1, eCasted);
383  MatrixArray<T> m17 = MatrixArray<T>(3, 2, 2, fCasted);
384  MatrixArray<T> m18 = MatrixArray<T>(1, 1, 2, gCasted);
385  MatrixArray<T> m19 = m17.MultiplyByLeftAndRightMatrix(m15, m16);
386  NS_TEST_ASSERT_MSG_EQ(m19, m18, "The matrices should be equal.");
387 
388  // test MultiplyByLeftAndRightMatrix
389  std::valarray<int> h{1, 3, 2, 2, 4, 0};
390  std::valarray<int> j{2, 2, 3, 4, 1, 3, 0, 5};
391  std::valarray<int> k{1, 2, 0, 0, 2, 3, 4, 1, 2, 3, 4, 1, 1, 2, 0, 0, 2, 3, 4, 1, 2, 3, 4, 1};
392  std::valarray<int> l{144, 132, 128, 104, 144, 132, 128, 104};
393  std::valarray<T> hCasted(h.size());
394  std::valarray<T> jCasted(j.size());
395  std::valarray<T> kCasted(k.size());
396  std::valarray<T> lCasted(l.size());
397  for (size_t i = 0; i < h.size(); ++i)
398  {
399  hCasted[i] = static_cast<T>(h[i]);
400  }
401  for (size_t i = 0; i < j.size(); ++i)
402  {
403  jCasted[i] = static_cast<T>(j[i]);
404  }
405  for (size_t i = 0; i < k.size(); ++i)
406  {
407  kCasted[i] = static_cast<T>(k[i]);
408  }
409  for (size_t i = 0; i < l.size(); ++i)
410  {
411  lCasted[i] = static_cast<T>(l[i]);
412  }
413  MatrixArray<T> m20 = MatrixArray<T>(2, 3, hCasted);
414  MatrixArray<T> m21 = MatrixArray<T>(4, 2, jCasted);
415  MatrixArray<T> m22 = MatrixArray<T>(3, 4, 2, kCasted);
416  MatrixArray<T> m23 = MatrixArray<T>(2, 2, 2, lCasted);
417  MatrixArray<T> m24 = m22.MultiplyByLeftAndRightMatrix(m20, m21);
418  NS_TEST_ASSERT_MSG_EQ(m24, m23, "The matrices should be equal.");
419  NS_LOG_INFO("m20:" << m20);
420  NS_LOG_INFO("m21:" << m21);
421  NS_LOG_INFO("m22:" << m22);
422  NS_LOG_INFO("m24 = m20 * m22 * m21" << m24);
423 
424  // test initialization with moving
425  size_t lCastedSize = lCasted.size();
426  NS_LOG_INFO("size() of lCasted before move: " << lCasted.size());
427  MatrixArray<T> m25 = MatrixArray<T>(2, 2, 2, std::move(lCasted));
428  NS_LOG_INFO("m25.GetSize ()" << m25.GetSize());
429  NS_TEST_ASSERT_MSG_EQ(lCastedSize, m25.GetSize(), "The number of elements are not equal.");
430 
431  size_t hCastedSize = hCasted.size();
432  NS_LOG_INFO("size() of hCasted before move: " << hCasted.size());
433  MatrixArray<T> m26 = MatrixArray<T>(2, 3, std::move(hCasted));
434  NS_LOG_INFO("m26.GetSize ()" << m26.GetSize());
435  NS_TEST_ASSERT_MSG_EQ(hCastedSize, m26.GetSize(), "The number of elements are not equal.");
436 
437  size_t jCastedSize = jCasted.size();
438  NS_LOG_INFO("size() of jCasted before move: " << jCasted.size());
439  MatrixArray<T> m27 = MatrixArray<T>(std::move(jCasted));
440  NS_LOG_INFO("m27.GetSize ()" << m27.GetSize());
441  NS_TEST_ASSERT_MSG_EQ(jCastedSize, m27.GetSize(), "The number of elements are not equal.");
442 }
443 
450 {
451  public:
459  ComplexMatrixArrayTestCase(const std::string& name);
461  ~ComplexMatrixArrayTestCase() override;
462 
463  private:
464  void DoRun() override;
465 };
466 
468  : TestCase("ComplexMatrixArrayTestCase")
469 {
470 }
471 
473  : TestCase(name)
474 {
475 }
476 
478 {
479 }
480 
481 void
483 {
484  std::valarray<std::complex<double>> complexValarray1 = {
485  {1, 1},
486  {2, 2},
487  {3, 3},
488  {4, 4},
489  {5, 5},
490  {6, 6},
491  {-1, 1},
492  {-2, 2},
493  {-3, 3},
494  {-4, 4},
495  {-5, 5},
496  {-6, 6},
497  };
498  std::valarray<std::complex<double>> complexValarray2 = {
499  {1, -1},
500  {4, -4},
501  {2, -2},
502  {5, -5},
503  {3, -3},
504  {6, -6},
505  {-1, -1},
506  {-4, -4},
507  {-2, -2},
508  {-5, -5},
509  {-3, -3},
510  {-6, -6},
511  };
512  ComplexMatrixArray m1 = ComplexMatrixArray(3, 2, 2, complexValarray1);
513  ComplexMatrixArray m2 = ComplexMatrixArray(2, 3, 2, complexValarray2);
514  ComplexMatrixArray m3 = m1.HermitianTranspose();
515  NS_LOG_INFO("m1 (3, 2, 2):" << m1);
516  NS_LOG_INFO("m2 (2, 3, 2):" << m2);
517  NS_LOG_INFO("m3 (2, 3, 2):" << m3);
518  NS_TEST_ASSERT_MSG_EQ(m2, m3, "m2 and m3 matrices should be equal");
519 }
520 
526 {
527  public:
530 };
531 
533  : TestSuite("matrix-array-test")
534 {
535  AddTestCase(new MatrixArrayTestCase<double>("Test MatrixArray<double>"));
536  AddTestCase(
537  new MatrixArrayTestCase<std::complex<double>>("Test MatrixArray<std::complex<double>>"));
538  AddTestCase(new MatrixArrayTestCase<int>("Test MatrixArray<int>"));
539  AddTestCase(new ComplexMatrixArrayTestCase("Test ComplexMatrixArray"));
540 }
541 
547 
548 } // namespace tests
549 } // namespace ns3
double f(double x, void *params)
Definition: 80211b.c:70
MatrixArray class inherits ValArray class and provides additional interfaces to ValArray which enable...
Definition: matrix-array.h:83
MatrixArray Transpose() const
This operator interprets the 3D array as an array of matrices, and performs a linear algebra operatio...
MatrixArray MultiplyByLeftAndRightMatrix(const MatrixArray< T > &lMatrix, const MatrixArray< T > &rMatrix) const
Multiply each matrix in the array by the left and the right matrix.
encapsulates test code
Definition: test.h:1060
void AddTestCase(TestCase *testCase, TestDuration duration=QUICK)
Add an individual child TestCase to this test suite.
Definition: test.cc:301
A suite of tests to run.
Definition: test.h:1256
const std::valarray< T > & GetValues() const
Returns underlying values.
Definition: val-array.h:560
size_t GetNumPages() const
Definition: val-array.h:393
size_t GetSize() const
Definition: val-array.h:400
size_t GetNumRows() const
Definition: val-array.h:379
size_t GetNumCols() const
Definition: val-array.h:386
Test for testing functions that apply to MatrixArrays that use complex numbers, such as HermitianTran...
void DoRun() override
Implementation to actually run this TestCase.
MatrixArray test case for testing constructors, operators and other functions.
MatrixArrayTestCase< T > & operator=(const MatrixArrayTestCase< T > &)=default
Copy assignment operator.
MatrixArrayTestCase(MatrixArrayTestCase< T > &&)=default
Move constructor.
MatrixArrayTestCase< T > & operator=(MatrixArrayTestCase< T > &&)=default
Move assignment operator.
MatrixArrayTestCase(const MatrixArrayTestCase< T > &)=default
Copy constructor.
void DoRun() override
Implementation to actually run this TestCase.
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:275
#define NS_TEST_ASSERT_MSG_EQ(actual, limit, msg)
Test that an actual and expected (limit) value are equal and report and abort if not.
Definition: test.h:144
#define NS_TEST_ASSERT_MSG_NE(actual, limit, msg)
Test that an actual and expected (limit) value are not equal and report and abort if not.
Definition: test.h:564
const double m1
First component modulus, 232 - 209.
Definition: rng-stream.cc:60
const double m2
Second component modulus, 232 - 22853.
Definition: rng-stream.cc:63
static MatrixArrayTestSuite g_matrixArrayTestSuite
MatrixArrayTestSuite instance variable.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
MatrixArray< std::complex< double > > ComplexMatrixArray
Create an alias for MatrixArray using complex type.
Definition: matrix-array.h:247