private static Complex LogGamma_Stirling(Complex z) { // work in the upper complex plane; i think this isn't actually necessary //if (z.Im < 0.0) return (LogGamma_Stirling(z.Conjugate).Conjugate); Complex f = (z - 0.5) * ComplexMath.Log(z) - z + 0.5 * Math.Log(Global.TwoPI); // reduce f.Im modulo 2*PI // result is cyclic in f.Im modulo 2*PI, but if f.Im starts off too big, the corrections // applied below will be lost because they are being added to a big number //f = new Complex(f.Re, AdvancedMath.Reduce(f.Im, 0.0)); // we should still do an optional reduction by 2 pi so that we get phase of Gamma right even // for large z. Complex zz = ComplexMath.Sqr(z); Complex zp = z; for (int i = 1; i < AdvancedIntegerMath.Bernoulli.Length; i++) { Complex f_old = f; f += AdvancedIntegerMath.Bernoulli[i] / ((2 * i) * (2 * i - 1)) / zp; if (f == f_old) { return(f); } zp *= zz; } throw new NonconvergenceException(); }
private static Complex LogGamma_Stirling(Complex z) { // work in the upper complex plane; i think this isn't actually necessary if (z.Im < 0.0) { return(LogGamma_Stirling(z.Conjugate).Conjugate); } Complex f = (z - 0.5) * ComplexMath.Log(z) - z + Math.Log(Global.TwoPI) / 2.0; // reduce f.Im modulo 2*PI // result is cyclic in f.Im modulo 2*PI, but if f.Im starts off too big, the corrections // applied below will be lost because they are being added to a big number f = new Complex(f.Re, AdvancedMath.Reduce(f.Im, 0.0)); Complex zz = z * z; Complex zp = z; for (int i = 1; i < AdvancedIntegerMath.Bernoulli.Length; i++) { Complex f_old = f; f += AdvancedIntegerMath.Bernoulli[i] / (2 * i) / (2 * i - 1) / zp; if (f == f_old) { return(f); } zp *= zz; } throw new NonconvergenceException(); }
private static Complex DiLog_Series_1(Complex e) { Complex f = Math.PI * Math.PI / 6.0; if (e == 0.0) { return(f); } Complex L = ComplexMath.Log(e); Complex ek = 1.0; for (int k = 1; k < Global.SeriesMax; k++) { Complex f_old = f; ek *= e; Complex df = ek * (L - 1.0 / k) / k; f += df; if (f == f_old) { return(f); } } throw new NonconvergenceException(); }
public static Complex LogGamma(Complex z) { Complex t = z + LanczosGP; return( Math.Log(Global.SqrtTwoPI) + (z - 0.5) * ComplexMath.Log(t) - t + ComplexMath.Log(Sum(z)) ); }
/// <summary> /// Computes the complex dilogarithm function, also called Spence's function. /// </summary> /// <param name="z">The complex argument.</param> /// <returns>The value Li<sub>2</sub>(z).</returns> /// <remarks> /// <para>This function is the analytic continuation of the dilogarithm function (<see cref="AdvancedMath.DiLog"/>) into the complex plane.</para> /// <para>The image below shows the complex dilogarithm function near the origin, using domain coloring.</para> /// <img src="../images/ComplexDiLogPlot.png" /> /// </remarks> /// <seealso cref="AdvancedMath.DiLog"/> /// <seealso href="http://mathworld.wolfram.com/Dilogarithm.html" /> public static Complex DiLog(Complex z) { Complex f; double a0 = ComplexMath.Abs(z); if (a0 > 1.0) { // outside the unit disk, reflect into the unit disk Complex ln = ComplexMath.Log(-z); f = -Math.PI * Math.PI / 6.0 - ln * ln / 2.0 - DiLog(1.0 / z); } else { // inside the unit disk... if (a0 < 0.75) { // close to 0, use the expansion about zero f = DiLog_Series_0(z); } else { // we are in the annulus near the edge of the unit disk if (z.Re < 0.0) { // reflect negative into positive half-disk // this avoids problems with the log expansion near -1 f = DiLog(z * z) / 2.0 - DiLog(-z); } else { // figure out whether we are close to 1 Complex e = 1.0 - z; if (ComplexMath.Abs(e) < 0.5) { // close to 1, use the expansion about 1 f = DiLog_Series_1(e); } else { // otherwise, use the log expansion, which is good // near the unit circle but not too close to 1 or -1 f = DiLog_Log_Series(z); } } } } if ((z.Re > 1.0) && (Math.Sign(f.Im) != Math.Sign(z.Im))) { f = f.Conjugate; } return(f); }
public void TestLog() { Complex arg; Complex result; arg = new Complex(1 / 2.0, 1 / 3.0); Assert.AreEqual(0.6009252125773315488532035, arg.GetModulus(), 1e-15); result = ComplexMath.Log(arg); Assert.AreEqual(-0.5092847904972866327857336, result.Re, 1e-15); Assert.AreEqual(0.5880026035475675512456111, result.Im, 1e-15); }
private static Complex LogGammaReflectionTerm(Complex z) { // s will overflow for large z.Im, but we are taking its log, so we should // re-formulate this Complex s = new Complex(MoreMath.SinPi(z.Re) * Math.Cosh(Math.PI * z.Im), MoreMath.CosPi(z.Re) * Math.Sinh(Math.PI * z.Im)); Complex logs = ComplexMath.Log(s); double m1 = Math.Log(MoreMath.Hypot(MoreMath.SinPi(z.Re), Math.Sinh(Math.PI * z.Im))); double t = Math.Abs(Math.PI * z.Im); double c1 = Math.Exp(-2.0 * t); double c2 = MoreMath.SinPi(z.Re) / Math.Sinh(t); double m2 = t + MoreMath.LogOnePlus(-c1) + 0.5 * MoreMath.LogOnePlus(c2 * c2) - Math.Log(2.0); return(Math.Log(Math.PI) - logs); }
public void Log() { Complex cd1 = new Complex(1.1, -2.2); Complex cd2 = new Complex(0, -2.2); Complex cd3 = new Complex(1.1, 0); Complex cd4 = new Complex(-1.1, 2.2); ComplexFloat cf1 = new ComplexFloat(1.1f, -2.2f); ComplexFloat cf2 = new ComplexFloat(0, -2.2f); ComplexFloat cf3 = new ComplexFloat(1.1f, 0); ComplexFloat cf4 = new ComplexFloat(-1.1f, 2.2f); Complex cdt = ComplexMath.Log(cd1); Assert.AreEqual(cdt.Real, 0.900, TOLERENCE); Assert.AreEqual(cdt.Imag, -1.107, TOLERENCE); cdt = ComplexMath.Log(cd2); Assert.AreEqual(cdt.Real, 0.788, TOLERENCE); Assert.AreEqual(cdt.Imag, -1.571, TOLERENCE); cdt = ComplexMath.Log(cd3); Assert.AreEqual(cdt.Real, 0.0953, TOLERENCE); Assert.AreEqual(cdt.Imag, 0, TOLERENCE); cdt = ComplexMath.Log(cd4); Assert.AreEqual(cdt.Real, 0.900, TOLERENCE); Assert.AreEqual(cdt.Imag, 2.034, TOLERENCE); ComplexFloat cft = ComplexMath.Log(cf1); Assert.AreEqual(cft.Real, 0.900, TOLERENCE); Assert.AreEqual(cft.Imag, -1.107, TOLERENCE); cft = ComplexMath.Log(cf2); Assert.AreEqual(cft.Real, 0.788, TOLERENCE); Assert.AreEqual(cft.Imag, -1.571, TOLERENCE); cft = ComplexMath.Log(cf3); Assert.AreEqual(cft.Real, 0.095, TOLERENCE); Assert.AreEqual(cft.Imag, 0, TOLERENCE); cft = ComplexMath.Log(cf4); Assert.AreEqual(cft.Real, 0.900, TOLERENCE); Assert.AreEqual(cft.Imag, 2.034, TOLERENCE); }
/// <summary> /// Compute the complex log Gamma function. /// </summary> /// <param name="z">The complex argument.</param> /// <returns>The principal complex value y for which exp(y) = Γ(z).</returns> /// <seealso cref="AdvancedMath.LogGamma" /> /// <seealso href="http://mathworld.wolfram.com/LogGammaFunction.html"/> public static Complex LogGamma(Complex z) { if (z.Im == 0.0 && z.Re < 0.0) { // Handle the pure negative case explicitly. double re = Math.Log(Math.PI / Math.Abs(MoreMath.SinPi(z.Re))) - AdvancedMath.LogGamma(1.0 - z.Re); double im = Math.PI * Math.Floor(z.Re); return(new Complex(re, im)); } else if (z.Re > 16.0 || Math.Abs(z.Im) > 16.0) { // According to https://dlmf.nist.gov/5.11, the Stirling asymptoic series is valid everywhere // except on the negative real axis. So at first I tried to use it for |z.Im| > 0, |z| > 16. But in practice, // it exhibits false convergence close to the negative real axis, e.g. z = -16 + i. So I have // moved to requiring |z| large and reasonably far from the negative real axis. return(Stirling.LogGamma(z)); } else if (z.Re >= 0.125) { return(Lanczos.LogGamma(z)); } else { // For the remaining z < 0, we need to use the reflection formula. // For large z.Im, SinPi(z) \propto e^{\pi |z.Im|} overflows even though its log does not. // It's possible to do some algebra to get around that problem, but it's not necessary // because for z.Im that big we would have used the Stirling series. Complex f = ComplexMath.Log(Math.PI / ComplexMath.SinPi(z)); Complex g = Lanczos.LogGamma(1.0 - z); // The reflection formula doesn't stay on the principal branch, so we need to add a multiple of 2 \pi i // to fix it up. See Hare, "Computing the Principal Branch of Log Gamma" for how to do this. // https://pdfs.semanticscholar.org/1c9d/8865836a312836500126cb47c3cbbed3043e.pdf Complex h = new Complex(0.0, 2.0 * Math.PI * Math.Floor(0.5 * (z.Re + 0.5))); if (z.Im < 0.0) { h = -h; } return(f - g + h); } }
private static Complex DiLog_Log_Series(Complex z) { Complex ln = ComplexMath.Log(z); Complex ln2 = ln * ln; Complex f = Math.PI * Math.PI / 6.0 + ln * (1.0 - ComplexMath.Log(-ln)) - ln2 / 4.0; Complex p = ln; for (int k = 1; k < AdvancedIntegerMath.Bernoulli.Length; k++) { Complex f_old = f; p *= ln2 / (2 * k + 1) / (2 * k); f += (-AdvancedIntegerMath.Bernoulli[k] / (2 * k)) * p; if (f == f_old) { return(f); } } throw new NonconvergenceException(); }
public void EvaluateGradient(double[] X, double[] P, double[][] DY) { double x = X[0]; if (_useFrequencyInsteadOmega) { x *= (2 * Math.PI); } DY[0][0] = 1; DY[1][0] = 0; Complex OneByDenom = 1 / ComplexMath.Pow(1 + ComplexMath.Pow(Complex.I * x * P[2], P[3]), P[4]); DY[0][1] = OneByDenom.Re; DY[1][1] = -OneByDenom.Im; Complex IXP2 = Complex.I * x * P[2]; Complex IXP2PowP3 = ComplexMath.Pow(IXP2, P[3]); Complex der2 = OneByDenom * -P[1] * P[2] * P[4] * IXP2PowP3 / (P[2] * (1 + IXP2PowP3)); DY[0][2] = der2.Re; DY[1][2] = -der2.Im; Complex der3 = OneByDenom * -P[1] * P[4] * IXP2PowP3 *ComplexMath.Log(IXP2) / (1 + IXP2PowP3); DY[0][3] = der3.Re; DY[1][3] = -der3.Im; Complex der4 = OneByDenom * -P[1] * ComplexMath.Log(1 + IXP2PowP3); DY[0][4] = der4.Re; DY[1][4] = -der4.Im; if (_useFlowTerm) { DY[0][5] = 0; DY[1][5] = _isDielectricData ? 1 / (x * 8.854187817e-12) : 1 / x; } }
/// <summary> /// Computes the entire complex exponential integral. /// </summary> /// <param name="z">The complex argument.</param> /// <returns>The value of Ein(z).</returns> /// <remarks> /// <para>The entire exponential integral function can be defined by an integral or an equivalent series.</para> /// <img src="..\images\EinIntegralSeries.png" /> /// <para>Both Ei(x) and E<sub>1</sub>(z) can be obtained from Ein(z).</para> /// <img src="..\images\E1EiEinRelation.png" /> /// <para>Unlike either Ei(x) or E<sub>1</sub>(z), Ein(z) is entire, that is, it has no poles or cuts anywhere /// in the complex plane.</para> /// </remarks> /// <seealso cref="AdvancedMath.IntegralEi(double)"/> /// <seealso cref="AdvancedMath.IntegralE(int, double)"/> public static Complex Ein(Complex z) { if (z.Re < -40.0) { // For sufficiently negative z, use the asymptotic expansion for Ei return(AdvancedMath.EulerGamma + ComplexMath.Log(-z) - IntegralEi_AsymptoticSeries(-z)); } else { // Ideally, we would like to use the series within some simple radius of the origin and the continued fraction // outside it. That works okay for the right half-plane, but for z.Re < 0 we have to contend with the fact that the // continued fraction fails on or near the negative real axis. That makes us use the series in an oddly shaped // region that extends much further from the origin than we would like into the left half-plane. It would be // great if we could find an alternative approach in that region. if (IsEinSeriesPrefered(z)) { return(Ein_Series(z)); } else { return(AdvancedMath.EulerGamma + ComplexMath.Log(z) + IntegeralE1_ContinuedFraction(z)); } } }
public static Complex LogGamma(Complex z) { return((z - 0.5) * ComplexMath.Log(z) - z + halfLogTwoPi + Sum(z)); }
public static Complex Psi(Complex z) { Complex t = z + LanczosGP; return(ComplexMath.Log(t) - LanczosG / t + LogSumPrime(z)); }