-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathGraph.cpp
More file actions
321 lines (265 loc) · 9.64 KB
/
Graph.cpp
File metadata and controls
321 lines (265 loc) · 9.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
#include "include/Graph.h"
#include "include/JointSort.h"
#include <algorithm>
using namespace Escape;
//Basic binary search procedure
// Input: pointer array, index of last entry end, and val to search for
// Output: index if val is found, and -1 otherwise
VertexIdx Escape::binarySearch(EdgeIdx *array, VertexIdx end, EdgeIdx val) {
VertexIdx low = 0;
VertexIdx high = end - 1;
VertexIdx mid;
while (low <= high) {
mid = (low + high) / 2;
if (array[mid] == val)
return mid;
if (array[mid] > val)
high = mid - 1;
if (array[mid] < val)
low = mid + 1;
}
return -1;
}
// comparator that only compares the first in pair
bool Escape::pairCompareFirst(Pair firstPair, Pair nextPair) {
return firstPair.first < nextPair.first;
}
// comparator that compares the second in pair. If they are equal, then compare first in pair
bool Escape::pairCompareSecond(Pair firstPair, Pair nextPair) {
if (firstPair.second != nextPair.second)
return firstPair.second < nextPair.second;
return firstPair.first < nextPair.first;
}
Graph Escape::newGraph(VertexIdx nVertices, EdgeIdx nEdges) {
return {nVertices, nEdges, new VertexIdx[nEdges], new VertexIdx[nEdges]};
}
Graph Graph::copy() const {
Graph ret = newGraph(nVertices, nEdges);
std::copy(srcs, srcs + nEdges, ret.srcs);
std::copy(dsts, dsts + nEdges, ret.dsts);
return ret;
}
void Graph::print(FILE *f) const {
fprintf(f, "nVertices=%lld nEdges=%lld\n", (int64_t) nVertices, (int64_t) nEdges);
for (EdgeIdx i = 0; i < nEdges; ++i)
fprintf(f, " %lld %lld -> %lld\n", (int64_t) i, (int64_t) srcs[i], (int64_t) dsts[i]);
}
void Escape::delGraph(Graph g) {
delete[] g.srcs;
delete[] g.dsts;
}
CGraph Escape::newCGraph(VertexIdx nVertices, EdgeIdx nEdges) {
return {nVertices, nEdges, new EdgeIdx[nVertices + 1], new VertexIdx[nEdges]};
}
CGraph CGraph::copy() const {
CGraph ret = newCGraph(nVertices, nEdges);
std::copy(offsets, offsets + nVertices + 1, ret.offsets);
std::copy(nbors, nbors + nEdges, ret.nbors);
return ret;
}
void CGraph::print(FILE *f) const {
fprintf(f, "nVertices=%lld nEdges=%lld\n", (int64_t) nVertices, (int64_t) nEdges);
for (VertexIdx i = 0; i < nVertices; ++i) {
fprintf(f, "%lld: ", (int64_t) i);
for (EdgeIdx j = offsets[i]; j < offsets[i + 1]; ++j)
fprintf(f, "%lld ", (int64_t) nbors[j]);
fprintf(f, "\n");
}
}
void CGraph::writeBinaryFile(const char *path) {
auto out_file = std::ofstream(path, std::ios::out | std::ios::binary);
if ( !out_file.is_open() ) {
printf("Could not write to the file %s",path);
return;
}
out_file.write(reinterpret_cast<const char*>(&nVertices), sizeof(nVertices));
out_file.write(reinterpret_cast<const char*>(&nEdges), sizeof(nEdges));
out_file.write(reinterpret_cast<const char*>(offsets), std::streamsize((nVertices+1)* sizeof(VertexIdx)));
out_file.write(reinterpret_cast<const char*>(nbors), nEdges*sizeof(EdgeIdx));
out_file.close();
if(!out_file.good()) {
std::cout << "Error occurred at writing time!" << std::endl;
}
}
void Escape::delCGraph(CGraph g) {
delete[] g.offsets;
delete[] g.nbors;
}
// Checks if edge (v1, v2) is present in CGraph
// If edge is present: Return index (in nbors) of v2 in neighbors of v1
// If edge is not present: Return -1
int CGraph::isEdge(VertexIdx v1, VertexIdx v2) {
if (v1 >= nVertices)
return -1;
for (EdgeIdx i = offsets[v1]; i < offsets[v1 + 1]; ++i)
if (nbors[i] == v2)
return i;
return -1;
}
//Checks if edge (v1, v2) is present using binary search
//Assumes CGraph is sorted by ID
//If edge is present: Return index (in nbors) of v2 in neighbors of v2
//If edge is not present: return -1
EdgeIdx CGraph::getEdgeBinary(VertexIdx v1, VertexIdx v2) const {
if (v1 >= nVertices)
return -1;
EdgeIdx low = offsets[v1];
EdgeIdx high = offsets[v1 + 1] - 1;
EdgeIdx mid;
while (low <= high) {
mid = (low + high) / 2;
if (nbors[mid] == v2)
return mid;
if (nbors[mid] > v2)
high = mid - 1;
else
low = mid + 1;
}
return -1;
}
//Checks if edge (v1, v2) is present using binary search
//Assumes CGraph is sorted by ID
//If edge is present: Return the number of edges between v1 and v2 in the adjacency list of v1
//If edge is not present: return -1
EdgeIdx CGraph::getEdgeCount(VertexIdx v1, VertexIdx v2) const {
if (v1 >= nVertices)
return -1;
EdgeIdx low = offsets[v1];
EdgeIdx high = offsets[v1 + 1] - 1;
EdgeIdx mid;
EdgeIdx found, multiplicity=0;
while (low <= high) {
mid = (low + high) / 2;
if (nbors[mid] == v2) {
multiplicity = 1;
found = mid+1;
while ( found <= high && nbors[found] == v2) {
multiplicity++;
found ++;
}
found = mid-1;
while ( found >= low && nbors[found] == v2) {
multiplicity ++;
found --;
}
return multiplicity;
}
if (nbors[mid] > v2)
high = mid - 1;
else
low = mid + 1;
}
return -1;
}
//Checks if edge (v1, v2) is present using binary search
//Assumes CGraph is sorted by ID
//If edge is present: return true
//If edge is not present: return false
bool CGraph::isEdgeBinary(VertexIdx v1, VertexIdx v2) const {
// VertexIdx deg1 = offsets[v1+1] - offsets[v1];
// VertexIdx deg2 = offsets[v2+1] - offsets[v2];
// if(deg2 < deg1)
// {
// VertexIdx swp = v1;
// v1 = v2;
// v2 = swp;
// }
//
EdgeIdx low = offsets[v1];
EdgeIdx high = offsets[v1 + 1] - 1;
EdgeIdx mid;
while (low <= high) {
mid = (low + high) / 2;
if (nbors[mid] == v2)
return true;
if (nbors[mid] > v2)
high = mid - 1;
else
low = mid + 1;
}
return false;
}
//Same as above, changing name since we retrieve the edge index as well.
//There is a good reason to have both isEdge and getEdge, since it's easier
//to answer the first question. Recommend changing isEdge to return bool - vv
EdgeIdx CGraph::getEdge(VertexIdx v1, VertexIdx v2) const {
if (v1 >= nVertices)
return -1;
for (EdgeIdx i = offsets[v1]; i < offsets[v1 + 1]; ++i)
if (nbors[i] == v2)
return i;
return -1;
}
// This sorts each individual adjacency list by vertex ID. This is useful for
// doing a binary search, or for merging neighbor lists to find common neighbors.
void CGraph::sortById() const {
for (VertexIdx i = 0; i < nVertices; i++)
std::sort(nbors + offsets[i], nbors + offsets[i + 1]);
}
// This outputs a new, isomorphic CGraph where vertex labels are in increasing order corresponding to degree.
// Thus, (after the relabeling), for all i < j, the degree of i is less than that of j.
CGraph CGraph::renameByDegreeOrder() const {
CGraph ret = newCGraph(nVertices, nEdges);
Pair *deg_info = new Pair[nVertices];
VertexIdx *mapping = new VertexIdx[nVertices];
VertexIdx *inverse = new VertexIdx[nVertices];
// Construct array of pairs, storing old vertex label and degree
for (VertexIdx i = 0; i < nVertices; i++) {
deg_info[i].first = i;
deg_info[i].second = offsets[i + 1] - offsets[i];
}
// sort the pairs by degree (if degree is same, sort by old vertex label)
std::sort(deg_info, deg_info + nVertices, Escape::pairCompareSecond);
// Construct the mapping of old vertex label to new vertex label
// So mapping[i] is what i is mapped to
// And inverse[i] is what maps to i
for (VertexIdx i = 0; i < nVertices; i++) {
mapping[deg_info[i].first] = i;
inverse[i] = deg_info[i].first;
}
// Initialize offsets of output CGraph
ret.offsets[0] = 0;
EdgeIdx current = 0;
// Loop over new vertices
for (VertexIdx new_label = 0; new_label < nVertices; new_label++) {
VertexIdx old_label = inverse[new_label]; // corresponding old label for new vertices
for (EdgeIdx pos = offsets[old_label]; pos < offsets[old_label + 1]; pos++) // loop over neighbors of old label
{
VertexIdx old_nbr = nbors[pos];
VertexIdx new_nbr = mapping[old_nbr]; //corresponding new neighbor
ret.nbors[current] = new_nbr; // insert new neighbor in nbors of output
current++;
}
ret.offsets[new_label +
1] = current; // all neighbors of new_label have been added, so we set offset for new_label+1
}
delete[] mapping;
delete[] inverse;
delete[] deg_info;
return ret;
}
CGraph Escape::makeCSR(Graph g, bool inPlace) {
//create a temporary graph for sorting unless the user requests in-place
//operation.
auto tmpG = inPlace ? g : g.copy();
auto begin = JSIterator<VertexIdx, VertexIdx>{tmpG.srcs, tmpG.dsts};
auto end = begin + tmpG.nEdges;
std::sort(begin, end);
//We steal the dsts array from tmpG.
CGraph ret = {g.nVertices, g.nEdges, new EdgeIdx[g.nVertices + 1], tmpG.dsts};
//Now we have everything sorted by src, compress:
VertexIdx cv = 0;
for (EdgeIdx i = 0; i < tmpG.nEdges; ++i) {
auto src = tmpG.srcs[i];
while (cv <= src)
ret.offsets[cv++] = i;
}
while (cv <= g.nVertices)
ret.offsets[cv++] = g.nEdges;
delete[] tmpG.srcs; //we retain tmpG.dsts in the output
ret.sortById();
return ret;
}
CGraph Escape::makeCSC(Graph g, bool inPlace) {
return makeCSR({g.nVertices, g.nEdges, g.dsts, g.srcs}, inPlace);
}