// Creates an infinite sequence of normally-distributed voters. public static IEnumerable <VoterFactory> Normal(int candidateCount, Random random) { while (true) { yield return(VoterFactory.Normal(candidateCount, random)); } }
/* * This creates electorates based on a Polya/Hoppe/Dirichlet model, with mutation. * You start with an "urn" of `seedVoter` voters from `seedModel`, * plus `alpha` "wildcard" voters. Then you draw a voter from the urn, * clone and mutate them, and put the original and clone back into the urn. * If you draw a "wildcard", use `seedModel` to make a new voter. * https://github.com/electionscience/vse-sim/blob/1d7e48f639fd5ffcf84883dce0873aa7d6fa6794/voterModels.py#L204 */ public static IEnumerable <VoterFactory> PolyaModel(this IEnumerable <VoterFactory> seedModel, Random random, int seedVoters = 2, int alpha = 1, double mutantFactor = .2) { if (seedModel == null) { throw new ArgumentNullException(nameof(seedModel)); } if (seedVoters < 1) { throw new ArgumentException(nameof(seedVoters), "Must be positive."); } if (alpha < 0) { throw new ArgumentException(nameof(alpha), "Must be non-negative."); } using (var seedEnumerator = seedModel.GetEnumerator()) { var urn = new List <VoterFactory>(); for (int i = 0; i < seedVoters; i++) { yield return(GetNext()); } while (true) { var i = random.Next(urn.Count + alpha); if (i < urn.Count) { var mutant = urn[i].HybridWith(VoterFactory.Normal(urn[i].CandidateCount, random), mutantFactor); urn.Add(mutant); yield return(mutant); } else { yield return(GetNext()); } } VoterFactory GetNext() { if (!seedEnumerator.MoveNext()) { throw new InvalidOperationException("Expected an infinite enumerable."); } urn.Add(seedEnumerator.Current); return(seedEnumerator.Current); } } }
// https://github.com/electionscience/vse-sim/blob/1d7e48f639fd5ffcf84883dce0873aa7d6fa6794/voterModels.py#L39 // If both are standard normal to start with, the result will be standard normal too. public VoterFactory HybridWith(VoterFactory other, double weight) { if (other == null) { throw new ArgumentNullException(nameof(other)); } if (other.m_utilities.Length != m_utilities.Length) { throw new ArgumentException(nameof(other), "May only combine voter factories with the same initial conditions."); } var combinedUtilities = new double[m_utilities.Length]; var denominator = Math.Sqrt(1 + weight * weight); for (int i = 0; i < m_utilities.Length; i++) { combinedUtilities[i] = (m_utilities[i] + weight * other.m_utilities[i]) / denominator; } return(new VoterFactory(combinedUtilities)); }
// Weights an electorate based on a common "quality" factor for each candidate which spans ideology public static IEnumerable <VoterFactory> Quality(this IEnumerable <VoterFactory> source, Random random, double weight = .5) { using (var sourceEnumerator = source.GetEnumerator()) { if (!sourceEnumerator.MoveNext()) { throw new InvalidOperationException("Expected an infinite enumerable."); } var quality = VoterFactory.Normal(sourceEnumerator.Current.CandidateCount, random); while (true) { yield return(sourceEnumerator.Current.HybridWith(quality, weight)); if (!sourceEnumerator.MoveNext()) { throw new InvalidOperationException("Expected an infinite enumerable."); } } } }