A Discrete-Event Network Simulator
Home
Tutorials ▼
English
Documentation ▼
Installation
Manual
Models
Contributing
Wiki
Development ▼
API Docs
Issue Tracker
Merge Requests
API
Loading...
Searching...
No Matches
symmetric-adjacency-matrix.h
Go to the documentation of this file.
1
/* Copyright (c) 2025 CTTC
2
*
3
* SPDX-License-Identifier: GPL-2.0-only
4
*
5
* Author: Gabriel Ferreira <gabrielcarvfer@gmail.com>
6
*/
7
8
#ifndef NS3_SYMMETRIC_ADJACENCY_MATRIX_H
9
#define NS3_SYMMETRIC_ADJACENCY_MATRIX_H
10
11
#include <algorithm>
12
#include <vector>
13
14
namespace
ns3
15
{
16
17
/**
18
* @brief A class representing a symmetric adjacency matrix.
19
*
20
* Since the matrix is symmetric, we save up on memory by
21
* storing only the lower left triangle, including the main
22
* diagonal.
23
*
24
* In pseudocode, the matrix is stored as a vector m_matrix, where
25
* each new row is accessed via an offset precomputed in m_rowOffsets.
26
* We also keep track of the number of rows in m_rows.
27
*
28
* A 4x4 matrix would be represented as follows:
29
*
30
* @code
31
* m_matrix= [
32
* 0
33
* 1 2
34
* 3 4 5
35
* 6 7 8 9
36
* ];
37
* m_rowOffsets = [0, 1, 3, 6];
38
* m_rows = 4;
39
* @endcode
40
*
41
* To add a new row (`AddRow()`) in the adjacency matrix (equivalent to an additional node in a
42
bidirected graph),
43
* we need to first add a new offset, then increment the number of rows and finally resize the
44
vector.
45
*
46
* @code
47
* m_rowOffsets.push_back(m_matrix.size());
48
* m_rows++;
49
* m_matrix.resize(m_matrix.size()+m_rows);
50
* @endcode
51
*
52
* The resulting state would be:
53
*
54
* @code
55
* m_rowOffsets = [0, 1, 3, 6, 10];
56
* m_rows = 5;
57
* m_matrix= [
58
* 0
59
* 1 2
60
* 3 4 5
61
* 6 7 8 9
62
* 10 11 12 13 14
63
* ];
64
* @endcode
65
*
66
* In this previous example, the elements of the matrix are
67
* the offset of the values from the beginning of the vector.
68
*
69
* In practice, this matrix could store the state between a given
70
* pair of a link between two nodes. The state could be a boolean
71
* value, in case just tracking valid/invalid,
72
* connected/disconnected link, or numerical types to store
73
* weights, which can be used for routing algorithms.
74
*
75
* The `adjacency-matrix-example` illustrates the usage of the adjacency matrix
76
* in a routing example.
77
*
78
* First we set up the matrix with capacity for 10 nodes.
79
* All values are initialized to maximum, to indicate a disconnected node.
80
*
81
* @code
82
* constexpr float maxFloat = std::numeric_limits<float>::max();
83
* // Create routing weight matrix for 10 nodes and initialize weights to infinity (disconnected)
84
* ns3::SymmetricAdjacencyMatrix<float> routeWeights(10, maxFloat);
85
* @endcode
86
*
87
* We can then map graph nodes into the table rows
88
* @code
89
* // Node | Corresponding matrix row
90
* // A | 0
91
* // B | 1
92
* // C | 2
93
* // D | 3
94
* // E | 4
95
* // F | 5
96
* // G | 6
97
* // H | 7
98
* // I | 8
99
* // J | 9
100
* @endcode
101
*
102
* Then proceed to populate the matrix to reflect the graph
103
*
104
* @code
105
* // A------5-------B-------------14-------C
106
* // \ \ /1|
107
* // \ 3 J |
108
* // \ \ /1 | 7
109
* // 4 E-2-F--4---G--3--H |
110
* // \ 8 / \ |
111
* // D-------- 10--I
112
*
113
* // Distance from nodes to other nodes
114
* routeWeights.SetValue(0, 1, 5); // A-B=5
115
* routeWeights.SetValue(1, 2, 14); // B-C=14
116
* routeWeights.SetValue(0, 3, 4); // A-D=4
117
* routeWeights.SetValue(1, 5, 3); // B-F=3
118
* routeWeights.SetValue(2, 9, 1); // C-J=1
119
* routeWeights.SetValue(9, 7, 1); // J-H=1
120
* routeWeights.SetValue(2, 8, 7); // C-I=7
121
* routeWeights.SetValue(3, 4, 8); // D-E=8
122
* routeWeights.SetValue(4, 5, 2); // E-F=2
123
* routeWeights.SetValue(5, 6, 4); // F-G=4
124
* routeWeights.SetValue(6, 7, 3); // G-H=3
125
* routeWeights.SetValue(7, 8, 10); // H-I=10
126
* @endcode
127
*
128
* Then we set the weights from the nodes to themselves as 0
129
* @code
130
* for (std::size_t i=0; i < routeWeights.GetRows(); i++)
131
* {
132
* routeWeights.SetValue(i, i, 0);
133
* }
134
* @endcode
135
*
136
* Create the known shortest paths
137
* @code
138
* std::map<std::pair<int, int>, std::vector<int>> routeMap;
139
* for (std::size_t i = 0; i < routeWeights.GetRows(); i++)
140
* {
141
* for (std::size_t j = 0; j < routeWeights.GetRows(); j++)
142
* {
143
* if (routeWeights.GetValue(i, j) != maxFloat)
144
* {
145
* if (i != j)
146
* {
147
* routeMap[{i, j}] = {(int)i, (int)j};
148
* }
149
* else
150
* {
151
* routeMap[{i, j}] = {(int)i};
152
* }
153
* }
154
* }
155
* }
156
* @endcode
157
*
158
* And we finally can proceed to assemble paths between nodes
159
* and store them in a routing table. In this case, by brute-force
160
*
161
* @code
162
* for (std::size_t bridgeNode = 0; bridgeNode < routeWeights.GetRows(); bridgeNode++)
163
* {
164
* for (std::size_t srcNode = 0; srcNode < routeWeights.GetRows(); srcNode++)
165
* {
166
* for (std::size_t dstNode = 0; dstNode < routeWeights.GetRows(); dstNode++)
167
* {
168
* auto weightA = routeWeights.GetValue(srcNode, bridgeNode);
169
* auto weightB = routeWeights.GetValue(bridgeNode, dstNode);
170
* // If there is a path between A and bridge, plus bridge and B
171
* if (std::max(weightA, weightB) == maxFloat)
172
* {
173
* continue;
174
* }
175
* // Check if sum of weights is lower than existing path
176
* auto weightAB = routeWeights.GetValue(srcNode, dstNode);
177
* if (weightA + weightB < weightAB)
178
* {
179
* // If it is, update adjacency matrix with the new weight of the shortest
180
* // path
181
* routeWeights.SetValue(srcNode, dstNode, weightA + weightB);
182
*
183
* // Retrieve the partial routes A->bridge and bridge->C,
184
* // and assemble the new route A->bridge->C
185
* const auto srcToBridgeRoute = routeMap.at({srcNode, bridgeNode});
186
* const auto bridgeToDstRoute = routeMap.at({bridgeNode, dstNode});
187
* std::vector<int> dst;
188
* dst.insert(dst.end(), srcToBridgeRoute.begin(), srcToBridgeRoute.end());
189
* dst.insert(dst.end(), bridgeToDstRoute.begin() + 1, bridgeToDstRoute.end());
190
* routeMap[{srcNode, dstNode}] = dst;
191
*
192
* // We also include the reverse path, since the graph is bidirectional
193
* std::vector<int> invDst(dst.rbegin(), dst.rend());
194
* routeMap[{dstNode, srcNode}] = invDst;
195
* }
196
* }
197
* }
198
* }
199
* @endcode
200
*
201
* After this, we have both the complete route, weight of the route, and the weights for each hop in
202
* the route.
203
*
204
* We can print all this information for a given route between nodes srcNodeOpt and
205
* dstNodeOpt with
206
*
207
* @code
208
* std::cout << "route between " << (char)(srcNodeOpt + 'A') << " and "
209
* << (char)(dstNodeOpt + 'A') << " (length "
210
* << routeWeights.GetValue(srcNodeOpt, dstNodeOpt) << "):";
211
* auto lastNodeNumber = srcNodeOpt;
212
* for (auto nodeNumber : routeMap.at({srcNodeOpt, dstNodeOpt}))
213
* {
214
* std::cout << "--" << routeWeights.GetValue(lastNodeNumber, nodeNumber) << "-->"
215
* << (char)('A' + nodeNumber);
216
* lastNodeNumber = nodeNumber;
217
* }
218
* @endcode
219
*
220
* Which, for example, between nodes A and I, would print
221
*
222
* @verbatim
223
route between A and I (length 24):--0-->A--5-->B--3-->F--4-->G--3-->H--1-->J--1-->C--7-->I
224
@endverbatim
225
*
226
* In case one of the links is disconnected, the weights of the adjacency matrix can be reset
227
* with SetValueAdjacent(disconnectedNode, maxFloat).
228
*
229
* Note that, in this implementation, all the routes containing the node need to be removed from
230
* routeMap, and the search needs to be re-executed.
231
*/
232
template
<
typename
T>
233
class
SymmetricAdjacencyMatrix
234
{
235
public
:
236
/**
237
* Default constructor
238
* @param [in] numRows The number of rows in the matrix
239
* @param [in] value The default initialization value of matrix
240
*/
241
SymmetricAdjacencyMatrix
(std::size_t numRows = 0, T value = {})
242
{
243
m_rows
= numRows;
244
m_matrix
.resize(
m_rows
* (
m_rows
+ 1) / 2);
245
std::fill(
m_matrix
.begin(),
m_matrix
.end(), value);
246
for
(std::size_t i = 0; i < numRows; i++)
247
{
248
m_rowOffsets
.push_back(i * (i + 1) / 2);
249
}
250
}
251
252
/**
253
* @brief Retrieve the value of matrix (row, column) node
254
* @param [in] row The row of the matrix to retrieve the value
255
* @param [in] column The column of the matrix to retrieve the value
256
* @return value retrieved from matrix (row, column) or matrix (column, row)
257
*/
258
T
GetValue
(std::size_t row, std::size_t column)
259
{
260
// Highest id should be always row, since we have only half matrix
261
const
auto
maxIndex = std::max(row, column);
262
const
auto
minIndex = std::min(row, column);
263
return
m_matrix
.at(
m_rowOffsets
.at(maxIndex) + minIndex);
264
}
265
266
/**
267
* @brief Set the value of matrix (row, column) node
268
* @param [in] row The row of the matrix to set the value
269
* @param [in] column The column of the matrix to set the value
270
* @param [in] value value to be assigned to matrix (row, column) or matrix (column, row)
271
*/
272
void
SetValue
(std::size_t row, std::size_t column, T value)
273
{
274
// Highest id should be always row, since we have only half matrix
275
const
auto
maxIndex = std::max(row, column);
276
const
auto
minIndex = std::min(row, column);
277
m_matrix
.at(
m_rowOffsets
.at(maxIndex) + minIndex) = value;
278
}
279
280
/**
281
* @brief Set the value of adjacent nodes of a given node (all columns of a given row, and its
282
* reflection)
283
* @param [in] row The row of the matrix to set the value
284
* @param [in] value Value to be assigned to matrix (row, column) or matrix (column, row)
285
*/
286
void
SetValueAdjacent
(std::size_t row, T value)
287
{
288
// Since we only store the lower-left half of the adjacency matrix,
289
// we need to set the adjacent values in both rows and columns involving this row id
290
291
// First set the columns of row m_id
292
for
(std::size_t i = 0; i < row; i++)
293
{
294
m_matrix
.at(
m_rowOffsets
.at(row) + i) = value;
295
}
296
// Then set the column m_id of rows >= m_id
297
for
(std::size_t i = row; i <
m_rows
; i++)
298
{
299
m_matrix
.at(
m_rowOffsets
.at(i) + row) = value;
300
}
301
}
302
303
/**
304
* @brief Add new row to the adjacency matrix
305
*/
306
void
AddRow
()
307
{
308
m_rowOffsets
.push_back(
m_matrix
.size());
309
m_rows
++;
310
m_matrix
.resize(
m_matrix
.size() +
m_rows
);
311
}
312
313
/**
314
* @brief Retrieve number of rows in the adjacency matrix
315
* @return the number of rows
316
*/
317
std::size_t
GetRows
()
318
{
319
return
m_rows
;
320
}
321
322
private
:
323
std::size_t
m_rows
;
//!< Number of rows in matrix
324
std::vector<T>
325
m_matrix
;
//!< The adjacency matrix. For efficiency purposes, we store only lower
326
//!< left half, including the main diagonal. It also is stored as a vector
327
//!< not to introduce gaps between different rows or items (in case T = bool)
328
std::vector<std::size_t>
m_rowOffsets
;
//!< Precomputed row starting offsets of m_matrix
329
};
330
331
}
// namespace ns3
332
#endif
// NS3_SYMMETRIC_ADJACENCY_MATRIX_H
ns3::SymmetricAdjacencyMatrix::GetRows
std::size_t GetRows()
Retrieve number of rows in the adjacency matrix.
Definition
symmetric-adjacency-matrix.h:317
ns3::SymmetricAdjacencyMatrix::m_matrix
std::vector< T > m_matrix
The adjacency matrix.
Definition
symmetric-adjacency-matrix.h:325
ns3::SymmetricAdjacencyMatrix::SetValue
void SetValue(std::size_t row, std::size_t column, T value)
Set the value of matrix (row, column) node.
Definition
symmetric-adjacency-matrix.h:272
ns3::SymmetricAdjacencyMatrix::GetValue
T GetValue(std::size_t row, std::size_t column)
Retrieve the value of matrix (row, column) node.
Definition
symmetric-adjacency-matrix.h:258
ns3::SymmetricAdjacencyMatrix::SymmetricAdjacencyMatrix
SymmetricAdjacencyMatrix(std::size_t numRows=0, T value={})
Default constructor.
Definition
symmetric-adjacency-matrix.h:241
ns3::SymmetricAdjacencyMatrix::AddRow
void AddRow()
Add new row to the adjacency matrix.
Definition
symmetric-adjacency-matrix.h:306
ns3::SymmetricAdjacencyMatrix::m_rowOffsets
std::vector< std::size_t > m_rowOffsets
Precomputed row starting offsets of m_matrix.
Definition
symmetric-adjacency-matrix.h:328
ns3::SymmetricAdjacencyMatrix::SetValueAdjacent
void SetValueAdjacent(std::size_t row, T value)
Set the value of adjacent nodes of a given node (all columns of a given row, and its reflection).
Definition
symmetric-adjacency-matrix.h:286
ns3::SymmetricAdjacencyMatrix::m_rows
std::size_t m_rows
Number of rows in matrix.
Definition
symmetric-adjacency-matrix.h:323
ns3
Every class exported by the ns3 library is enclosed in the ns3 namespace.
src
antenna
utils
symmetric-adjacency-matrix.h
Generated on
for ns-3 by
1.15.0