cross-count.js 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. import _ from 'lodash'
  2. /*
  3. * A function that takes a layering (an array of layers, each with an array of
  4. * ordererd nodes) and a graph and returns a weighted crossing count.
  5. *
  6. * Pre-conditions:
  7. *
  8. * 1. Input graph must be simple (not a multigraph), directed, and include
  9. * only simple edges.
  10. * 2. Edges in the input graph must have assigned weights.
  11. *
  12. * Post-conditions:
  13. *
  14. * 1. The graph and layering matrix are left unchanged.
  15. *
  16. * This algorithm is derived from Barth, et al., "Bilayer Cross Counting."
  17. */
  18. function crossCount (g, layering) {
  19. let cc = 0
  20. for (let i = 1; i < layering.length; ++i) {
  21. cc += twoLayerCrossCount(g, layering[i - 1], layering[i])
  22. }
  23. return cc
  24. }
  25. function twoLayerCrossCount (g, northLayer, southLayer) {
  26. // Sort all of the edges between the north and south layers by their position
  27. // in the north layer and then the south. Map these edges to the position of
  28. // their head in the south layer.
  29. const southPos = _.zipObject(southLayer,
  30. _.map(southLayer, function (v, i) { return i }))
  31. const southEntries = _.flatten(_.map(northLayer, function (v) {
  32. return _.chain(g.outEdges(v))
  33. .map(function (e) {
  34. return { pos: southPos[e.w], weight: g.edge(e).weight }
  35. })
  36. .sortBy('pos')
  37. .value()
  38. }), true)
  39. // Build the accumulator tree
  40. let firstIndex = 1
  41. while (firstIndex < southLayer.length) {
  42. firstIndex <<= 1
  43. }
  44. const treeSize = 2 * firstIndex - 1
  45. firstIndex -= 1
  46. const tree = _.map(new Array(treeSize), function () { return 0 })
  47. // Calculate the weighted crossings
  48. let cc = 0
  49. _.forEach(southEntries.forEach(function (entry) {
  50. let index = entry.pos + firstIndex
  51. tree[index] += entry.weight
  52. let weightSum = 0
  53. while (index > 0) {
  54. if (index % 2) {
  55. weightSum += tree[index + 1]
  56. }
  57. index = (index - 1) >> 1
  58. tree[index] += entry.weight
  59. }
  60. cc += entry.weight * weightSum
  61. }))
  62. return cc
  63. }
  64. export default crossCount