graph.cc 22.4 KB
Newer Older
1
/**
2
 *  Copyright (c) 2018 by Contributors
3
4
 * @file graph/graph.cc
 * @brief DGL graph index implementation
5
6
 */
#include <dgl/graph.h>
Da Zheng's avatar
Da Zheng committed
7
#include <dgl/sampler.h>
8

Minjie Wang's avatar
impl  
Minjie Wang committed
9
#include <algorithm>
10
#include <functional>
11
#include <set>
GaiYu0's avatar
GaiYu0 committed
12
#include <tuple>
13
14
#include <unordered_map>

GaiYu0's avatar
GaiYu0 committed
15
#include "../c_api_common.h"
Minjie Wang's avatar
Minjie Wang committed
16

Minjie Wang's avatar
impl  
Minjie Wang committed
17
namespace dgl {
18

19
Graph::Graph(IdArray src_ids, IdArray dst_ids, size_t num_nodes) {
20
21
  CHECK(aten::IsValidIdArray(src_ids));
  CHECK(aten::IsValidIdArray(dst_ids));
22
23
  this->AddVertices(num_nodes);
  num_edges_ = src_ids->shape[0];
24
  CHECK(static_cast<int64_t>(num_edges_) == dst_ids->shape[0])
25
26
27
      << "vectors in COO must have the same length";
  const dgl_id_t* src_data = static_cast<dgl_id_t*>(src_ids->data);
  const dgl_id_t* dst_data = static_cast<dgl_id_t*>(dst_ids->data);
28
29
  all_edges_src_.reserve(num_edges_);
  all_edges_dst_.reserve(num_edges_);
30
  for (uint64_t i = 0; i < num_edges_; i++) {
31
32
33
    auto src = src_data[i];
    auto dst = dst_data[i];
    CHECK(HasVertex(src) && HasVertex(dst))
34
        << "Invalid vertices: src=" << src << " dst=" << dst;
35
36

    adjlist_[src].succ.push_back(dst);
37
    adjlist_[src].edge_id.push_back(i);
38
    reverse_adjlist_[dst].succ.push_back(src);
39
    reverse_adjlist_[dst].edge_id.push_back(i);
40
41
42
43
44
45

    all_edges_src_.push_back(src);
    all_edges_dst_.push_back(dst);
  }
}

46
47
48
49
50
51
52
53
54
55
56
57
bool Graph::IsMultigraph() const {
  if (num_edges_ <= 1) {
    return false;
  }

  typedef std::pair<int64_t, int64_t> Pair;
  std::vector<Pair> pairs;
  pairs.reserve(num_edges_);
  for (uint64_t eid = 0; eid < num_edges_; ++eid) {
    pairs.emplace_back(all_edges_src_[eid], all_edges_dst_[eid]);
  }
  // sort according to src and dst ids
58
59
60
61
62
63
  std::sort(pairs.begin(), pairs.end(), [](const Pair& t1, const Pair& t2) {
    return std::get<0>(t1) < std::get<0>(t2) ||
           (std::get<0>(t1) == std::get<0>(t2) &&
            std::get<1>(t1) < std::get<1>(t2));
  });
  for (uint64_t eid = 0; eid < num_edges_ - 1; ++eid) {
64
    // As src and dst are all sorted, we only need to compare i and i+1
65
66
67
    if (std::get<0>(pairs[eid]) == std::get<0>(pairs[eid + 1]) &&
        std::get<1>(pairs[eid]) == std::get<1>(pairs[eid + 1]))
      return true;
68
69
70
71
72
  }

  return false;
}

Minjie Wang's avatar
impl  
Minjie Wang committed
73
74
75
void Graph::AddVertices(uint64_t num_vertices) {
  CHECK(!read_only_) << "Graph is read-only. Mutations are not allowed.";
  adjlist_.resize(adjlist_.size() + num_vertices);
Minjie Wang's avatar
Minjie Wang committed
76
  reverse_adjlist_.resize(reverse_adjlist_.size() + num_vertices);
Minjie Wang's avatar
impl  
Minjie Wang committed
77
78
79
80
81
}

void Graph::AddEdge(dgl_id_t src, dgl_id_t dst) {
  CHECK(!read_only_) << "Graph is read-only. Mutations are not allowed.";
  CHECK(HasVertex(src) && HasVertex(dst))
82
      << "Invalid vertices: src=" << src << " dst=" << dst;
83

Minjie Wang's avatar
impl  
Minjie Wang committed
84
  dgl_id_t eid = num_edges_++;
85

Minjie Wang's avatar
impl  
Minjie Wang committed
86
  adjlist_[src].succ.push_back(dst);
Minjie Wang's avatar
Minjie Wang committed
87
88
89
  adjlist_[src].edge_id.push_back(eid);
  reverse_adjlist_[dst].succ.push_back(src);
  reverse_adjlist_[dst].edge_id.push_back(eid);
90

Minjie Wang's avatar
Minjie Wang committed
91
92
  all_edges_src_.push_back(src);
  all_edges_dst_.push_back(dst);
Minjie Wang's avatar
impl  
Minjie Wang committed
93
94
95
96
}

void Graph::AddEdges(IdArray src_ids, IdArray dst_ids) {
  CHECK(!read_only_) << "Graph is read-only. Mutations are not allowed.";
97
98
  CHECK(aten::IsValidIdArray(src_ids)) << "Invalid src id array.";
  CHECK(aten::IsValidIdArray(dst_ids)) << "Invalid dst id array.";
Minjie Wang's avatar
impl  
Minjie Wang committed
99
  const auto srclen = src_ids->shape[0];
Minjie Wang's avatar
Minjie Wang committed
100
  const auto dstlen = dst_ids->shape[0];
Minjie Wang's avatar
impl  
Minjie Wang committed
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
  const int64_t* src_data = static_cast<int64_t*>(src_ids->data);
  const int64_t* dst_data = static_cast<int64_t*>(dst_ids->data);
  if (srclen == 1) {
    // one-many
    for (int64_t i = 0; i < dstlen; ++i) {
      AddEdge(src_data[0], dst_data[i]);
    }
  } else if (dstlen == 1) {
    // many-one
    for (int64_t i = 0; i < srclen; ++i) {
      AddEdge(src_data[i], dst_data[0]);
    }
  } else {
    // many-many
    CHECK(srclen == dstlen) << "Invalid src and dst id array.";
    for (int64_t i = 0; i < srclen; ++i) {
      AddEdge(src_data[i], dst_data[i]);
    }
  }
}

BoolArray Graph::HasVertices(IdArray vids) const {
123
  CHECK(aten::IsValidIdArray(vids)) << "Invalid vertex id array.";
Minjie Wang's avatar
impl  
Minjie Wang committed
124
125
126
127
  const auto len = vids->shape[0];
  BoolArray rst = BoolArray::Empty({len}, vids->dtype, vids->ctx);
  const int64_t* vid_data = static_cast<int64_t*>(vids->data);
  int64_t* rst_data = static_cast<int64_t*>(rst->data);
Minjie Wang's avatar
Minjie Wang committed
128
  const int64_t nverts = NumVertices();
Minjie Wang's avatar
impl  
Minjie Wang committed
129
  for (int64_t i = 0; i < len; ++i) {
130
    rst_data[i] = (vid_data[i] < nverts && vid_data[i] >= 0) ? 1 : 0;
Minjie Wang's avatar
impl  
Minjie Wang committed
131
132
133
134
135
  }
  return rst;
}

// O(E)
136
bool Graph::HasEdgeBetween(dgl_id_t src, dgl_id_t dst) const {
Minjie Wang's avatar
impl  
Minjie Wang committed
137
138
139
140
141
  if (!HasVertex(src) || !HasVertex(dst)) return false;
  const auto& succ = adjlist_[src].succ;
  return std::find(succ.begin(), succ.end(), dst) != succ.end();
}

142
143
// O(E*k) pretty slow
BoolArray Graph::HasEdgesBetween(IdArray src_ids, IdArray dst_ids) const {
144
145
  CHECK(aten::IsValidIdArray(src_ids)) << "Invalid src id array.";
  CHECK(aten::IsValidIdArray(dst_ids)) << "Invalid dst id array.";
Minjie Wang's avatar
impl  
Minjie Wang committed
146
  const auto srclen = src_ids->shape[0];
Minjie Wang's avatar
Minjie Wang committed
147
  const auto dstlen = dst_ids->shape[0];
Minjie Wang's avatar
impl  
Minjie Wang committed
148
149
150
151
152
153
154
155
  const auto rstlen = std::max(srclen, dstlen);
  BoolArray rst = BoolArray::Empty({rstlen}, src_ids->dtype, src_ids->ctx);
  int64_t* rst_data = static_cast<int64_t*>(rst->data);
  const int64_t* src_data = static_cast<int64_t*>(src_ids->data);
  const int64_t* dst_data = static_cast<int64_t*>(dst_ids->data);
  if (srclen == 1) {
    // one-many
    for (int64_t i = 0; i < dstlen; ++i) {
156
      rst_data[i] = HasEdgeBetween(src_data[0], dst_data[i]) ? 1 : 0;
Minjie Wang's avatar
impl  
Minjie Wang committed
157
158
159
160
    }
  } else if (dstlen == 1) {
    // many-one
    for (int64_t i = 0; i < srclen; ++i) {
161
      rst_data[i] = HasEdgeBetween(src_data[i], dst_data[0]) ? 1 : 0;
Minjie Wang's avatar
impl  
Minjie Wang committed
162
163
164
165
166
    }
  } else {
    // many-many
    CHECK(srclen == dstlen) << "Invalid src and dst id array.";
    for (int64_t i = 0; i < srclen; ++i) {
167
      rst_data[i] = HasEdgeBetween(src_data[i], dst_data[i]) ? 1 : 0;
Minjie Wang's avatar
impl  
Minjie Wang committed
168
169
170
171
172
173
    }
  }
  return rst;
}

// The data is copy-out; support zero-copy?
Minjie Wang's avatar
Minjie Wang committed
174
IdArray Graph::Predecessors(dgl_id_t vid, uint64_t radius) const {
Minjie Wang's avatar
impl  
Minjie Wang committed
175
  CHECK(HasVertex(vid)) << "invalid vertex: " << vid;
Minjie Wang's avatar
Minjie Wang committed
176
  CHECK(radius >= 1) << "invalid radius: " << radius;
177
178
  std::set<dgl_id_t> vset;

179
  for (auto& it : reverse_adjlist_[vid].succ) vset.insert(it);
180
181

  const int64_t len = vset.size();
182
183
  IdArray rst = IdArray::Empty(
      {len}, DGLDataType{kDGLInt, 64, 1}, DGLContext{kDGLCPU, 0});
Minjie Wang's avatar
impl  
Minjie Wang committed
184
  int64_t* rst_data = static_cast<int64_t*>(rst->data);
185
186

  std::copy(vset.begin(), vset.end(), rst_data);
Minjie Wang's avatar
impl  
Minjie Wang committed
187
188
189
190
  return rst;
}

// The data is copy-out; support zero-copy?
Minjie Wang's avatar
Minjie Wang committed
191
IdArray Graph::Successors(dgl_id_t vid, uint64_t radius) const {
Minjie Wang's avatar
impl  
Minjie Wang committed
192
  CHECK(HasVertex(vid)) << "invalid vertex: " << vid;
Minjie Wang's avatar
Minjie Wang committed
193
  CHECK(radius >= 1) << "invalid radius: " << radius;
194
195
  std::set<dgl_id_t> vset;

196
  for (auto& it : adjlist_[vid].succ) vset.insert(it);
197
198

  const int64_t len = vset.size();
199
200
  IdArray rst = IdArray::Empty(
      {len}, DGLDataType{kDGLInt, 64, 1}, DGLContext{kDGLCPU, 0});
Minjie Wang's avatar
impl  
Minjie Wang committed
201
  int64_t* rst_data = static_cast<int64_t*>(rst->data);
202
203

  std::copy(vset.begin(), vset.end(), rst_data);
Minjie Wang's avatar
impl  
Minjie Wang committed
204
205
206
207
  return rst;
}

// O(E)
208
IdArray Graph::EdgeId(dgl_id_t src, dgl_id_t dst) const {
209
210
  CHECK(HasVertex(src) && HasVertex(dst))
      << "invalid edge: " << src << " -> " << dst;
211

Minjie Wang's avatar
impl  
Minjie Wang committed
212
  const auto& succ = adjlist_[src].succ;
213
214
  std::vector<dgl_id_t> edgelist;

Minjie Wang's avatar
impl  
Minjie Wang committed
215
  for (size_t i = 0; i < succ.size(); ++i) {
216
    if (succ[i] == dst) edgelist.push_back(adjlist_[src].edge_id[i]);
Minjie Wang's avatar
impl  
Minjie Wang committed
217
  }
218
219
220

  // FIXME: signed?  Also it seems that we are using int64_t everywhere...
  const int64_t len = edgelist.size();
221
222
  IdArray rst = IdArray::Empty(
      {len}, DGLDataType{kDGLInt, 64, 1}, DGLContext{kDGLCPU, 0});
223
224
225
226
227
228
  // FIXME: signed?
  int64_t* rst_data = static_cast<int64_t*>(rst->data);

  std::copy(edgelist.begin(), edgelist.end(), rst_data);

  return rst;
Minjie Wang's avatar
impl  
Minjie Wang committed
229
230
231
}

// O(E*k) pretty slow
232
EdgeArray Graph::EdgeIds(IdArray src_ids, IdArray dst_ids) const {
233
234
  CHECK(aten::IsValidIdArray(src_ids)) << "Invalid src id array.";
  CHECK(aten::IsValidIdArray(dst_ids)) << "Invalid dst id array.";
Minjie Wang's avatar
impl  
Minjie Wang committed
235
  const auto srclen = src_ids->shape[0];
Minjie Wang's avatar
Minjie Wang committed
236
  const auto dstlen = dst_ids->shape[0];
237
238
239
  int64_t i, j;

  CHECK((srclen == dstlen) || (srclen == 1) || (dstlen == 1))
240
      << "Invalid src and dst id array.";
241
242
243

  const int64_t src_stride = (srclen == 1 && dstlen != 1) ? 0 : 1;
  const int64_t dst_stride = (dstlen == 1 && srclen != 1) ? 0 : 1;
Minjie Wang's avatar
impl  
Minjie Wang committed
244
245
  const int64_t* src_data = static_cast<int64_t*>(src_ids->data);
  const int64_t* dst_data = static_cast<int64_t*>(dst_ids->data);
246
247
248

  std::vector<dgl_id_t> src, dst, eid;

249
250
  for (i = 0, j = 0; i < srclen && j < dstlen;
       i += src_stride, j += dst_stride) {
251
    const dgl_id_t src_id = src_data[i], dst_id = dst_data[j];
252
253
    CHECK(HasVertex(src_id) && HasVertex(dst_id))
        << "invalid edge: " << src_id << " -> " << dst_id;
254
255
256
    const auto& succ = adjlist_[src_id].succ;
    for (size_t k = 0; k < succ.size(); ++k) {
      if (succ[k] == dst_id) {
257
258
259
        src.push_back(src_id);
        dst.push_back(dst_id);
        eid.push_back(adjlist_[src_id].edge_id[k]);
260
      }
Minjie Wang's avatar
impl  
Minjie Wang committed
261
262
    }
  }
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278

  int64_t rstlen = src.size();
  IdArray rst_src = IdArray::Empty({rstlen}, src_ids->dtype, src_ids->ctx);
  IdArray rst_dst = IdArray::Empty({rstlen}, src_ids->dtype, src_ids->ctx);
  IdArray rst_eid = IdArray::Empty({rstlen}, src_ids->dtype, src_ids->ctx);
  int64_t* rst_src_data = static_cast<int64_t*>(rst_src->data);
  int64_t* rst_dst_data = static_cast<int64_t*>(rst_dst->data);
  int64_t* rst_eid_data = static_cast<int64_t*>(rst_eid->data);

  std::copy(src.begin(), src.end(), rst_src_data);
  std::copy(dst.begin(), dst.end(), rst_dst_data);
  std::copy(eid.begin(), eid.end(), rst_eid_data);

  return EdgeArray{rst_src, rst_dst, rst_eid};
}

279
EdgeArray Graph::FindEdges(IdArray eids) const {
280
  CHECK(aten::IsValidIdArray(eids)) << "Invalid edge id array";
281
282
283
284
285
286
287
288
289
290
291
292
  int64_t len = eids->shape[0];

  IdArray rst_src = IdArray::Empty({len}, eids->dtype, eids->ctx);
  IdArray rst_dst = IdArray::Empty({len}, eids->dtype, eids->ctx);
  IdArray rst_eid = IdArray::Empty({len}, eids->dtype, eids->ctx);
  int64_t* eid_data = static_cast<int64_t*>(eids->data);
  int64_t* rst_src_data = static_cast<int64_t*>(rst_src->data);
  int64_t* rst_dst_data = static_cast<int64_t*>(rst_dst->data);
  int64_t* rst_eid_data = static_cast<int64_t*>(rst_eid->data);

  for (uint64_t i = 0; i < (uint64_t)len; ++i) {
    dgl_id_t eid = eid_data[i];
293
    if (eid >= num_edges_) LOG(FATAL) << "invalid edge id:" << eid;
294
295
296
297
298
299
300

    rst_src_data[i] = all_edges_src_[eid];
    rst_dst_data[i] = all_edges_dst_[eid];
    rst_eid_data[i] = eid;
  }

  return EdgeArray{rst_src, rst_dst, rst_eid};
Minjie Wang's avatar
impl  
Minjie Wang committed
301
302
303
}

// O(E)
304
EdgeArray Graph::InEdges(dgl_id_t vid) const {
305
  CHECK(HasVertex(vid)) << "invalid vertex: " << vid;
Minjie Wang's avatar
Minjie Wang committed
306
  const int64_t len = reverse_adjlist_[vid].succ.size();
307
308
309
310
311
312
  IdArray src = IdArray::Empty(
      {len}, DGLDataType{kDGLInt, 64, 1}, DGLContext{kDGLCPU, 0});
  IdArray dst = IdArray::Empty(
      {len}, DGLDataType{kDGLInt, 64, 1}, DGLContext{kDGLCPU, 0});
  IdArray eid = IdArray::Empty(
      {len}, DGLDataType{kDGLInt, 64, 1}, DGLContext{kDGLCPU, 0});
313
  int64_t* src_data = static_cast<int64_t*>(src->data);
Minjie Wang's avatar
impl  
Minjie Wang committed
314
  int64_t* dst_data = static_cast<int64_t*>(dst->data);
315
316
  int64_t* eid_data = static_cast<int64_t*>(eid->data);
  for (int64_t i = 0; i < len; ++i) {
Minjie Wang's avatar
Minjie Wang committed
317
318
    src_data[i] = reverse_adjlist_[vid].succ[i];
    eid_data[i] = reverse_adjlist_[vid].edge_id[i];
319
320
321
  }
  std::fill(dst_data, dst_data + len, vid);
  return EdgeArray{src, dst, eid};
Minjie Wang's avatar
impl  
Minjie Wang committed
322
323
324
}

// O(E)
325
EdgeArray Graph::InEdges(IdArray vids) const {
326
  CHECK(aten::IsValidIdArray(vids)) << "Invalid vertex id array.";
Minjie Wang's avatar
impl  
Minjie Wang committed
327
328
329
330
331
  const auto len = vids->shape[0];
  const int64_t* vid_data = static_cast<int64_t*>(vids->data);
  int64_t rstlen = 0;
  for (int64_t i = 0; i < len; ++i) {
    CHECK(HasVertex(vid_data[i])) << "Invalid vertex: " << vid_data[i];
Minjie Wang's avatar
Minjie Wang committed
332
    rstlen += reverse_adjlist_[vid_data[i]].succ.size();
Minjie Wang's avatar
impl  
Minjie Wang committed
333
334
335
  }
  IdArray src = IdArray::Empty({rstlen}, vids->dtype, vids->ctx);
  IdArray dst = IdArray::Empty({rstlen}, vids->dtype, vids->ctx);
336
  IdArray eid = IdArray::Empty({rstlen}, vids->dtype, vids->ctx);
Minjie Wang's avatar
impl  
Minjie Wang committed
337
338
  int64_t* src_ptr = static_cast<int64_t*>(src->data);
  int64_t* dst_ptr = static_cast<int64_t*>(dst->data);
339
  int64_t* eid_ptr = static_cast<int64_t*>(eid->data);
Minjie Wang's avatar
impl  
Minjie Wang committed
340
  for (int64_t i = 0; i < len; ++i) {
Minjie Wang's avatar
Minjie Wang committed
341
342
    const auto& pred = reverse_adjlist_[vid_data[i]].succ;
    const auto& eids = reverse_adjlist_[vid_data[i]].edge_id;
Minjie Wang's avatar
impl  
Minjie Wang committed
343
344
345
    for (size_t j = 0; j < pred.size(); ++j) {
      *(src_ptr++) = pred[j];
      *(dst_ptr++) = vid_data[i];
346
      *(eid_ptr++) = eids[j];
Minjie Wang's avatar
impl  
Minjie Wang committed
347
348
    }
  }
349
  return EdgeArray{src, dst, eid};
Minjie Wang's avatar
impl  
Minjie Wang committed
350
351
352
}

// O(E)
353
EdgeArray Graph::OutEdges(dgl_id_t vid) const {
354
355
  CHECK(HasVertex(vid)) << "invalid vertex: " << vid;
  const int64_t len = adjlist_[vid].succ.size();
356
357
358
359
360
361
  IdArray src = IdArray::Empty(
      {len}, DGLDataType{kDGLInt, 64, 1}, DGLContext{kDGLCPU, 0});
  IdArray dst = IdArray::Empty(
      {len}, DGLDataType{kDGLInt, 64, 1}, DGLContext{kDGLCPU, 0});
  IdArray eid = IdArray::Empty(
      {len}, DGLDataType{kDGLInt, 64, 1}, DGLContext{kDGLCPU, 0});
Minjie Wang's avatar
impl  
Minjie Wang committed
362
  int64_t* src_data = static_cast<int64_t*>(src->data);
363
364
365
366
  int64_t* dst_data = static_cast<int64_t*>(dst->data);
  int64_t* eid_data = static_cast<int64_t*>(eid->data);
  for (int64_t i = 0; i < len; ++i) {
    dst_data[i] = adjlist_[vid].succ[i];
Minjie Wang's avatar
Minjie Wang committed
367
    eid_data[i] = adjlist_[vid].edge_id[i];
368
369
370
  }
  std::fill(src_data, src_data + len, vid);
  return EdgeArray{src, dst, eid};
Minjie Wang's avatar
impl  
Minjie Wang committed
371
372
373
}

// O(E)
374
EdgeArray Graph::OutEdges(IdArray vids) const {
375
  CHECK(aten::IsValidIdArray(vids)) << "Invalid vertex id array.";
Minjie Wang's avatar
impl  
Minjie Wang committed
376
377
378
379
380
381
382
383
384
  const auto len = vids->shape[0];
  const int64_t* vid_data = static_cast<int64_t*>(vids->data);
  int64_t rstlen = 0;
  for (int64_t i = 0; i < len; ++i) {
    CHECK(HasVertex(vid_data[i])) << "Invalid vertex: " << vid_data[i];
    rstlen += adjlist_[vid_data[i]].succ.size();
  }
  IdArray src = IdArray::Empty({rstlen}, vids->dtype, vids->ctx);
  IdArray dst = IdArray::Empty({rstlen}, vids->dtype, vids->ctx);
385
  IdArray eid = IdArray::Empty({rstlen}, vids->dtype, vids->ctx);
Minjie Wang's avatar
impl  
Minjie Wang committed
386
387
  int64_t* src_ptr = static_cast<int64_t*>(src->data);
  int64_t* dst_ptr = static_cast<int64_t*>(dst->data);
388
  int64_t* eid_ptr = static_cast<int64_t*>(eid->data);
Minjie Wang's avatar
impl  
Minjie Wang committed
389
390
  for (int64_t i = 0; i < len; ++i) {
    const auto& succ = adjlist_[vid_data[i]].succ;
Minjie Wang's avatar
Minjie Wang committed
391
    const auto& eids = adjlist_[vid_data[i]].edge_id;
Minjie Wang's avatar
impl  
Minjie Wang committed
392
393
394
    for (size_t j = 0; j < succ.size(); ++j) {
      *(src_ptr++) = vid_data[i];
      *(dst_ptr++) = succ[j];
395
      *(eid_ptr++) = eids[j];
Minjie Wang's avatar
impl  
Minjie Wang committed
396
397
    }
  }
398
  return EdgeArray{src, dst, eid};
Minjie Wang's avatar
impl  
Minjie Wang committed
399
400
}

Minjie Wang's avatar
Minjie Wang committed
401
// O(E*log(E)) if sort is required; otherwise, O(E)
402
EdgeArray Graph::Edges(const std::string& order) const {
Minjie Wang's avatar
impl  
Minjie Wang committed
403
  const int64_t len = num_edges_;
404
405
406
407
408
409
  IdArray src = IdArray::Empty(
      {len}, DGLDataType{kDGLInt, 64, 1}, DGLContext{kDGLCPU, 0});
  IdArray dst = IdArray::Empty(
      {len}, DGLDataType{kDGLInt, 64, 1}, DGLContext{kDGLCPU, 0});
  IdArray eid = IdArray::Empty(
      {len}, DGLDataType{kDGLInt, 64, 1}, DGLContext{kDGLCPU, 0});
Minjie Wang's avatar
Minjie Wang committed
410

411
  if (order == "srcdst") {
Minjie Wang's avatar
Minjie Wang committed
412
413
414
    typedef std::tuple<int64_t, int64_t, int64_t> Tuple;
    std::vector<Tuple> tuples;
    tuples.reserve(len);
Minjie Wang's avatar
Minjie Wang committed
415
416
    for (uint64_t eid = 0; eid < num_edges_; ++eid) {
      tuples.emplace_back(all_edges_src_[eid], all_edges_dst_[eid], eid);
Minjie Wang's avatar
Minjie Wang committed
417
    }
Minjie Wang's avatar
Minjie Wang committed
418
    // sort according to src and dst ids
419
420
421
422
423
    std::sort(
        tuples.begin(), tuples.end(), [](const Tuple& t1, const Tuple& t2) {
          return std::get<0>(t1) < std::get<0>(t2) ||
                 (std::get<0>(t1) == std::get<0>(t2) &&
                  std::get<1>(t1) < std::get<1>(t2));
Minjie Wang's avatar
Minjie Wang committed
424
        });
425

Minjie Wang's avatar
Minjie Wang committed
426
427
428
    // make return arrays
    int64_t* src_ptr = static_cast<int64_t*>(src->data);
    int64_t* dst_ptr = static_cast<int64_t*>(dst->data);
429
    int64_t* eid_ptr = static_cast<int64_t*>(eid->data);
Minjie Wang's avatar
Minjie Wang committed
430
    for (size_t i = 0; i < tuples.size(); ++i) {
Minjie Wang's avatar
Minjie Wang committed
431
432
      src_ptr[i] = std::get<0>(tuples[i]);
      dst_ptr[i] = std::get<1>(tuples[i]);
433
      eid_ptr[i] = std::get<2>(tuples[i]);
Minjie Wang's avatar
Minjie Wang committed
434
435
436
437
    }
  } else {
    int64_t* src_ptr = static_cast<int64_t*>(src->data);
    int64_t* dst_ptr = static_cast<int64_t*>(dst->data);
438
    int64_t* eid_ptr = static_cast<int64_t*>(eid->data);
Minjie Wang's avatar
Minjie Wang committed
439
440
441
442
    std::copy(all_edges_src_.begin(), all_edges_src_.end(), src_ptr);
    std::copy(all_edges_dst_.begin(), all_edges_dst_.end(), dst_ptr);
    for (uint64_t eid = 0; eid < num_edges_; ++eid) {
      eid_ptr[eid] = eid;
Minjie Wang's avatar
Minjie Wang committed
443
    }
Minjie Wang's avatar
impl  
Minjie Wang committed
444
445
  }

446
  return EdgeArray{src, dst, eid};
Minjie Wang's avatar
impl  
Minjie Wang committed
447
448
449
450
}

// O(V)
DegreeArray Graph::InDegrees(IdArray vids) const {
451
  CHECK(aten::IsValidIdArray(vids)) << "Invalid vertex id array.";
Minjie Wang's avatar
impl  
Minjie Wang committed
452
453
454
455
456
457
458
  const auto len = vids->shape[0];
  const int64_t* vid_data = static_cast<int64_t*>(vids->data);
  DegreeArray rst = DegreeArray::Empty({len}, vids->dtype, vids->ctx);
  int64_t* rst_data = static_cast<int64_t*>(rst->data);
  for (int64_t i = 0; i < len; ++i) {
    const auto vid = vid_data[i];
    CHECK(HasVertex(vid)) << "Invalid vertex: " << vid;
Minjie Wang's avatar
Minjie Wang committed
459
    rst_data[i] = reverse_adjlist_[vid].succ.size();
Minjie Wang's avatar
impl  
Minjie Wang committed
460
461
462
463
464
465
  }
  return rst;
}

// O(V)
DegreeArray Graph::OutDegrees(IdArray vids) const {
466
  CHECK(aten::IsValidIdArray(vids)) << "Invalid vertex id array.";
Minjie Wang's avatar
impl  
Minjie Wang committed
467
468
469
470
471
472
473
474
475
476
477
478
  const auto len = vids->shape[0];
  const int64_t* vid_data = static_cast<int64_t*>(vids->data);
  DegreeArray rst = DegreeArray::Empty({len}, vids->dtype, vids->ctx);
  int64_t* rst_data = static_cast<int64_t*>(rst->data);
  for (int64_t i = 0; i < len; ++i) {
    const auto vid = vid_data[i];
    CHECK(HasVertex(vid)) << "Invalid vertex: " << vid;
    rst_data[i] = adjlist_[vid].succ.size();
  }
  return rst;
}

Minjie Wang's avatar
Minjie Wang committed
479
Subgraph Graph::VertexSubgraph(IdArray vids) const {
480
  CHECK(aten::IsValidIdArray(vids)) << "Invalid vertex id array.";
Minjie Wang's avatar
Minjie Wang committed
481
482
483
484
485
486
487
488
  const auto len = vids->shape[0];
  std::unordered_map<dgl_id_t, dgl_id_t> oldv2newv;
  std::vector<dgl_id_t> edges;
  const int64_t* vid_data = static_cast<int64_t*>(vids->data);
  for (int64_t i = 0; i < len; ++i) {
    oldv2newv[vid_data[i]] = i;
  }
  Subgraph rst;
489
  rst.graph = std::make_shared<Graph>();
Minjie Wang's avatar
Minjie Wang committed
490
  rst.induced_vertices = vids;
491
  rst.graph->AddVertices(len);
Minjie Wang's avatar
Minjie Wang committed
492
493
494
495
496
497
498
499
  for (int64_t i = 0; i < len; ++i) {
    const dgl_id_t oldvid = vid_data[i];
    const dgl_id_t newvid = i;
    for (size_t j = 0; j < adjlist_[oldvid].succ.size(); ++j) {
      const dgl_id_t oldsucc = adjlist_[oldvid].succ[j];
      if (oldv2newv.count(oldsucc)) {
        const dgl_id_t newsucc = oldv2newv[oldsucc];
        edges.push_back(adjlist_[oldvid].edge_id[j]);
500
        rst.graph->AddEdge(newvid, newsucc);
Minjie Wang's avatar
Minjie Wang committed
501
502
503
      }
    }
  }
504
505
506
507
508
  rst.induced_edges = IdArray::Empty(
      {static_cast<int64_t>(edges.size())}, vids->dtype, vids->ctx);
  std::copy(
      edges.begin(), edges.end(),
      static_cast<int64_t*>(rst.induced_edges->data));
Minjie Wang's avatar
Minjie Wang committed
509
  return rst;
Minjie Wang's avatar
impl  
Minjie Wang committed
510
}
Minjie Wang's avatar
Minjie Wang committed
511

512
Subgraph Graph::EdgeSubgraph(IdArray eids, bool preserve_nodes) const {
513
  CHECK(aten::IsValidIdArray(eids)) << "Invalid edge id array.";
514
515
516
517
518
  const auto len = eids->shape[0];
  std::vector<dgl_id_t> nodes;
  const int64_t* eid_data = static_cast<int64_t*>(eids->data);

  Subgraph rst;
519
520
521
522
523
524
525
526
527
528
529
  if (!preserve_nodes) {
    std::unordered_map<dgl_id_t, dgl_id_t> oldv2newv;

    for (int64_t i = 0; i < len; ++i) {
      const dgl_id_t src_id = all_edges_src_[eid_data[i]];
      const dgl_id_t dst_id = all_edges_dst_[eid_data[i]];
      if (oldv2newv.insert(std::make_pair(src_id, oldv2newv.size())).second)
        nodes.push_back(src_id);
      if (oldv2newv.insert(std::make_pair(dst_id, oldv2newv.size())).second)
        nodes.push_back(dst_id);
    }
530

531
    rst.graph = std::make_shared<Graph>();
532
533
534
535
536
537
538
539
540
541
542
    rst.induced_edges = eids;
    rst.graph->AddVertices(nodes.size());

    for (int64_t i = 0; i < len; ++i) {
      const dgl_id_t src_id = all_edges_src_[eid_data[i]];
      const dgl_id_t dst_id = all_edges_dst_[eid_data[i]];
      rst.graph->AddEdge(oldv2newv[src_id], oldv2newv[dst_id]);
    }

    rst.induced_vertices = IdArray::Empty(
        {static_cast<int64_t>(nodes.size())}, eids->dtype, eids->ctx);
543
544
545
    std::copy(
        nodes.begin(), nodes.end(),
        static_cast<int64_t*>(rst.induced_vertices->data));
546
  } else {
547
    rst.graph = std::make_shared<Graph>();
548
549
550
551
552
553
554
555
    rst.induced_edges = eids;
    rst.graph->AddVertices(NumVertices());

    for (int64_t i = 0; i < len; ++i) {
      dgl_id_t src_id = all_edges_src_[eid_data[i]];
      dgl_id_t dst_id = all_edges_dst_[eid_data[i]];
      rst.graph->AddEdge(src_id, dst_id);
    }
556

557
    for (uint64_t i = 0; i < NumVertices(); ++i) nodes.push_back(i);
558
559
560

    rst.induced_vertices = IdArray::Empty(
        {static_cast<int64_t>(nodes.size())}, eids->dtype, eids->ctx);
561
562
563
    std::copy(
        nodes.begin(), nodes.end(),
        static_cast<int64_t*>(rst.induced_vertices->data));
564
  }
565
566

  return rst;
Minjie Wang's avatar
Minjie Wang committed
567
568
}

569
570
std::vector<IdArray> Graph::GetAdj(
    bool transpose, const std::string& fmt) const {
571
572
  uint64_t num_edges = NumEdges();
  uint64_t num_nodes = NumVertices();
573
  if (fmt == "coo") {
574
    IdArray idx = IdArray::Empty(
575
        {2 * static_cast<int64_t>(num_edges)}, DGLDataType{kDGLInt, 64, 1},
576
        DGLContext{kDGLCPU, 0});
577
    int64_t* idx_data = static_cast<int64_t*>(idx->data);
578
579
    if (transpose) {
      std::copy(all_edges_src_.begin(), all_edges_src_.end(), idx_data);
580
581
      std::copy(
          all_edges_dst_.begin(), all_edges_dst_.end(), idx_data + num_edges);
582
583
    } else {
      std::copy(all_edges_dst_.begin(), all_edges_dst_.end(), idx_data);
584
585
      std::copy(
          all_edges_src_.begin(), all_edges_src_.end(), idx_data + num_edges);
586
    }
587
    IdArray eid = IdArray::Empty(
588
        {static_cast<int64_t>(num_edges)}, DGLDataType{kDGLInt, 64, 1},
589
        DGLContext{kDGLCPU, 0});
590
    int64_t* eid_data = static_cast<int64_t*>(eid->data);
591
592
593
594
595
    for (uint64_t eid = 0; eid < num_edges; ++eid) {
      eid_data[eid] = eid;
    }
    return std::vector<IdArray>{idx, eid};
  } else if (fmt == "csr") {
596
    IdArray indptr = IdArray::Empty(
597
        {static_cast<int64_t>(num_nodes) + 1}, DGLDataType{kDGLInt, 64, 1},
598
        DGLContext{kDGLCPU, 0});
599
    IdArray indices = IdArray::Empty(
600
        {static_cast<int64_t>(num_edges)}, DGLDataType{kDGLInt, 64, 1},
601
        DGLContext{kDGLCPU, 0});
602
    IdArray eid = IdArray::Empty(
603
        {static_cast<int64_t>(num_edges)}, DGLDataType{kDGLInt, 64, 1},
604
        DGLContext{kDGLCPU, 0});
605
606
607
608
    int64_t* indptr_data = static_cast<int64_t*>(indptr->data);
    int64_t* indices_data = static_cast<int64_t*>(indices->data);
    int64_t* eid_data = static_cast<int64_t*>(eid->data);
    const AdjacencyList* adjlist;
609
610
611
612
613
614
615
616
617
618
    if (transpose) {
      // Out-edges.
      adjlist = &adjlist_;
    } else {
      // In-edges.
      adjlist = &reverse_adjlist_;
    }
    indptr_data[0] = 0;
    for (size_t i = 0; i < adjlist->size(); i++) {
      indptr_data[i + 1] = indptr_data[i] + adjlist->at(i).succ.size();
619
620
621
622
623
624
      std::copy(
          adjlist->at(i).succ.begin(), adjlist->at(i).succ.end(),
          indices_data + indptr_data[i]);
      std::copy(
          adjlist->at(i).edge_id.begin(), adjlist->at(i).edge_id.end(),
          eid_data + indptr_data[i]);
625
626
627
628
629
630
631
632
    }
    return std::vector<IdArray>{indptr, indices, eid};
  } else {
    LOG(FATAL) << "unsupported format";
    return std::vector<IdArray>();
  }
}

Minjie Wang's avatar
impl  
Minjie Wang committed
633
}  // namespace dgl