Package parasol :: Module parameters
[hide private]
[frames] | no frames]

Source Code for Module parasol.parameters

  1   
  2  __author__ = "Charlie Taylor (charlietaylor@sourceforge.net)" 
  3  __version__ = " 1.0 " 
  4  __date__ = "Jan 1, 2009" 
  5  __copyright__ = "Copyright (c) 2009 Charlie Taylor" 
  6  __license__ = "BSD" 
  7   
  8  from Goal import Goal 
  9  from cast import floatDammit, intDammit 
 10  from scipy.optimize import fminbound 
 11   
 12  POS_INF =  1.0E300 
 13  NEG_INF = -1.0E300 
 14   
 15   
16 -class MinMaxPair( object ):
17 ''' 18 A MinMaxPair is a paired InputParam and OutputParam 19 where the val property of the InputParam is found 20 such that the val property of the OutputParam is minimized 21 or maximized for the mathematical model under consideration 22 (i.e. as modeled by the function call, functionToCall). 23 24 @note: The InputParam's val property will be found in the range minVal to maxVal 25 such that OutputParam.val is optimized when calling functionToCall 26 (i.e. a Min/Max constraint is applied to the parameter pair). 27 28 @note: It is the users responsibility to assure that InputParam.val is used 29 in functionToCall 30 AND that functionToCall reassigns the val property of OutputParam 31 '''
32 - def __init__(self, inpParam=None, outParam=None, functionToCall=None, 33 findmin=0, tolerance=1.0E-6, maxLoops=400, failValue=None):
34 35 '''Initialize parameter properties 36 37 @param inpParam: input parameter object (InputParam) 38 @param outParam: output parameter object (OutputParam) 39 @param functionToCall: function using InputParam to calc OutputParam (callable) 40 @param findmin: findmin is a logic flag (1=minimize, 0=maximize) (int) 41 @param tolerance: allowable error of OutputParam.val (float) 42 @param maxLoops: maximum loops in root solver 43 @param failValue: returned value if solution attempt fails, 44 (if not input use inpParam.minVal) 45 ''' 46 self.inpParam = inpParam #: InputParam object 47 self.outParam = outParam #: OutputParam object 48 self.functionToCall = functionToCall #: function using InputParam to calc OutputParam 49 self.findmin = floatDammit(findmin) #: flag to minimize or maximize OutputParam.val 50 self.tolerance = floatDammit(tolerance) #: allowable error of OutputParam.val 51 self.maxLoops = intDammit(maxLoops) #: maximum loops in root solver 52 53 if failValue==None: 54 failValue = inpParam.minVal 55 self.failValue = failValue #: returned value if solution attempt fails
56 57
58 - def optFunc(self, val):
59 self.inpParam.val = val 60 self.functionToCall() 61 62 #print 'in optFunc, val=',val,' result val=',self.outParam.val 63 64 if self.findmin: # minimize or maximize 65 return self.outParam.val 66 else: 67 return -1.0 * self.outParam.val
68
69 - def reCalc(self):
70 '''calculate the value of inpParam.val that optimizes outParam.val 71 Show non-convergence notification messages if applicable. 72 ''' 73 74 val, fval, ierror, numFuncCalls = fminbound(self.optFunc, 75 self.inpParam.minVal, self.inpParam.maxVal, 76 args=(), xtol=self.tolerance, maxfun=self.maxLoops, full_output=1, disp=1) 77 78 #print 'val, fval, ierror, numFuncCalls=',val, fval, ierror, numFuncCalls 79 80 if ierror: 81 self.inpParam.val = floatDammit(self.failValue) 82 else: 83 self.inpParam.val = val
84 85
86 -class FeasiblePair( object ):
87 ''' 88 A FeasiblePair is a paired InputParam and OutputParam 89 where the val property of the InputParam is found 90 such that the val property of the OutputParam is equal to 91 the desired feasibleVal for the mathematical model under consideration 92 (i.e. as modeled by the function call, functionToCall). 93 94 @note: The InputParam's val property will be found in the range minVal to maxVal 95 such that OutputParam.val==feasibleVal when calling functionToCall 96 (i.e. A Feasibilty constraint is applied to the parameter pair). 97 98 @note: It is the users responsibility to assure that InputParam.val is used 99 in functionToCall 100 AND that functionToCall reassigns the val property of OutputParam 101 '''
102 - def __init__(self, inpParam=None, outParam=None, functionToCall=None, 103 feasibleVal=0.0, tolerance=1.0E-6, maxLoops=40, failValue=None):
104 105 '''Initialize parameter properties 106 107 @param inpParam: input parameter object (InputParam) 108 @param outParam: output parameter object (OutputParam) 109 @param functionToCall: function using InputParam to calc OutputParam (callable) 110 @param feasibleVal: feasible value that OutputParam Must have (float) 111 @param tolerance: allowable error of OutputParam.val (float) 112 @param maxLoops: maximum loops in root solver 113 @param failValue: returned value if solution attempt fails, 114 (if not input use inpParam.minVal) 115 ''' 116 self.inpParam = inpParam #: InputParam object 117 self.outParam = outParam #: OutputParam object 118 self.functionToCall = functionToCall #: function using InputParam to calc OutputParam 119 self.feasibleVal = floatDammit(feasibleVal) #: feasable value of OutputParam.val 120 self.tolerance = floatDammit(tolerance) #: allowable error of OutputParam.val 121 self.maxLoops = intDammit(maxLoops) #: maximum loops in root solver 122 123 if failValue==None: 124 failValue = inpParam.minVal 125 self.failValue = failValue #: returned value if solution attempt fails 126 127 self.G = Goal(goalVal=feasibleVal, minX=inpParam.minVal, maxX=inpParam.maxVal, 128 funcOfX=self.feasibleFunc, tolerance=tolerance, maxLoops=maxLoops, failValue=failValue)
129
130 - def feasibleFunc(self, val):
131 self.inpParam.val = val 132 self.functionToCall() 133 return self.outParam.val
134
135 - def reCalc(self):
136 '''calculate the value of inpParam.val that gives feasibleVal==outParam.val''' 137 138 val, ierror = self.G() 139 if ierror: 140 self.inpParam.val = floatDammit(self.failValue) 141 else: 142 self.inpParam.val = val
143
144 -class OutputParam( object ):
145 ''' 146 Generic Output Parameter. 147 148 Output Parameter has a current value and limit values. 149 150 @note: If loLimit and hiLimit are reversed, they are corrected; 151 Their default values are effectively at negative and positive infinity. 152 153 @note: If assigning val, limits are NOT checked, however, 154 "inRange" function will return false; this is because out-of-range 155 assignments are allowed and expected. 156 '''
157 - def __init__(self, name='a', description='speed of sound', units='ft/sec', 158 val=1.0, loLimit=NEG_INF, hiLimit=POS_INF):
159 160 '''Initialize parameter properties 161 162 @param name: simple name (str) 163 @param description: long description (str) 164 @param units: physical units; Blank string if no units (str) 165 @param val: current numeric value (float) 166 @param loLimit: lower limit value (float) 167 @param hiLimit: upper limit value value (float) 168 ''' 169 170 # if loLimit, hiLimit are reversed, correct them 171 if loLimit > hiLimit: 172 loLimit, hiLimit = hiLimit, loLimit 173 174 self.name = name #: simple name 175 self.description = description #: long description 176 self.units = units #: physical units; Blank string if no units 177 self.val = floatDammit(val) #: current numeric value 178 self.loLimit = floatDammit(loLimit) #: lower limit value 179 self.hiLimit = floatDammit(hiLimit) #: upper limit value
180
181 - def inRange(self):
182 '''returns True if val is within limits, False otherwise''' 183 return self.val>=self.loLimit and self.val<=self.hiLimit
184
185 - def hasLowConstraint(self):
186 '''returns True if loLimit is > NEG_INF''' 187 return self.loLimit > NEG_INF
188
189 - def hasHighConstraint(self):
190 '''returns True if hiLimit is < POS_INF''' 191 return self.hiLimit < POS_INF
192
193 - def isConstrained(self):
194 return self.hasLowConstraint() or self.hasHighConstraint()
195 196
197 -class InputParam( object ):
198 ''' 199 Generic Input Parameter. 200 201 Has a current value and a range of possible values 202 (arranged either in linear steps or geometric steps) 203 204 @note: If stepVal is input, then use it to create range List (rangeL) 205 Otherwise use NSteps to create range List 206 207 @note: If linear is False, then use geometric steps from minVal to maxVal 208 209 @note: If geometric range is illegal, switch to linear range 210 '''
211 - def __init__(self, name='a', description='speed of sound', units='ft/sec', 212 val=1.0, minVal=0.0, maxVal=10.0, NSteps=10, stepVal=None, linear=1):
213 214 '''Initialize parameter properties and calculate range list (rangeL) 215 216 @param name: simple name (str) 217 @param description: long description (str) 218 @param units: physical units; Blank string if no units (str) 219 @param val: current numeric value (float) 220 @param minVal: minimum value (float) 221 @param maxVal: maximum value (float) 222 @param NSteps: number of steps from minVal to maxVal in rangeL (int) 223 @param stepVal: if input, then step size from minVal to maxVal in rangeL (float) 224 @param linear: linear/geometric flag for steps from minVal to maxVal (boolean or int) 225 ''' 226 227 # if minVal, maxVal are reversed, correct them 228 if minVal > maxVal: 229 minVal, maxVal = maxVal, minVal 230 231 self.name = name #: simple name 232 self.description = description #: long description 233 self.units = units #: physical units; Blank string if no units 234 self.val = floatDammit(val) #: current numeric value 235 self.savedVal = self.val #: temporary storage location for val 236 self.InitialVal = self.val #: initial val (for possible reset) 237 238 self.minVal = floatDammit(minVal) #: minimum value 239 self.maxVal = floatDammit(maxVal) #: maximum value 240 self.linear = intDammit(linear) #: linear/geometric flag for steps from minVal to maxVal 241 242 if NSteps<1: NSteps=1 243 244 if stepVal and linear: #use stepVal only if linear steps 245 try: 246 NSteps = int( (maxVal-minVal)/stepVal ) 247 except: 248 stepVal = (maxVal-minVal)/float(NSteps) 249 else: 250 stepVal = (maxVal-minVal)/float(NSteps) 251 252 self.NSteps = NSteps #: number of steps from minVal to maxVal in rangeL 253 self.stepVal = stepVal #: if input, then step size from minVal to maxVal in rangeL 254 255 256 if linear : 257 self.buildLinearRange() 258 else: 259 try: 260 self.buildGeometricRange() 261 self.stepVal = None #: undefined if using geometric range 262 except: 263 # only annoy the user slightly with non-critical error 264 print 'WARNING... Switched to linear range in parameters.InputParam' 265 self.buildLinearRange() 266 267 # make scaleFactor for other modules to use (e.g. optimize) 268 self.scaleFactor = (abs(self.minVal) + abs(self.maxVal)) / 2.0
269
270 - def buildLinearRange(self):
271 self.rangeL = [self.minVal] #: list of values from minVal to maxVal (inclusive) 272 N=1 273 while self.rangeL[-1] + self.stepVal < self.maxVal: 274 self.rangeL.append( self.minVal + self.stepVal*N ) 275 N += 1 276 self.rangeL.append( self.maxVal ) 277 278 # check for tiny last step; Disallow less than stepVal/20 279 if self.rangeL[-1]-self.rangeL[-2] < self.stepVal/20.0: 280 del(self.rangeL[-2])
281 282
283 - def buildGeometricRange(self):
284 self.rangeL = [self.minVal] #: list of values from minVal to maxVal (inclusive) 285 N=1 286 ratio = self.maxVal / self.minVal 287 if ratio==0.0: 288 stepRat = 1./0. # raise exception if ratio is zero 289 290 stepRat = ratio**( 1.0/float(self.NSteps) ) 291 292 while self.rangeL[-1] * stepRat < self.maxVal: 293 self.rangeL.append( self.rangeL[-1] * stepRat ) 294 N += 1 295 self.rangeL.append( self.maxVal ) 296 297 # check for tiny last step; Disallow stepRat/20 difference in geometric ratio 298 if ratio>1.0: 299 if self.rangeL[-1]/self.rangeL[-2] < stepRat/20.0: 300 del(self.rangeL[-2]) 301 else: 302 if self.rangeL[-1]/self.rangeL[-2] > stepRat*19.0/20.0: 303 del(self.rangeL[-2])
304 305 if __name__ == "__main__": 306 307 IP = InputParam(name='a', description='speed of sound', units='ft/sec', 308 val=1.0, minVal=1000.0, maxVal=6000.0, NSteps=5, stepVal=None, linear=1) 309 print 'check linear range' 310 print IP.rangeL 311 312 print 'check output param' 313 OP = OutputParam(name='delay', description='delay in sound arrival', units='sec', 314 val=1.0, loLimit=1.0, hiLimit=10.0) 315 316 OP.val = 0.0 317 print OP.val,'within limits=',OP.inRange() 318 319 print 320 print 'checking feasible pair'
321 - def feasTest():
322 OP.val = 7000./IP.val # MUST reassign OutputParam.val property 323 return
324 325 FP = FeasiblePair( inpParam=IP, outParam=OP, functionToCall=feasTest, 326 feasibleVal=5.0, tolerance=1.0E-6, maxLoops=40, failValue=None) 327 FP.reCalc() 328 print 'for IP.val=',IP.val,'OP.val=',OP.val,'IP.val should be 1400.0' 329 330 331 print 332 print 'checking min/max pair'
333 - def minmaxTest():
334 x = (IP.val-1000.0)/1000.0 335 OP.val = 10.0 * (x**2 - x**3) # MUST reassign OutputParam.val property 336 return
337 338 MP = MinMaxPair( inpParam=IP, outParam=OP, functionToCall=minmaxTest, 339 findmin=0, tolerance=1.0E-6, maxLoops=400, failValue=None) 340 MP.reCalc() 341 print 'for IP.val=',IP.val,'OP.val=',OP.val,'IP.val should be 1666.666...' 342