internal static Dictionary<Nonterminal, double> GetNullable(ISet<Production> originalProductions) {
var results = new Dictionary<Nonterminal, double>();
var productionByNt = BuildLookupTable(originalProductions);
var nonterminals = GetNonterminals(originalProductions);
foreach (var nt in nonterminals) {
ICollection<Production> productions;
if (!productionByNt.TryGetValue(nt, out productions)) {
productionByNt[nt] = new List<Production>();
}
}
var indexToNonterminal = nonterminals.ToArray();
var nonterminalToIndex = new Dictionary<Nonterminal, int>();
for (int i = 0; i < nonterminals.Count; i++) {
nonterminalToIndex[indexToNonterminal[i]] = i;
}
// seeding the estimates with a magic value
// this keeps all iterations behaving the same
var previousEstimates = Enumerable.Repeat(_magicStartProbability, indexToNonterminal.Length).ToArray();
var currentEstimates = new double[indexToNonterminal.Length];
// figure out which nonterminals are definitely not nullable, and go ahead and set them that way
var nullableNonterminals = GetNullableNonterminals(originalProductions);
foreach (var nt in nonterminals) {
if (!nullableNonterminals.Contains(nt)) {
previousEstimates[nonterminalToIndex[nt]] = 0.0;
}
}
bool changed = true;
while (changed == true) {
changed = false;
Array.Clear(currentEstimates, 0, currentEstimates.Length);
// consider each nonterminal. calculate a new nullable estimate based on the previous
for (int i = 0; i < currentEstimates.Length; i++) {
var nt = indexToNonterminal[i];
var productions = productionByNt[nt];
double weightSum = 0.0; // productions.Sum((p) => p.Weight);
foreach (var production in productions) {
var productionWeight = production.Weight;
weightSum += productionWeight;
var innerProb = GetProductionProbability(production, nonterminalToIndex, previousEstimates);
currentEstimates[i] += productionWeight * innerProb;
}
if (weightSum == 0.0) {
currentEstimates[i] = 0.0;
} else {
currentEstimates[i] /= weightSum;
}
if (currentEstimates[i] > previousEstimates[i]) {
throw new Exception("Didn't expect estimates to increase");
} else if (currentEstimates[i] < previousEstimates[i]) {
changed = true;
}
}
Helpers.Swap(ref previousEstimates, ref currentEstimates);
}
for (int i = 0; i < nonterminals.Count; i++) {
var nt = indexToNonterminal[i];
results[nt] = previousEstimates[i];
}
return results;
}