/// <summary> /// Get the mean and variance after truncation. /// </summary> /// <param name="mean"></param> /// <param name="variance"></param> public void GetMeanAndVariance(out double mean, out double variance) { if (this.Gamma.IsPointMass) { mean = this.Gamma.Point; variance = 0.0; } else if (!IsProper()) { throw new ImproperDistributionException(this); } else { // Apply the recurrence GammaUpper(s+1,x,false) = s*GammaUpper(s,x,false) + x^s*exp(-x) // Z = GammaUpper(s,r*l,false) - GammaUpper(s,r*u,false) // E[x] = (GammaUpper(s+1,r*l,false) - GammaUpper(s+1,r*u,false))/r/Z // = (s + ((r*l)^s*exp(-r*l) - (r*u)^s*exp(-r*u))/Z)/r double rl = this.Gamma.Rate * LowerBound; double ru = this.Gamma.Rate * UpperBound; double m = this.Gamma.Shape / this.Gamma.Rate; double offset, offset2; if (ru > double.MaxValue) { double logZ = GetLogNormalizer(); if (logZ < double.MinValue) { mean = GetMode(); variance = 0.0; return; } offset = Math.Exp(MMath.GammaUpperLogScale(this.Gamma.Shape, rl) - logZ); offset2 = (rl - this.Gamma.Shape) / this.Gamma.Rate * offset; } else { // This fails when GammaUpperScale underflows to 0 double Z = GetNormalizer(); if (Z == 0) { mean = GetMode(); variance = 0.0; return; } double gammaUpperScaleLower = MMath.GammaUpperScale(this.Gamma.Shape, rl); double gammaUpperScaleUpper = MMath.GammaUpperScale(this.Gamma.Shape, ru); offset = (gammaUpperScaleLower - gammaUpperScaleUpper) / Z; offset2 = ((rl - this.Gamma.Shape) / this.Gamma.Rate * gammaUpperScaleLower - (ru - this.Gamma.Shape) / this.Gamma.Rate * gammaUpperScaleUpper) / Z; } if (rl == this.Gamma.Shape) { mean = LowerBound + offset / this.Gamma.Rate; } else { mean = (this.Gamma.Shape + offset) / this.Gamma.Rate; if (mean < LowerBound) { mean = MMath.NextDouble(mean); } if (mean < LowerBound) { mean = MMath.NextDouble(mean); } } if (mean > double.MaxValue) { variance = mean; } else { variance = (m + offset2 + (1 - offset) * offset / this.Gamma.Rate) / this.Gamma.Rate; } } }