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 }