namespace AIStudio.Tools; public static class IConfidenceExtensions { public static TargetWindow DetermineTargetWindow(this IReadOnlyList items, TargetWindowStrategy strategy, int numMaximumItems = 30) where T : IConfidence { switch (strategy) { case TargetWindowStrategy.A_FEW_GOOD_ONES: return new(1, 2, 3, 0f); case TargetWindowStrategy.TOP10_BETTER_THAN_GUESSING: var numItemsBetterThanGuessing = items.Count(x => x.Confidence > 0.5f); if(numItemsBetterThanGuessing < 3) return new(1, 2, 3, 0.5f); // We want the top 10% of items better than guessing: var numTop10Percent = (int) MathF.Floor(numItemsBetterThanGuessing * 0.1f); // When these 10% are just a few items, we take them all: if (numTop10Percent <= 10) { var diff = numItemsBetterThanGuessing - numTop10Percent; var num50Percent = (int) MathF.Floor(numItemsBetterThanGuessing * 0.5f); return new(num50Percent, num50Percent + 1, Math.Max(numItemsBetterThanGuessing, diff), 0.5f); } // Let's define the size of the window: const int MIN_NUM_ITEMS = 3; var windowMin = Math.Max(MIN_NUM_ITEMS + 1, numTop10Percent); windowMin = Math.Min(windowMin, numMaximumItems - 1); var totalMin = Math.Max(MIN_NUM_ITEMS, windowMin - 3); var windowSize = (int)MathF.Max(MathF.Floor(numTop10Percent * 0.1f), MathF.Min(10, numTop10Percent)); var windowMax = Math.Min(numMaximumItems, numTop10Percent + windowSize); return new(totalMin, windowMin, windowMax, 0.5f); case TargetWindowStrategy.NONE: default: return new(-1, -1, -1, 0f); } } /// /// Determine the optimal confidence threshold for a list of items /// in order to match a target window of number of items. /// /// The list of confidence items to analyze. /// The target window for the number of items. /// The maximum number of steps to search for the threshold. /// The type of items in the list. /// The confidence threshold. public static float GetConfidenceThreshold(this IReadOnlyList items, TargetWindow targetWindow, int maxSteps = 10) where T : IConfidence { if(!targetWindow.IsValid()) { var logger = Program.SERVICE_PROVIDER.GetService>()!; logger.LogWarning("The target window is invalid. Returning 0f as threshold."); return 0f; } var confidenceValues = items.Select(x => x.Confidence).ToList(); var minConfidence = confidenceValues.Min(); var lowerBound = MathF.Max(minConfidence, targetWindow.MinThreshold); var upperBound = confidenceValues.Max(); // // We search for a threshold so that we have between // targetWindowMin and targetWindowMax items. When not // possible, we take all items (e.g., threshold = 0f; depends on the used window strategy) // var threshold = 0.0f; // Check the case where the confidence values are too close: if (upperBound - minConfidence >= 0.01) { var previousThreshold = threshold; for (var i = 0; i < maxSteps; i++) { threshold = lowerBound + (upperBound - lowerBound) * i / maxSteps; var numMatches = items.Count(x => x.Confidence >= threshold); if (numMatches <= targetWindow.NumMinItems) { threshold = previousThreshold; break; } if (targetWindow.InsideWindow(numMatches)) break; previousThreshold = threshold; } } else { var logger = Program.SERVICE_PROVIDER.GetService>()!; logger.LogWarning("The confidence values are too close. Returning 0f as threshold."); } return threshold; } }