View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2024.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
36  //
37  // ******************************************************************************
38  package ffx.numerics.func1d;
39  
40  import static ffx.numerics.func1d.QuasiLinearThetaMap.Branch;
41  import static ffx.numerics.func1d.QuasiLinearThetaMap.Branch.A;
42  import static ffx.numerics.func1d.QuasiLinearThetaMap.Branch.B;
43  import static ffx.numerics.func1d.QuasiLinearThetaMap.Branch.C;
44  import static ffx.numerics.func1d.QuasiLinearThetaMap.Branch.D;
45  import static java.lang.String.format;
46  import static org.junit.Assert.assertEquals;
47  import static org.junit.Assert.assertTrue;
48  
49  import java.util.Arrays;
50  import java.util.Collection;
51  import java.util.OptionalDouble;
52  
53  import ffx.utilities.FFXTest;
54  import org.junit.Test;
55  import org.junit.runner.RunWith;
56  import org.junit.runners.Parameterized;
57  
58  @RunWith(Parameterized.class)
59  public class QuasiLinearSwitchTest extends FFXTest {
60    private static final double TOL = 1E-11;
61    private final String info;
62    private final QuasiLinearThetaMap mapF;
63    private final double[] expectedConsts;
64    private final double[][] expecteds;
65    private final Branch[] primaryBranch;
66    private final Branch[] secondaryBranch;
67    private final int nVals;
68  
69    public QuasiLinearSwitchTest(
70        String info,
71        OptionalDouble theta0,
72        double[] eConsts,
73        double[][] eVals,
74        Branch[] primary,
75        Branch[] secondary) {
76      this.info = info;
77      mapF =
78          theta0.isPresent()
79              ? new QuasiLinearThetaMap(theta0.getAsDouble())
80              : new QuasiLinearThetaMap();
81      expectedConsts = eConsts;
82      expecteds = eVals;
83      primaryBranch = primary;
84      secondaryBranch = secondary;
85      nVals = eVals.length;
86      assertTrue(
87          "Inequal number of arguments for expected vals & branches!",
88          nVals == primary.length && nVals == secondary.length);
89    }
90  
91    @Parameterized.Parameters
92    public static Collection<Object[]> data() {
93      return Arrays.asList(
94          new Object[][] {
95            {
96              // Test each endpoint & each midpoint.
97              "Default Linear Theta Map",
98              OptionalDouble.empty(),
99              // r, a, b, c
100             new double[] {
101               6.586293806435631, 0.32876610686815605, -0.016424593043157933, -5.586293806435631
102             },
103             new double[][] {
104               {-Math.PI, 1.0, 0.0, -3.2931469032178153},
105               {(0.05 - Math.PI), 0.995884423889855, -0.16458874650913316, -3.28903132710767},
106               {(0.1 - Math.PI), 0.9835479823563423, -0.32876610686815605, -3.2766948855741576},
107               {0.5 * (0.1 - Math.PI), 0.4835616946565922, -0.32876610686815605, 0.0},
108               {-0.1, 0.0164520176436577, -0.32876610686815605, 3.276694885574158},
109               {-0.05, 0.004115576110145532, -0.16458874650913274, 3.28903132710767},
110               {0, 0, 0, 3.2931469032178153},
111               {0.05, 0.004115576110145532, 0.16458874650913274, 3.28903132710767},
112               {0.1, 0.0164520176436577, 0.32876610686815605, 3.276694885574158},
113               {0.5 * (Math.PI - 0.1), 0.48356169465659227, 0.32876610686815605, 0.0},
114               {Math.PI - 0.1, 0.9835479823563427, 0.3287661068681553, -3.2766948855741576},
115               {Math.PI - 0.05, 0.995884423889855, 0.16458874650913316, -3.28903132710767},
116               {Math.PI, 1.0, 0.0, -3.2931469032178153}
117             },
118             new QuasiLinearThetaMap.Branch[] {D, D, D, C, A, A, A, A, A, B, D, D, D},
119             new QuasiLinearThetaMap.Branch[] {D, D, C, C, C, A, A, A, B, B, B, D, D}
120           },
121           {
122             "Linear Theta Map: t0 = 0.1 (manual)",
123             OptionalDouble.of(0.1),
124             new double[] {
125               6.586293806435631, 0.32876610686815605, -0.016424593043157933, -5.586293806435631
126             },
127             new double[][] {
128               {-Math.PI, 1.0, 0.0, -3.2931469032178153},
129               {(0.05 - Math.PI), 0.995884423889855, -0.16458874650913316, -3.28903132710767},
130               {(0.1 - Math.PI), 0.9835479823563423, -0.32876610686815605, -3.2766948855741576},
131               {0.5 * (0.1 - Math.PI), 0.4835616946565922, -0.32876610686815605, 0.0},
132               {-0.1, 0.0164520176436577, -0.32876610686815605, 3.276694885574158},
133               {-0.05, 0.004115576110145532, -0.16458874650913274, 3.28903132710767},
134               {0, 0, 0, 3.2931469032178153},
135               {0.05, 0.004115576110145532, 0.16458874650913274, 3.28903132710767},
136               {0.1, 0.0164520176436577, 0.32876610686815605, 3.276694885574158},
137               {0.5 * (Math.PI - 0.1), 0.48356169465659227, 0.32876610686815605, 0.0},
138               {Math.PI - 0.1, 0.9835479823563427, 0.3287661068681553, -3.2766948855741576},
139               {Math.PI - 0.05, 0.995884423889855, 0.16458874650913316, -3.28903132710767},
140               {Math.PI, 1.0, 0.0, -3.2931469032178153}
141             },
142             new QuasiLinearThetaMap.Branch[] {D, D, D, C, A, A, A, A, A, B, D, D, D},
143             new QuasiLinearThetaMap.Branch[] {D, D, C, C, C, A, A, A, B, B, B, D, D}
144           },
145         });
146   }
147 
148   @Test
149   public void testPoints() {
150     for (int i = 0; i < 4; i++) {
151       assertEquals(
152           "Incorrect constant " + i + ": ", mapF.getConstants()[i], expectedConsts[i], TOL);
153     }
154     for (int i = 0; i < nVals; i++) {
155       double[] exp = expecteds[i];
156       Branch prim = primaryBranch[i];
157       Branch secon = secondaryBranch[i];
158       if (prim == secon) {
159         testMidPoint(exp, prim);
160       } else {
161         testJoint(exp, prim, secon);
162       }
163     }
164   }
165 
166   @Override
167   public String toString() {
168     return info;
169   }
170 
171   private void testMidPoint(double[] expect, Branch br) {
172     double t = expect[0];
173     assertEquals("Incorrect branching!", br, mapF.getBranch(t));
174     double eVal = expect[1];
175     double val = mapF.valueAt(t);
176     assertEquals("A midpoint value was incorrect!", val, eVal, TOL);
177     val = mapF.val(t, br);
178     assertEquals("A midpoint value was incorrect when the branch was specified!", val, eVal, TOL);
179 
180     eVal = expect[2];
181     val = mapF.firstDerivative(t);
182     assertEquals("A midpoint first derivative was incorrect!", val, eVal, TOL);
183     val = mapF.fd(t, br);
184     assertEquals(
185         "A midpoint first derivative was incorrect when the branch was specified!", val, eVal, TOL);
186 
187     eVal = expect[3];
188     val = mapF.secondDerivative(t);
189     assertEquals("A midpoint second derivative was incorrect!", val, eVal, TOL);
190     val = mapF.sd(t, br);
191     assertEquals(
192         "A midpoint second derivative was incorrect when the branch was specified!",
193         val,
194         eVal,
195         TOL);
196   }
197 
198   private void testJoint(double[] expect, Branch prim, Branch second) {
199     double t = expect[0];
200     Branch received = mapF.getBranch(t);
201     boolean rightBranch = (prim == received) || (second == received);
202     assertTrue(
203         format(
204             " Did not find any expected branch! Found: %s. "
205                 + "Primary expected: %s. Secondary expected: %s",
206             received, prim, second),
207         rightBranch);
208 
209     double eVal = expect[1];
210     double val = mapF.valueAt(t);
211     assertEquals("A joint value was incorrect!", val, eVal, TOL);
212     val = mapF.val(t, prim);
213     assertEquals("A joint value was incorrect when the branch was specified!", val, eVal, TOL);
214     val = mapF.val(t, second);
215     assertEquals("A joint value was incorrect for the alternate branch!", val, eVal, TOL);
216 
217     eVal = expect[2];
218     val = mapF.firstDerivative(t);
219     assertEquals("A joint first derivative was incorrect!", val, eVal, TOL);
220     val = mapF.fd(t, prim);
221     assertEquals(
222         "A joint first derivative was incorrect when the branch was specified!", val, eVal, TOL);
223     val = mapF.fd(t, second);
224     assertEquals(
225         "A joint first derivative was incorrect for the alternate branch!", val, eVal, TOL);
226 
227     eVal = expect[3];
228     val = mapF.sd(t, prim);
229     assertEquals("A joint second derivative was incorrect!", val, eVal, TOL);
230   }
231 }