public void TestExp() { Complex result; result = ComplexMath.Exp(new Complex(0.5, 0.5)); Assert.AreEqual(1.446889036584169158051583, result.Re, 1e-15); Assert.AreEqual(0.7904390832136149118432626, result.Im, 1e-15); }
/// <summary> /// Computes the complex Gamma function. /// </summary> /// <param name="z">The complex argument.</param> /// <returns>The complex value of Γ(z).</returns> /// <remarks> /// <para>The image below shows the complex Γ function near the origin using domain coloring.</para> /// <img src="../images/ComplexGammaPlot.png" /> /// </remarks> /// <seealso cref="AdvancedMath.Gamma(double)"/> /// <seealso href="http://en.wikipedia.org/wiki/Gamma_function" /> /// <seealso href="http://mathworld.wolfram.com/GammaFunction.html" /> public static Complex Gamma(Complex z) { if (z.Re < 0.5) { // 1-z form return(Math.PI / Gamma(1.0 - z) / ComplexMath.Sin(Math.PI * z)); // -z form //return (-Math.PI / Gamma(-z) / z / ComplexMath.Sin(Math.PI * z)); } return(ComplexMath.Exp(LogGamma(z))); }
private static Complex IntegralEi_AsymptoticSeries(Complex z) { Complex dy = 1.0; Complex y = dy; for (int k = 1; k < Global.SeriesMax; k++) { Complex y_old = y; dy = dy * k / z; y = y_old + dy; if (y == y_old) { return(ComplexMath.Exp(z) / z * y); } } throw new NonconvergenceException(); }
/// <summary> /// Computes the complex Gamma function. /// </summary> /// <param name="z">The complex argument.</param> /// <returns>The complex value of Γ(z).</returns> /// <remarks> /// <para>The image below shows the complex Γ function near the origin using domain coloring.</para> /// <img src="../images/ComplexGammaPlot.png" /> /// </remarks> /// <seealso cref="AdvancedMath.Gamma(double)"/> /// <seealso href="http://en.wikipedia.org/wiki/Gamma_function" /> /// <seealso href="http://mathworld.wolfram.com/GammaFunction.html" /> public static Complex Gamma(Complex z) { if (z.Re < 0.5) { // 1-z form return(Math.PI / Gamma(1.0 - z) / ComplexMath.SinPi(z)); } else if (ComplexMath.Abs(z) < 16.0) { return(Lanczos.Gamma(z)); } else { // Add flag to do z-reduction return(ComplexMath.Exp(LogGamma_Stirling(z))); } }
/// <summary> /// Computes the complex Faddeeva function. /// </summary> /// <param name="z">The complex argument.</param> /// <returns>The complex value of w(z).</returns> /// <remarks> /// <para>The Faddeeva function w(z) is related to the error function with a complex argument.</para> /// <img src="../images/FaddeevaErfcRelation.png" /> /// <para>It also has an integral representation.</para> /// <img src="../images/FaddeevaIntegral.png" /> /// <para>For purely imaginary values, it reduces to the complementary error function (<see cref="AdvancedMath.Erfc"/>). /// For purely real values, it reduces to Dawson's integral (<see cref="AdvancedMath.Dawson"/>).</para> /// <para>It appears in the computation of the Voigt line profile function V(x;σ,γ).</para> /// <img src="../images/Voigt.png" /> /// <para>Near the origin, w(z) ≈ 1. To accurately determine w(z) - 1 in this region, use the <see cref="Erf"/> /// function. Away from the origin near the large negative imaginary axis, the magnitude w(z) increases rapidly and /// may overflow.</para> /// <para>The image below shows the complex Faddeeva function near the origin, using domain coloring.</para> /// <img src="../images/ComplexFaddeevaPlot.png" /> /// </remarks> /// <seealso cref="AdvancedComplexMath.Erf"/> /// <seealso cref="AdvancedMath.Erf" /> /// <seealso cref="AdvancedMath.Erfc" /> /// <seealso cref="AdvancedMath.Dawson"/> /// <seealso href="http://en.wikipedia.org/wiki/Voigt_profile" /> public static Complex Faddeeva(Complex z) { // use reflection formulae to ensure that we are in the first quadrant if (z.Im < 0.0) { return(2.0 * ComplexMath.Exp(-z * z) - Faddeeva(-z)); } if (z.Re < 0.0) { return(Faddeeva(-z.Conjugate).Conjugate); } double r = ComplexMath.Abs(z); if (r < 2.0) { // use series for small z return(ComplexMath.Exp(-z * z) * (1.0 - Erf_Series(-ComplexMath.I * z))); //return (Faddeeva_Series(z)); } else if ((z.Im < 0.1) && (z.Re < 30.0)) { // this is a special, awkward region // along the real axis, Re{w(x)} ~ e^{-x^2}; the Weideman algorthm doesen't compute this small number // well and the Laplace continued fraction misses it entirely; therefore very close to the real axis // we will use an analytic result on the real axis and Taylor expand to where we need to go. // unfortunately the Taylor expansion converges poorly for large x, so we drop this work-arround near x~30, // when this real part becomes too small to represent as a double anyway double x = z.Re; double y = z.Im; return(Faddeeva_Taylor(new Complex(x, 0.0), Math.Exp(-x * x) + 2.0 * AdvancedMath.Dawson(x) / Global.SqrtPI * ComplexMath.I, new Complex(0.0, y))); } else if (r > 7.0) { // use Laplace continued fraction for large z return(Faddeeva_ContinuedFraction(z)); } else { // use Weideman algorithm for intermediate region return(Faddeeva_Weideman(z)); } }
public void Exp() { 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.Exp(cd1); Assert.AreEqual(cdt.Real, -1.768, TOLERENCE); Assert.AreEqual(cdt.Imag, -2.429, TOLERENCE); cdt = ComplexMath.Exp(cd2); Assert.AreEqual(cdt.Real, -0.589, TOLERENCE); Assert.AreEqual(cdt.Imag, -0.808, TOLERENCE); cdt = ComplexMath.Exp(cd3); Assert.AreEqual(cdt.Real, 3.004, TOLERENCE); Assert.AreEqual(cdt.Imag, 0, TOLERENCE); cdt = ComplexMath.Exp(cd4); Assert.AreEqual(cdt.Real, -0.196, TOLERENCE); Assert.AreEqual(cdt.Imag, 0.269, TOLERENCE); ComplexFloat cft = ComplexMath.Exp(cf1); Assert.AreEqual(cft.Real, -1.768, TOLERENCE); Assert.AreEqual(cft.Imag, -2.429, TOLERENCE); cft = ComplexMath.Exp(cf2); Assert.AreEqual(cft.Real, -0.589, TOLERENCE); Assert.AreEqual(cft.Imag, -0.808, TOLERENCE); cft = ComplexMath.Exp(cf3); Assert.AreEqual(cft.Real, 3.004, TOLERENCE); Assert.AreEqual(cft.Imag, 0, TOLERENCE); cft = ComplexMath.Exp(cf4); Assert.AreEqual(cft.Real, -0.196, TOLERENCE); Assert.AreEqual(cft.Imag, 0.269, TOLERENCE); }
// This continued fraction is valid for |z| >> 1, except close to the negative real axis. private static Complex IntegeralE1_ContinuedFraction(Complex z) { int a = 1; // a_1 Complex b = z + 1.0; // b_1 Complex D = 1.0 / b; // D_1 = 1 / b_1 (denominator of a_1 / b_1) Complex Df = a / b; // Df_1 = f_1 - f_0 = a_1 / b_1 Complex f = 0.0 + Df; // f_1 = f_0 + Df_1 = b_0 + a_1 / b_1 (here b_0 = 0) for (int k = 1; k < Global.SeriesMax; k++) { Complex f_old = f; a = -k * k; b += 2.0; D = 1.0 / (b + a * D); Df = (b * D - 1.0) * Df; f += Df; if (f == f_old) { return(ComplexMath.Exp(-z) * f); } } throw new NonconvergenceException(); }
public static Complex AiryAi_Asymptotic(Complex z) { Debug.Assert(ComplexMath.Abs(z) >= 9.0); if (z.Re >= 0.0) { Complex xi = 2.0 / 3.0 * ComplexMath.Pow(z, 3.0 / 2.0); Airy_Asymptotic_Subseries(xi, out Complex u0, out Complex v0, out Complex u1, out Complex v1); Complex e = ComplexMath.Exp(xi); Complex q = ComplexMath.Pow(z, 1.0 / 4.0); return(0.5 / Global.SqrtPI / q / e * u1); } else { z = -z; Complex xi = 2.0 / 3.0 * ComplexMath.Pow(z, 3.0 / 2.0); Airy_Asymptotic_Subseries(xi, out Complex u0, out Complex v0, out Complex u1, out Complex v1); Complex c = ComplexMath.Cos(xi); Complex s = ComplexMath.Sin(xi); throw new NotImplementedException(); } }
/// <summary> /// Computes the complex error function. /// </summary> /// <param name="z">The complex argument.</param> /// <returns>The value of erf(z).</returns> /// <remarks> /// <para>This function is the analytic continuation of the error function (<see cref="AdvancedMath.Erf"/>) to the complex plane.</para> /// <para>The image below shows the complex error function near the origin, using domain coloring.</para> /// <img src="../images/ComplexErfPlot.png" /> /// <para>The complex error function is entire: it has no poles, cuts, or discontinuities anywhere in the complex plane.</para> /// <para>For pure imaginary arguments, erf(z) reduces to the Dawson integral (<see cref="AdvancedMath.Dawson"/>).</para> /// <para>Away from the origin near the real axis, the real part of erf(z) quickly approaches ±1. To accurately determine /// the small difference erf(z) ∓ 1 in this region, use the <see cref="Faddeeva"/> function. Away from the origin near /// the imaginary axis, the magnitude of erf(z) increases very quickly. Although erf(z) may overflow in this region, you /// can still accurately determine the value of the product erf(z) exp(z<sup>2</sup>) using the <see cref="Faddeeva"/> /// function.</para> /// </remarks> /// <seealso cref="AdvancedMath.Erf"/> /// <seealso cref="AdvancedMath.Dawson"/> /// <seealso cref="AdvancedComplexMath.Faddeeva"/> public static Complex Erf(Complex z) { double r = ComplexMath.Abs(z); if (r < 4.0) { // near the origin, use the series return(Erf_Series(z)); } else { // otherwise, just compute from Faddeva if (z.Re < 0.0) { // since Fadddeeva blows up for negative z.Re, use erf(z) = -erf(-z) return(ComplexMath.Exp(-z * z) * Faddeeva(-ComplexMath.I * z) - 1.0); } else { return(1.0 - ComplexMath.Exp(-z * z) * Faddeeva(ComplexMath.I * z)); } // we don't do this near the origin beause we would loose accuracy in the very small real parts there by subtracting from 1 } }
private static Complex ComplexTestFunction( double x ) { return(ComplexMath.Exp(new Complex(-x, -x))); }