Sinc.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.math3.analysis.function;

  18. import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
  19. import org.apache.commons.math3.analysis.FunctionUtils;
  20. import org.apache.commons.math3.analysis.UnivariateFunction;
  21. import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
  22. import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
  23. import org.apache.commons.math3.exception.DimensionMismatchException;
  24. import org.apache.commons.math3.util.FastMath;

  25. /**
  26.  * <a href="http://en.wikipedia.org/wiki/Sinc_function">Sinc</a> function,
  27.  * defined by
  28.  * <pre><code>
  29.  *   sinc(x) = 1            if x = 0,
  30.  *             sin(x) / x   otherwise.
  31.  * </code></pre>
  32.  *
  33.  * @since 3.0
  34.  */
  35. public class Sinc implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
  36.     /**
  37.      * Value below which the computations are done using Taylor series.
  38.      * <p>
  39.      * The Taylor series for sinc even order derivatives are:
  40.      * <pre>
  41.      * d^(2n)sinc/dx^(2n)     = Sum_(k>=0) (-1)^(n+k) / ((2k)!(2n+2k+1)) x^(2k)
  42.      *                        = (-1)^n     [ 1/(2n+1) - x^2/(4n+6) + x^4/(48n+120) - x^6/(1440n+5040) + O(x^8) ]
  43.      * </pre>
  44.      * </p>
  45.      * <p>
  46.      * The Taylor series for sinc odd order derivatives are:
  47.      * <pre>
  48.      * d^(2n+1)sinc/dx^(2n+1) = Sum_(k>=0) (-1)^(n+k+1) / ((2k+1)!(2n+2k+3)) x^(2k+1)
  49.      *                        = (-1)^(n+1) [ x/(2n+3) - x^3/(12n+30) + x^5/(240n+840) - x^7/(10080n+45360) + O(x^9) ]
  50.      * </pre>
  51.      * </p>
  52.      * <p>
  53.      * So the ratio of the fourth term with respect to the first term
  54.      * is always smaller than x^6/720, for all derivative orders.
  55.      * This implies that neglecting this term and using only the first three terms induces
  56.      * a relative error bounded by x^6/720. The SHORTCUT value is chosen such that this
  57.      * relative error is below double precision accuracy when |x| <= SHORTCUT.
  58.      * </p>
  59.      */
  60.     private static final double SHORTCUT = 6.0e-3;
  61.     /** For normalized sinc function. */
  62.     private final boolean normalized;

  63.     /**
  64.      * The sinc function, {@code sin(x) / x}.
  65.      */
  66.     public Sinc() {
  67.         this(false);
  68.     }

  69.     /**
  70.      * Instantiates the sinc function.
  71.      *
  72.      * @param normalized If {@code true}, the function is
  73.      * <code> sin(&pi;x) / &pi;x</code>, otherwise {@code sin(x) / x}.
  74.      */
  75.     public Sinc(boolean normalized) {
  76.         this.normalized = normalized;
  77.     }

  78.     /** {@inheritDoc} */
  79.     public double value(final double x) {
  80.         final double scaledX = normalized ? FastMath.PI * x : x;
  81.         if (FastMath.abs(scaledX) <= SHORTCUT) {
  82.             // use Taylor series
  83.             final double scaledX2 = scaledX * scaledX;
  84.             return ((scaledX2 - 20) * scaledX2 + 120) / 120;
  85.         } else {
  86.             // use definition expression
  87.             return FastMath.sin(scaledX) / scaledX;
  88.         }
  89.     }

  90.     /** {@inheritDoc}
  91.      * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
  92.      */
  93.     @Deprecated
  94.     public UnivariateFunction derivative() {
  95.         return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
  96.     }

  97.     /** {@inheritDoc}
  98.      * @since 3.1
  99.      */
  100.     public DerivativeStructure value(final DerivativeStructure t)
  101.         throws DimensionMismatchException {

  102.         final double scaledX  = (normalized ? FastMath.PI : 1) * t.getValue();
  103.         final double scaledX2 = scaledX * scaledX;

  104.         double[] f = new double[t.getOrder() + 1];

  105.         if (FastMath.abs(scaledX) <= SHORTCUT) {

  106.             for (int i = 0; i < f.length; ++i) {
  107.                 final int k = i / 2;
  108.                 if ((i & 0x1) == 0) {
  109.                     // even derivation order
  110.                     f[i] = (((k & 0x1) == 0) ? 1 : -1) *
  111.                            (1.0 / (i + 1) - scaledX2 * (1.0 / (2 * i + 6) - scaledX2 / (24 * i + 120)));
  112.                 } else {
  113.                     // odd derivation order
  114.                     f[i] = (((k & 0x1) == 0) ? -scaledX : scaledX) *
  115.                            (1.0 / (i + 2) - scaledX2 * (1.0 / (6 * i + 24) - scaledX2 / (120 * i + 720)));
  116.                 }
  117.             }

  118.         } else {

  119.             final double inv = 1 / scaledX;
  120.             final double cos = FastMath.cos(scaledX);
  121.             final double sin = FastMath.sin(scaledX);

  122.             f[0] = inv * sin;

  123.             // the nth order derivative of sinc has the form:
  124.             // dn(sinc(x)/dxn = [S_n(x) sin(x) + C_n(x) cos(x)] / x^(n+1)
  125.             // where S_n(x) is an even polynomial with degree n-1 or n (depending on parity)
  126.             // and C_n(x) is an odd polynomial with degree n-1 or n (depending on parity)
  127.             // S_0(x) = 1, S_1(x) = -1, S_2(x) = -x^2 + 2, S_3(x) = 3x^2 - 6...
  128.             // C_0(x) = 0, C_1(x) = x, C_2(x) = -2x, C_3(x) = -x^3 + 6x...
  129.             // the general recurrence relations for S_n and C_n are:
  130.             // S_n(x) = x S_(n-1)'(x) - n S_(n-1)(x) - x C_(n-1)(x)
  131.             // C_n(x) = x C_(n-1)'(x) - n C_(n-1)(x) + x S_(n-1)(x)
  132.             // as per polynomials parity, we can store both S_n and C_n in the same array
  133.             final double[] sc = new double[f.length];
  134.             sc[0] = 1;

  135.             double coeff = inv;
  136.             for (int n = 1; n < f.length; ++n) {

  137.                 double s = 0;
  138.                 double c = 0;

  139.                 // update and evaluate polynomials S_n(x) and C_n(x)
  140.                 final int kStart;
  141.                 if ((n & 0x1) == 0) {
  142.                     // even derivation order, S_n is degree n and C_n is degree n-1
  143.                     sc[n] = 0;
  144.                     kStart = n;
  145.                 } else {
  146.                     // odd derivation order, S_n is degree n-1 and C_n is degree n
  147.                     sc[n] = sc[n - 1];
  148.                     c = sc[n];
  149.                     kStart = n - 1;
  150.                 }

  151.                 // in this loop, k is always even
  152.                 for (int k = kStart; k > 1; k -= 2) {

  153.                     // sine part
  154.                     sc[k]     = (k - n) * sc[k] - sc[k - 1];
  155.                     s         = s * scaledX2 + sc[k];

  156.                     // cosine part
  157.                     sc[k - 1] = (k - 1 - n) * sc[k - 1] + sc[k -2];
  158.                     c         = c * scaledX2 + sc[k - 1];

  159.                 }
  160.                 sc[0] *= -n;
  161.                 s      = s * scaledX2 + sc[0];

  162.                 coeff *= inv;
  163.                 f[n]   = coeff * (s * sin + c * scaledX * cos);

  164.             }

  165.         }

  166.         if (normalized) {
  167.             double scale = FastMath.PI;
  168.             for (int i = 1; i < f.length; ++i) {
  169.                 f[i]  *= scale;
  170.                 scale *= FastMath.PI;
  171.             }
  172.         }

  173.         return t.compose(f);

  174.     }

  175. }