using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace BotSharp.Algorithm.HiddenMarkovModel.MathHelpers { public static class LogHelper { /// /// Computes log(1+x) without losing precision for small sample of x. /// /// /// /// References: /// - http://www.johndcook.com/csharp_log_one_plus_x.html /// /// public static double Log1p(double x) { if (x <= -1.0) return Double.NaN; if (System.Math.Abs(x) > 1e-4) return System.Math.Log(1.0 + x); // Use Taylor approx. log(1 + x) = x - x^2/2 with error roughly x^3/3 // Since |x| < 10^-4, |x|^3 < 10^-12, relative error less than 10^-8 return (-0.5 * x + 1.0) * x; } /// /// Computes x + y without losing precision using ln(x) and ln(y). /// /// public static double LogSum(double lna, double lnc) { if (lna == Double.NegativeInfinity) return lnc; if (lnc == Double.NegativeInfinity) return lna; if (lna > lnc) return lna + Log1p(System.Math.Exp(lnc - lna)); return lnc + Log1p(System.Math.Exp(lna - lnc)); } /// /// Computes x + y without losing precision using ln(x) and ln(y). /// /// public static double LogSum(float lna, float lnc) { if (lna == Single.NegativeInfinity) return lnc; if (lnc == Single.NegativeInfinity) return lna; if (lna > lnc) return lna + Log1p(System.Math.Exp(lnc - lna)); return lnc + Log1p(System.Math.Exp(lna - lnc)); } /// /// Elementwise Log operation. /// /// public static double[,] Log(this double[,] value) { int rows = value.GetLength(0); int cols = value.GetLength(1); double[,] r = new double[rows, cols]; for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) r[i, j] = System.Math.Log(value[i, j]); return r; } /// /// Elementwise Exp operation. /// /// public static double[,] Exp(this double[,] value) { int rows = value.GetLength(0); int cols = value.GetLength(1); double[,] r = new double[rows, cols]; for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) r[i, j] = System.Math.Exp(value[i, j]); return r; } /// /// Elementwise Exp operation. /// /// public static double[] Exp(this double[] value) { double[] r = new double[value.Length]; for (int i = 0; i < value.Length; i++) r[i] = System.Math.Exp(value[i]); return r; } /// /// Elementwise Log operation. /// /// public static double[] Log(this double[] value) { double[] r = new double[value.Length]; for (int i = 0; i < value.Length; i++) r[i] = System.Math.Log(value[i]); return r; } private static double[] lnfcache; /// /// Returns the log factorial of a number (ln(n!)) /// /// public static double LogFactorial(int n) { if (lnfcache == null) lnfcache = new double[101]; if (n < 0) { // GetFactorial is not defined for negative numbers. throw new ArgumentException("Argument cannot be negative.", "n"); } if (n <= 1) { // GetFactorial for n between 0 and 1 is 1, so log(factorial(n)) is 0. return 0.0; } if (n <= 100) { // Compute the factorial using ln(gamma(n)) approximation, using the cache // if the value has been previously computed. return (lnfcache[n] > 0) ? lnfcache[n] : (lnfcache[n] = Gamma.Log(n + 1.0)); } else { // Just compute the factorial using ln(gamma(n)) approximation. return Gamma.Log(n + 1.0); } } } }