public void EllipticNomeAgreement() { foreach (double k in TestUtilities.GenerateRealValues(1.0E-2, 1.0, 4)) { double K = AdvancedMath.EllipticK(k); double k1 = Math.Sqrt((1.0 - k) * (1.0 + k)); double K1 = AdvancedMath.EllipticK(k1); double q = AdvancedMath.EllipticNome(k); // For k << 1, this test fails if formulated as q = e^{\pi K' / K}. // Problem is that computation of k' looses some digits to cancellation, // then K' is near singularity so error is amplified, then // exp function amplifies error some more. Investigation indicates // that q agrees with Mathematica to nearly all digits, so problem // isn't in nome function. Run test in log space and don't let k get // extremely small. Assert.IsTrue(TestUtilities.IsNearlyEqual( Math.Log(q), -Math.PI * K1 / K )); } }
public void JacobiIntegrals() { foreach (double k in TestUtilities.GenerateRealValues(1.0E-1, 1.0, 4)) { // DLMF 22.14.18 says // \int_{0}^{K(k)} \ln(\dn(t, k)) \, dt = \frac{1}{2} K(k) \ln k' double K = AdvancedMath.EllipticK(k); double k1 = Math.Sqrt(1.0 - k * k); double I1 = FunctionMath.Integrate( x => Math.Log(AdvancedMath.JacobiDn(x, k)), Interval.FromEndpoints(0.0, K) ); Assert.IsTrue(TestUtilities.IsNearlyEqual(I1, K / 2.0 * Math.Log(k1))); // If k is small, log(k1) is subject to cancellation error, so don't pick k too small. // It also gives values for the analogous integrals with \sn and \cn, // but their values involve K'(k), for which we don't have a method. // Mathworld (http://mathworld.wolfram.com/CompleteEllipticIntegraloftheSecondKind.html) says // \int_{0}^{K(k)} \dn^2(t, k) = E(k) double I2 = FunctionMath.Integrate( u => MoreMath.Sqr(AdvancedMath.JacobiDn(u, k)), Interval.FromEndpoints(0.0, K) ); Assert.IsTrue(TestUtilities.IsNearlyEqual(I2, AdvancedMath.EllipticE(k))); } }
public void EllipticKCatalanIntegral() { System.Diagnostics.Stopwatch timer = System.Diagnostics.Stopwatch.StartNew(); Interval i = Interval.FromEndpoints(0.0, 1.0); // http://mathworld.wolfram.com/CatalansConstant.html equation 37 Func <double, double> f1 = delegate(double k) { return(AdvancedMath.EllipticK(k)); }; Assert.IsTrue(TestUtilities.IsNearlyEqual( FunctionMath.Integrate(f1, i), 2.0 * AdvancedMath.Catalan )); Func <double, double> f2 = delegate(double k) { return(AdvancedMath.EllipticK(k) * k); }; Assert.IsTrue(TestUtilities.IsNearlyEqual( FunctionMath.Integrate(f2, i), 1.0 )); timer.Stop(); Console.WriteLine(timer.ElapsedTicks); }
public void EllipticLandenTransform() { foreach (double k in TestUtilities.GenerateUniformRealValues(0.0, 1.0, 8)) { double kp = Math.Sqrt(1.0 - k * k); double k1 = (1.0 - kp) / (1.0 + kp); // For complete 1st kind Assert.IsTrue(TestUtilities.IsNearlyEqual( AdvancedMath.EllipticK(k), (1.0 + k1) * AdvancedMath.EllipticK(k1) )); // For complete 2nd kind Assert.IsTrue(TestUtilities.IsNearlyEqual( AdvancedMath.EllipticE(k) + kp * AdvancedMath.EllipticK(k), (1.0 + kp) * AdvancedMath.EllipticE(k1) )); /* * foreach (double t in TestUtilities.GenerateUniformRealValues(0.0, Math.PI / 2.0, 4)) { * * double s = Math.Sin(t); * double t1 = Math.Asin((1.0 + kp) * s * Math.Sqrt(1.0 - s * s) / Math.Sqrt(1.0 - k * k * s * s)); * * // Since t1 can be > pi/2, we need to handle a larger range of angles before we can test the incomplete cases * * * } * */ } }
public void EllipticKIntegrals() { Interval i = Interval.FromEndpoints(0.0, 1.0); // http://mathworld.wolfram.com/CatalansConstant.html equation 37 Assert.IsTrue(TestUtilities.IsNearlyEqual( FunctionMath.Integrate( k => AdvancedMath.EllipticK(k), Interval.FromEndpoints(0.0, 1.0)), 2.0 * AdvancedMath.Catalan )); Assert.IsTrue(TestUtilities.IsNearlyEqual( FunctionMath.Integrate( k => AdvancedMath.EllipticK(k) * k, Interval.FromEndpoints(0.0, 1.0)), 1.0 )); Assert.IsTrue(TestUtilities.IsNearlyEqual( FunctionMath.Integrate( k => AdvancedMath.EllipticK(k) / (1.0 + k), Interval.FromEndpoints(0.0, 1.0)), Math.PI * Math.PI / 8.0 )); }
public void JacobiPeriodic() { double u = 3.1; double k = 0.2; double K = AdvancedMath.EllipticK(k); Assert.IsTrue(TestUtilities.IsNearlyEqual( AdvancedMath.JacobiSn(u, k), -AdvancedMath.JacobiSn(u + 2.0 * K, k) )); Assert.IsTrue(TestUtilities.IsNearlyEqual( AdvancedMath.JacobiSn(u, k), AdvancedMath.JacobiSn(u + 4.0 * K, k) )); Assert.IsTrue(TestUtilities.IsNearlyEqual( AdvancedMath.JacobiCn(u, k), -AdvancedMath.JacobiCn(u + 2.0 * K, k) )); Assert.IsTrue(TestUtilities.IsNearlyEqual( AdvancedMath.JacobiCn(u, k), AdvancedMath.JacobiCn(u + 4.0 * K, k) )); Assert.IsTrue(TestUtilities.IsNearlyEqual( AdvancedMath.JacobiDn(u, k), AdvancedMath.JacobiDn(u + 2.0 * K, k) )); }
public void EllipticKInequality() { // DLMF 19.9.1 foreach (double k in TestUtilities.GenerateUniformRealValues(0.0, 1.0, 4)) { double S = AdvancedMath.EllipticK(k) + Math.Log(Math.Sqrt(1.0 - k * k)); Assert.IsTrue((Math.Log(4.0) <= S) && (S <= Math.PI / 2.0)); } }
public void EllipticFCompleteAgreement() { foreach (double k in TestUtilities.GenerateRealValues(0.01, 1.0, 8)) { Assert.IsTrue(TestUtilities.IsNearlyEqual( AdvancedMath.EllipticF(Math.PI / 2.0, k), AdvancedMath.EllipticK(k) )); } }
public void EllipticLegendreRelation() { foreach (double k in TestUtilities.GenerateRealValues(0.01, 1.0, 8)) { double kp = Math.Sqrt(1.0 - k * k); double E = AdvancedMath.EllipticE(k); double EP = AdvancedMath.EllipticE(kp); double K = AdvancedMath.EllipticK(k); double KP = AdvancedMath.EllipticK(kp); Assert.IsTrue(TestUtilities.IsNearlyEqual(K * EP + KP * E, Math.PI / 2.0 + K * KP)); } }
public void CompleteEllipticHypergeometricAgreement() { foreach (double k in TestUtilities.GenerateRealValues(1.0E-4, 1.0, 4)) { Assert.IsTrue(TestUtilities.IsNearlyEqual( AdvancedMath.EllipticK(k), Math.PI / 2.0 * AdvancedMath.Hypergeometric2F1(0.5, 0.5, 1.0, k * k) )); Assert.IsTrue(TestUtilities.IsNearlyEqual( AdvancedMath.EllipticE(k), Math.PI / 2.0 * AdvancedMath.Hypergeometric2F1(0.5, -0.5, 1.0, k * k) )); } }
public void EllipticFDisplacement() { foreach (int m in TestUtilities.GenerateUniformIntegerValues(-100, 100, 4)) { foreach (double phi in TestUtilities.GenerateUniformRealValues(-Math.PI / 2.0, +Math.PI / 2.0, 4)) { foreach (double k in TestUtilities.GenerateRealValues(1.0E-4, 1.0, 4)) { Assert.IsTrue(TestUtilities.IsNearlyEqual( AdvancedMath.EllipticF(m * Math.PI + phi, k), 2 * m * AdvancedMath.EllipticK(k) + AdvancedMath.EllipticF(phi, k) )); } } } }
public void EllipticKIntegration() { // The defining integral for K Interval i = Interval.FromEndpoints(0.0, Math.PI / 2.0); foreach (double k in TestUtilities.GenerateRealValues(0.01, 1.0, 8)) { Func <double, double> f = delegate(double t) { double z = k * Math.Sin(t); return(1.0 / Math.Sqrt(1.0 - z * z)); }; Assert.IsTrue(TestUtilities.IsNearlyEqual( FunctionMath.Integrate(f, i), AdvancedMath.EllipticK(k) )); } }
public void OdePendulum() { // Without the small-angle approximation, the period of a pendulum is not // independent of angle. Instead, it is given by // P = 4 K(\sin(\phi_0) / 2) // where \phi_0 is the release angle and K is the complete elliptic function // of the first kind. // See https://en.wikipedia.org/wiki/Pendulum_(mathematics) // We compute for an initial angle of 45 degrees, far beyond a small angle. Func <double, double, double> rhs = (double t, double u) => - MoreMath.Sin(u); double u0 = Math.PI / 4.0; double p = 4.0 * AdvancedMath.EllipticK(MoreMath.Sin(u0 / 2.0)); OdeResult r = FunctionMath.IntegrateConservativeOde(rhs, 0.0, u0, 0.0, p); Assert.IsTrue(TestUtilities.IsNearlyEqual(r.Y, u0)); Console.WriteLine(r.EvaluationCount); }
public void JacobiSpecial() { // Verify values at u = 0, K/2, K given in DMLF 22.5i and A&S double k = 1.0 / 3.1; double k1 = Math.Sqrt(1.0 - k * k); double K = AdvancedMath.EllipticK(k); // u = 0 Assert.IsTrue(TestUtilities.IsNearlyEqual(AdvancedMath.JacobiSn(0.0, k), 0.0)); Assert.IsTrue(TestUtilities.IsNearlyEqual(AdvancedMath.JacobiCn(0.0, k), 1.0)); Assert.IsTrue(TestUtilities.IsNearlyEqual(AdvancedMath.JacobiDn(0.0, k), 1.0)); // u = K / 2 Assert.IsTrue(TestUtilities.IsNearlyEqual(AdvancedMath.JacobiSn(K / 2.0, k), 1.0 / Math.Sqrt(1.0 + k1))); Assert.IsTrue(TestUtilities.IsNearlyEqual(AdvancedMath.JacobiCn(K / 2.0, k), Math.Sqrt(k1 / (1.0 + k1)))); Assert.IsTrue(TestUtilities.IsNearlyEqual(AdvancedMath.JacobiDn(K / 2.0, k), Math.Sqrt(k1))); // u = K Assert.IsTrue(TestUtilities.IsNearlyEqual(AdvancedMath.JacobiSn(K, k), 1.0)); Assert.IsTrue(TestUtilities.IsNearlyEqual(AdvancedMath.JacobiCn(K, k), 0.0)); Assert.IsTrue(TestUtilities.IsNearlyEqual(AdvancedMath.JacobiDn(K, k), k1)); }
public void EllipticKExtremeValues() { Assert.IsTrue(TestUtilities.IsNearlyEqual(AdvancedMath.EllipticK(Double.Epsilon), Math.PI / 2.0)); Assert.IsTrue(AdvancedMath.EllipticK(1.0 - 2.0E-16) < Double.PositiveInfinity); Assert.IsTrue(Double.IsNaN(AdvancedMath.EllipticK(Double.NaN))); }
public void OdeEuler() { // Euler's equations for rigid body motion (without external forces) are // I_1 \dot{\omega}_1 = (I_2 - I_3) \omega_2 \omega_3 // I_2 \dot{\omega}_2 = (I_3 - I_1) \omega_3 \omega_1 // I_3 \dot{\omega}_3 = (I_1 - I_2) \omega_1 \omega_2 // where \omega's are rotations about the principal axes and I's are the moments // of inertia about those axes. The rotational kinetic energy // I_1 \omega_1^2 + I_2 \omega_2^2 + I_3 \omega_3^2 = 2 E // and angular momentum // I_1^2 \omega_1^2 + I_2^2 \omega_2^2 + I_3^2 \omega_3^2 = L^2 // are conserved quantities. // Landau & Lifshitz, Mechanics (3rd edition) solves these equations. // Note // (\cn u)' = - (\sn u)(\dn u) // (\sn u)' = (\dn u)(\cn u) // (\dn u)' = -k^2 (\cn u)(\sn u) // So a solution with \omega_1 ~ \cn, \omega_2 ~ \sn, \omega_3 ~ \dn // would seem to work, if we can re-scale variables to eliminate factors. // In fact, this turns out to be the case. // Label axis so that I_3 > I_2 > I_1. If L^2 > 2 E I_2, define // v^2 = \frac{(I_3 - I_2)(L^2 - 2E I_1)}{I_1 I_2 I_3} // k^2 = \frac{(I_2 - I_1)(2E I_3 - L^2)}{(I_3 - I_2)(L^2 - 2E I_1)} // A_1^2 = \frac{2E I_3 - L^2}{I_1 (I_3 - I_1)} // A_2^2 = \frac{2E I_3 - L^2}{I_2 (I_3 - I_2)} // A_3^2 = \frac{L^2 - 2E I_1}{I_3 (I_3 - I_1)} // (If L^2 < 2 E I_2, just switch subscripts 1 <-> 3). Then // \omega_1 = A_1 \cn (c t, k) // \omega_2 = A_2 \sn (c t, k) // \omega_3 = A_3 \dn (c t, k) // Period is complete when v T = 4 K. ColumnVector I = new ColumnVector(3.0, 4.0, 5.0); Func <double, IReadOnlyList <double>, IReadOnlyList <double> > rhs = (double t, IReadOnlyList <double> w) => { return(new ColumnVector((I[1] - I[2]) * w[1] * w[2] / I[0], (I[2] - I[0]) * w[2] * w[0] / I[1], (I[0] - I[1]) * w[0] * w[1] / I[2])); }; ColumnVector w0 = new ColumnVector(6.0, 0.0, 1.0); // Determine L^2 and 2 E double L2 = EulerAngularMomentum(I, w0); double E2 = EulerKineticEnergy(I, w0); // As Landau points out, these inequalities should be ensured by the definitions of E and L. Debug.Assert(E2 * I[0] < L2); Debug.Assert(L2 < E2 * I[2]); double v, k; Func <double, ColumnVector> sln; if (L2 > E2 * I[1]) { v = Math.Sqrt((I[2] - I[1]) * (L2 - E2 * I[0]) / I[0] / I[1] / I[2]); k = Math.Sqrt((I[1] - I[0]) * (E2 * I[2] - L2) / (I[2] - I[1]) / (L2 - E2 * I[0])); ColumnVector A = new ColumnVector( Math.Sqrt((E2 * I[2] - L2) / I[0] / (I[2] - I[0])), Math.Sqrt((E2 * I[2] - L2) / I[1] / (I[2] - I[1])), Math.Sqrt((L2 - E2 * I[0]) / I[2] / (I[2] - I[0])) ); sln = t => new ColumnVector( A[0] * AdvancedMath.JacobiCn(v * t, k), A[1] * AdvancedMath.JacobiSn(v * t, k), A[2] * AdvancedMath.JacobiDn(v * t, k) ); } else { v = Math.Sqrt((I[0] - I[1]) * (L2 - E2 * I[2]) / I[0] / I[1] / I[2]); k = Math.Sqrt((I[1] - I[2]) * (E2 * I[0] - L2) / (I[0] - I[1]) / (L2 - E2 * I[2])); ColumnVector A = new ColumnVector( Math.Sqrt((L2 - E2 * I[2]) / I[0] / (I[0] - I[2])), Math.Sqrt((E2 * I[0] - L2) / I[1] / (I[0] - I[1])), Math.Sqrt((E2 * I[0] - L2) / I[2] / (I[0] - I[2])) ); sln = t => new ColumnVector( A[0] * AdvancedMath.JacobiDn(v * t, k), A[1] * AdvancedMath.JacobiSn(v * t, k), A[2] * AdvancedMath.JacobiCn(v * t, k) ); } Debug.Assert(k < 1.0); double T = 4.0 * AdvancedMath.EllipticK(k) / v; int listenerCount = 0; MultiOdeSettings settings = new MultiOdeSettings() { Listener = (MultiOdeResult q) => { listenerCount++; // Verify that energy and angular momentum conservation is respected Assert.IsTrue(TestUtilities.IsNearlyEqual(EulerKineticEnergy(I, q.Y), E2, q.Settings)); Assert.IsTrue(TestUtilities.IsNearlyEqual(EulerAngularMomentum(I, q.Y), L2, q.Settings)); // Verify that the result agrees with the analytic solution //ColumnVector YP = new ColumnVector( // A[0] * AdvancedMath.JacobiCn(v * q.X, k), // A[1] * AdvancedMath.JacobiSn(v * q.X, k), // A[2] * AdvancedMath.JacobiDn(v * q.X, k) //); Assert.IsTrue(TestUtilities.IsNearlyEqual(q.Y, sln(q.X), q.Settings)); } }; MultiOdeResult result = MultiFunctionMath.IntegrateOde(rhs, 0.0, w0, T, settings); Console.WriteLine(result.EvaluationCount); //MultiOdeResult r2 = NewMethods.IntegrateOde(rhs, 0.0, w0, T, settings); // Verify that one period of evolution has brought us back to our initial state Assert.IsTrue(TestUtilities.IsNearlyEqual(result.X, T)); Assert.IsTrue(TestUtilities.IsNearlyEqual(result.Y, w0, settings)); Assert.IsTrue(listenerCount > 0); }
public void EllipticKSpecialCases() { Assert.IsTrue(TestUtilities.IsNearlyEqual(AdvancedMath.EllipticK(0.0), Math.PI / 2.0)); Assert.IsTrue(TestUtilities.IsNearlyEqual(AdvancedMath.EllipticK(1.0 / Math.Sqrt(2.0)), Math.Pow(AdvancedMath.Gamma(1.0 / 4.0), 2.0) / Math.Sqrt(Math.PI) / 4.0)); Assert.IsTrue(Double.IsPositiveInfinity(AdvancedMath.EllipticK(1.0))); }
public void RambleIntegrals() { // W_{n}(s) = \int_{0}^{1} dx_1 \cdots dx_n \vert \sum_k e^{2 \pi i x_k} \vert^{s} // appears in problems of random walk in n dimensions. This is an oscilatory integral. // Analytic values are known for W_3(1) and W_3(-1) (https://www.carma.newcastle.edu.au/jon/walks.pdf). // Also W_2(1) = 4 / \pi and even arguments (http://www.carma.newcastle.edu.au/jon/walkstalk.pdf). // W_4(-1) can be related to a one-dimensional integral involving elliptic functions // (https://www.carma.newcastle.edu.au/jon/walks2.pdf and http://crd-legacy.lbl.gov/~dhbailey/dhbpapers/combat.pdf). // This is an oscilatory integral, so drop the required precision. IntegrationSettings settings2 = new IntegrationSettings() { RelativePrecision = 1.0E-4 }; IntegrationSettings settings3 = new IntegrationSettings() { RelativePrecision = 1.0E-3 }; IntegrationSettings settings4 = new IntegrationSettings() { RelativePrecision = 1.0E-2 }; Assert.IsTrue(RambleIntegral(2, 1, settings3).Estimate.ConfidenceInterval(0.95).ClosedContains(4.0 / Math.PI)); Assert.IsTrue(RambleIntegral(3, -1, settings3).Estimate.ConfidenceInterval(0.95).ClosedContains( 3.0 / 16.0 * Math.Pow(2.0, 1.0 / 3.0) / MoreMath.Pow(Math.PI, 4) * MoreMath.Pow(AdvancedMath.Gamma(1.0 / 3.0), 6) )); Assert.IsTrue(RambleIntegral(3, 1, settings3).Estimate.ConfidenceInterval(0.95).ClosedContains( 3.0 / 16.0 * Math.Pow(2.0, 1.0 / 3.0) / MoreMath.Pow(Math.PI, 4) * MoreMath.Pow(AdvancedMath.Gamma(1.0 / 3.0), 6) + 27.0 / 4.0 * Math.Pow(2.0, 2.0 / 3.0) / MoreMath.Pow(Math.PI, 4) * MoreMath.Pow(AdvancedMath.Gamma(2.0 / 3.0), 6) )); Assert.IsTrue(RambleIntegral(4, -1, settings4).Estimate.ConfidenceInterval(0.95).ClosedContains( 8.0 / MoreMath.Pow(Math.PI, 3) * FunctionMath.Integrate(k => MoreMath.Sqr(AdvancedMath.EllipticK(k)), Interval.FromEndpoints(0.0, 1.0)) )); }
public void HypergeometricBasicFunctions() { foreach (double x in xs) { // A&S 15.1.3 if (x < 1.0 && (x != 0.0)) { Assert.IsTrue(TestUtilities.IsNearlyEqual( AdvancedMath.Hypergeometric2F1(1.0, 1.0, 2.0, x), -Math.Log(1.0 - x) / x )); } // A&S 15.1.5 if (x != 0.0) { Assert.IsTrue(TestUtilities.IsNearlyEqual( AdvancedMath.Hypergeometric2F1(0.5, 1.0, 1.5, -x * x), Math.Atan(x) / x )); } // A&S 15.1.6 if ((-1.0 <= x) && (x <= 1.0) && (x != 0.0)) { Assert.IsTrue(TestUtilities.IsNearlyEqual( AdvancedMath.Hypergeometric2F1(0.5, 0.5, 1.5, x * x), Math.Asin(x) / x )); } // Complete elliptic integrals if ((0.0 <= x) && (x < 1.0)) { Assert.IsTrue(TestUtilities.IsNearlyEqual( Math.PI / 2.0 * AdvancedMath.Hypergeometric2F1(0.5, 0.5, 1.0, x * x), AdvancedMath.EllipticK(x) )); Assert.IsTrue(TestUtilities.IsNearlyEqual( Math.PI / 2.0 * AdvancedMath.Hypergeometric2F1(-0.5, 0.5, 1.0, x * x), AdvancedMath.EllipticE(x) )); } } }
public void EllipticPiSpecialCharacteristic() { foreach (double k in TestUtilities.GenerateRealValues(1.0E-4, 1.0, 4)) { // DLMF 19.6.1 Assert.IsTrue(TestUtilities.IsNearlyEqual(AdvancedMath.EllipticPi(k * k, k), AdvancedMath.EllipticE(k) / (1.0 - k * k))); // DLMF 19.6.2 Assert.IsTrue(TestUtilities.IsNearlyEqual(AdvancedMath.EllipticPi(-k, k), Math.PI / 4.0 / (1.0 + k) + AdvancedMath.EllipticK(k) / 2.0)); // DLMF 19.6.3 Assert.IsTrue(TestUtilities.IsNearlyEqual(AdvancedMath.EllipticPi(0.0, k), AdvancedMath.EllipticK(k))); Assert.IsTrue(Double.IsPositiveInfinity(AdvancedMath.EllipticPi(1.0, k))); } }