• Main Page
  • Namespaces
  • Classes
  • Files
  • File List

/home/mark/model/software/ScrumPy/ScrumPy/Util/Simplex.py

00001 """
00002 M.G.Poolman 20/06/01 onwards
00003 module Simplex 
00004 do simplex optimisations a la Nelder and Mead
00005 loosely based on the description in Numerical Recipies in C
00006 """
00007 import random
00008 
00009 class Vertex:
00010     def __init__(self, Params, func, eval=1,**fargs):
00011         """ pre: func is a function that returns a numerical value
00012                  Params is a list of numbers that can be used as parameters to func """
00013                  
00014         self.Params = Params[:]
00015         self.nParams = len(Params)
00016   
00017         self.func = func
00018         if eval:
00019             self.Eval(**fargs)
00020 
00021 
00022     def __len__(self):
00023         return self.nParams
00024 
00025     def __repr__(self):
00026         rv = ""
00027         for p in self.Params:
00028             rv = rv + str(p) + " "
00029         return rv + "  " + str(self.Value)
00030    
00031     def Eval(self, **fargs):
00032         self.Value = self.func(self.Params, **fargs)
00033         
00034     def Copy(self):
00035         rv = Vertex(self.Params, self.func, eval=0)
00036         rv.Value = self.Value
00037         return rv
00038 
00039     def __getitem__(self,n):
00040        return self.Params[n]
00041        
00042     def __setitem__(self,n,value):
00043        self.Params[n] = value
00044         
00045     def __cmp__(self, other):
00046         
00047         diff = self.Value - other.Value
00048         
00049         if diff >0:
00050             return 1
00051         if diff < 0:
00052             return -1
00053             
00054         return 0
00055 
00056     def Randomise(self,fac):
00057         for pidx in range(len(self.Params)):
00058             p = self.Params[pidx]
00059             lo = p*(1-fac)
00060             hi = p*(1+fac)
00061             self.Params[pidx]  = random.uniform(lo, hi) 
00062 
00063 
00064 class Simplex:
00065     
00066     def __init__(self, StartParas, Func, **fargs):
00067         
00068         self.Vertices = []
00069         self.fargs = fargs  # optional arguments passed to the user defined objective function
00070         for n in range(len(StartParas)):
00071             self.Vertices.append(Vertex(StartParas[n], Func, **fargs))
00072     
00073         self.nVertices = len(self.Vertices)    
00074         self.nParams = self.Vertices[0].nParams # assume all s.v[n] are same (implicit in algorithm)
00075 
00076         self.Vertices.sort()
00077         self.RecalcPsum()        
00078         self.WorstIdx = self.nVertices-1        # because indexing starts at 0
00079         self.Worst2Idx = self.WorstIdx-1
00080         self.BestIdx = 0
00081 
00082     def __repr__(self):
00083         rv = ""
00084         for v in self.Vertices:
00085             rv = rv + str(v) + "\n"
00086             
00087         return rv
00088 
00089 
00090     def Eval(self):
00091         for v in self.Vertices:
00092             v.Eval(**self.fargs)
00093         self.Vertices.sort()
00094 
00095 
00096     def StopFunc(self):
00097         """ users can overload this to provide custom stopping criteria """
00098         self.Iters = self.Iters + 1
00099         return self.Iters == 100
00100 
00101 
00102     def RecalcPsum(self):
00103         self.Psum = []
00104         for p in range (self.nParams):
00105             sum = 0.0 
00106             for v in range(self.nVertices):
00107                 sum = sum + self.Vertices[v][p] 
00108             self.Psum.append(sum)
00109      
00110      
00111     def ReplaceWorst(self, Better):
00112 
00113         for p in range(self.nParams):  # keep the Psum list up to date
00114             self.Psum[p] = self.Psum[p] - self.Vertices[self.WorstIdx][p] + Better[p]
00115          
00116         del self.Vertices[self.WorstIdx]
00117         
00118         try:
00119             v = 0                                             # find the point to insert Better 
00120             while self.Vertices[v].Value < Better.Value: # keeping self.Vertices sorted 
00121                 v = v + 1
00122             self.Vertices.insert(v, Better)
00123         except:
00124             self.Vertices.append(Better)
00125 
00126 
00127     def AwayFromWorst(self, Distance):
00128         """ reflect worst point through face of simplex by Distance and replace if better,
00129         Equivalent to the "amotry" routine in NRiC. """
00130      
00131         fac1 = (1.0 - Distance)/self.nParams
00132         fac2 = fac1 - Distance
00133 
00134         TryVert = self.Vertices[self.WorstIdx].Copy()
00135         WorstVal = TryVert.Value
00136 
00137         for p in range(TryVert.nParams):
00138             TryVert[p]= self.Psum[p] * fac1 - self.Vertices[self.WorstIdx][p]*fac2
00139 
00140 
00141         TryVert.Eval(**self.fargs)
00142         if TryVert.Value < WorstVal:
00143             self.ReplaceWorst(TryVert)
00144 
00145         return TryVert.Value
00146      
00147 
00148     def ContractToBest(self):
00149      
00150          fac = 0.5
00151          BestV = self.Vertices[0]
00152          for v in self.Vertices[1:]:
00153              for p in range(self.nParams):
00154                  v[p] = fac*(v[p]+BestV[p])
00155              v.Eval(**self.fargs)
00156          self.Vertices.sort()
00157          self.RecalcPsum()
00158      
00159 
00160     def Run(self):
00161         """ do the downhil simplex until self.StopFun() == 1 """
00162          
00163         Alpha = -1
00164         Beta = 0.5
00165         Gamma = 2.0
00166         self.Iters = 0
00167          
00168         while not self.StopFunc():
00169             
00170             BestVal = self.Vertices[self.BestIdx].Value
00171             WorstVal = self.Vertices[self.WorstIdx].Value
00172             Worst2Val = self.Vertices[self.Worst2Idx].Value
00173             
00174             TryVal = self.AwayFromWorst(Alpha)
00175             if TryVal <= BestVal:               # better than prev. best
00176                 TryVal = self.AwayFromWorst(Gamma)      # so go some more in same direction
00177             elif TryVal >= Worst2Val:
00178                 TryVal = self.AwayFromWorst(Beta) # try 1 dim contraction
00179                 if TryVal >= WorstVal:          # still no improvement
00180                     self.ContractToBest()    
00181                     
00182     def Randomise(self,fac):
00183         for v in self.Vertices:
00184             v.Randomise(fac)
00185             
00186         
00187           
00188      
00189      
00190      
00191      
00192      
00193      
00194      
00195      
00196      
00197      
00198      
00199      

Generated on Tue Sep 4 2012 15:38:02 for ScrumPy by  doxygen 1.7.1