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.switching;
39  
40  import static java.lang.String.format;
41  import static org.apache.commons.math3.util.FastMath.pow;
42  
43  /**
44   * A PowerSwitch interpolates between 0 and 1 vi f(x) = (ax)^beta, where x must be between 0 and
45   * 1/a.
46   *
47   * @author Jacob M. Litman
48   * @author Michael J. Schnieders
49   */
50  public class PowerSwitch implements UnivariateSwitchingFunction {
51  
52    /** The multiplier a. */
53    private final double a;
54    /** The power of the switch. */
55    private final double beta;
56    /** The upper bound of the switch. */
57    private final double ub;
58  
59    /** Default Constructor of the PowerSwitch: constructs a linear switch. */
60    public PowerSwitch() {
61      this(1.0, 1.0);
62    }
63  
64    /**
65     * Constructor of the PowerSwitch.
66     *
67     * @param a The upper bound of the switch is 1.0 / a.
68     * @param beta The power of the function f(x) = (ax)^beta,
69     */
70    public PowerSwitch(double a, double beta) {
71      if (a <= 0) {
72        throw new IllegalArgumentException(" The constant a must be > 0");
73      }
74      if (beta == 0) {
75        throw new IllegalArgumentException(" The exponent must be > 0 (preferably >= 1)");
76      }
77      this.a = a;
78      this.beta = beta;
79      ub = 1.0 / a;
80    }
81  
82    /** {@inheritDoc} */
83    @Override
84    public boolean constantOutsideBounds() {
85      return false;
86    }
87  
88    /** {@inheritDoc} */
89    @Override
90    public double firstDerivative(double x) throws IllegalArgumentException {
91      x *= a;
92      return beta * a * pow(x, beta - 1);
93    }
94  
95    /**
96     * Gets the value of beta in f(x) = (a*x)^beta
97     *
98     * @return Exponent of input
99     */
100   public double getExponent() {
101     return beta;
102   }
103 
104   /** {@inheritDoc} */
105   @Override
106   public int getHighestOrderZeroDerivative() {
107     return 0;
108   }
109 
110   /**
111    * Gets the value of a in f(x) = (a*x)^beta.
112    *
113    * @return Multiplier of input
114    */
115   public double getMultiplier() {
116     return a;
117   }
118 
119   /** {@inheritDoc} */
120   @Override
121   public double getOneBound() {
122     return ub;
123   }
124 
125   /** {@inheritDoc} */
126   @Override
127   public double getZeroBound() {
128     return 0;
129   }
130 
131   /**
132    * Power switch derivatives can be zero at the zero bound if the exponent is greater than the
133    * derivative order.
134    *
135    * @return the highest order zero derivative at zero bound
136    */
137   @Override
138   public int highestOrderZeroDerivativeAtZeroBound() {
139     return beta >= 1 ? ((int) beta) - 1 : 0;
140   }
141 
142   /** {@inheritDoc} */
143   @Override
144   public double nthDerivative(double x, int order) throws IllegalArgumentException {
145     x *= a;
146     if (order < 1) {
147       throw new IllegalArgumentException("Order must be >= 1");
148     }
149     switch (order) {
150       case 1 -> {
151         return firstDerivative(x);
152       }
153       case 2 -> {
154         return secondDerivative(x);
155       }
156       default -> {
157         double orderDiff = order - beta;
158         if (orderDiff % 1.0 == 0 && orderDiff >= 1.0) {
159           return 0.0;
160         } else {
161           double val = pow(x, beta - order);
162           for (int i = 0; i < order; i++) {
163             val *= (beta - i) * a;
164           }
165           return val;
166         }
167       }
168     }
169   }
170 
171   /** {@inheritDoc} */
172   @Override
173   public double secondDerivative(double x) throws IllegalArgumentException {
174     x *= a;
175     return beta == 1.0 ? 0.0 : beta * (beta - 1) * a * a * pow(x, beta - 2);
176   }
177 
178   /** {@inheritDoc} */
179   @Override
180   public boolean symmetricToUnity() {
181     return (beta == 1.0);
182   }
183 
184   /** {@inheritDoc} */
185   @Override
186   public String toString() {
187     return format("Power switching function f(x) = (%8.4g * x)^%8.4g", a, beta);
188   }
189 
190   /** {@inheritDoc} */
191   @Override
192   public boolean validOutsideBounds() {
193     return false;
194   }
195 
196   /** {@inheritDoc} */
197   @Override
198   public double valueAt(double x) throws IllegalArgumentException {
199     x *= a;
200     return pow(x, beta);
201   }
202 }