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.abs;
42 import static org.apache.commons.math3.util.FastMath.max;
43
44 import java.util.logging.Logger;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 public class CompositeSwitch implements UnivariateSwitchingFunction {
60
61 private static final Logger logger = Logger.getLogger(CompositeSwitch.class.getName());
62
63
64
65
66 private final UnivariateSwitchingFunction primaryFunction;
67
68
69
70
71 private final UnivariateSwitchingFunction startSwitch;
72
73
74
75
76 private final UnivariateSwitchingFunction endSwitch;
77
78
79
80 private final double lbPrimary;
81
82
83
84 private final double ubPrimary;
85
86
87
88 private final double lb;
89
90
91
92 private final double ub;
93
94
95
96 private final double multLB;
97 private final double fdLB;
98 private final double fdLB2;
99
100 private final double multUB;
101 private final double fdUB;
102 private final double fdUB2;
103
104
105
106
107
108
109 public CompositeSwitch() {
110 this(new PowerSwitch());
111 }
112
113
114
115
116
117
118
119 public CompositeSwitch(UnivariateSwitchingFunction primary) {
120 this(primary, new MultiplicativeSwitch(), new MultiplicativeSwitch(), 0.1, 0.9);
121 }
122
123
124
125
126
127
128
129
130
131
132
133
134 public CompositeSwitch(UnivariateSwitchingFunction primary, UnivariateSwitchingFunction start,
135 UnivariateSwitchingFunction end, double lbPrimary, double ubPrimary) {
136 this(primary, start, end, lbPrimary, ubPrimary, 0, 1);
137 }
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152 public CompositeSwitch(UnivariateSwitchingFunction primary, UnivariateSwitchingFunction start,
153 UnivariateSwitchingFunction end, double lbPrimary, double ubPrimary, double lb, double ub) {
154 if (lbPrimary > ubPrimary) {
155 throw new IllegalArgumentException(
156 format(
157 " Lower primary bound %10.4g was greater than upper primary bound %10.4g",
158 lbPrimary, ubPrimary));
159 }
160 if (lb > ub) {
161 throw new IllegalArgumentException(
162 format(" Lower bound %10.4g was greater than upper bound %10.4g", lb, ub));
163 }
164 assert lb < lbPrimary && ub > ubPrimary;
165
166 primaryFunction = primary;
167 startSwitch = start;
168 endSwitch = end;
169 this.lbPrimary = lbPrimary;
170 this.ubPrimary = ubPrimary;
171 this.lb = lb;
172 this.ub = ub;
173
174 fdLB = lbPrimary - lb;
175 multLB = 1.0 / fdLB;
176 fdLB2 = fdLB * fdLB;
177
178 fdUB = ub - ubPrimary;
179 multUB = 1.0 / fdUB;
180 fdUB2 = fdUB * fdUB;
181 }
182
183 @Override
184 public boolean constantOutsideBounds() {
185 return startSwitch.constantOutsideBounds() && endSwitch.constantOutsideBounds();
186 }
187
188 @Override
189 public double firstDerivative(double x) throws IllegalArgumentException {
190 if (x < lbPrimary) {
191 return fdLower(x);
192 } else if (x > ubPrimary) {
193 return fdUpper(x);
194 } else {
195 return primaryFunction.firstDerivative(x);
196 }
197 }
198
199 @Override
200 public int getHighestOrderZeroDerivative() {
201 return Math.max(
202 Math.max(
203 startSwitch.getHighestOrderZeroDerivative(), endSwitch.getHighestOrderZeroDerivative()),
204 primaryFunction.getHighestOrderZeroDerivative());
205 }
206
207 @Override
208 public double getOneBound() {
209 return ub;
210 }
211
212 @Override
213 public double getZeroBound() {
214 return lb;
215 }
216
217 @Override
218 public double nthDerivative(double x, int order) throws IllegalArgumentException {
219 return switch (order) {
220 case 0 -> valueAt(x);
221 case 1 -> firstDerivative(x);
222 case 2 -> secondDerivative(x);
223 default -> throw new IllegalArgumentException(
224 " Composite switches do not yet have support for arbitrary derivatives");
225 };
226 }
227
228 @Override
229 public double secondDerivative(double x) throws IllegalArgumentException {
230 if (x < lbPrimary) {
231 return sdLower(x);
232 } else if (x > ubPrimary) {
233 return sdUpper(x);
234 } else {
235 return primaryFunction.secondDerivative(x);
236 }
237 }
238
239 @Override
240 public boolean symmetricToUnity() {
241 return primaryFunction.symmetricToUnity()
242 && startSwitch.equals(endSwitch)
243 && (lbPrimary - lb == ub - ubPrimary);
244 }
245
246 @Override
247 public String toString() {
248 StringBuilder sb = new StringBuilder(
249 format(" Composite switch with overall range %12.5g-%12.5g, "
250 + "with an inner range %12.5g-%12.5g", lb, ub, lbPrimary, ubPrimary));
251 sb.append("\n Primary switch: ").append(primaryFunction.toString());
252 sb.append("\n Start switch: ").append(startSwitch.toString());
253 sb.append("\n End switch: ").append(endSwitch.toString());
254 return sb.toString();
255 }
256
257 @Override
258 public boolean validOutsideBounds() {
259 return startSwitch.constantOutsideBounds() && endSwitch.constantOutsideBounds();
260 }
261
262 @Override
263 public double valueAt(double x) throws IllegalArgumentException {
264 if (x < lbPrimary) {
265 return valLower(x);
266 } else if (x > ubPrimary) {
267 return valUpper(x);
268 } else {
269 return primaryFunction.valueAt(x);
270 }
271 }
272
273 private boolean approxEquals(double x1, double x2) {
274 return approxEquals(x1, x2, 1E-11);
275 }
276
277
278
279
280
281
282
283
284
285 private boolean approxEquals(double x1, double x2, double tol) {
286 double largerVal = max(abs(x1), abs(x2));
287 if (largerVal == 0) {
288
289 return true;
290 } else if (largerVal > 1E-6) {
291
292 return abs((x1 - x2) / largerVal) < tol;
293 } else {
294
295 return abs(x1 - x2) < tol;
296 }
297 }
298
299
300
301
302
303
304
305 private boolean testJoints() {
306 if (!approxEquals(valLower(lbPrimary), primaryFunction.valueAt(lbPrimary))) {
307 return false;
308 }
309 if (!approxEquals(valUpper(ubPrimary), primaryFunction.valueAt(ubPrimary))) {
310 return false;
311 }
312
313 if (!approxEquals(fdLower(lbPrimary), primaryFunction.firstDerivative(lbPrimary))) {
314 return false;
315 }
316 if (!approxEquals(fdUpper(ubPrimary), primaryFunction.firstDerivative(ubPrimary))) {
317 return false;
318 }
319
320 if (!approxEquals(sdLower(lbPrimary), primaryFunction.secondDerivative(lbPrimary))) {
321 return false;
322 }
323 return approxEquals(sdUpper(ubPrimary), primaryFunction.secondDerivative(ubPrimary));
324 }
325
326 private double lbX(double x) {
327 return (x - lb) * multLB;
328 }
329
330 private double ubX(double x) {
331 return (ub - x) * multUB;
332 }
333
334
335
336
337
338
339
340 private double valLower(double x) {
341 return startSwitch.valueAt(lbX(x)) * primaryFunction.valueAt(x);
342 }
343
344
345
346
347
348
349
350 private double valUpper(double x) {
351 return endSwitch.valueAt(ubX(x)) * primaryFunction.valueAt(x);
352 }
353
354 private double fdLower(double x) {
355 double swX = lbX(x);
356 double val = primaryFunction.firstDerivative(x) * startSwitch.valueAt(swX);
357 val += primaryFunction.valueAt(x) * startSwitch.firstDerivative(swX) * fdLB;
358 return val;
359 }
360
361 private double fdUpper(double x) {
362 double swX = ubX(x);
363 double val = primaryFunction.firstDerivative(x) * endSwitch.valueAt(swX);
364 val += primaryFunction.valueAt(x) * endSwitch.firstDerivative(swX) * fdUB;
365 return val;
366 }
367
368 private double sdLower(double x) {
369 double swX = lbX(x);
370 double val = primaryFunction.secondDerivative(x) * startSwitch.valueAt(swX);
371 val += (2 * primaryFunction.firstDerivative(x) * startSwitch.firstDerivative(swX) * fdLB);
372 val += (primaryFunction.valueAt(x) * startSwitch.secondDerivative(swX) * fdLB2);
373 return val;
374 }
375
376 private double sdUpper(double x) {
377 double swX = ubX(x);
378 double val = primaryFunction.secondDerivative(x) * endSwitch.valueAt(swX);
379 val += (2 * primaryFunction.firstDerivative(x) * endSwitch.firstDerivative(swX) * fdUB);
380 val += (primaryFunction.valueAt(x) * endSwitch.secondDerivative(swX) * fdUB2);
381 return val;
382 }
383 }