A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
matrix-array.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 "matrix-array.h"
21
22#ifdef HAVE_EIGEN3
23#include <Eigen/Dense>
24#endif
25
26namespace ns3
27{
28
29#ifdef HAVE_EIGEN3
30template <class T>
31using EigenMatrix = Eigen::Map<Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>>;
32template <class T>
33using ConstEigenMatrix = Eigen::Map<const Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>>;
34#endif
35
36template <class T>
37MatrixArray<T>::MatrixArray(size_t numRows, size_t numCols, size_t numPages)
38 : ValArray<T>(numRows, numCols, numPages)
39{
40}
41
42template <class T>
43MatrixArray<T>::MatrixArray(const std::valarray<T>& values)
44 : ValArray<T>(values)
45{
46}
47
48template <class T>
49MatrixArray<T>::MatrixArray(std::valarray<T>&& values)
50 : ValArray<T>(std::move(values))
51{
52}
53
54template <class T>
55MatrixArray<T>::MatrixArray(const std::vector<T>& values)
56 : ValArray<T>(values)
57{
58}
59
60template <class T>
61MatrixArray<T>::MatrixArray(size_t numRows, size_t numCols, const std::valarray<T>& values)
62 : ValArray<T>(numRows, numCols, values)
63{
64}
65
66template <class T>
67MatrixArray<T>::MatrixArray(size_t numRows, size_t numCols, std::valarray<T>&& values)
68 : ValArray<T>(numRows, numCols, std::move(values))
69{
70}
71
72template <class T>
74 size_t numCols,
75 size_t numPages,
76 const std::valarray<T>& values)
77 : ValArray<T>(numRows, numCols, numPages, values)
78{
79}
80
81template <class T>
83 size_t numCols,
84 size_t numPages,
85 std::valarray<T>&& values)
86 : ValArray<T>(numRows, numCols, numPages, std::move(values))
87{
88}
89
90template <class T>
93{
94 NS_ASSERT_MSG(m_numPages == rhs.m_numPages, "MatrixArrays have different numbers of matrices.");
95 NS_ASSERT_MSG(m_numCols == rhs.m_numRows, "Inner dimensions of matrices mismatch.");
96
97 MatrixArray<T> res{m_numRows, rhs.m_numCols, m_numPages};
98
99 for (size_t page = 0; page < res.m_numPages; ++page)
100 {
101#ifdef HAVE_EIGEN3 // Eigen found and enabled Eigen optimizations
102
103 ConstEigenMatrix<T> lhsEigenMatrix(GetPagePtr(page), m_numRows, m_numCols);
104 ConstEigenMatrix<T> rhsEigenMatrix(rhs.GetPagePtr(page), rhs.m_numRows, rhs.m_numCols);
105 EigenMatrix<T> resEigenMatrix(res.GetPagePtr(page), res.m_numRows, res.m_numCols);
106 resEigenMatrix = lhsEigenMatrix * rhsEigenMatrix;
108#else // Eigen not found or Eigen optimizations not enabled
109
110 size_t matrixOffset = page * m_numRows * m_numCols;
111 size_t rhsMatrixOffset = page * rhs.m_numRows * rhs.m_numCols;
112 for (size_t i = 0; i < res.m_numRows; ++i)
114 for (size_t j = 0; j < res.m_numCols; ++j)
115 {
116 res(i, j, page) = (m_values[std::slice(matrixOffset + i, m_numCols, m_numRows)] *
117 rhs.m_values[std::slice(rhsMatrixOffset + j * rhs.m_numRows,
118 rhs.m_numRows,
119 1)])
120 .sum();
122 }
123
124#endif
125 }
126 return res;
127}
128
129template <class T>
132{
133 // Create the matrix where m_numRows = this.m_numCols, m_numCols = this.m_numRows,
134 // m_numPages = this.m_numPages
135 MatrixArray<T> res{m_numCols, m_numRows, m_numPages};
136
137 for (size_t page = 0; page < m_numPages; ++page)
139#ifdef HAVE_EIGEN3 // Eigen found and Eigen optimizations enabled
140
141 ConstEigenMatrix<T> thisMatrix(GetPagePtr(page), m_numRows, m_numCols);
142 EigenMatrix<T> resEigenMatrix(res.GetPagePtr(page), res.m_numRows, res.m_numCols);
143 resEigenMatrix = thisMatrix.transpose();
144
145#else // Eigen not found or Eigen optimizations not enabled
146
147 size_t matrixIndex = page * m_numRows * m_numCols;
148 for (size_t i = 0; i < m_numRows; ++i)
149 {
150 res.m_values[std::slice(matrixIndex + i * res.m_numRows, res.m_numRows, 1)] =
151 m_values[std::slice(matrixIndex + i, m_numCols, m_numRows)];
152 }
153
154#endif
155 }
156 return res;
157}
158
159template <class T>
160MatrixArray<T>
162{
163 MatrixArray<T> res{1, 1, m_numPages};
164 NS_ASSERT_MSG(m_numRows == m_numCols, "Matrix is not square");
165 // In case of small matrices, we use a fast path
166 if (m_numRows == 1)
167 {
168 return *this;
169 }
170 // Calculate determinant for each matrix
171 for (size_t page = 0; page < m_numPages; ++page)
172 {
173 res(0, 0, page) = 0;
174 auto pageValues = GetPagePtr(page);
175
176 // Fast path for 2x2 matrices
177 if (m_numRows == 2)
178 {
179 res(0, 0, page) = pageValues[0] * pageValues[3] - pageValues[1] * pageValues[2];
180 continue;
181 }
182 for (size_t detN = 0; detN < m_numRows; detN++)
183 {
184 auto partDetP = T{0} + 1.0;
185 auto partDetN = T{0} + 1.0;
186 for (size_t row = 0; row < m_numRows; row++)
187 {
188 // Wraparound not to have to extend the matrix
189 // Positive determinant
190 size_t col = (row + detN) % m_numCols;
191 partDetP *= pageValues[row * m_numCols + col];
192
193 // Negative determinant
194 col = m_numCols - 1 - (row + detN) % m_numCols;
195 partDetN *= pageValues[row * m_numCols + col];
197 res(0, 0, page) += partDetP - partDetN;
198 }
199 }
200 return res;
202
203template <class T>
206{
207 MatrixArray<T> res{1, 1, m_numPages};
208 for (size_t page = 0; page < m_numPages; ++page)
209 {
210 // Calculate the sum of squared absolute values of each matrix page
211 res[page] = 0;
212 auto pagePtr = this->GetPagePtr(page);
213 for (size_t i = 0; i < m_numRows * m_numCols; i++)
214 {
215 auto absVal = std::abs(pagePtr[i]);
216 res[page] += absVal * absVal;
217 }
218 res[page] = sqrt(res[page]);
219 }
220 return res;
221}
222
223template <class T>
226 const MatrixArray<T>& rMatrix) const
227{
228 NS_ASSERT_MSG(lMatrix.m_numPages == 1 && rMatrix.m_numPages == 1,
229 "The left and right MatrixArray should have only one page.");
230 NS_ASSERT_MSG(lMatrix.m_numCols == m_numRows,
231 "Left vector numCols and this MatrixArray numRows mismatch.");
232 NS_ASSERT_MSG(m_numCols == rMatrix.m_numRows,
233 "Right vector numRows and this MatrixArray numCols mismatch.");
234
235 MatrixArray<T> res{lMatrix.m_numRows, rMatrix.m_numCols, m_numPages};
236
237#ifdef HAVE_EIGEN3
238
239 ConstEigenMatrix<T> lMatrixEigen(lMatrix.GetPagePtr(0), lMatrix.m_numRows, lMatrix.m_numCols);
240 ConstEigenMatrix<T> rMatrixEigen(rMatrix.GetPagePtr(0), rMatrix.m_numRows, rMatrix.m_numCols);
241#endif
242
243 for (size_t page = 0; page < m_numPages; ++page)
244 {
245#ifdef HAVE_EIGEN3 // Eigen found and Eigen optimizations enabled
246
247 ConstEigenMatrix<T> matrixEigen(GetPagePtr(page), m_numRows, m_numCols);
248 EigenMatrix<T> resEigenMap(res.GetPagePtr(page), res.m_numRows, res.m_numCols);
249
250 resEigenMap = lMatrixEigen * matrixEigen * rMatrixEigen;
251
252#else // Eigen not found or Eigen optimizations not enabled
253
254 size_t matrixOffset = page * m_numRows * m_numCols;
255 for (size_t resRow = 0; resRow < res.m_numRows; ++resRow)
256 {
257 for (size_t resCol = 0; resCol < res.m_numCols; ++resCol)
258 {
259 // create intermediate row result, a multiply of resRow row of lMatrix and each
260 // column of this matrix
261 std::valarray<T> interRes(m_numCols);
262 for (size_t thisCol = 0; thisCol < m_numCols; ++thisCol)
263 {
264 interRes[thisCol] =
265 (lMatrix
266 .m_values[std::slice(resRow, lMatrix.m_numCols, lMatrix.m_numRows)] *
267 m_values[std::slice(matrixOffset + thisCol * m_numRows, m_numRows, 1)])
268 .sum();
269 }
270 // multiply intermediate results and resCol column of the rMatrix
271 res(resRow, resCol, page) =
272 (interRes *
273 rMatrix.m_values[std::slice(resCol * rMatrix.m_numRows, rMatrix.m_numRows, 1)])
274 .sum();
275 }
276 }
277#endif
278 }
279 return res;
280}
281
282template <class T>
283template <bool EnableBool, typename>
284MatrixArray<T>
286{
287 MatrixArray<std::complex<double>> retMatrix = this->Transpose();
288
289 for (size_t index = 0; index < this->GetSize(); ++index)
290 {
291 retMatrix.m_values[index] = std::conj(retMatrix.m_values[index]);
292 }
293 return retMatrix;
294}
295
296template <class T>
298MatrixArray<T>::MakeNCopies(size_t nCopies) const
299{
300 NS_ASSERT_MSG(m_numPages == 1, "The MatrixArray should have only one page to be copied.");
301 auto copiedMatrix = MatrixArray<T>{m_numRows, m_numCols, nCopies};
302 for (size_t copy = 0; copy < nCopies; copy++)
303 {
304 for (size_t i = 0; i < m_numRows * m_numCols; i++)
305 {
306 copiedMatrix.GetPagePtr(copy)[i] = m_values[i];
307 }
308 }
309 return copiedMatrix;
310}
311
312template <class T>
315{
316 NS_ASSERT_MSG(page < m_numPages, "The page to extract from the MatrixArray is out of bounds.");
317 auto extractedPage = MatrixArray<T>{m_numRows, m_numCols, 1};
318
319 for (size_t i = 0; i < m_numRows * m_numCols; ++i)
320 {
321 extractedPage.m_values[i] = GetPagePtr(page)[i];
322 }
323 return extractedPage;
324}
325
326template <class T>
329{
330 auto jointMatrix =
331 MatrixArray<T>{pages.front().GetNumRows(), pages.front().GetNumCols(), pages.size()};
332 for (size_t page = 0; page < jointMatrix.GetNumPages(); page++)
333 {
334 NS_ASSERT_MSG(pages[page].GetNumRows() == jointMatrix.GetNumRows(),
335 "All page matrices should have the same number of rows");
336 NS_ASSERT_MSG(pages[page].GetNumCols() == jointMatrix.GetNumCols(),
337 "All page matrices should have the same number of columns");
338 NS_ASSERT_MSG(pages[page].GetNumPages() == 1,
339 "All page matrices should have a single page");
340
341 size_t i = 0;
342 for (auto a : pages[page].GetValues())
343 {
344 jointMatrix.GetPagePtr(page)[i] = a;
345 i++;
346 }
347 }
348 return jointMatrix;
349}
350
351template <class T>
353MatrixArray<T>::IdentityMatrix(const size_t size, const size_t pages)
354{
355 auto identityMatrix = MatrixArray<T>{size, size, pages};
356 for (std::size_t page = 0; page < pages; page++)
357 {
358 for (std::size_t i = 0; i < size; i++)
359 {
360 identityMatrix(i, i, page) = 1.0;
361 }
362 }
363 return identityMatrix;
364}
365
366template <class T>
369{
370 NS_ASSERT_MSG(likeme.GetNumRows() == likeme.GetNumCols(), "Template array is not square.");
371 return IdentityMatrix(likeme.GetNumRows(), likeme.GetNumPages());
372}
373
375 const;
377template class MatrixArray<double>;
378template class MatrixArray<int>;
379
380} // namespace ns3
MatrixArray class inherits ValArray class and provides additional interfaces to ValArray which enable...
Definition: matrix-array.h:83
MatrixArray< T > HermitianTranspose() const
Function that performs the Hermitian transpose of this MatrixArray and returns a new matrix that is t...
MatrixArray operator*(const T &rhs) const
Element-wise multiplication with a scalar value.
Definition: matrix-array.h:298
MatrixArray Determinant() const
This operator calculates a vector o determinants, one for each page.
static MatrixArray< T > IdentityMatrix(const size_t size, const size_t pages=1)
Function produces an identity MatrixArray with the specified size.
static MatrixArray< T > JoinPages(const std::vector< MatrixArray< T > > &pages)
Function joins multiple pages into a single MatrixArray.
MatrixArray Transpose() const
This operator interprets the 3D array as an array of matrices, and performs a linear algebra operatio...
MatrixArray< T > MakeNCopies(size_t nCopies) const
Function that copies the current 1-page matrix into a new matrix with n copies of the original matrix...
MatrixArray()=default
MatrixArray< T > ExtractPage(size_t page) const
Function extracts a page from a MatrixArray.
MatrixArray MultiplyByLeftAndRightMatrix(const MatrixArray< T > &lMatrix, const MatrixArray< T > &rMatrix) const
Multiply each matrix in the array by the left and the right matrix.
MatrixArray FrobeniusNorm() const
This operator calculates a vector of Frobenius norm, one for each page.
ValArray is a class to efficiently store 3D array.
Definition: val-array.h:85
T * GetPagePtr(size_t pageIndex)
Get a data pointer to a specific 2D array for use in linear algebra libraries.
Definition: val-array.h:527
size_t GetNumPages() const
Definition: val-array.h:398
size_t m_numCols
The size of the second dimension, i.e., the number of columns of each 2D array.
Definition: val-array.h:372
std::valarray< T > m_values
The data values.
Definition: val-array.h:375
size_t GetNumRows() const
Definition: val-array.h:384
size_t m_numRows
The size of the first dimension, i.e., the number of rows of each 2D array.
Definition: val-array.h:370
size_t GetNumCols() const
Definition: val-array.h:391
size_t m_numPages
The size of the third dimension, i.e., the number of 2D arrays.
Definition: val-array.h:374
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:86
Every class exported by the ns3 library is enclosed in the ns3 namespace.
uint32_t GetSize(Ptr< const Packet > packet, const WifiMacHeader *hdr, bool isAmpdu)
Return the total size of the packet after WifiMacHeader and FCS trailer have been added.
Definition: wifi-utils.cc:134
STL namespace.