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 * https://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 /*
19 * This is not the original file distributed by the Apache Software Foundation
20 * It has been modified by the Hipparchus project
21 */
22
23 package org.hipparchus.analysis.function;
24
25 import org.hipparchus.analysis.ParametricUnivariateFunction;
26 import org.hipparchus.analysis.differentiation.Derivative;
27 import org.hipparchus.analysis.differentiation.UnivariateDifferentiableFunction;
28 import org.hipparchus.exception.MathIllegalArgumentException;
29 import org.hipparchus.exception.NullArgumentException;
30 import org.hipparchus.util.FastMath;
31 import org.hipparchus.util.MathUtils;
32 import org.hipparchus.util.SinCos;
33
34 /**
35 * <a href="http://en.wikipedia.org/wiki/Harmonic_oscillator">
36 * simple harmonic oscillator</a> function.
37 *
38 */
39 public class HarmonicOscillator implements UnivariateDifferentiableFunction {
40 /** Amplitude. */
41 private final double amplitude;
42 /** Angular frequency. */
43 private final double omega;
44 /** Phase. */
45 private final double phase;
46
47 /**
48 * Harmonic oscillator function.
49 *
50 * @param amplitude Amplitude.
51 * @param omega Angular frequency.
52 * @param phase Phase.
53 */
54 public HarmonicOscillator(double amplitude,
55 double omega,
56 double phase) {
57 this.amplitude = amplitude;
58 this.omega = omega;
59 this.phase = phase;
60 }
61
62 /** {@inheritDoc} */
63 @Override
64 public double value(double x) {
65 return value(omega * x + phase, amplitude);
66 }
67
68 /**
69 * Parametric function where the input array contains the parameters of
70 * the harmonic oscillator function, ordered as follows:
71 * <ul>
72 * <li>Amplitude</li>
73 * <li>Angular frequency</li>
74 * <li>Phase</li>
75 * </ul>
76 */
77 public static class Parametric implements ParametricUnivariateFunction {
78
79 /** Empty constructor.
80 * <p>
81 * This constructor is not strictly necessary, but it prevents spurious
82 * javadoc warnings with JDK 18 and later.
83 * </p>
84 * @since 3.0
85 */
86 public Parametric() { // NOPMD - unnecessary constructor added intentionally to make javadoc happy
87 // nothing to do
88 }
89
90 /**
91 * Computes the value of the harmonic oscillator at {@code x}.
92 *
93 * @param x Value for which the function must be computed.
94 * @param param Values of norm, mean and standard deviation.
95 * @return the value of the function.
96 * @throws NullArgumentException if {@code param} is {@code null}.
97 * @throws MathIllegalArgumentException if the size of {@code param} is
98 * not 3.
99 */
100 @Override
101 public double value(double x, double ... param)
102 throws MathIllegalArgumentException, NullArgumentException {
103 validateParameters(param);
104 return HarmonicOscillator.value(x * param[1] + param[2], param[0]);
105 }
106
107 /**
108 * Computes the value of the gradient at {@code x}.
109 * The components of the gradient vector are the partial
110 * derivatives of the function with respect to each of the
111 * <em>parameters</em> (amplitude, angular frequency and phase).
112 *
113 * @param x Value at which the gradient must be computed.
114 * @param param Values of amplitude, angular frequency and phase.
115 * @return the gradient vector at {@code x}.
116 * @throws NullArgumentException if {@code param} is {@code null}.
117 * @throws MathIllegalArgumentException if the size of {@code param} is
118 * not 3.
119 */
120 @Override
121 public double[] gradient(double x, double ... param)
122 throws MathIllegalArgumentException, NullArgumentException {
123 validateParameters(param);
124
125 final double amplitude = param[0];
126 final double omega = param[1];
127 final double phase = param[2];
128
129 final double xTimesOmegaPlusPhase = omega * x + phase;
130 final double a = HarmonicOscillator.value(xTimesOmegaPlusPhase, 1);
131 final double p = -amplitude * FastMath.sin(xTimesOmegaPlusPhase);
132 final double w = p * x;
133
134 return new double[] { a, w, p };
135 }
136
137 /**
138 * Validates parameters to ensure they are appropriate for the evaluation of
139 * the {@link #value(double,double[])} and {@link #gradient(double,double[])}
140 * methods.
141 *
142 * @param param Values of norm, mean and standard deviation.
143 * @throws NullArgumentException if {@code param} is {@code null}.
144 * @throws MathIllegalArgumentException if the size of {@code param} is
145 * not 3.
146 */
147 private void validateParameters(double[] param)
148 throws MathIllegalArgumentException, NullArgumentException {
149 MathUtils.checkNotNull(param);
150 MathUtils.checkDimension(param.length, 3);
151 }
152 }
153
154 /**
155 * @param xTimesOmegaPlusPhase {@code omega * x + phase}.
156 * @param amplitude Amplitude.
157 * @return the value of the harmonic oscillator function at {@code x}.
158 */
159 private static double value(double xTimesOmegaPlusPhase,
160 double amplitude) {
161 return amplitude * FastMath.cos(xTimesOmegaPlusPhase);
162 }
163
164 /** {@inheritDoc}
165 */
166 @Override
167 public <T extends Derivative<T>> T value(T t)
168 throws MathIllegalArgumentException {
169 final double x = t.getValue();
170 double[] f = new double[t.getOrder() + 1];
171
172 final double alpha = omega * x + phase;
173 final SinCos scAlpha = FastMath.sinCos(alpha);
174 f[0] = amplitude * scAlpha.cos();
175 if (f.length > 1) {
176 f[1] = -amplitude * omega * scAlpha.sin();
177 final double mo2 = - omega * omega;
178 for (int i = 2; i < f.length; ++i) {
179 f[i] = mo2 * f[i - 2];
180 }
181 }
182
183 return t.compose(f);
184
185 }
186
187 }