Source: compute/voteCasters/pairwise/castPairwise.js

  1. /** @module */
  2. import * as types from '@paretoman/votekit-types'
  3. import castPairwisePlanes2D from './castPairwisePlanes2D.js'
  4. import castPairwiseIntervals1D from './castPairwiseIntervals1D.js'
  5. import castPairwiseGrid from './castPairwiseGrid.js'
  6. /**
  7. * Voters cast votes for candidates.
  8. * @param {types.typesGeometry.geometry} geometry - geometry for casting votes
  9. * @param {types.typesCast.castOptions} castOptions - options for how to cast votes.
  10. * @returns {types.typesVotes.votes} votes
  11. */
  12. export default function castPairwise(geometry, castOptions) {
  13. const { canPoints, voterGeoms, dimensions, parties } = geometry
  14. const { verbosity } = castOptions
  15. const someGaussian2D = voterGeoms.some((v) => v.densityProfile === 'gaussian') && dimensions === 2
  16. const castRegions = (dimensions === 1)
  17. ? castPairwiseIntervals1D
  18. : castPairwisePlanes2D
  19. const cast = (someGaussian2D) ? castPairwiseGrid : castRegions
  20. // get fraction of votes for each candidate so we can summarize results
  21. let totalVotes = 0
  22. // should ideally make a set of polygons for each ranking so that we avoid repeating rankings.
  23. const n = canPoints.length
  24. const winsPairwise = Array(n).fill(0)
  25. for (let i = 0; i < n; i++) {
  26. winsPairwise[i] = Array(n).fill(0)
  27. }
  28. const votesByGeom = []
  29. voterGeoms.forEach((voterGeom, g) => {
  30. const votesForGeom = cast(voterGeom, geometry, castOptions)
  31. const { winsPairwise: winsPairwiseForGeom,
  32. totalVotes: totalVotesForGeom } = votesForGeom
  33. for (let i = 0; i < n; i++) {
  34. for (let k = 0; k < n; k++) {
  35. winsPairwise[i][k] += winsPairwiseForGeom[i][k]
  36. }
  37. }
  38. totalVotes += totalVotesForGeom
  39. if (verbosity < 2) return
  40. votesByGeom[g] = votesForGeom
  41. })
  42. const invTotalCount = 1 / totalVotes
  43. const winFractionPairwise = winsPairwise.map((x) => x.map((a) => a * invTotalCount))
  44. const pairwiseTallies = { winFractionPairwise }
  45. const numCans = canPoints.length
  46. const votes = { pairwiseTallies, parties, numCans }
  47. if (verbosity < 1) return votes
  48. // borda scores
  49. const bordaScoreSumByCan = Array(n).fill(0)
  50. const bordaFractionAverageByCan = Array(n)
  51. const invTotalCountTimesNMinus1 = invTotalCount / (n - 1)
  52. for (let i = 0; i < n; i++) {
  53. for (let k = 0; k < n; k++) {
  54. bordaScoreSumByCan[i] += winsPairwise[i][k]
  55. }
  56. bordaFractionAverageByCan[i] = bordaScoreSumByCan[i] * invTotalCountTimesNMinus1
  57. }
  58. const candidateTallies = { bordaFractionAverageByCan }
  59. votes.candidateTallies = candidateTallies
  60. if (verbosity < 2) return votes
  61. votes.votesByGeom = votesByGeom
  62. return votes
  63. }