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