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.potential.utils;
39
40 import static java.lang.Math.ulp;
41 import static org.junit.Assert.assertEquals;
42 import static org.junit.Assert.assertFalse;
43 import static org.junit.Assert.assertTrue;
44
45 import ffx.numerics.switching.MultiplicativeSwitch;
46 import ffx.numerics.switching.PowerSwitch;
47 import ffx.numerics.switching.SquaredTrigSwitch;
48 import ffx.numerics.switching.UnivariateSwitchingFunction;
49 import java.util.logging.Logger;
50
51 import ffx.utilities.FFXTest;
52 import org.apache.commons.math3.util.FastMath;
53 import org.junit.Test;
54
55
56
57
58
59
60
61
62 public class SwitchFunctionTest extends FFXTest {
63 private static final Logger logger = Logger.getLogger(SwitchFunctionTest.class.getName());
64
65
66 private static final double ULP_ONE_2 = 2.0 * Math.ulp(1.0);
67 private static final double ULP_ONE_10 = 5.0 * ULP_ONE_2;
68 private static final double ULP_ONE_100 = 50.0 * ULP_ONE_2;
69
70 private static final double ULP_ZERO_2 = 2.0 * Math.ulp(0.0);
71 private static final double ULP_ZERO_10 = 5.0 * ULP_ZERO_2;
72 private static final double ULP_ZERO_100 = 50.0 * ULP_ZERO_2;
73
74 private static final double LOOSE_TOLERANCE = 0.000001;
75 private static final double MID_TOLERANCE = 1.0E-10;
76
77
78 @Test
79 public void multSwitchTest() {
80 logger.info(" Testing multiplicative switch functionality");
81 MultiplicativeSwitch sf = new MultiplicativeSwitch();
82 standardTest(sf);
83
84 assertEquals(
85 "Default multiplicative switch zero bound != 0.0", 0.0, sf.getZeroBound(), ULP_ZERO_2);
86 assertEquals("Default power-switch one bound != 1.0", 1.0, sf.getOneBound(), ULP_ONE_2);
87 assertFalse("Power switches are not constant outside the bounds.", sf.constantOutsideBounds());
88 assertFalse("Power switches are not valid outside the bounds.", sf.validOutsideBounds());
89 assertEquals(
90 "Default power-switch max-zero-derivative should return 2",
91 2,
92 sf.getHighestOrderZeroDerivative());
93 assertTrue(
94 "Default power-switch should be equal unity with symmetric inputs", sf.symmetricToUnity());
95
96 sf = new MultiplicativeSwitch(1.0, 0.0);
97 standardTest(sf);
98
99 sf = new MultiplicativeSwitch(9.0, 7.2);
100 standardTest(sf, LOOSE_TOLERANCE);
101 }
102
103
104 @Test
105 public void testPowerInterpolation() {
106 logger.info(" Testing default power switch");
107 PowerSwitch funct = new PowerSwitch();
108 standardTest(funct);
109 assertEquals("Default power-switch zero bound != 0.0", 0.0, funct.getZeroBound(), ULP_ZERO_2);
110 assertEquals("Default power-switch one bound != 1.0", 1.0, funct.getOneBound(), ULP_ONE_2);
111 assertFalse(
112 "Power switches are not constant outside the bounds.", funct.constantOutsideBounds());
113 assertFalse("Power switches are not valid outside the bounds.", funct.validOutsideBounds());
114 assertEquals(
115 "Default power-switch max-zero-derivative should return 0",
116 0,
117 funct.getHighestOrderZeroDerivative());
118 assertTrue(
119 "Default power-switch should be equal unity with symmetric inputs",
120 funct.symmetricToUnity());
121
122 for (double x = 0; x <= 1.0; x += 0.01) {
123 double delta = 10.0 * ulp(x);
124 double valAt = funct.valueAt(x);
125 assertEquals(
126 String.format(
127 "Value of default power-switch at %8.4g should be itself, was %8.4g", x, valAt),
128 x,
129 valAt,
130 delta);
131 double derivAt = funct.firstDerivative(x);
132 assertEquals(
133 String.format(
134 "First derivative of default power switch at %8.4g should always be 1.0, was %8.4g",
135 x, derivAt),
136 1.0,
137 derivAt,
138 ULP_ONE_10);
139 double d2 = funct.secondDerivative(x);
140 assertEquals(
141 String.format(
142 "Second derivative of default power switch at %8.4g should always be 0.0, was %8.4g",
143 x, d2),
144 0.0,
145 d2,
146 ULP_ZERO_10);
147 }
148
149 logger.info(" Testing manually-constructed default power switch");
150 funct = new PowerSwitch(1.0, 1.0);
151 standardTest(funct);
152 assertEquals("Default power-switch zero bound != 0.0", 0.0, funct.getZeroBound(), ULP_ZERO_2);
153 assertEquals("Default power-switch one bound != 1.0", 1.0, funct.getOneBound(), ULP_ONE_2);
154 assertFalse(
155 "Power switches are not constant outside the bounds.", funct.constantOutsideBounds());
156 assertFalse("Power switches are not valid outside the bounds.", funct.validOutsideBounds());
157 assertEquals(
158 "Default power-switch max-zero-derivative should return 0",
159 0,
160 funct.getHighestOrderZeroDerivative());
161 assertTrue(
162 "Default power-switch should be equal unity with symmetric inputs",
163 funct.symmetricToUnity());
164
165 for (double x = 0; x <= 1.0; x += 0.01) {
166 double delta = 10.0 * ulp(x);
167 double valAt = funct.valueAt(x);
168 assertEquals(
169 String.format(
170 "Value of default power-switch at %8.4g should be itself, was %8.4g", x, valAt),
171 x,
172 valAt,
173 delta);
174 double derivAt = funct.firstDerivative(x);
175 assertEquals(
176 String.format(
177 "First derivative of default power switch at %8.4g should always be 1.0, was %8.4g",
178 x, derivAt),
179 1.0,
180 derivAt,
181 ULP_ONE_10);
182 double d2 = funct.secondDerivative(x);
183 assertEquals(
184 String.format(
185 "Second derivative of default power switch at %8.4g should always be 0.0, was %8.4g",
186 x, d2),
187 0.0,
188 d2,
189 ULP_ZERO_10);
190 }
191
192 logger.info(" Testing linear power switch with doubled bounds");
193 funct = new PowerSwitch(0.5, 1.0);
194 standardTest(funct);
195 assertEquals(
196 String.format("Power-switch %s zero bound != 0.0", funct.toString()),
197 0.0,
198 funct.getZeroBound(),
199 ULP_ZERO_2);
200 assertEquals(
201 String.format("Power-switch %s one bound != 2.0", funct.toString()),
202 2.0,
203 funct.getOneBound(),
204 2.0 * ulp(2.0));
205 assertFalse(
206 "Power switches are not constant outside the bounds.", funct.constantOutsideBounds());
207 assertFalse("Power switches are not valid outside the bounds.", funct.validOutsideBounds());
208 assertEquals(
209 String.format("Power-switch %s max-zero-derivative should return 0", funct.toString()),
210 0,
211 funct.getHighestOrderZeroDerivative());
212 assertTrue(
213 String.format(
214 "Power-switch %s should be equal unity with symmetric inputs", funct.toString()),
215 funct.symmetricToUnity());
216
217 for (double x = 0; x <= 1.0; x += 0.01) {
218 double delta = 10.0 * ulp(x);
219 double valAt = funct.valueAt(x);
220 assertEquals(
221 String.format(
222 "Value of power-switch %s at %8.4g should be 0.5 * itself, was %8.4g",
223 funct.toString(), x, valAt),
224 0.5 * x,
225 valAt,
226 delta);
227 double derivAt = funct.firstDerivative(x);
228 assertEquals(
229 String.format(
230 "First derivative of power-switch %s at %8.4g should always be 0.5, was %8.4g",
231 funct.toString(), x, derivAt),
232 0.5,
233 derivAt,
234 ULP_ONE_10);
235 double d2 = funct.secondDerivative(x);
236 assertEquals(
237 String.format(
238 "Second derivative of power-switch %s at %8.4g should always be 0.0, was %8.4g",
239 funct.toString(), x, d2),
240 0.0,
241 d2,
242 ULP_ZERO_10);
243 }
244
245 logger.info(" Testing power-2 switching function");
246 funct = new PowerSwitch(1.0, 2.0);
247 standardTest(funct);
248 assertEquals(
249 String.format("Power-switch %s zero bound != 0.0", funct.toString()),
250 0.0,
251 funct.getZeroBound(),
252 ULP_ZERO_2);
253 assertEquals(
254 String.format("Power-switch %s one bound != 1.0", funct.toString()),
255 1.0,
256 funct.getOneBound(),
257 ULP_ONE_2);
258 assertFalse(
259 "Power switches are not constant outside the bounds.", funct.constantOutsideBounds());
260 assertFalse("Power switches are not valid outside the bounds.", funct.validOutsideBounds());
261 assertEquals(
262 String.format("Power-switch %s max-zero-derivative should return 0", funct.toString()),
263 0,
264 funct.getHighestOrderZeroDerivative());
265
266 double beta = funct.getExponent();
267 for (double x = 0; x <= 1.0; x += 0.01) {
268 double delta = 10.0 * ulp(x);
269 double valAt = funct.valueAt(x);
270 double trueVal = x * x;
271 assertEquals(
272 String.format(
273 "Value of power-switch %s at %8.4g should be %8.4g, was %8.4g",
274 funct.toString(), x, trueVal, valAt),
275 trueVal,
276 valAt,
277 delta);
278
279 double derivAt = funct.firstDerivative(x);
280 trueVal = beta * FastMath.pow(x, (beta - 1.0));
281 assertEquals(
282 String.format(
283 "First derivative of power-switch %s at %8.4g should be %8.4g, was %8.4g",
284 funct.toString(), x, trueVal, derivAt),
285 trueVal,
286 derivAt,
287 10.0 * ulp(trueVal));
288
289
290 trueVal = 2;
291 double d2 = funct.secondDerivative(x);
292 assertEquals(
293 String.format(
294 "Second derivative of power-switch %s at %8.4g should always be %8.4g, was %8.4g",
295 funct.toString(), x, trueVal, d2),
296 trueVal,
297 d2,
298 ULP_ZERO_10);
299 }
300
301 logger.info(" Testing power-2 switching function with double-wide bounds");
302 funct = new PowerSwitch(0.5, 2.0);
303 double ub = funct.getOneBound();
304 standardTest(funct);
305 assertEquals(
306 String.format("Power-switch %s zero bound != 0.0", funct.toString()),
307 0.0,
308 funct.getZeroBound(),
309 ULP_ZERO_2);
310 assertEquals(
311 String.format("Power-switch %s one bound != 2.0", funct.toString()),
312 2.0,
313 funct.getOneBound(),
314 2.0 * ulp(2.0));
315 assertFalse(
316 "Power switches are not constant outside the bounds.", funct.constantOutsideBounds());
317 assertFalse("Power switches are not valid outside the bounds.", funct.validOutsideBounds());
318 assertEquals(
319 String.format("Power-switch %s max-zero-derivative should return 0", funct.toString()),
320 0,
321 funct.getHighestOrderZeroDerivative());
322
323 beta = funct.getExponent();
324 for (double x = 0; x <= ub; x += 0.01) {
325 double delta = 10.0 * ulp(x);
326 double valAt = funct.valueAt(x);
327 double trueVal = x * x * 0.25;
328 assertEquals(
329 String.format(
330 "Value of power-switch %s at %8.4g should be %8.4g, was %8.4g",
331 funct.toString(), x, trueVal, valAt),
332 trueVal,
333 valAt,
334 delta);
335
336 double derivAt = funct.firstDerivative(x);
337 trueVal = beta * 0.25 * FastMath.pow(x, (beta - 1.0));
338 assertEquals(
339 String.format(
340 "First derivative of power-switch %s at %8.4g should be %8.4g, was %8.4g",
341 funct.toString(), x, trueVal, derivAt),
342 trueVal,
343 derivAt,
344 10.0 * ulp(trueVal));
345
346
347 trueVal = 0.5;
348 double d2 = funct.secondDerivative(x);
349 assertEquals(
350 String.format(
351 "Second derivative of power-switch %s at %8.4g should always be %8.4g, was %8.4g",
352 funct.toString(), x, trueVal, d2),
353 trueVal,
354 d2,
355 ULP_ZERO_10);
356 }
357
358 logger.info(" Testing power-4 switching function");
359 funct = new PowerSwitch(1.0, 4.0);
360 ub = funct.getOneBound();
361 standardTest(funct);
362 assertEquals(
363 String.format("Power-switch %s zero bound != 0.0", funct.toString()),
364 0.0,
365 funct.getZeroBound(),
366 ULP_ZERO_2);
367 assertEquals(
368 String.format("Power-switch %s one bound != 1.0", funct.toString()),
369 1.0,
370 funct.getOneBound(),
371 ULP_ONE_2);
372 assertFalse(
373 "Power switches are not constant outside the bounds.", funct.constantOutsideBounds());
374 assertFalse("Power switches are not valid outside the bounds.", funct.validOutsideBounds());
375 assertEquals(
376 String.format("Power-switch %s max-zero-derivative should return 0", funct.toString()),
377 0,
378 funct.getHighestOrderZeroDerivative());
379
380 beta = funct.getExponent();
381 for (double x = 0; x <= ub; x += 0.01) {
382 double delta = 10.0 * ulp(x);
383 double valAt = funct.valueAt(x);
384 double trueVal = x * x * x * x;
385 assertEquals(
386 String.format(
387 "Value of power-switch %s at %8.4g should be %8.4g, was %8.4g",
388 funct.toString(), x, trueVal, valAt),
389 trueVal,
390 valAt,
391 delta);
392
393 double derivAt = funct.firstDerivative(x);
394 trueVal = beta * FastMath.pow(x, (beta - 1.0));
395 assertEquals(
396 String.format(
397 "First derivative of power-switch %s at %8.4g should be %8.4g, was %8.4g",
398 funct.toString(), x, trueVal, derivAt),
399 trueVal,
400 derivAt,
401 10.0 * ulp(trueVal));
402
403 trueVal = beta * (beta - 1.0) * FastMath.pow(x, (beta - 2.0));
404 double d2 = funct.secondDerivative(x);
405 assertEquals(
406 String.format(
407 "Second derivative of power-switch %s at %8.4g should always be %8.4g, was %8.4g",
408 funct.toString(), x, trueVal, d2),
409 trueVal,
410 d2,
411 ULP_ZERO_10);
412 }
413
414 logger.info(" Testing power-4 switching function with double-wide bounds");
415 funct = new PowerSwitch(0.5, 4.0);
416 ub = funct.getOneBound();
417 standardTest(funct);
418 assertEquals(
419 String.format("Power-switch %s zero bound != 0.0", funct.toString()),
420 0.0,
421 funct.getZeroBound(),
422 ULP_ZERO_2);
423 assertEquals(
424 String.format("Power-switch %s one bound != 2.0", funct.toString()),
425 2.0,
426 funct.getOneBound(),
427 2.0 * ulp(2.0));
428 assertFalse(
429 "Power switches are not constant outside the bounds.", funct.constantOutsideBounds());
430 assertFalse("Power switches are not valid outside the bounds.", funct.validOutsideBounds());
431 assertEquals(
432 String.format("Power-switch %s max-zero-derivative should return 0", funct.toString()),
433 0,
434 funct.getHighestOrderZeroDerivative());
435
436 beta = funct.getExponent();
437 for (double x = 0; x <= ub; x += 0.01) {
438 double delta = 10.0 * ulp(x);
439 double valAt = funct.valueAt(x);
440 double trueVal = 0.0625 * x * x * x * x;
441 assertEquals(
442 String.format(
443 "Value of power-switch %s at %8.4g should be %8.4g, was %8.4g",
444 funct.toString(), x, trueVal, valAt),
445 trueVal,
446 valAt,
447 delta);
448
449 double derivAt = funct.firstDerivative(x);
450 trueVal = beta * 0.0625 * FastMath.pow(x, (beta - 1.0));
451 assertEquals(
452 String.format(
453 "First derivative of power-switch %s at %8.4g should be %8.4g, was %8.4g",
454 funct.toString(), x, trueVal, derivAt),
455 trueVal,
456 derivAt,
457 10.0 * ulp(trueVal));
458
459 trueVal = 0.0625 * beta * (beta - 1.0) * FastMath.pow(x, (beta - 2.0));
460 double d2 = funct.secondDerivative(x);
461 assertEquals(
462 String.format(
463 "Second derivative of power-switch %s at %8.4g should always be %8.4g, was %8.4g",
464 funct.toString(), x, trueVal, d2),
465 trueVal,
466 d2,
467 ULP_ZERO_10);
468 }
469
470 logger.info(" Testing square-root switching function");
471 funct = new PowerSwitch(1.0, 0.5);
472 ub = funct.getOneBound();
473 standardTest(funct);
474 assertEquals(
475 String.format("Power-switch %s zero bound != 0.0", funct.toString()),
476 0.0,
477 funct.getZeroBound(),
478 ULP_ZERO_2);
479 assertEquals(
480 String.format("Power-switch %s one bound != 1.0", funct.toString()),
481 1.0,
482 funct.getOneBound(),
483 ULP_ONE_2);
484 assertFalse(
485 "Power switches are not constant outside the bounds.", funct.constantOutsideBounds());
486 assertFalse("Power switches are not valid outside the bounds.", funct.validOutsideBounds());
487 assertEquals(
488 String.format("Power-switch %s max-zero-derivative should return 0", funct.toString()),
489 0,
490 funct.getHighestOrderZeroDerivative());
491
492 beta = funct.getExponent();
493 for (double x = 0; x <= ub; x += 0.01) {
494 double delta = 10.0 * ulp(x);
495 double valAt = funct.valueAt(x);
496 double trueVal = FastMath.sqrt(x);
497 assertEquals(
498 String.format(
499 "Value of power-switch %s at %8.4g should be %8.4g, was %8.4g",
500 funct.toString(), x, trueVal, valAt),
501 trueVal,
502 valAt,
503 delta);
504
505 double derivAt = funct.firstDerivative(x);
506 trueVal = beta * FastMath.pow(x, (beta - 1.0));
507 assertEquals(
508 String.format(
509 "First derivative of power-switch %s at %8.4g should be %8.4g, was %8.4g",
510 funct.toString(), x, trueVal, derivAt),
511 trueVal,
512 derivAt,
513 10.0 * ulp(trueVal));
514
515 trueVal = beta * (beta - 1.0) * FastMath.pow(x, (beta - 2.0));
516 double d2 = funct.secondDerivative(x);
517 assertEquals(
518 String.format(
519 "Second derivative of power-switch %s at %8.4g should always be %8.4g, was %8.4g",
520 funct.toString(), x, trueVal, d2),
521 trueVal,
522 d2,
523 ULP_ZERO_10);
524 }
525
526 logger.info(" Testing square-root switching function with double-wide bounds");
527 funct = new PowerSwitch(0.5, 0.5);
528 ub = funct.getOneBound();
529 standardTest(funct);
530 assertEquals(
531 String.format("Power-switch %s zero bound != 0.0", funct.toString()),
532 0.0,
533 funct.getZeroBound(),
534 ULP_ZERO_2);
535 assertEquals(
536 String.format("Power-switch %s one bound != 2.0", funct.toString()),
537 2.0,
538 funct.getOneBound(),
539 2.0 * ulp(2.0));
540 assertFalse(
541 "Power switches are not constant outside the bounds.", funct.constantOutsideBounds());
542 assertFalse("Power switches are not valid outside the bounds.", funct.validOutsideBounds());
543 assertEquals(
544 String.format("Power-switch %s max-zero-derivative should return 0", funct.toString()),
545 0,
546 funct.getHighestOrderZeroDerivative());
547
548 beta = funct.getExponent();
549 double sqrt05 = FastMath.sqrt(0.5);
550 for (double x = 0; x <= ub; x += 0.01) {
551 double delta = 10.0 * ulp(x);
552 double valAt = funct.valueAt(x);
553 double trueVal = sqrt05 * FastMath.sqrt(x);
554 assertEquals(
555 String.format(
556 "Value of power-switch %s at %8.4g should be %8.4g, was %8.4g",
557 funct.toString(), x, trueVal, valAt),
558 trueVal,
559 valAt,
560 delta);
561
562 double derivAt = funct.firstDerivative(x);
563 trueVal = 0.5 * beta * FastMath.pow(0.5 * x, (beta - 1.0));
564 assertEquals(
565 String.format(
566 "First derivative of power-switch %s at %8.4g should be %8.4g, was %8.4g",
567 funct.toString(), x, trueVal, derivAt),
568 trueVal,
569 derivAt,
570 10.0 * ulp(trueVal));
571
572 trueVal = 0.25 * beta * (beta - 1.0) * FastMath.pow(0.5 * x, (beta - 2.0));
573 double d2 = funct.secondDerivative(x);
574 assertEquals(
575 String.format(
576 "Second derivative of power-switch %s at %8.4g should always be %8.4g, was %8.4g",
577 funct.toString(), x, trueVal, d2),
578 trueVal,
579 d2,
580 ULP_ZERO_10);
581 }
582 }
583
584 @Test
585 public void trigTest() {
586 logger.info(" Testing trigonometric switch functionality");
587 double piOverTwo = Math.PI * 0.5;
588
589 SquaredTrigSwitch sf = new SquaredTrigSwitch(false);
590 standardTest(sf, MID_TOLERANCE);
591 double a = piOverTwo;
592 assertEquals("Default sine switch zero bound != 0.0", 0.0, sf.getZeroBound(), ULP_ZERO_2);
593 assertEquals("Default sine switch one bound != 1.0", 1.0, sf.getOneBound(), ULP_ONE_2);
594 assertFalse("Sine switches are not constant outside the bounds.", sf.constantOutsideBounds());
595 assertTrue("Sine switches are valid outside the bounds.", sf.validOutsideBounds());
596 assertEquals(
597 "Default sine switch max-zero-derivative should return 1",
598 1,
599 sf.getHighestOrderZeroDerivative());
600 assertTrue(
601 "Default sine switch should be equal unity with symmetric inputs", sf.symmetricToUnity());
602
603 for (double x = 0; x <= 1.0; x += 0.01) {
604 double delta = 10.0 * ulp(x);
605 double ax = a * x;
606 double sinOf = FastMath.sin(ax);
607 double cosOf = FastMath.cos(ax);
608
609 double valAt = sf.valueAt(x);
610 double trueVal = sinOf * sinOf;
611 assertEquals(
612 String.format(
613 "Value of default sine switch at %8.4g should be %8.4g, was %8.4g",
614 x, trueVal, valAt),
615 trueVal,
616 valAt,
617 delta);
618
619 double derivAt = sf.firstDerivative(x);
620 trueVal = 2.0 * a * sinOf * cosOf;
621 delta = (trueVal < 1.0E-10) ? 1.0E-14 : 100.0 * ulp(trueVal);
622 assertEquals(
623 String.format(
624 "First derivative of default sine switch at %8.4g should be %8.4g, was %8.4g",
625 x, trueVal, derivAt),
626 trueVal,
627 derivAt,
628 delta);
629
630 double d2 = sf.secondDerivative(x);
631 trueVal = 2.0 * a * a * ((cosOf * cosOf) - (sinOf * sinOf));
632 delta = (trueVal < 1.0E-10) ? 1.0E-14 : 100.0 * ulp(trueVal);
633 assertEquals(
634 String.format(
635 "Second derivative of default sine switch at %8.4g should be %8.4g, was %8.4g",
636 x, trueVal, d2),
637 trueVal,
638 d2,
639 delta);
640 }
641
642 logger.info(" Testing manually-constructed default sine-squared switch.");
643
644 sf = new SquaredTrigSwitch(piOverTwo, false);
645 standardTest(sf, MID_TOLERANCE);
646 a = piOverTwo;
647 assertEquals("Default sine switch zero bound != 0.0", 0.0, sf.getZeroBound(), ULP_ZERO_2);
648 assertEquals("Default sine switch one bound != 1.0", 1.0, sf.getOneBound(), ULP_ONE_2);
649 assertFalse("Sine switches are not constant outside the bounds.", sf.constantOutsideBounds());
650 assertTrue("Sine switches are valid outside the bounds.", sf.validOutsideBounds());
651 assertEquals(
652 "Default sine switch max-zero-derivative should return 1",
653 1,
654 sf.getHighestOrderZeroDerivative());
655 assertTrue(
656 "Default sine switch should be equal unity with symmetric inputs", sf.symmetricToUnity());
657
658 for (double x = 0; x <= 1.0; x += 0.01) {
659 double delta = 10.0 * ulp(x);
660 double ax = a * x;
661 double sinOf = FastMath.sin(ax);
662 double cosOf = FastMath.cos(ax);
663
664 double valAt = sf.valueAt(x);
665 double trueVal = sinOf * sinOf;
666 assertEquals(
667 String.format(
668 "Value of default sine switch at %8.4g should be %8.4g, was %8.4g",
669 x, trueVal, valAt),
670 trueVal,
671 valAt,
672 delta);
673
674 double derivAt = sf.firstDerivative(x);
675 trueVal = 2.0 * a * sinOf * cosOf;
676 delta = (trueVal < 1.0E-10) ? 1.0E-14 : 100.0 * ulp(trueVal);
677 assertEquals(
678 String.format(
679 "First derivative of default sine switch at %8.4g should be %8.4g, was %8.4g",
680 x, trueVal, derivAt),
681 trueVal,
682 derivAt,
683 delta);
684
685 double d2 = sf.secondDerivative(x);
686 trueVal = 2.0 * a * a * ((cosOf * cosOf) - (sinOf * sinOf));
687 delta = (trueVal < 1.0E-10) ? 1.0E-14 : 100.0 * ulp(trueVal);
688 assertEquals(
689 String.format(
690 "Second derivative of default sine switch at %8.4g should be %8.4g, was %8.4g",
691 x, trueVal, d2),
692 trueVal,
693 d2,
694 delta);
695 }
696
697 logger.info(" Testing default cosine-squared switch.");
698
699 sf = new SquaredTrigSwitch(true);
700 standardTest(sf, MID_TOLERANCE);
701 a = piOverTwo;
702 assertEquals("Default cosine switch zero bound != 1.0", 1.0, sf.getZeroBound(), ULP_ONE_2);
703 assertEquals("Default cosine switch one bound != 0.0", 0.0, sf.getOneBound(), ULP_ZERO_2);
704 assertFalse("Cosine switches are not constant outside the bounds.", sf.constantOutsideBounds());
705 assertTrue("Cosine switches are valid outside the bounds.", sf.validOutsideBounds());
706 assertEquals(
707 "Default cosine switch max-zero-derivative should return 1",
708 1,
709 sf.getHighestOrderZeroDerivative());
710 assertTrue(
711 "Default cosine switch should be equal unity with symmetric inputs", sf.symmetricToUnity());
712
713 for (double x = 0; x <= 1.0; x += 0.01) {
714 double delta = 10.0 * ulp(x);
715 double ax = a * x;
716 double sinOf = FastMath.sin(ax);
717 double cosOf = FastMath.cos(ax);
718
719 double valAt = sf.valueAt(x);
720 double trueVal = cosOf * cosOf;
721 assertEquals(
722 String.format(
723 "Value of default cosine switch at %8.4g should be %8.4g, was %8.4g",
724 x, trueVal, valAt),
725 trueVal,
726 valAt,
727 delta);
728
729 double derivAt = sf.firstDerivative(x);
730 trueVal = -2.0 * a * sinOf * cosOf;
731 delta = (trueVal < 1.0E-10) ? 1.0E-14 : 100.0 * ulp(trueVal);
732 assertEquals(
733 String.format(
734 "First derivative of default cosine switch at %8.4g should be %8.4g, was %8.4g",
735 x, trueVal, derivAt),
736 trueVal,
737 derivAt,
738 delta);
739
740 double d2 = sf.secondDerivative(x);
741 trueVal = 2.0 * a * a * ((sinOf * sinOf) - (cosOf * cosOf));
742 delta = (trueVal < 1.0E-10) ? 1.0E-14 : 100.0 * ulp(trueVal);
743 assertEquals(
744 String.format(
745 "Second derivative of default cosine switch at %8.4g should be %8.4g, was %8.4g",
746 x, trueVal, d2),
747 trueVal,
748 d2,
749 delta);
750 }
751
752 logger.info(" Testing manually constructed default cosine-squared switch.");
753
754 sf = new SquaredTrigSwitch(piOverTwo, true);
755 standardTest(sf, MID_TOLERANCE);
756 a = piOverTwo;
757 assertEquals("Default cosine switch zero bound != 1.0", 1.0, sf.getZeroBound(), ULP_ONE_2);
758 assertEquals("Default cosine switch one bound != 0.0", 0.0, sf.getOneBound(), ULP_ZERO_2);
759 assertFalse("Cosine switches are not constant outside the bounds.", sf.constantOutsideBounds());
760 assertTrue("Cosine switches are valid outside the bounds.", sf.validOutsideBounds());
761 assertEquals(
762 "Default cosine switch max-zero-derivative should return 1",
763 1,
764 sf.getHighestOrderZeroDerivative());
765 assertTrue(
766 "Default cosine switch should be equal unity with symmetric inputs", sf.symmetricToUnity());
767
768 for (double x = 0; x <= 1.0; x += 0.01) {
769 double delta = 10.0 * ulp(x);
770 double ax = a * x;
771 double sinOf = FastMath.sin(ax);
772 double cosOf = FastMath.cos(ax);
773
774 double valAt = sf.valueAt(x);
775 double trueVal = cosOf * cosOf;
776 assertEquals(
777 String.format(
778 "Value of default cosine switch at %8.4g should be %8.4g, was %8.4g",
779 x, trueVal, valAt),
780 trueVal,
781 valAt,
782 delta);
783
784 double derivAt = sf.firstDerivative(x);
785 trueVal = -2.0 * a * sinOf * cosOf;
786 delta = (trueVal < 1.0E-10) ? 1.0E-14 : 100.0 * ulp(trueVal);
787 assertEquals(
788 String.format(
789 "First derivative of default cosine switch at %8.4g should be %8.4g, was %8.4g",
790 x, trueVal, derivAt),
791 trueVal,
792 derivAt,
793 delta);
794
795 double d2 = sf.secondDerivative(x);
796 trueVal = 2.0 * a * a * ((sinOf * sinOf) - (cosOf * cosOf));
797 delta = (trueVal < 1.0E-10) ? 1.0E-14 : 100.0 * ulp(trueVal);
798 assertEquals(
799 String.format(
800 "Second derivative of default cosine switch at %8.4g should be %8.4g, was %8.4g",
801 x, trueVal, d2),
802 trueVal,
803 d2,
804 delta);
805 }
806
807 logger.info(" Testing sine-squared switch with unadjusted (pi/2) bounds.");
808
809 a = 1.0;
810 sf = new SquaredTrigSwitch(a, false);
811 standardTest(sf, MID_TOLERANCE);
812 assertEquals(
813 String.format("Sine switch %s zero bound != 0.0", sf), 0.0, sf.getZeroBound(), ULP_ZERO_2);
814 assertEquals(
815 String.format("Sine switch %s one bound != 1.0", sf),
816 piOverTwo,
817 sf.getOneBound(),
818 ULP_ONE_2);
819 assertFalse("Sine switches are not constant outside the bounds.", sf.constantOutsideBounds());
820 assertTrue("Sine switches are valid outside the bounds.", sf.validOutsideBounds());
821 assertEquals(
822 "Sine switch max-zero-derivative should return 1", 1, sf.getHighestOrderZeroDerivative());
823 assertTrue("Sine switch should be equal unity with symmetric inputs", sf.symmetricToUnity());
824
825 for (double x = 0; x <= 1.0; x += 0.01) {
826 double delta = 10.0 * ulp(x);
827 double ax = a * x;
828 double sinOf = FastMath.sin(ax);
829 double cosOf = FastMath.cos(ax);
830
831 double valAt = sf.valueAt(x);
832 double trueVal = sinOf * sinOf;
833 assertEquals(
834 String.format(
835 "Value of sine switch %s at %8.4g should be %8.4g, was %8.4g", sf, x, trueVal, valAt),
836 trueVal,
837 valAt,
838 delta);
839
840 double derivAt = sf.firstDerivative(x);
841 trueVal = 2.0 * a * sinOf * cosOf;
842 delta = (trueVal < 1.0E-10) ? 1.0E-14 : 100.0 * ulp(trueVal);
843 assertEquals(
844 String.format(
845 "First derivative of sine switch %s at %8.4g should be %8.4g, was %8.4g",
846 sf, x, trueVal, derivAt),
847 trueVal,
848 derivAt,
849 delta);
850
851 double d2 = sf.secondDerivative(x);
852 trueVal = 2.0 * a * a * ((cosOf * cosOf) - (sinOf * sinOf));
853 delta = (trueVal < 1.0E-10) ? 1.0E-14 : 100.0 * ulp(trueVal);
854 assertEquals(
855 String.format(
856 "Second derivative of sine switch %s at %8.4g should be %8.4g, was %8.4g",
857 sf, x, trueVal, d2),
858 trueVal,
859 d2,
860 delta);
861 }
862
863 logger.info(" Testing sine-squared switch with doubled (2.0) bounds.");
864
865 a = 0.5 * piOverTwo;
866 sf = new SquaredTrigSwitch(a, false);
867 standardTest(sf, MID_TOLERANCE);
868 assertEquals(
869 String.format("Sine switch %s zero bound != 0.0", sf), 0.0, sf.getZeroBound(), ULP_ZERO_2);
870 assertEquals(
871 String.format("Sine switch %s one bound != 1.0", sf),
872 2.0,
873 sf.getOneBound(),
874 2.0 * ulp(2.0));
875 assertFalse("Sine switches are not constant outside the bounds.", sf.constantOutsideBounds());
876 assertTrue("Sine switches are valid outside the bounds.", sf.validOutsideBounds());
877 assertEquals(
878 "Sine switch max-zero-derivative should return 1", 1, sf.getHighestOrderZeroDerivative());
879 assertTrue("Sine switch should be equal unity with symmetric inputs", sf.symmetricToUnity());
880
881 for (double x = 0; x <= 1.0; x += 0.01) {
882 double delta = 10.0 * ulp(x);
883 double ax = a * x;
884 double sinOf = FastMath.sin(ax);
885 double cosOf = FastMath.cos(ax);
886
887 double valAt = sf.valueAt(x);
888 double trueVal = sinOf * sinOf;
889 assertEquals(
890 String.format(
891 "Value of sine switch %s at %8.4g should be %8.4g, was %8.4g", sf, x, trueVal, valAt),
892 trueVal,
893 valAt,
894 delta);
895
896 double derivAt = sf.firstDerivative(x);
897 trueVal = 2.0 * a * sinOf * cosOf;
898 delta = (trueVal < 1.0E-10) ? 1.0E-14 : 100.0 * ulp(trueVal);
899 assertEquals(
900 String.format(
901 "First derivative of sine switch %s at %8.4g should be %8.4g, was %8.4g",
902 sf, x, trueVal, derivAt),
903 trueVal,
904 derivAt,
905 delta);
906
907 double d2 = sf.secondDerivative(x);
908 trueVal = 2.0 * a * a * ((cosOf * cosOf) - (sinOf * sinOf));
909 delta = (trueVal < 1.0E-10) ? 1.0E-14 : 100.0 * ulp(trueVal);
910 assertEquals(
911 String.format(
912 "Second derivative of sine switch %s at %8.4g should be %8.4g, was %8.4g",
913 sf, x, trueVal, d2),
914 trueVal,
915 d2,
916 delta);
917 }
918
919 logger.info(" Testing cosine-squared switch with unadjusted (pi/2) bounds.");
920
921 a = 1.0;
922 sf = new SquaredTrigSwitch(a, true);
923 standardTest(sf, MID_TOLERANCE);
924 assertEquals(
925 String.format("Cosine switch %s zero bound != 1.0", sf),
926 piOverTwo,
927 sf.getZeroBound(),
928 2.0 * ulp(piOverTwo));
929 assertEquals(
930 String.format("Cosine switch %s one bound != 0.0", sf), 0.0, sf.getOneBound(), ULP_ZERO_2);
931 assertFalse("Cosine switches are not constant outside the bounds.", sf.constantOutsideBounds());
932 assertTrue("Cosine switches are valid outside the bounds.", sf.validOutsideBounds());
933 assertEquals(
934 "Cosine switch max-zero-derivative should return 1", 1, sf.getHighestOrderZeroDerivative());
935 assertTrue("Cosine switch should be equal unity with symmetric inputs", sf.symmetricToUnity());
936
937 for (double x = 0; x <= 1.0; x += 0.01) {
938 double delta = 50.0 * ulp(x);
939 double ax = a * x;
940 double sinOf = FastMath.sin(ax);
941 double cosOf = FastMath.cos(ax);
942
943 double valAt = sf.valueAt(x);
944 double trueVal = cosOf * cosOf;
945 assertEquals(
946 String.format(
947 "Value of cosine switch %s at %8.4g should be %8.4g, was %8.4g",
948 sf, x, trueVal, valAt),
949 trueVal,
950 valAt,
951 delta);
952
953 double derivAt = sf.firstDerivative(x);
954 trueVal = -2.0 * a * sinOf * cosOf;
955 delta = (trueVal < 1.0E-10) ? 1.0E-14 : 200.0 * ulp(trueVal);
956 assertEquals(
957 String.format(
958 "First derivative of cosine switch %s at %8.4g should be %8.4g, was %8.4g",
959 sf, x, trueVal, derivAt),
960 trueVal,
961 derivAt,
962 delta);
963
964 double d2 = sf.secondDerivative(x);
965 trueVal = 2.0 * a * a * ((sinOf * sinOf) - (cosOf * cosOf));
966 delta = (trueVal < 1.0E-10) ? 1.0E-14 : 200.0 * ulp(trueVal);
967 assertEquals(
968 String.format(
969 "Second derivative of cosine switch %s at %8.4g should be %8.4g, was %8.4g",
970 sf, x, trueVal, d2),
971 trueVal,
972 d2,
973 delta);
974 }
975
976 logger.info(" Testing cosine-squared switch with doubled (2.0) bounds..");
977
978 a = 0.5 * piOverTwo;
979 sf = new SquaredTrigSwitch(a, true);
980 standardTest(sf, MID_TOLERANCE);
981 assertEquals(
982 String.format("Cosine switch %s zero bound != 2.0", sf),
983 2.0,
984 sf.getZeroBound(),
985 2.0 * ulp(2.0));
986 assertEquals(
987 String.format("Cosine switch %s one bound != 0.0", sf), 0.0, sf.getOneBound(), ULP_ZERO_2);
988 assertFalse("Cosine switches are not constant outside the bounds.", sf.constantOutsideBounds());
989 assertTrue("Cosine switches are valid outside the bounds.", sf.validOutsideBounds());
990 assertEquals(
991 "Cosine switch max-zero-derivative should return 1", 1, sf.getHighestOrderZeroDerivative());
992 assertTrue("Cosine switch should be equal unity with symmetric inputs", sf.symmetricToUnity());
993
994 for (double x = 0; x <= 1.0; x += 0.01) {
995 double delta = 50.0 * ulp(x);
996 double ax = a * x;
997 double sinOf = FastMath.sin(ax);
998 double cosOf = FastMath.cos(ax);
999
1000 double valAt = sf.valueAt(x);
1001 double trueVal = cosOf * cosOf;
1002 assertEquals(
1003 String.format(
1004 "Value of cosine switch %s at %8.4g should be %8.4g, was %8.4g",
1005 sf, x, trueVal, valAt),
1006 trueVal,
1007 valAt,
1008 delta);
1009
1010 double derivAt = sf.firstDerivative(x);
1011 trueVal = -2.0 * a * sinOf * cosOf;
1012 delta = (trueVal < 1.0E-10) ? 1.0E-14 : 200.0 * ulp(trueVal);
1013 assertEquals(
1014 String.format(
1015 "First derivative of cosine switch %s at %8.4g should be %8.4g, was %8.4g",
1016 sf, x, trueVal, derivAt),
1017 trueVal,
1018 derivAt,
1019 delta);
1020
1021 double d2 = sf.secondDerivative(x);
1022 trueVal = 2.0 * a * a * ((sinOf * sinOf) - (cosOf * cosOf));
1023 delta = (trueVal < 1.0E-10) ? 1.0E-14 : 200.0 * ulp(trueVal);
1024 assertEquals(
1025 String.format(
1026 "Second derivative of cosine switch %s at %8.4g should be %8.4g, was %8.4g",
1027 sf, x, trueVal, d2),
1028 trueVal,
1029 d2,
1030 delta);
1031 }
1032 }
1033
1034
1035
1036
1037
1038
1039
1040 private void standardTest(UnivariateSwitchingFunction sf) {
1041 standardTest(sf, ULP_ONE_100);
1042 }
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052 private void standardTest(UnivariateSwitchingFunction sf, double tolerance) {
1053 double oneBound = sf.getOneBound();
1054 double zeroBound = sf.getZeroBound();
1055 double increment = ((oneBound - zeroBound) * 0.01);
1056
1057 double minBound = 0.0 - tolerance;
1058 double maxBound = 1.0 + tolerance;
1059
1060 for (int i = 0; i < 101; i++) {
1061 double pastLB = i * increment;
1062
1063 double x = (i == 100) ? oneBound : zeroBound + pastLB;
1064 double val = sf.valueAt(x);
1065 assertTrue(
1066 String.format(
1067 "Switching function %s value at %8.4g was %8.4g, not in the range 0-1 inclusive",
1068 sf.toString(), x, val),
1069 val >= minBound && val <= maxBound);
1070
1071 if (sf.symmetricToUnity()) {
1072 double symmX = oneBound - pastLB;
1073 double symmVal = sf.valueAt(symmX);
1074 assertEquals(
1075 String.format(
1076 "Switching function %s should be "
1077 + "symmetrical; values %7.4f and %7.4f at %7.4f and %7.4f "
1078 + "do not sum to unity",
1079 sf.toString(), val, symmVal, x, symmX),
1080 1.0,
1081 (val + symmVal),
1082 tolerance);
1083 }
1084 }
1085
1086 boolean validOutside = sf.validOutsideBounds();
1087 boolean constantOutside = sf.constantOutsideBounds();
1088 double valAtUB = sf.valueAt(sf.getOneBound());
1089 double valAtLB = sf.valueAt(sf.getZeroBound());
1090 if (Math.abs(valAtLB) < tolerance) {
1091 assertEquals(
1092 String.format(
1093 "Switching function %s value at zero bound %8.4g was not 0.0 or 1.0, was %8.4g",
1094 sf.toString(), zeroBound, valAtLB),
1095 0.0,
1096 valAtLB,
1097 tolerance);
1098 assertEquals(
1099 String.format(
1100 "Switching function %s value at one bound %8.4g was not 0.0 or 1.0, was %8.4g",
1101 sf.toString(), oneBound, valAtUB),
1102 1.0,
1103 valAtUB,
1104 tolerance);
1105 } else {
1106 assertEquals(
1107 String.format(
1108 "Switching function %s value at zero bound %8.4g was not 0.0 or 1.0, was %8.4g",
1109 sf.toString(), zeroBound, valAtLB),
1110 1.0,
1111 valAtLB,
1112 tolerance);
1113 assertEquals(
1114 String.format(
1115 "Switching function %s value at one bound %8.4g was not 0.0 or 1.0, was %8.4g",
1116 sf.toString(), oneBound, valAtUB),
1117 0.0,
1118 valAtUB,
1119 tolerance);
1120 logger.info(
1121 String.format(
1122 " Value of switching function %s at zero bound was 1.0, not 0.0; switching functions usually start at 0",
1123 sf));
1124 }
1125 if (validOutside || constantOutside) {
1126 for (int i = 1; i < 251; i++) {
1127 double pastBounds = i * increment;
1128 double x = zeroBound - pastBounds;
1129 double val = sf.valueAt(x);
1130 assertTrue(
1131 String.format(
1132 "Switching function %s value at %8.4g (outside lb-ub) was %8.4g, not in the range 0-1 inclusive",
1133 sf.toString(), x, val),
1134 val > minBound && val < maxBound);
1135 if (constantOutside) {
1136 assertEquals(
1137 String.format(
1138 "Switching function %s value at %8.4g was %8.4g, did not match zero bound value %8.4g",
1139 sf.toString(), x, val, valAtLB),
1140 valAtLB,
1141 val,
1142 tolerance);
1143 }
1144
1145 x = oneBound + pastBounds;
1146 val = sf.valueAt(x);
1147 assertTrue(
1148 String.format(
1149 "Switching function %s value at %8.4g (outside lb-ub) was %8.4g, not in the range 0-1 inclusive",
1150 sf.toString(), x, val),
1151 val >= 0.0 && val <= 1.0);
1152 if (constantOutside) {
1153 assertEquals(
1154 String.format(
1155 "Switching function %s value at %8.4g was %8.4g, did not match one bound value %8.4g",
1156 sf.toString(), x, val, valAtUB),
1157 valAtUB,
1158 val,
1159 tolerance);
1160 }
1161 }
1162 }
1163
1164 int maxZeroOrder = sf.getHighestOrderZeroDerivative();
1165 if (maxZeroOrder >= 1) {
1166 double deriv = sf.firstDerivative(zeroBound);
1167 assertEquals(
1168 String.format(
1169 "Switching function %s first derivative at lb %8.4g was nonzero value %8.4g",
1170 sf.toString(), zeroBound, deriv),
1171 0.0,
1172 deriv,
1173 tolerance);
1174 deriv = sf.firstDerivative(oneBound);
1175 assertEquals(
1176 String.format(
1177 "Switching function %s first derivative at ub %8.4g was nonzero value %8.4g",
1178 sf.toString(), oneBound, deriv),
1179 0.0,
1180 deriv,
1181 tolerance);
1182 if (maxZeroOrder >= 2) {
1183 deriv = sf.secondDerivative(zeroBound);
1184 assertEquals(
1185 String.format(
1186 "Switching function %s second derivative at lb %8.4g was nonzero value %8.4g",
1187 sf.toString(), zeroBound, deriv),
1188 0.0,
1189 deriv,
1190 tolerance);
1191 deriv = sf.secondDerivative(oneBound);
1192 assertEquals(
1193 String.format(
1194 "Switching function %s second derivative at ub %8.4g was nonzero value %8.4g",
1195 sf.toString(), oneBound, deriv),
1196 0.0,
1197 deriv,
1198 tolerance);
1199 for (int i = 3; i <= maxZeroOrder; i++) {
1200 deriv = sf.nthDerivative(zeroBound, i);
1201 assertEquals(
1202 String.format(
1203 "Switching function %s %d-order derivative at lb %8.4g was nonzero value %8.4g",
1204 sf.toString(), i, zeroBound, deriv),
1205 0.0,
1206 deriv,
1207 tolerance);
1208 deriv = sf.nthDerivative(oneBound, i);
1209 assertEquals(
1210 String.format(
1211 "Switching function %s %d-order derivative at ub %8.4g was nonzero value %8.4g",
1212 sf.toString(), i, oneBound, deriv),
1213 0.0,
1214 deriv,
1215 tolerance);
1216 }
1217 }
1218 }
1219
1220 int maxOrderAtZero = sf.highestOrderZeroDerivativeAtZeroBound();
1221 if (maxOrderAtZero != maxZeroOrder && maxOrderAtZero > 0) {
1222 double deriv = sf.firstDerivative(zeroBound);
1223 assertEquals(
1224 String.format(
1225 "Switching function %s first derivative at lb %8.4g was nonzero value %8.4g",
1226 sf.toString(), zeroBound, deriv),
1227 0.0,
1228 deriv,
1229 tolerance);
1230 if (maxOrderAtZero >= 2) {
1231 deriv = sf.secondDerivative(zeroBound);
1232 assertEquals(
1233 String.format(
1234 "Switching function %s second derivative at lb %8.4g was nonzero value %8.4g",
1235 sf.toString(), zeroBound, deriv),
1236 0.0,
1237 deriv,
1238 tolerance);
1239 for (int i = 3; i <= maxZeroOrder; i++) {
1240 deriv = sf.nthDerivative(zeroBound, i);
1241 assertEquals(
1242 String.format(
1243 "Switching function %s %d-order derivative at lb %8.4g was nonzero value %8.4g",
1244 sf.toString(), i, zeroBound, deriv),
1245 0.0,
1246 deriv,
1247 tolerance);
1248 }
1249 }
1250 }
1251 }
1252 }