Galois
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GraphHelpers.h
Go to the documentation of this file.
1 /*
2  * This file belongs to the Galois project, a C++ library for exploiting
3  * parallelism. The code is being released under the terms of the 3-Clause BSD
4  * License (a copy is located in LICENSE.txt at the top-level directory).
5  *
6  * Copyright (C) 2018, The University of Texas at Austin. All rights reserved.
7  * UNIVERSITY EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES CONCERNING THIS
8  * SOFTWARE AND DOCUMENTATION, INCLUDING ANY WARRANTIES OF MERCHANTABILITY,
9  * FITNESS FOR ANY PARTICULAR PURPOSE, NON-INFRINGEMENT AND WARRANTIES OF
10  * PERFORMANCE, AND ANY WARRANTY THAT MIGHT OTHERWISE ARISE FROM COURSE OF
11  * DEALING OR USAGE OF TRADE. NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH
12  * RESPECT TO THE USE OF THE SOFTWARE OR DOCUMENTATION. Under no circumstances
13  * shall University be liable for incidental, special, indirect, direct or
14  * consequential damages or loss of profits, interruption of business, or
15  * related expenses which may arise from use of Software or Documentation,
16  * including but not limited to those resulting from defects in Software and/or
17  * Documentation, or loss or inaccuracy of data of any kind.
18  */
19 
20 #pragma once
21 
22 #include <cassert>
23 #include <vector>
24 
25 #include <boost/iterator/counting_iterator.hpp>
26 
27 #include "galois/config.h"
28 #include "galois/gIO.h"
29 
30 namespace galois {
31 namespace graphs {
32 
33 namespace internal {
57 // Note: "inline" may be required if PrefixSumType is exactly the same type
58 // in 2 different translation units; otherwise it should be fine
59 template <typename PrefixSumType>
60 size_t findIndexPrefixSum(size_t nodeWeight, size_t edgeWeight,
61  size_t targetWeight, uint64_t lb, uint64_t ub,
62  PrefixSumType& edgePrefixSum, uint64_t edgeOffset,
63  uint64_t nodeOffset) {
64  assert(nodeWeight != 0 || edgeWeight != 0);
65 
66  while (lb < ub) {
67  size_t mid = lb + (ub - lb) / 2;
68  size_t num_edges;
69 
70  if ((mid + nodeOffset) != 0) {
71  num_edges = edgePrefixSum[mid - 1 + nodeOffset] - edgeOffset;
72  } else {
73  num_edges = 0;
74  }
75 
76  size_t weight = num_edges * edgeWeight + mid * nodeWeight;
77 
78  if (weight < targetWeight) {
79  lb = mid + 1;
80  } else if (weight >= targetWeight) {
81  ub = mid;
82  }
83  }
84 
85  return lb;
86 }
87 
99 uint32_t determine_block_division(uint32_t numDivisions,
100  std::vector<unsigned>& scaleFactor);
101 
102 } // end namespace internal
103 
136 // Note: "inline" may be required if PrefixSumType is exactly the same type
137 // in 2 different translation units; otherwise it should be fine
138 // If inline is used, then apparently you cannot use typedefs, so get rid
139 // of those if the need arises.
140 template <typename PrefixSumType, typename NodeType = uint64_t>
142  NodeType numNodes, uint64_t numEdges, size_t nodeWeight, size_t edgeWeight,
143  size_t id, size_t total, PrefixSumType& edgePrefixSum,
144  std::vector<unsigned> scaleFactor = std::vector<unsigned>(),
145  uint64_t edgeOffset = 0, uint64_t nodeOffset = 0) {
146  typedef boost::counting_iterator<NodeType> iterator;
147  typedef boost::counting_iterator<uint64_t> edge_iterator;
148  typedef std::pair<iterator, iterator> NodeRange;
149  typedef std::pair<edge_iterator, edge_iterator> EdgeRange;
150  typedef std::pair<NodeRange, EdgeRange> GraphRange;
151 
152  // numNodes = 0 corner case
153  if (numNodes == 0) {
154  return GraphRange(NodeRange(iterator(0), iterator(0)),
155  EdgeRange(edge_iterator(0), edge_iterator(0)));
156  }
157 
158  assert(nodeWeight != 0 || edgeWeight != 0);
159  assert(total >= 1);
160  assert(id < total);
161 
162  // weight of all data
163  uint64_t weight = numNodes * nodeWeight + (numEdges + 1) * edgeWeight;
164  // determine the number of blocks to divide among total divisions + setup the
165  // scale factor vector if necessary
166  uint32_t numBlocks = internal::determine_block_division(total, scaleFactor);
167  // weight of a block (one block for each division by default; if scale
168  // factor specifies something different, then use that instead)
169  uint64_t blockWeight = (weight + numBlocks - 1) / numBlocks;
170  // galois::gDebug("weight ", weight, " numblock ", numBlocks, " blockwegith ",
171  // blockWeight);
172 
173  // lower and upper blocks that this division should use determined
174  // using scaleFactor
175  uint32_t blockLower;
176  if (id != 0) {
177  blockLower = scaleFactor[id - 1];
178  } else {
179  blockLower = 0;
180  }
181 
182  uint32_t blockUpper = scaleFactor[id];
183 
184  assert(blockLower <= blockUpper);
185  // galois::gDebug("Unit ", id, " block ", blockLower, " to ",
186  // blockUpper, "; ", blockLower * blockWeight, " ",
187  // blockUpper * blockWeight);
188 
189  uint64_t nodesLower;
190  // use prefix sum to find node bounds
191  if (blockLower == 0) {
192  nodesLower = 0;
193  } else {
194  nodesLower = internal::findIndexPrefixSum(
195  nodeWeight, edgeWeight, blockWeight * blockLower, 0, numNodes,
196  edgePrefixSum, edgeOffset, nodeOffset);
197  }
198 
199  uint64_t nodesUpper;
200  nodesUpper = internal::findIndexPrefixSum(
201  nodeWeight, edgeWeight, blockWeight * blockUpper, nodesLower, numNodes,
202  edgePrefixSum, edgeOffset, nodeOffset);
203 
204  // get the edges bounds using node lower/upper bounds
205  uint64_t edgesLower = numEdges;
206  uint64_t edgesUpper = numEdges;
207 
208  if (nodesLower != nodesUpper) {
209  if ((nodesLower + nodeOffset) != 0) {
210  edgesLower = edgePrefixSum[nodesLower - 1 + nodeOffset] - edgeOffset;
211  } else {
212  edgesLower = 0;
213  }
214 
215  edgesUpper = edgePrefixSum[nodesUpper - 1 + nodeOffset] - edgeOffset;
216  }
217 
218  // galois::gDebug("Unit ", id, " nodes ", nodesLower, " to ",
219  // nodesUpper, " edges ", edgesLower, " ",
220  // edgesUpper);
221 
222  return GraphRange(
223  NodeRange(iterator(nodesLower), iterator(nodesUpper)),
224  EdgeRange(edge_iterator(edgesLower), edge_iterator(edgesUpper)));
225 }
226 
227 // second internal namespace
228 namespace internal {
229 
241 bool unitRangeCornerCaseHandle(uint32_t unitsToSplit, uint32_t beginNode,
242  uint32_t endNode,
243  std::vector<uint32_t>& returnRanges);
244 
262 template <typename GraphTy>
263 void determineUnitRangesLoopGraph(GraphTy& graph, uint32_t unitsToSplit,
264  uint32_t beginNode, uint32_t endNode,
265  std::vector<uint32_t>& returnRanges,
266  uint32_t nodeAlpha) {
267  assert(beginNode != endNode);
268 
269  uint32_t numNodesInRange = endNode - beginNode;
270  uint64_t numEdgesInRange =
271  graph.edge_end(endNode - 1) - graph.edge_begin(beginNode);
272  uint64_t edgeOffset = *graph.edge_begin(beginNode);
273 
274  returnRanges[0] = beginNode;
275  std::vector<unsigned int> dummyScaleFactor;
276 
277  for (uint32_t i = 0; i < unitsToSplit; i++) {
278  // determine division for unit i
279  auto nodeSplits =
280  divideNodesBinarySearch<GraphTy, uint32_t>(
281  numNodesInRange, numEdgesInRange, nodeAlpha, 1, i, unitsToSplit,
282  graph, dummyScaleFactor, edgeOffset, beginNode)
283  .first;
284 
285  // i.e. if there are actually assigned nodes
286  if (nodeSplits.first != nodeSplits.second) {
287  if (i != 0) {
288  assert(returnRanges[i] == *(nodeSplits.first) + beginNode);
289  } else { // i == 0
290  assert(returnRanges[i] == beginNode);
291  }
292  returnRanges[i + 1] = *(nodeSplits.second) + beginNode;
293  } else {
294  // unit assinged no nodes, copy last one
295  returnRanges[i + 1] = returnRanges[i];
296  }
297 
298  galois::gDebug("LoopGraph Unit ", i, " gets nodes ", returnRanges[i],
299  " to ", returnRanges[i + 1], ", num edges is ",
300  graph.edge_end(returnRanges[i + 1] - 1) -
301  graph.edge_begin(returnRanges[i]));
302  }
303 }
304 
322 template <typename VectorTy>
323 void determineUnitRangesLoopPrefixSum(VectorTy& prefixSum,
324  uint32_t unitsToSplit, uint32_t beginNode,
325  uint32_t endNode,
326  std::vector<uint32_t>& returnRanges,
327  uint32_t nodeAlpha) {
328  assert(beginNode != endNode);
329 
330  uint32_t numNodesInRange = endNode - beginNode;
331 
332  uint64_t numEdgesInRange;
333  uint64_t edgeOffset;
334  if (beginNode != 0) {
335  numEdgesInRange = prefixSum[endNode - 1] - prefixSum[beginNode - 1];
336  edgeOffset = prefixSum[beginNode - 1];
337  } else {
338  numEdgesInRange = prefixSum[endNode - 1];
339  edgeOffset = 0;
340  }
341 
342  returnRanges[0] = beginNode;
343  std::vector<unsigned int> dummyScaleFactor;
344 
345  for (uint32_t i = 0; i < unitsToSplit; i++) {
346  // determine division for unit i
347  auto nodeSplits =
348  divideNodesBinarySearch<VectorTy, uint32_t>(
349  numNodesInRange, numEdgesInRange, nodeAlpha, 1, i, unitsToSplit,
350  prefixSum, dummyScaleFactor, edgeOffset, beginNode)
351  .first;
352 
353  // i.e. if there are actually assigned nodes
354  if (nodeSplits.first != nodeSplits.second) {
355  if (i != 0) {
356  assert(returnRanges[i] == *(nodeSplits.first) + beginNode);
357  } else { // i == 0
358  assert(returnRanges[i] == beginNode);
359  }
360  returnRanges[i + 1] = *(nodeSplits.second) + beginNode;
361  } else {
362  // unit assinged no nodes
363  returnRanges[i + 1] = returnRanges[i];
364  }
365 
366  galois::gDebug("Unit ", i, " gets nodes ", returnRanges[i], " to ",
367  returnRanges[i + 1]);
368  }
369 }
370 
379 void unitRangeSanity(uint32_t unitsToSplit, uint32_t beginNode,
380  uint32_t endNode, std::vector<uint32_t>& returnRanges);
381 
382 } // namespace internal
383 
402 template <typename GraphTy>
403 std::vector<uint32_t> determineUnitRangesFromGraph(GraphTy& graph,
404  uint32_t unitsToSplit,
405  uint32_t nodeAlpha = 0) {
406  uint32_t totalNodes = graph.size();
407 
408  std::vector<uint32_t> returnRanges;
409  returnRanges.resize(unitsToSplit + 1);
410 
411  // check corner cases
412  if (internal::unitRangeCornerCaseHandle(unitsToSplit, 0, totalNodes,
413  returnRanges)) {
414  return returnRanges;
415  }
416 
417  // no corner cases: onto main loop over nodes that determines
418  // node ranges
419  internal::determineUnitRangesLoopGraph(graph, unitsToSplit, 0, totalNodes,
420  returnRanges, nodeAlpha);
421 
422  internal::unitRangeSanity(unitsToSplit, 0, totalNodes, returnRanges);
423 
424  return returnRanges;
425 }
426 
447 template <typename GraphTy>
448 std::vector<uint32_t>
449 determineUnitRangesFromGraph(GraphTy& graph, uint32_t unitsToSplit,
450  uint32_t beginNode, uint32_t endNode,
451  uint32_t nodeAlpha = 0) {
452  std::vector<uint32_t> returnRanges;
453  returnRanges.resize(unitsToSplit + 1);
454 
455  if (internal::unitRangeCornerCaseHandle(unitsToSplit, beginNode, endNode,
456  returnRanges)) {
457  return returnRanges;
458  }
459 
460  // no corner cases: onto main loop over nodes that determines
461  // node ranges
462  internal::determineUnitRangesLoopGraph(graph, unitsToSplit, beginNode,
463  endNode, returnRanges, nodeAlpha);
464 
465  internal::unitRangeSanity(unitsToSplit, beginNode, endNode, returnRanges);
466 
467  return returnRanges;
468 }
469 
483 template <typename VectorTy>
484 std::vector<uint32_t> determineUnitRangesFromPrefixSum(uint32_t unitsToSplit,
485  VectorTy& edgePrefixSum,
486  uint32_t nodeAlpha = 0) {
487  assert(unitsToSplit > 0);
488 
489  std::vector<uint32_t> nodeRanges;
490  nodeRanges.resize(unitsToSplit + 1);
491 
492  nodeRanges[0] = 0;
493 
494  uint32_t numNodes = edgePrefixSum.size();
495  // handle corner case TODO there are better ways to do this, i.e. call helper
496  if (numNodes == 0) {
497  nodeRanges[0] = 0;
498 
499  for (uint32_t i = 0; i < unitsToSplit; i++) {
500  nodeRanges[i + 1] = 0;
501  }
502  return nodeRanges;
503  }
504 
505  uint64_t numEdges = edgePrefixSum[numNodes - 1];
506 
507  for (uint32_t i = 0; i < unitsToSplit; i++) {
508  auto nodeSplits =
509  divideNodesBinarySearch<VectorTy, uint32_t>(
510  numNodes, numEdges, nodeAlpha, 1, i, unitsToSplit, edgePrefixSum)
511  .first;
512 
513  // i.e. if there are actually assigned nodes
514  if (nodeSplits.first != nodeSplits.second) {
515  if (i != 0) {
516  assert(nodeRanges[i] == *(nodeSplits.first));
517  } else { // i == 0
518  assert(nodeRanges[i] == 0);
519  }
520  nodeRanges[i + 1] = *(nodeSplits.second);
521  } else {
522  // unit assinged no nodes
523  nodeRanges[i + 1] = nodeRanges[i];
524  }
525 
526  galois::gDebug("Unit ", i, " gets nodes ", nodeRanges[i], " to ",
527  nodeRanges[i + 1]);
528  }
529 
530  return nodeRanges;
531 }
532 
549 template <typename VectorTy>
550 std::vector<uint32_t>
551 determineUnitRangesFromPrefixSum(uint32_t unitsToSplit, VectorTy& edgePrefixSum,
552  uint32_t beginNode, uint32_t endNode,
553  uint32_t nodeAlpha = 0) {
554  std::vector<uint32_t> returnRanges;
555  returnRanges.resize(unitsToSplit + 1);
556 
557  if (internal::unitRangeCornerCaseHandle(unitsToSplit, beginNode, endNode,
558  returnRanges)) {
559  return returnRanges;
560  }
561 
562  // no corner cases: onto main loop over nodes that determines
563  // node ranges
564  internal::determineUnitRangesLoopPrefixSum(
565  edgePrefixSum, unitsToSplit, beginNode, endNode, returnRanges, nodeAlpha);
566 
567  internal::unitRangeSanity(unitsToSplit, beginNode, endNode, returnRanges);
568 
569  return returnRanges;
570 }
571 
572 } // end namespace graphs
573 } // end namespace galois
auto divideNodesBinarySearch(NodeType numNodes, uint64_t numEdges, size_t nodeWeight, size_t edgeWeight, size_t id, size_t total, PrefixSumType &edgePrefixSum, std::vector< unsigned > scaleFactor=std::vector< unsigned >(), uint64_t edgeOffset=0, uint64_t nodeOffset=0)
Returns 2 ranges (one for nodes, one for edges) for a particular division.
Definition: GraphHelpers.h:141
void gDebug(Args &&...GALOIS_USED_ONLY_IN_DEBUG(args))
Prints a debug string from a sequence of things; prints nothing if NDEBUG is defined.
Definition: gIO.h:72
std::vector< uint32_t > determineUnitRangesFromGraph(GraphTy &graph, uint32_t unitsToSplit, uint32_t nodeAlpha=0)
Determines node division ranges for all nodes in a graph and returns it in an offset vector...
Definition: GraphHelpers.h:403
std::vector< uint32_t > determineUnitRangesFromPrefixSum(uint32_t unitsToSplit, VectorTy &edgePrefixSum, uint32_t nodeAlpha=0)
Uses the divideByNode function (which is binary search based) to divide nodes among units using a pro...
Definition: GraphHelpers.h:484