public void AssociatedLegendreOrthonormalityL() { // don't let l and m get too big, or numerical integration will fail due to heavily oscilatory behavior int[] ells = TestUtilities.GenerateIntegerValues(1, 10, 4); for (int ki = 0; ki < ells.Length; ki++) { int k = ells[ki]; for (int li = 0; li <= ki; li++) { int l = ells[li]; foreach (int m in TestUtilities.GenerateUniformIntegerValues(0, Math.Min(k, l), 4)) { Func <double, double> f = delegate(double x) { return(OrthogonalPolynomials.LegendreP(k, m, x) * OrthogonalPolynomials.LegendreP(l, m, x)); }; double I = FunctionMath.Integrate(f, Interval.FromEndpoints(-1.0, 1.0)); Console.WriteLine("k={0} l={1} m={2} I={3}", k, l, m, I); if (k == l) { Assert.IsTrue(TestUtilities.IsNearlyEqual( I, 2.0 / (2 * k + 1) * Math.Exp(AdvancedIntegerMath.LogFactorial(l + m) - AdvancedIntegerMath.LogFactorial(l - m)) )); } else { Assert.IsTrue(Math.Abs(I) < TestUtilities.TargetPrecision); } } } } }
private double DurbinSeriesQPrime(double t) { if (t >= n) { return(0.0); } // first term double s = 2.0 * Math.Pow((n - t) / n, n - 1); // higher terms int jmax = (int)Math.Floor(n - t); if (jmax == (n - t)) { jmax--; } for (int j = 1; j <= jmax; j++) { double B = Math.Exp(AdvancedIntegerMath.LogFactorial(n - 1) - AdvancedIntegerMath.LogFactorial(j) - AdvancedIntegerMath.LogFactorial(n - j)); double C = Math.Pow((t + j) / n, j - 1) * Math.Pow((n - j - t) / n, n - j); double D = 1.0 + (j - 1) * t / (t + j) - (n - j) * t / (n - j - t); s += -2.0 * B * C * D; } return(s); }
public int GetNext(Random rng) { while (true) { double u = rng.NextDouble() - 0.5; double v = rng.NextDouble(); double us = 0.5 - Math.Abs(u); int k = (int)Math.Floor((2.0 * a / us + b) * u + lambda + 0.43); if (us > 0.07 && v < vr) { return(k); } if (k < 0) { continue; } if (us < 0.013 && v > us) { continue; } double ai = 1.1239 + 1.1328 / (b - 3.4); if (Math.Log(v * ai / (a / (us * us) + b)) <= -lambda + k * lnmu - AdvancedIntegerMath.LogFactorial(k)) { return(k); } } }
public void FactorialDoubleFactorialRelationship() { // n! = n!! (n-1)!! foreach (int n in TestUtilities.GenerateIntegerValues(1, 100, 5)) { Assert.IsTrue(TestUtilities.IsNearlyEqual( AdvancedIntegerMath.LogFactorial(n), AdvancedIntegerMath.LogDoubleFactorial(n) + AdvancedIntegerMath.LogDoubleFactorial(n - 1) )); } }
// the chi2 test fails when the expected values of some entries are small, because the entry values are not normally distributed // in the 2X2 case, the probably of any given entry value can be computed; it is given by // N_R1! N_R2! N_C1! N_C2! // P = ---------------------------- // N_11! N_12! N_21! N_22! N! // the exact test computes this probability for the actual table and for all other tables having the same row and column totals // the test statistic is the fraction of tables that have a lower probability than the actual table; it is uniformly distributed // on [0,1]. if this fraction is very small, the actual table is particularly unlikely under the null hypothesis of no correlation /// <summary> /// Performs a Fisher exact test. /// </summary> /// <returns>The results of the test. The test statistic is the summed probability of all tables exhibiting equal or stronger correlations, /// and its likelyhood under the null hypothesis is the (left) probability to obtain a smaller value. Note that, in this case, the test /// statistic itself is the likelyhood.</returns> /// <remarks><para>The Fisher exact test tests for correlations between row and column entries. It is a robust, non-parametric test, /// which, unlike the χ<sup>2</sup> test (see <see cref="ContingencyTable.PearsonChiSquaredTest"/>), can safely be used for tables /// with small, even zero-valued, entries.</para> /// <para>The Fisher test computes, under the null hypothesis of no correlation, the exact probability of all 2 X 2 tables with the /// same row and column totals as the given table. It then sums the probabilities of all tables that are as or less probable than /// the given table. In this way it determines the total probability of obtaining a 2 X 2 table which is at least as improbable /// as the given one.</para> /// <para>The test is two-sided, i.e. when considering less probable tables it does not distinguish between tables exhibiting /// the same and the opposite correlation as the given one.</para> /// </remarks> /// <seealso href="http://en.wikipedia.org/wiki/Fisher_exact_test"/> public TestResult FisherExactTest() { // store row and column totals int[] R = this.RowTotals; int[] C = this.ColumnTotals; int N = this.Total; // compute the critical probability double x = AdvancedIntegerMath.LogFactorial(R[0]) + AdvancedIntegerMath.LogFactorial(R[1]) + AdvancedIntegerMath.LogFactorial(C[0]) + AdvancedIntegerMath.LogFactorial(C[1]) - AdvancedIntegerMath.LogFactorial(N); double lnPc = x - AdvancedIntegerMath.LogFactorial(this[0, 0]) - AdvancedIntegerMath.LogFactorial(this[0, 1]) - AdvancedIntegerMath.LogFactorial(this[1, 0]) - AdvancedIntegerMath.LogFactorial(this[1, 1]); // compute all possible 2 X 2 matrices with these row and column totals // compute the total probability of getting a matrix as or less probable than the measured one double P = 0.0; int[,] test = new int[2, 2]; int min = Math.Max(C[0] - R[1], 0); int max = Math.Min(R[0], C[0]); for (int i = min; i <= max; i++) { test[0, 0] = i; test[0, 1] = R[0] - i; test[1, 0] = C[0] - i; test[1, 1] = R[1] - test[1, 0]; double lnP = x - AdvancedIntegerMath.LogFactorial(test[0, 0]) - AdvancedIntegerMath.LogFactorial(test[0, 1]) - AdvancedIntegerMath.LogFactorial(test[1, 0]) - AdvancedIntegerMath.LogFactorial(test[1, 1]); if (lnP <= lnPc) { P += Math.Exp(lnP); } } // return the result return(new TestResult(P, new UniformDistribution(Interval.FromEndpoints(0.0, 1.0)))); }
/// <inheritdoc /> public override double ProbabilityMass(int k) { if (k < 0) { return(0.0); } else { // these are the same expression, but the form for small arguments is faster and the form for large arguments avoids overflow if (k < 16) { return(Math.Exp(-mu) * MoreMath.Pow(mu, k) / AdvancedIntegerMath.Factorial(k)); } else { return(Math.Exp( k * Math.Log(mu) - AdvancedIntegerMath.LogFactorial(k) - mu )); } } }
private void ComputeCounts() { // our results must fit into a long, which takes us up to about n~20 if (AdvancedIntegerMath.LogFactorial(n) > Math.Log(Int64.MaxValue)) { throw new InvalidOperationException(); } // pre-compute some values we will use int shiftIncrement = n * (n + 1) / 2; sMin = n * (n + 1) * (n + 2) / 6; sMax = n * (n + 1) * (2 * n + 1) / 6; //Console.WriteLine("sMin = {0} sMax = {1}", sMin, sMax); // to improve performance, we calculate counts only up to the middle bin and use symmetry to obtain the higher ones // the midpoint is of course (sMin+sMax)/2, but for even n this is a half-integer // since we want to include the middle bin but integer arithmetic rounds down, we // add one before halving; this gives the right maximum bin to compute for both even and odd n sMid = (sMin + sMax + 1) / 2; bool baseSign = (n % 2 == 0); // array to accumulate permanent polynomial coefficients // only the entries between sMin and sMax matter, as per (1) and (2) above long[] totalP = new long[sMid + 1]; // as per Ryser's formula, loop over all cominations of rows // use b as a length 2^n bitmask indicating which columns to include in a combination ulong bmax = (((ulong)1) << n); // skip b = 0 since that means include no columns, resulting in a zero contribution // skip even b values as we will compute their contributions by using (3) above // do the b=1 case specially // when only one column enters, the product over all rows is a simply the monomial x^{(c+1) n n(+1) / 2} // knowing this, we can avoid all the multiplication to get us there // all the one column cases are produced by left-shifting the b=1 case // only those for which the power lies in [sMin,sMax] need be recorded int p = shiftIncrement; while (p < sMin) { p += shiftIncrement; } while (p <= sMid) { totalP[p] += baseSign ? -1 : 1; //totalP[p] += 1; p += shiftIncrement; } // okay, now we are ready for the remaining combinations of columns for (ulong b = 3; b < bmax; b += 2) { //Console.WriteLine("k={0}", k); // compute the row sum polynomial for the first row // while we are at it, we compute the sign of the combination // and note the degree of the polynomial as well long[] productP = new long[n + 1]; //bool sign = true; bool sign = baseSign; int d = 0; for (int c = 0; c < n; c++) { ulong bp = ((ulong)1) << c; if ((b & bp) != 0) { /* productP[c + 1] = 1; */ productP[c] = 1; /* d = c + 1; */ d = c; sign = !sign; } } // row sum polynomials for higher rows are obtained from those from // the first row by increasing the stride. just continue to multiply // the product polynomial by each row sum polynomial to get the total // contribution of the set for (int r = 1; r < n; r++) { int da = productP.Length - 1; productP = LongPolynomialMultiply(productP, da, b, d, r + 1, sMid); } //LongPolynomialMath.Write(productP, 0, productP.Length-1); // add the contribution that particular k, // then the contributions from left-shifting it, // which as per (3) are obtained by shifting the powers by n(n+1)/2 // for each left-shift /* int shift = 0; */ int shift = shiftIncrement; ulong k2 = b; while (k2 < bmax) { LongPolynomialAdd(totalP, productP, shift, sMin, sMid, sign); k2 = k2 << 1; shift += shiftIncrement; } } // include the overall (-1)^n //if (n % 2 == 0) { // LongPolynomialNegate(totalP, sMin, sMid); //} counts = new long[sMid - sMin + 1]; Array.Copy(totalP, sMin, counts, 0, counts.Length); //for (int i = 0; i < counts.Length; i++) { // counts[i] = totalP[sMin + i]; //} //for (int i = sMin; i <= sMid; i++) { // Console.WriteLine("{0} {1}", i, totalP[i]); //} }
/// <summary> /// Performs a Fisher exact test. /// </summary> /// <returns>The results of the test. The test statistic is the summed probability of all tables exhibiting equal or stronger correlations, /// and its likelihood under the null hypothesis is the (left) probability to obtain a smaller value. Note that, in this case, the test /// statistic itself is the likelihood.</returns> /// <remarks><para>The Fisher exact test tests for correlations between row and column entries. It is a robust, non-parametric test, /// which, unlike the χ<sup>2</sup> test (see <see cref="ContingencyTable{R,C}.PearsonChiSquaredTest"/>), can safely be used for tables /// with small, even zero-valued, entries.</para> /// <para>The Fisher test computes, under the null hypothesis of no correlation, the exact probability of all 2 X 2 tables with the /// same row and column totals as the given table. It then sums the probabilities of all tables that are as or less probable than /// the given table. In this way it determines the total probability of obtaining a 2 X 2 table which is at least as improbable /// as the given one.</para> /// <para>The test is two-sided, i.e. when considering less probable tables it does not distinguish between tables exhibiting /// the same and the opposite correlation as the given one.</para> /// </remarks> /// <seealso href="http://en.wikipedia.org/wiki/Fisher_exact_test"/> public TestResult FisherExactTest() { // Store row and column totals int[] R = new int[] { counts[0, 0] + counts[0, 1], counts[1, 0] + counts[1, 1] }; int[] C = new int[] { counts[0, 0] + counts[1, 0], counts[0, 1] + counts[1, 1] }; int N = counts[0, 0] + counts[0, 1] + counts[1, 0] + counts[1, 1]; Debug.Assert(R[0] + R[1] == N); Debug.Assert(C[0] + C[1] == N); // Compute the critical probability of the measured matrix given the marginal totals // and the assumption of no association. double x = AdvancedIntegerMath.LogFactorial(R[0]) + AdvancedIntegerMath.LogFactorial(R[1]) + AdvancedIntegerMath.LogFactorial(C[0]) + AdvancedIntegerMath.LogFactorial(C[1]) - AdvancedIntegerMath.LogFactorial(N); double lnPc = x - AdvancedIntegerMath.LogFactorial(counts[0, 0]) - AdvancedIntegerMath.LogFactorial(counts[0, 1]) - AdvancedIntegerMath.LogFactorial(counts[1, 0]) - AdvancedIntegerMath.LogFactorial(counts[1, 1]); // Compute all possible 2 X 2 matrices with these row and column totals. // Find the total probability of getting a matrix as or less probable than the measured one. double P = 0.0; int[,] test = new int[2, 2]; int min = Math.Max(C[0] - R[1], 0); int max = Math.Min(R[0], C[0]); // If the relevant hypergeometric distribution over i is symmetric, then lnP for the i on // the other side is exactly equal to lnPc. But floating point noise means that it might come // in ever so slightly higher, causing us to incorrectly exclude that corresponding bin. This // caused a bug. So handle the symmetric case specially. if ((R[0] == R[1]) || (C[0] == C[1])) { double mid = (min + max) / 2.0; if (counts[0, 0] == mid) { P = 1.0; } else { int imax = (counts[0, 0] < mid) ? counts[0, 0] : min + (max - counts[0, 0]); for (int i = min; i <= imax; i++) { test[0, 0] = i; test[0, 1] = R[0] - i; test[1, 0] = C[0] - i; test[1, 1] = R[1] - test[1, 0]; double lnP = x - AdvancedIntegerMath.LogFactorial(test[0, 0]) - AdvancedIntegerMath.LogFactorial(test[0, 1]) - AdvancedIntegerMath.LogFactorial(test[1, 0]) - AdvancedIntegerMath.LogFactorial(test[1, 1]); P += Math.Exp(lnP); } P *= 2.0; Debug.Assert(P <= 1.0); } } else { for (int i = min; i <= max; i++) { test[0, 0] = i; test[0, 1] = R[0] - i; test[1, 0] = C[0] - i; test[1, 1] = R[1] - test[1, 0]; double lnP = x - AdvancedIntegerMath.LogFactorial(test[0, 0]) - AdvancedIntegerMath.LogFactorial(test[0, 1]) - AdvancedIntegerMath.LogFactorial(test[1, 0]) - AdvancedIntegerMath.LogFactorial(test[1, 1]); if (lnP <= lnPc) { P += Math.Exp(lnP); } } } return(new TestResult("P", P, new UniformDistribution(Interval.FromEndpoints(0.0, 1.0)), TestType.LeftTailed)); }