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 |
|
---|
18 | package agents.anac.y2019.harddealer.math3.analysis.function;
|
---|
19 |
|
---|
20 | import agents.anac.y2019.harddealer.math3.analysis.DifferentiableUnivariateFunction;
|
---|
21 | import agents.anac.y2019.harddealer.math3.analysis.FunctionUtils;
|
---|
22 | import agents.anac.y2019.harddealer.math3.analysis.UnivariateFunction;
|
---|
23 | import agents.anac.y2019.harddealer.math3.analysis.differentiation.DerivativeStructure;
|
---|
24 | import agents.anac.y2019.harddealer.math3.analysis.differentiation.UnivariateDifferentiableFunction;
|
---|
25 | import agents.anac.y2019.harddealer.math3.exception.DimensionMismatchException;
|
---|
26 | import agents.anac.y2019.harddealer.math3.util.FastMath;
|
---|
27 |
|
---|
28 | /**
|
---|
29 | * <a href="http://en.wikipedia.org/wiki/Sinc_function">Sinc</a> function,
|
---|
30 | * defined by
|
---|
31 | * <pre><code>
|
---|
32 | * sinc(x) = 1 if x = 0,
|
---|
33 | * sin(x) / x otherwise.
|
---|
34 | * </code></pre>
|
---|
35 | *
|
---|
36 | * @since 3.0
|
---|
37 | */
|
---|
38 | public class Sinc implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
|
---|
39 | /**
|
---|
40 | * Value below which the computations are done using Taylor series.
|
---|
41 | * <p>
|
---|
42 | * The Taylor series for sinc even order derivatives are:
|
---|
43 | * <pre>
|
---|
44 | * d^(2n)sinc/dx^(2n) = Sum_(k>=0) (-1)^(n+k) / ((2k)!(2n+2k+1)) x^(2k)
|
---|
45 | * = (-1)^n [ 1/(2n+1) - x^2/(4n+6) + x^4/(48n+120) - x^6/(1440n+5040) + O(x^8) ]
|
---|
46 | * </pre>
|
---|
47 | * </p>
|
---|
48 | * <p>
|
---|
49 | * The Taylor series for sinc odd order derivatives are:
|
---|
50 | * <pre>
|
---|
51 | * d^(2n+1)sinc/dx^(2n+1) = Sum_(k>=0) (-1)^(n+k+1) / ((2k+1)!(2n+2k+3)) x^(2k+1)
|
---|
52 | * = (-1)^(n+1) [ x/(2n+3) - x^3/(12n+30) + x^5/(240n+840) - x^7/(10080n+45360) + O(x^9) ]
|
---|
53 | * </pre>
|
---|
54 | * </p>
|
---|
55 | * <p>
|
---|
56 | * So the ratio of the fourth term with respect to the first term
|
---|
57 | * is always smaller than x^6/720, for all derivative orders.
|
---|
58 | * This implies that neglecting this term and using only the first three terms induces
|
---|
59 | * a relative error bounded by x^6/720. The SHORTCUT value is chosen such that this
|
---|
60 | * relative error is below double precision accuracy when |x| <= SHORTCUT.
|
---|
61 | * </p>
|
---|
62 | */
|
---|
63 | private static final double SHORTCUT = 6.0e-3;
|
---|
64 | /** For normalized sinc function. */
|
---|
65 | private final boolean normalized;
|
---|
66 |
|
---|
67 | /**
|
---|
68 | * The sinc function, {@code sin(x) / x}.
|
---|
69 | */
|
---|
70 | public Sinc() {
|
---|
71 | this(false);
|
---|
72 | }
|
---|
73 |
|
---|
74 | /**
|
---|
75 | * Instantiates the sinc function.
|
---|
76 | *
|
---|
77 | * @param normalized If {@code true}, the function is
|
---|
78 | * <code> sin(πx) / πx</code>, otherwise {@code sin(x) / x}.
|
---|
79 | */
|
---|
80 | public Sinc(boolean normalized) {
|
---|
81 | this.normalized = normalized;
|
---|
82 | }
|
---|
83 |
|
---|
84 | /** {@inheritDoc} */
|
---|
85 | public double value(final double x) {
|
---|
86 | final double scaledX = normalized ? FastMath.PI * x : x;
|
---|
87 | if (FastMath.abs(scaledX) <= SHORTCUT) {
|
---|
88 | // use Taylor series
|
---|
89 | final double scaledX2 = scaledX * scaledX;
|
---|
90 | return ((scaledX2 - 20) * scaledX2 + 120) / 120;
|
---|
91 | } else {
|
---|
92 | // use definition expression
|
---|
93 | return FastMath.sin(scaledX) / scaledX;
|
---|
94 | }
|
---|
95 | }
|
---|
96 |
|
---|
97 | /** {@inheritDoc}
|
---|
98 | * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
|
---|
99 | */
|
---|
100 | @Deprecated
|
---|
101 | public UnivariateFunction derivative() {
|
---|
102 | return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
|
---|
103 | }
|
---|
104 |
|
---|
105 | /** {@inheritDoc}
|
---|
106 | * @since 3.1
|
---|
107 | */
|
---|
108 | public DerivativeStructure value(final DerivativeStructure t)
|
---|
109 | throws DimensionMismatchException {
|
---|
110 |
|
---|
111 | final double scaledX = (normalized ? FastMath.PI : 1) * t.getValue();
|
---|
112 | final double scaledX2 = scaledX * scaledX;
|
---|
113 |
|
---|
114 | double[] f = new double[t.getOrder() + 1];
|
---|
115 |
|
---|
116 | if (FastMath.abs(scaledX) <= SHORTCUT) {
|
---|
117 |
|
---|
118 | for (int i = 0; i < f.length; ++i) {
|
---|
119 | final int k = i / 2;
|
---|
120 | if ((i & 0x1) == 0) {
|
---|
121 | // even derivation order
|
---|
122 | f[i] = (((k & 0x1) == 0) ? 1 : -1) *
|
---|
123 | (1.0 / (i + 1) - scaledX2 * (1.0 / (2 * i + 6) - scaledX2 / (24 * i + 120)));
|
---|
124 | } else {
|
---|
125 | // odd derivation order
|
---|
126 | f[i] = (((k & 0x1) == 0) ? -scaledX : scaledX) *
|
---|
127 | (1.0 / (i + 2) - scaledX2 * (1.0 / (6 * i + 24) - scaledX2 / (120 * i + 720)));
|
---|
128 | }
|
---|
129 | }
|
---|
130 |
|
---|
131 | } else {
|
---|
132 |
|
---|
133 | final double inv = 1 / scaledX;
|
---|
134 | final double cos = FastMath.cos(scaledX);
|
---|
135 | final double sin = FastMath.sin(scaledX);
|
---|
136 |
|
---|
137 | f[0] = inv * sin;
|
---|
138 |
|
---|
139 | // the nth order derivative of sinc has the form:
|
---|
140 | // dn(sinc(x)/dxn = [S_n(x) sin(x) + C_n(x) cos(x)] / x^(n+1)
|
---|
141 | // where S_n(x) is an even polynomial with degree n-1 or n (depending on parity)
|
---|
142 | // and C_n(x) is an odd polynomial with degree n-1 or n (depending on parity)
|
---|
143 | // S_0(x) = 1, S_1(x) = -1, S_2(x) = -x^2 + 2, S_3(x) = 3x^2 - 6...
|
---|
144 | // C_0(x) = 0, C_1(x) = x, C_2(x) = -2x, C_3(x) = -x^3 + 6x...
|
---|
145 | // the general recurrence relations for S_n and C_n are:
|
---|
146 | // S_n(x) = x S_(n-1)'(x) - n S_(n-1)(x) - x C_(n-1)(x)
|
---|
147 | // C_n(x) = x C_(n-1)'(x) - n C_(n-1)(x) + x S_(n-1)(x)
|
---|
148 | // as per polynomials parity, we can store both S_n and C_n in the same array
|
---|
149 | final double[] sc = new double[f.length];
|
---|
150 | sc[0] = 1;
|
---|
151 |
|
---|
152 | double coeff = inv;
|
---|
153 | for (int n = 1; n < f.length; ++n) {
|
---|
154 |
|
---|
155 | double s = 0;
|
---|
156 | double c = 0;
|
---|
157 |
|
---|
158 | // update and evaluate polynomials S_n(x) and C_n(x)
|
---|
159 | final int kStart;
|
---|
160 | if ((n & 0x1) == 0) {
|
---|
161 | // even derivation order, S_n is degree n and C_n is degree n-1
|
---|
162 | sc[n] = 0;
|
---|
163 | kStart = n;
|
---|
164 | } else {
|
---|
165 | // odd derivation order, S_n is degree n-1 and C_n is degree n
|
---|
166 | sc[n] = sc[n - 1];
|
---|
167 | c = sc[n];
|
---|
168 | kStart = n - 1;
|
---|
169 | }
|
---|
170 |
|
---|
171 | // in this loop, k is always even
|
---|
172 | for (int k = kStart; k > 1; k -= 2) {
|
---|
173 |
|
---|
174 | // sine part
|
---|
175 | sc[k] = (k - n) * sc[k] - sc[k - 1];
|
---|
176 | s = s * scaledX2 + sc[k];
|
---|
177 |
|
---|
178 | // cosine part
|
---|
179 | sc[k - 1] = (k - 1 - n) * sc[k - 1] + sc[k -2];
|
---|
180 | c = c * scaledX2 + sc[k - 1];
|
---|
181 |
|
---|
182 | }
|
---|
183 | sc[0] *= -n;
|
---|
184 | s = s * scaledX2 + sc[0];
|
---|
185 |
|
---|
186 | coeff *= inv;
|
---|
187 | f[n] = coeff * (s * sin + c * scaledX * cos);
|
---|
188 |
|
---|
189 | }
|
---|
190 |
|
---|
191 | }
|
---|
192 |
|
---|
193 | if (normalized) {
|
---|
194 | double scale = FastMath.PI;
|
---|
195 | for (int i = 1; i < f.length; ++i) {
|
---|
196 | f[i] *= scale;
|
---|
197 | scale *= FastMath.PI;
|
---|
198 | }
|
---|
199 | }
|
---|
200 |
|
---|
201 | return t.compose(f);
|
---|
202 |
|
---|
203 | }
|
---|
204 |
|
---|
205 | }
|
---|