InnerQuantiles and OuterQuantiles throw on infinite quantiles. InnerQuantiles.GetLogProb and GetQuantile are non-decreasing.

This commit is contained in:
Tom Minka 2019-06-07 20:36:09 +01:00
Родитель 70ae46cd79
Коммит b7ab86af1b
6 изменённых файлов: 88 добавлений и 22 удалений

Просмотреть файл

@ -2585,9 +2585,8 @@ namespace Microsoft.ML.Probabilistic.Math
// Therefore scale <= double.MaxValue/Y.Count/abs(Y[j]*US[j,k]).
double absY = System.Math.Abs(Y[j]);
double absU = System.Math.Abs(US[j, k]);
bool Ygt1 = (absY > 1);
double thisUpperBound;
if (Ygt1)
if (absY > 1)
{
thisUpperBound = limit / absY / absU;
}

Просмотреть файл

@ -48,6 +48,8 @@ namespace Microsoft.ML.Probabilistic.Distributions
{
this.quantiles[i] = canGetQuantile.GetQuantile((i + 1.0) / (quantileCount + 1.0));
}
OuterQuantiles.AssertFinite(quantiles, nameof(canGetQuantile));
OuterQuantiles.AssertNondecreasing(quantiles, nameof(canGetQuantile));
lowerGaussian = GetLowerGaussian(quantiles);
upperGaussian = GetUpperGaussian(quantiles);
}
@ -72,16 +74,17 @@ namespace Microsoft.ML.Probabilistic.Distributions
return quantiles;
}
/// <inheritdoc/>
public double GetProbLessThan(double x)
{
int n = quantiles.Length;
if (x < quantiles[0])
{
return lowerGaussian.GetProbLessThan(x);
return Math.Min(lowerGaussian.GetProbLessThan(x), 1.0/(n+1));
}
int n = quantiles.Length;
if (x > quantiles[n - 1])
{
return upperGaussian.GetProbLessThan(x);
return Math.Max(upperGaussian.GetProbLessThan(x), n/(n+1.0));
}
return GetProbLessThan(x, quantiles);
}
@ -156,7 +159,7 @@ namespace Microsoft.ML.Probabilistic.Distributions
double mean, stddev;
GetGaussianFromQuantiles(quantiles[0], p0, quantiles[i], p1, out mean, out stddev);
Gaussian result = Gaussian.FromMeanAndVariance(mean, stddev * stddev);
if (!result.IsProper() || double.IsNaN(result.GetMean()) || double.IsInfinity(result.GetMean()))
if (result.IsPointMass || !result.IsProper() || double.IsNaN(result.GetMean()) || double.IsInfinity(result.GetMean()))
{
return Gaussian.PointMass(quantiles[0]);
}
@ -184,6 +187,10 @@ namespace Microsoft.ML.Probabilistic.Distributions
double p1 = (i + 1.0) / (n + 1);
double mean, stddev;
GetGaussianFromQuantiles(quantiles[n - 1], p0, quantiles[i], p1, out mean, out stddev);
if (double.IsNaN(mean) || double.IsInfinity(mean))
{
return Gaussian.PointMass(quantiles[n - 1]);
}
Gaussian result = Gaussian.FromMeanAndVariance(mean, stddev * stddev);
if (!result.IsProper() || double.IsNaN(result.GetMean()) || double.IsInfinity(result.GetMean()))
{

Просмотреть файл

@ -32,8 +32,8 @@ namespace Microsoft.ML.Probabilistic.Distributions
{
for (int i = 0; i < array.Length; i++)
{
if (double.IsInfinity(array[i])) throw new ArgumentOutOfRangeException(paramName, $"{paramName}[{i}] {array[i]}");
if (double.IsNaN(array[i])) throw new ArgumentOutOfRangeException(paramName, $"{paramName}[{i}] {array[i]}");
if (double.IsInfinity(array[i])) throw new ArgumentOutOfRangeException(paramName, array[i], $"Array element is infinite: {paramName}[{i}]={array[i]}");
if (double.IsNaN(array[i])) throw new ArgumentOutOfRangeException(paramName, array[i], $"Array element is NaN: {paramName}[{i}]={array[i]}");
}
}
@ -41,7 +41,7 @@ namespace Microsoft.ML.Probabilistic.Distributions
{
for (int i = 1; i < array.Length; i++)
{
if (array[i] < array[i - 1]) throw new ArgumentException($"Array is not non-decreasing: {paramName}[{i}] {array[i]} < {paramName}[{i - 1}] {array[i - 1]}", paramName);
if (array[i] < array[i - 1]) throw new ArgumentException($"Decreasing array elements: {paramName}[{i}] {array[i]} < {paramName}[{i - 1}] {array[i - 1]}", paramName);
}
}
@ -124,19 +124,23 @@ namespace Microsoft.ML.Probabilistic.Distributions
/// <summary>
/// Returns the largest quantile such that ((quantile - lowerItem)/(upperItem - lowerItem) + lowerIndex)/(n-1) &lt;= probability.
/// </summary>
/// <param name="probability"></param>
/// <param name="probability">A number between 0 and 1.</param>
/// <param name="lowerIndex"></param>
/// <param name="lowerItem"></param>
/// <param name="upperItem"></param>
/// <param name="n"></param>
/// <param name="lowerItem">Must be finite.</param>
/// <param name="upperItem">Must be finite and at least lowerItem.</param>
/// <param name="n">Must be greater than 1</param>
/// <returns></returns>
internal static double GetQuantile(double probability, double lowerIndex, double lowerItem, double upperItem, long n)
{
if (n == 1) throw new ArgumentOutOfRangeException("n == 1");
if(probability < 0) throw new ArgumentOutOfRangeException(nameof(probability), probability, $"{nameof(probability)} < 0");
if (probability > 1) throw new ArgumentOutOfRangeException(nameof(probability), probability, $"{nameof(probability)} > 1");
if (n <= 1) throw new ArgumentOutOfRangeException(nameof(n), n, "n <= 1");
double pos = MMath.LargestDoubleProduct(n - 1, probability);
double frac = MMath.LargestDoubleSum(-lowerIndex, pos);
if (upperItem < lowerItem) throw new ArgumentOutOfRangeException(nameof(upperItem), upperItem, $"{nameof(upperItem)} ({upperItem}) < {nameof(lowerItem)} ({lowerItem})");
if (upperItem == lowerItem) return lowerItem;
// The above check ensures diff > 0
double diff = upperItem - lowerItem;
if (diff == 0) return lowerItem;
double offset = MMath.LargestDoubleProduct(diff, frac);
return MMath.LargestDoubleSum(lowerItem, offset);
}

Просмотреть файл

@ -137,10 +137,12 @@ namespace Microsoft.ML.Probabilistic.Distributions
if (probability > 1.0) throw new ArgumentOutOfRangeException(nameof(probability), "probability > 1.0");
// compute the min and max of the retained items
double lowerBound = double.PositiveInfinity, upperBound = double.NegativeInfinity;
bool countGreaterThanZero = false;
for (int bufferIndex = 0; bufferIndex < buffers.Length; bufferIndex++)
{
double[] buffer = buffers[bufferIndex];
int count = countInBuffer[bufferIndex];
if (count > 0) countGreaterThanZero = true;
for (int i = 0; i < count; i++)
{
double item = buffer[i];
@ -150,7 +152,7 @@ namespace Microsoft.ML.Probabilistic.Distributions
}
if (probability == 1.0) return MMath.NextDouble(upperBound);
if (probability == 0.0) return lowerBound;
if (double.IsPositiveInfinity(lowerBound)) throw new Exception("QuantileEstimator has no data");
if (!countGreaterThanZero) throw new Exception("QuantileEstimator has no data");
if (lowerBound == upperBound) return upperBound;
// use bisection
while (true)

Просмотреть файл

@ -104,8 +104,6 @@ namespace TestApp
#endif
watch.Stop();
Console.WriteLine("elapsed time = {0}ms", watch.ElapsedMilliseconds);
Console.ReadKey();
}
}
}

Различия файлов скрыты, потому что одна или несколько строк слишком длинны