1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package ffx.numerics.switching;
39
40 import static java.lang.String.format;
41 import static org.apache.commons.math3.util.FastMath.cos;
42 import static org.apache.commons.math3.util.FastMath.pow;
43 import static org.apache.commons.math3.util.FastMath.sin;
44
45 import java.util.function.DoubleUnaryOperator;
46
47
48
49
50
51
52
53
54 public class SquaredTrigSwitch implements UnivariateSwitchingFunction {
55
56 private static final double PI_OVER_TWO = Math.PI * 0.5;
57 private final double multiplier;
58 private final double halfPeriod;
59 private final DoubleUnaryOperator xTransform;
60 private final boolean cosine;
61
62
63
64
65
66
67
68 public SquaredTrigSwitch(boolean cosine) {
69 this(PI_OVER_TWO, cosine);
70 }
71
72
73
74
75
76
77
78
79 public SquaredTrigSwitch(double coefficient, boolean cosine) {
80 multiplier = coefficient;
81 halfPeriod = PI_OVER_TWO / multiplier;
82 xTransform = cosine ? this::cosineTransform : this::sineTransform;
83 this.cosine = cosine;
84 }
85
86
87 @Override
88 public boolean constantOutsideBounds() {
89 return false;
90 }
91
92
93 @Override
94 public double firstDerivative(double x) throws IllegalArgumentException {
95 x = xTransform.applyAsDouble(x);
96 return 2.0 * sin(x) * cos(x) * multiplier;
97 }
98
99
100 @Override
101 public int getHighestOrderZeroDerivative() {
102 return 1;
103 }
104
105
106 @Override
107 public double getOneBound() {
108 return cosine ? 0 : halfPeriod;
109 }
110
111
112
113
114
115
116 public double getPeriod() {
117 return 2.0 * halfPeriod;
118 }
119
120
121 @Override
122 public double getZeroBound() {
123 return cosine ? halfPeriod : 0;
124 }
125
126
127
128
129
130
131 public boolean isCosine() {
132 return cosine;
133 }
134
135
136 @Override
137 public double nthDerivative(double x, int order) throws IllegalArgumentException {
138 if (order < 1) {
139 throw new IllegalArgumentException("Order must be >= 1");
140 }
141 x = xTransform.applyAsDouble(x);
142 double sinVal = sin(x);
143 double cosVal = cos(x);
144 double multPow = pow(multiplier, order);
145
146 return switch (order % 4) {
147 case 0 -> pow(2.0, order - 1) * multPow * ((sinVal * sinVal) - (cosVal * cosVal));
148 case 1 -> pow(2.0, order) * multPow * sinVal * cosVal;
149 case 2 -> pow(2.0, order - 1) * multPow * ((cosVal * cosVal) - (sinVal * sinVal));
150 case 3 -> -1.0 * pow(2.0, order) * multPow * sinVal * cosVal;
151 default -> throw new ArithmeticException("A positive number modulo 4 was not 0-3");
152 };
153 }
154
155
156 @Override
157 public double secondDerivative(double x) throws IllegalArgumentException {
158 double val = 2.0 * multiplier * multiplier;
159 x = xTransform.applyAsDouble(x);
160 double cosTerm = cos(x);
161 double sinTerm = sin(x);
162
163 return val * ((cosTerm * cosTerm) - (sinTerm * sinTerm));
164 }
165
166
167 @Override
168 public boolean symmetricToUnity() {
169 return true;
170 }
171
172
173 @Override
174 public String toString() {
175 if (cosine) {
176 return format("Cosine-squared switching function of form f(x) = cos^2(%8.4g * x)", multiplier);
177 } else {
178 return format("Sine-squared switching function of form f(x) = sin^2(%8.4g * x)", multiplier);
179 }
180 }
181
182
183 @Override
184 public boolean validOutsideBounds() {
185 return true;
186 }
187
188
189 @Override
190 public double valueAt(double x) throws IllegalArgumentException {
191 x = xTransform.applyAsDouble(x);
192 x = sin(x);
193 x *= x;
194 return x;
195 }
196
197
198
199
200
201
202
203 private double sineTransform(double x) {
204 return x * multiplier;
205 }
206
207
208
209
210
211
212
213 private double cosineTransform(double x) {
214 return PI_OVER_TWO + (x * multiplier);
215 }
216 }