FieldJacobiTheta.java
- /*
- * Licensed to the Hipparchus project under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The Hipparchus project licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.hipparchus.special.elliptic.jacobi;
- import org.hipparchus.CalculusFieldElement;
- import org.hipparchus.util.FastMath;
- import org.hipparchus.util.FieldSinCos;
- /** Algorithm computing Jacobi theta functions.
- * @param <T> the type of the field elements
- * @since 2.0
- */
- public class FieldJacobiTheta<T extends CalculusFieldElement<T>> {
- /** Maximum number of terms in the Fourier series. */
- private static final int N_MAX = 100;
- /** Nome. */
- private final T q;
- /** q². */
- private final T qSquare;
- /** ∜q. */
- private final T qFourth;
- /** Simple constructor.
- * <p>
- * The nome {@code q} can be computed using ratios of complete elliptic integrals
- * ({@link org.hipparchus.special.elliptic.legendre.LegendreEllipticIntegral#nome(CalculusFieldElement)
- * LegendreEllipticIntegral.nome(m)} which are themselves defined in term of parameter m,
- * where m=k² and k is the elliptic modulus.
- * </p>
- * @param q nome
- */
- public FieldJacobiTheta(final T q) {
- this.q = q;
- this.qSquare = q.multiply(q);
- this.qFourth = FastMath.sqrt(FastMath.sqrt(q));
- }
- /** Get the nome.
- * @return nome
- */
- public T getQ() {
- return q;
- }
- /** Evaluate the Jacobi theta functions.
- * @param z argument of the functions
- * @return container for the four Jacobi theta functions θ₁(z|τ), θ₂(z|τ), θ₃(z|τ), and θ₄(z|τ)
- */
- public FieldTheta<T> values(final T z) {
- // the computation is based on Fourier series,
- // see Digital Library of Mathematical Functions section 20.2
- // https://dlmf.nist.gov/20.2
- final T zero = q.getField().getZero();
- final T one = q.getField().getOne();
- // base angle for Fourier Series
- final FieldSinCos<T> sc1 = FastMath.sinCos(z);
- // recursion rules initialization
- double sgn = 1.0;
- T qNN = one;
- T qTwoN = one;
- T qNNp1 = one;
- FieldSinCos<T> sc2n1 = sc1;
- final double eps = FastMath.ulp(one).getReal();
- // Fourier series
- T sum1 = sc1.sin();
- T sum2 = sc1.cos();
- T sum3 = zero;
- T sum4 = zero;
- for (int n = 1; n < N_MAX; ++n) {
- sgn = -sgn; // (-1)ⁿ⁻¹ ← (-1)ⁿ
- qNN = qNN.multiply(qTwoN).multiply(q); // q⁽ⁿ⁻¹⁾⁽ⁿ⁻¹⁾ ← qⁿⁿ
- qTwoN = qTwoN.multiply(qSquare); // q²⁽ⁿ⁻¹⁾ ← q²ⁿ
- qNNp1 = qNNp1.multiply(qTwoN); // q⁽ⁿ⁻¹⁾ⁿ ← qⁿ⁽ⁿ⁺¹⁾
- sc2n1 = FieldSinCos.sum(sc2n1, sc1); // {sin|cos}([2n-1] z) ← {sin|cos}(2n z)
- sum3 = sum3.add(sc2n1.cos().multiply(qNN));
- sum4 = sum4.add(sc2n1.cos().multiply(qNN.multiply(sgn)));
- sc2n1 = FieldSinCos.sum(sc2n1, sc1); // {sin|cos}(2n z) ← {sin|cos}([2n+1] z)
- sum1 = sum1.add(sc2n1.sin().multiply(qNNp1.multiply(sgn)));
- sum2 = sum2.add(sc2n1.cos().multiply(qNNp1));
- if (qNNp1.norm() <= eps) {
- // we have reach convergence
- break;
- }
- }
- return new FieldTheta<>(sum1.multiply(qFourth.multiply(2)),
- sum2.multiply(qFourth.multiply(2)),
- sum3.multiply(2).add(1),
- sum4.multiply(2).add(1));
- }
- }