| 1 | """ |
|---|
| 2 | Solvers |
|---|
| 3 | ======= |
|---|
| 4 | |
|---|
| 5 | This module contains an optimization routine based on the simulated |
|---|
| 6 | annealing algorithm. |
|---|
| 7 | |
|---|
| 8 | A minimal interface that mimics a scipy.optimize interface has been |
|---|
| 9 | implemented, and functionality from the mystic solver API has been added |
|---|
| 10 | with reasonable defaults. |
|---|
| 11 | |
|---|
| 12 | Minimal function interface to optimization routines:: |
|---|
| 13 | anneal -- Simulated Annealing solver |
|---|
| 14 | |
|---|
| 15 | The corresponding solver built on mystic's AbstractSolver is:: |
|---|
| 16 | AnnealSolver -- a simulated annealing solver |
|---|
| 17 | |
|---|
| 18 | Mystic solver behavior activated in anneal:: |
|---|
| 19 | - EvaluationMonitor = Sow() |
|---|
| 20 | - StepMonitor = Sow() |
|---|
| 21 | - enable_signal_handler() |
|---|
| 22 | - termination = AveragePopulationImprovement(tolerance) |
|---|
| 23 | |
|---|
| 24 | Usage |
|---|
| 25 | ===== |
|---|
| 26 | |
|---|
| 27 | See `mystic.examples.test_anneal` for an example of using |
|---|
| 28 | AnnealSolver. |
|---|
| 29 | |
|---|
| 30 | All solvers included in this module provide the standard signal handling. |
|---|
| 31 | For more information, see `mystic.mystic.abstract_solver`. |
|---|
| 32 | |
|---|
| 33 | |
|---|
| 34 | History |
|---|
| 35 | ========== |
|---|
| 36 | Based on anneal.py from scipy.optimize: |
|---|
| 37 | Original Author: Travis Oliphant 2002 |
|---|
| 38 | Bug-fixes in 2006 by Tim Leslie |
|---|
| 39 | |
|---|
| 40 | Adapted for Mystic, 2009 |
|---|
| 41 | """ |
|---|
| 42 | |
|---|
| 43 | __all__ = ['AnnealSolver','anneal'] |
|---|
| 44 | |
|---|
| 45 | # Mystic and numpy imports |
|---|
| 46 | from mystic.tools import Null, wrap_function |
|---|
| 47 | from mystic.tools import wrap_bounds |
|---|
| 48 | import numpy |
|---|
| 49 | from numpy import asarray, tan, exp, ones, squeeze, sign, \ |
|---|
| 50 | all, log, sqrt, pi, shape, array, minimum, where |
|---|
| 51 | from numpy import random |
|---|
| 52 | |
|---|
| 53 | from abstract_solver import AbstractSolver |
|---|
| 54 | |
|---|
| 55 | ############################################################# |
|---|
| 56 | # Helper classes and methods |
|---|
| 57 | |
|---|
| 58 | _double_min = numpy.finfo(float).min |
|---|
| 59 | _double_max = numpy.finfo(float).max |
|---|
| 60 | |
|---|
| 61 | # Base class for annealing schedules |
|---|
| 62 | class base_schedule(object): |
|---|
| 63 | def __init__(self): |
|---|
| 64 | self.dwell = 20 |
|---|
| 65 | self.learn_rate = 0.5 |
|---|
| 66 | self.lower = -10 |
|---|
| 67 | self.upper = 10 |
|---|
| 68 | self.Ninit = 50 |
|---|
| 69 | self.accepted = 0 |
|---|
| 70 | self.tests = 0 |
|---|
| 71 | self.feval = 0 |
|---|
| 72 | self.k = 0 |
|---|
| 73 | self.T = None |
|---|
| 74 | |
|---|
| 75 | def init(self, **options): |
|---|
| 76 | self.__dict__.update(options) |
|---|
| 77 | self.lower = asarray(self.lower) |
|---|
| 78 | self.lower = where(self.lower == numpy.NINF, -_double_max, self.lower) |
|---|
| 79 | self.upper = asarray(self.upper) |
|---|
| 80 | self.upper = where(self.upper == numpy.PINF, _double_max, self.upper) |
|---|
| 81 | self.k = 0 |
|---|
| 82 | self.accepted = 0 |
|---|
| 83 | self.feval = 0 |
|---|
| 84 | self.tests = 0 |
|---|
| 85 | |
|---|
| 86 | def getstart_temp(self): |
|---|
| 87 | """ Find a matching starting temperature and starting parameters vector |
|---|
| 88 | i.e. find x0 such that func(x0) = T0. |
|---|
| 89 | |
|---|
| 90 | Parameters |
|---|
| 91 | ---------- |
|---|
| 92 | best_state : _state |
|---|
| 93 | A _state object to store the function value and x0 found. |
|---|
| 94 | |
|---|
| 95 | Returns |
|---|
| 96 | ------- |
|---|
| 97 | x0 : array |
|---|
| 98 | The starting parameters vector. |
|---|
| 99 | """ |
|---|
| 100 | |
|---|
| 101 | assert(not self.dims is None) |
|---|
| 102 | lrange = self.lower |
|---|
| 103 | urange = self.upper |
|---|
| 104 | fmax = _double_min |
|---|
| 105 | fmin = _double_max |
|---|
| 106 | for _ in range(self.Ninit): |
|---|
| 107 | x0 = random.uniform(size=self.dims)*(urange-lrange) + lrange |
|---|
| 108 | fval = self.func(x0, *self.args) |
|---|
| 109 | self.feval += 1 |
|---|
| 110 | if fval > fmax: |
|---|
| 111 | fmax = fval |
|---|
| 112 | if fval < fmin: |
|---|
| 113 | fmin = fval |
|---|
| 114 | bestEnergy = fval |
|---|
| 115 | bestSolution = array(x0) |
|---|
| 116 | |
|---|
| 117 | self.T0 = (fmax-fmin)*1.5 |
|---|
| 118 | return bestSolution, bestEnergy |
|---|
| 119 | |
|---|
| 120 | def accept_test(self, dE): |
|---|
| 121 | T = self.T |
|---|
| 122 | self.tests += 1 |
|---|
| 123 | if dE < 0: |
|---|
| 124 | self.accepted += 1 |
|---|
| 125 | return 1 |
|---|
| 126 | p = exp(-dE*1.0/self.boltzmann/T) |
|---|
| 127 | if (p > random.uniform(0.0, 1.0)): |
|---|
| 128 | self.accepted += 1 |
|---|
| 129 | return 1 |
|---|
| 130 | return 0 |
|---|
| 131 | |
|---|
| 132 | def update_guess(self, x0): |
|---|
| 133 | pass |
|---|
| 134 | |
|---|
| 135 | def update_temp(self, x0): |
|---|
| 136 | pass |
|---|
| 137 | |
|---|
| 138 | # Simulated annealing schedules: 'fast', 'cauchy', and 'boltzmann' |
|---|
| 139 | |
|---|
| 140 | # A schedule due to Lester Ingber |
|---|
| 141 | class fast_sa(base_schedule): |
|---|
| 142 | def init(self, **options): |
|---|
| 143 | self.__dict__.update(options) |
|---|
| 144 | if self.m is None: |
|---|
| 145 | self.m = 1.0 |
|---|
| 146 | if self.n is None: |
|---|
| 147 | self.n = 1.0 |
|---|
| 148 | self.c = self.m * exp(-self.n * self.quench) |
|---|
| 149 | |
|---|
| 150 | def update_guess(self, x0): |
|---|
| 151 | x0 = asarray(x0) |
|---|
| 152 | u = squeeze(random.uniform(0.0, 1.0, size=self.dims)) |
|---|
| 153 | T = self.T |
|---|
| 154 | y = sign(u-0.5)*T*((1+1.0/T)**abs(2*u-1)-1.0) |
|---|
| 155 | xc = y*(self.upper - self.lower) |
|---|
| 156 | xnew = x0 + xc |
|---|
| 157 | return xnew |
|---|
| 158 | |
|---|
| 159 | def update_temp(self): |
|---|
| 160 | self.T = self.T0*exp(-self.c * self.k**(self.quench)) |
|---|
| 161 | self.k += 1 |
|---|
| 162 | return |
|---|
| 163 | |
|---|
| 164 | class cauchy_sa(base_schedule): |
|---|
| 165 | def update_guess(self, x0): |
|---|
| 166 | x0 = asarray(x0) |
|---|
| 167 | numbers = squeeze(random.uniform(-pi/2, pi/2, size=self.dims)) |
|---|
| 168 | xc = self.learn_rate * self.T * tan(numbers) |
|---|
| 169 | xnew = x0 + xc |
|---|
| 170 | return xnew |
|---|
| 171 | |
|---|
| 172 | def update_temp(self): |
|---|
| 173 | self.T = self.T0/(1+self.k) |
|---|
| 174 | self.k += 1 |
|---|
| 175 | return |
|---|
| 176 | |
|---|
| 177 | class boltzmann_sa(base_schedule): |
|---|
| 178 | def update_guess(self, x0): |
|---|
| 179 | std = minimum(sqrt(self.T)*ones(self.dims), (self.upper-self.lower)/3.0/self.learn_rate) |
|---|
| 180 | x0 = asarray(x0) |
|---|
| 181 | xc = squeeze(random.normal(0, 1.0, size=self.dims)) |
|---|
| 182 | |
|---|
| 183 | xnew = x0 + xc*std*self.learn_rate |
|---|
| 184 | return xnew |
|---|
| 185 | |
|---|
| 186 | def update_temp(self): |
|---|
| 187 | self.k += 1 |
|---|
| 188 | self.T = self.T0 / log(self.k+1.0) |
|---|
| 189 | return |
|---|
| 190 | |
|---|
| 191 | class _state(object): |
|---|
| 192 | def __init__(self): |
|---|
| 193 | self.x = None |
|---|
| 194 | self.cost = None |
|---|
| 195 | |
|---|
| 196 | ################################################################################ |
|---|
| 197 | |
|---|
| 198 | class AnnealSolver(AbstractSolver): |
|---|
| 199 | """Simulated annealing optimization.""" |
|---|
| 200 | |
|---|
| 201 | def __init__(self, dim): |
|---|
| 202 | """ |
|---|
| 203 | Takes one initial input: |
|---|
| 204 | dim -- dimensionality of the problem |
|---|
| 205 | |
|---|
| 206 | All important class members are inherited from AbstractSolver. |
|---|
| 207 | """ |
|---|
| 208 | AbstractSolver.__init__(self, dim) |
|---|
| 209 | |
|---|
| 210 | def Solve(self, func, termination, sigint_callback=None, |
|---|
| 211 | EvaluationMonitor=Null, StepMonitor=Null, ExtraArgs=(), **kwds): |
|---|
| 212 | """Minimize a function using simulated annealing. |
|---|
| 213 | |
|---|
| 214 | Description: |
|---|
| 215 | |
|---|
| 216 | Uses a simulated annealing algorithm to find the minimum of |
|---|
| 217 | a function of one or more variables. |
|---|
| 218 | |
|---|
| 219 | Inputs: |
|---|
| 220 | |
|---|
| 221 | func -- the Python function or method to be minimized. |
|---|
| 222 | termination -- callable object providing termination conditions. |
|---|
| 223 | |
|---|
| 224 | Additional Inputs: |
|---|
| 225 | |
|---|
| 226 | sigint_callback -- callback function for signal handler. |
|---|
| 227 | EvaluationMonitor -- a callable object that will be passed x, fval |
|---|
| 228 | whenever the cost function is evaluated. |
|---|
| 229 | StepMonitor -- a callable object that will be passed x, fval |
|---|
| 230 | after the end of an iteration. |
|---|
| 231 | ExtraArgs -- extra arguments for func. |
|---|
| 232 | |
|---|
| 233 | Further Inputs: |
|---|
| 234 | |
|---|
| 235 | schedule -- Annealing schedule to use: 'cauchy', 'fast', or 'boltzmann' |
|---|
| 236 | [default='fast'] |
|---|
| 237 | T0 -- Initial Temperature (estimated as 1.2 times the largest |
|---|
| 238 | cost-function deviation over random points in the range) |
|---|
| 239 | [default=None] |
|---|
| 240 | learn_rate -- scale constant for adjusting guesses |
|---|
| 241 | [default=0.5] |
|---|
| 242 | boltzmann -- Boltzmann constant in acceptance test |
|---|
| 243 | (increase for less stringent test at each temperature). |
|---|
| 244 | [default=1.0] |
|---|
| 245 | quench, m, n -- Parameters to alter fast_sa schedule |
|---|
| 246 | [all default to 1.0] |
|---|
| 247 | dwell -- The number of times to search the space at each temperature. |
|---|
| 248 | [default=50] |
|---|
| 249 | |
|---|
| 250 | Optional Termination Conditions: |
|---|
| 251 | Tf -- Final goal temperature |
|---|
| 252 | [default=1e-12] |
|---|
| 253 | maxaccept -- Maximum changes to accept |
|---|
| 254 | [default=None] |
|---|
| 255 | """ |
|---|
| 256 | #allow for inputs that don't conform to AbstractSolver interface |
|---|
| 257 | args = ExtraArgs |
|---|
| 258 | x0 = self.population[0] |
|---|
| 259 | |
|---|
| 260 | schedule = "fast" |
|---|
| 261 | T0 = None |
|---|
| 262 | boltzmann = 1.0 |
|---|
| 263 | learn_rate = 0.5 |
|---|
| 264 | dwell = 50 |
|---|
| 265 | quench = 1.0 |
|---|
| 266 | m = 1.0 |
|---|
| 267 | n = 1.0 |
|---|
| 268 | |
|---|
| 269 | Tf = 1e-12 # or None? |
|---|
| 270 | self._maxaccept = None |
|---|
| 271 | |
|---|
| 272 | self.disp = 0 |
|---|
| 273 | self.callback = None |
|---|
| 274 | |
|---|
| 275 | if kwds.has_key('schedule'): schedule = kwds['schedule'] |
|---|
| 276 | if kwds.has_key('T0'): T0 = kwds['T0'] |
|---|
| 277 | if kwds.has_key('boltzmann'): boltzmann = kwds['boltzmann'] |
|---|
| 278 | if kwds.has_key('learn_rate'): learn_rate = kwds['learn_rate'] |
|---|
| 279 | if kwds.has_key('dwell'): dwell = kwds['dwell'] |
|---|
| 280 | if kwds.has_key('quench'): quench = kwds['quench'] |
|---|
| 281 | if kwds.has_key('m'): m = kwds['m'] |
|---|
| 282 | if kwds.has_key('n'): n = kwds['n'] |
|---|
| 283 | |
|---|
| 284 | if kwds.has_key('Tf'): Tf = kwds['Tf'] |
|---|
| 285 | if kwds.has_key('maxaccept'): self._maxaccept = kwds['maxaccept'] |
|---|
| 286 | |
|---|
| 287 | if kwds.has_key('disp'): self.disp = kwds['disp'] |
|---|
| 288 | if kwds.has_key('callback'): self.callback = kwds['callback'] |
|---|
| 289 | |
|---|
| 290 | #------------------------------------------------------------- |
|---|
| 291 | |
|---|
| 292 | import signal |
|---|
| 293 | self._EARLYEXIT = False |
|---|
| 294 | |
|---|
| 295 | fcalls, func = wrap_function(func, ExtraArgs, EvaluationMonitor) |
|---|
| 296 | |
|---|
| 297 | if self._useStrictRange: |
|---|
| 298 | x0 = self._clipGuessWithinRangeBoundary(x0) |
|---|
| 299 | # Note: wrap_bounds changes the results slightly from the original |
|---|
| 300 | func = wrap_bounds(func, self._strictMin, self._strictMax) |
|---|
| 301 | |
|---|
| 302 | #generate signal_handler |
|---|
| 303 | self._generateHandler(sigint_callback) |
|---|
| 304 | if self._handle_sigint: signal.signal(signal.SIGINT, self.signal_handler) |
|---|
| 305 | #------------------------------------------------------------- |
|---|
| 306 | |
|---|
| 307 | schedule = eval(schedule+'_sa()') |
|---|
| 308 | # initialize the schedule |
|---|
| 309 | schedule.init(dims=shape(x0),func=func,args=args,boltzmann=boltzmann,T0=T0, |
|---|
| 310 | learn_rate=learn_rate, lower=self._strictMin, upper=self._strictMax, |
|---|
| 311 | m=m, n=n, quench=quench, dwell=dwell) |
|---|
| 312 | |
|---|
| 313 | if self._maxiter is None: |
|---|
| 314 | self._maxiter = 400 |
|---|
| 315 | |
|---|
| 316 | current_state, last_state = _state(), _state() |
|---|
| 317 | if T0 is None: |
|---|
| 318 | x0, self.bestEnergy = schedule.getstart_temp() |
|---|
| 319 | self.bestSolution = x0 |
|---|
| 320 | else: |
|---|
| 321 | #self.bestSolution = None |
|---|
| 322 | self.bestSolution = x0 |
|---|
| 323 | self.bestEnergy = 300e8 |
|---|
| 324 | |
|---|
| 325 | retval = 0 |
|---|
| 326 | last_state.x = asarray(x0).copy() |
|---|
| 327 | fval = func(x0,*args) |
|---|
| 328 | schedule.feval += 1 |
|---|
| 329 | last_state.cost = fval |
|---|
| 330 | if last_state.cost < self.bestEnergy: |
|---|
| 331 | self.bestEnergy = fval |
|---|
| 332 | self.bestSolution = asarray(x0).copy() |
|---|
| 333 | schedule.T = schedule.T0 |
|---|
| 334 | fqueue = [100, 300, 500, 700] |
|---|
| 335 | self.population = asarray(fqueue)*1.0 |
|---|
| 336 | iters = 0 |
|---|
| 337 | while 1: |
|---|
| 338 | StepMonitor(self.bestSolution, self.bestEnergy) |
|---|
| 339 | for n in range(dwell): |
|---|
| 340 | current_state.x = schedule.update_guess(last_state.x) |
|---|
| 341 | current_state.cost = func(current_state.x,*args) |
|---|
| 342 | schedule.feval += 1 |
|---|
| 343 | |
|---|
| 344 | dE = current_state.cost - last_state.cost |
|---|
| 345 | if schedule.accept_test(dE): |
|---|
| 346 | last_state.x = current_state.x.copy() |
|---|
| 347 | last_state.cost = current_state.cost |
|---|
| 348 | if last_state.cost < self.bestEnergy: |
|---|
| 349 | self.bestSolution = last_state.x.copy() |
|---|
| 350 | self.bestEnergy = last_state.cost |
|---|
| 351 | schedule.update_temp() |
|---|
| 352 | |
|---|
| 353 | iters += 1 |
|---|
| 354 | fqueue.append(squeeze(last_state.cost)) |
|---|
| 355 | fqueue.pop(0) |
|---|
| 356 | af = asarray(fqueue)*1.0 |
|---|
| 357 | |
|---|
| 358 | # Update monitors/variables |
|---|
| 359 | self.population = af |
|---|
| 360 | self.energy_history.append(self.bestEnergy) |
|---|
| 361 | |
|---|
| 362 | if self.callback is not None: |
|---|
| 363 | self.callback(self.bestSolution) |
|---|
| 364 | |
|---|
| 365 | # Stopping conditions |
|---|
| 366 | # - last saved values of f from each cooling step |
|---|
| 367 | # are all very similar (effectively cooled) |
|---|
| 368 | # - Tf is set and we are below it |
|---|
| 369 | # - maxfun is set and we are past it |
|---|
| 370 | # - maxiter is set and we are past it |
|---|
| 371 | # - maxaccept is set and we are past it |
|---|
| 372 | |
|---|
| 373 | if self._EARLYEXIT or termination(self): |
|---|
| 374 | # How to deal with the below warning? It uses feps, which is passed |
|---|
| 375 | # to termination, so it would be repetitive to also pass it to Solve(). |
|---|
| 376 | #if abs(af[-1]-best_state.cost) > feps*10: |
|---|
| 377 | #print "Warning: Cooled to %f at %s but this is not" \ |
|---|
| 378 | # % (squeeze(last_state.cost), str(squeeze(last_state.x))) \ |
|---|
| 379 | # + " the smallest point found." |
|---|
| 380 | break |
|---|
| 381 | if (Tf is not None) and (schedule.T < Tf): |
|---|
| 382 | break |
|---|
| 383 | # if (self._maxfun is not None) and (schedule.feval > self._maxfun): |
|---|
| 384 | # retval = 1 |
|---|
| 385 | # break |
|---|
| 386 | if (self._maxfun is not None) and (fcalls[0] > self._maxfun): |
|---|
| 387 | retval = 1 |
|---|
| 388 | break |
|---|
| 389 | if (iters > self._maxiter): |
|---|
| 390 | retval = 2 |
|---|
| 391 | break |
|---|
| 392 | if (self._maxaccept is not None) and (schedule.accepted > self._maxaccept): |
|---|
| 393 | break |
|---|
| 394 | |
|---|
| 395 | signal.signal(signal.SIGINT,signal.default_int_handler) |
|---|
| 396 | |
|---|
| 397 | # Store some information. Is there a better way to do this? |
|---|
| 398 | self.generations = iters # Number of iterations |
|---|
| 399 | self.T = schedule.T # Final temperature |
|---|
| 400 | self.accept = schedule.accepted # Number of tests accepted |
|---|
| 401 | |
|---|
| 402 | # code below here pushes output to scipy.optimize interface |
|---|
| 403 | |
|---|
| 404 | if self.disp: |
|---|
| 405 | if retval == 1: |
|---|
| 406 | print "Warning: Maximum number of function evaluations has "\ |
|---|
| 407 | "been exceeded." |
|---|
| 408 | elif retval == 2: |
|---|
| 409 | print "Warning: Maximum number of iterations has been exceeded" |
|---|
| 410 | else: |
|---|
| 411 | print "Optimization terminated successfully." |
|---|
| 412 | print " Current function value: %f" % self.bestEnergy |
|---|
| 413 | print " Iterations: %d" % iters |
|---|
| 414 | print " Function evaluations: %d" % fcalls[0] |
|---|
| 415 | |
|---|
| 416 | return |
|---|
| 417 | |
|---|
| 418 | ################################################################################## |
|---|
| 419 | # function interface for using AnnealSolver |
|---|
| 420 | |
|---|
| 421 | def anneal(func, x0, args=(), schedule='fast', full_output=0, |
|---|
| 422 | T0=None, Tf=1e-12, maxaccept=None, lower= -100, upper= 100, maxiter=400, |
|---|
| 423 | boltzmann=1.0, learn_rate=0.5, feps=1e-6, quench=1.0, m=1.0, n=1.0, |
|---|
| 424 | maxfun=None, dwell=50, callback=None, disp=0, retall=0): |
|---|
| 425 | """Minimize a function using simulated annealing. |
|---|
| 426 | |
|---|
| 427 | Inputs: |
|---|
| 428 | |
|---|
| 429 | func -- Function to be optimized |
|---|
| 430 | x0 -- Parameters to be optimized over. Should be a list. |
|---|
| 431 | |
|---|
| 432 | Optional Inputs: |
|---|
| 433 | |
|---|
| 434 | args -- Extra parameters to function |
|---|
| 435 | schedule -- Annealing schedule to use. |
|---|
| 436 | Choices are: 'fast', 'cauchy', 'boltzmann' |
|---|
| 437 | full_output -- Non-zero to return optional outputs |
|---|
| 438 | retall -- Non-zero to return list of solutions at each iteration. |
|---|
| 439 | disp -- Non-zero to print convergence messages. |
|---|
| 440 | T0 -- Initial Temperature (estimated as 1.2 times the largest |
|---|
| 441 | cost-function deviation over random points in the range) |
|---|
| 442 | Tf -- Final goal temperature |
|---|
| 443 | maxfun -- Maximum function evaluations |
|---|
| 444 | maxaccept -- Maximum changes to accept |
|---|
| 445 | maxiter -- Maximum cooling iterations |
|---|
| 446 | learn_rate -- scale constant for adjusting guesses |
|---|
| 447 | boltzmann -- Boltzmann constant in acceptance test |
|---|
| 448 | (increase for less stringent test at each temperature). |
|---|
| 449 | feps -- Stopping relative error tolerance for the function value in |
|---|
| 450 | last four coolings. |
|---|
| 451 | quench, m, n -- Parameters to alter fast_sa schedule |
|---|
| 452 | lower, upper -- lower and upper bounds on x0. |
|---|
| 453 | dwell -- The number of times to search the space at each temperature. |
|---|
| 454 | |
|---|
| 455 | Outputs: (xmin, {fopt, iters, feval, T, accept}, retval, {allvecs}) |
|---|
| 456 | |
|---|
| 457 | xmin -- Point giving smallest value found |
|---|
| 458 | fopt -- Minimum value of function found |
|---|
| 459 | iters -- Number of cooling iterations |
|---|
| 460 | feval -- Number of function evaluations |
|---|
| 461 | T -- final temperature |
|---|
| 462 | accept -- Number of tests accepted. |
|---|
| 463 | retval -- Flag indicating stopping condition: |
|---|
| 464 | 1 : Maximum function evaluations |
|---|
| 465 | 2 : Maximum iterations reached |
|---|
| 466 | 3 : Maximum accepted query locations reached |
|---|
| 467 | allvecs -- a list of solutions at each iteration. |
|---|
| 468 | """ |
|---|
| 469 | |
|---|
| 470 | from mystic.tools import Sow |
|---|
| 471 | from mystic.termination import AveragePopulationImprovement |
|---|
| 472 | stepmon = Sow() |
|---|
| 473 | evalmon = Sow() |
|---|
| 474 | |
|---|
| 475 | x0 = asarray(x0) |
|---|
| 476 | solver = AnnealSolver(len(x0)) |
|---|
| 477 | solver.SetInitialPoints(x0) |
|---|
| 478 | solver.enable_signal_handler() |
|---|
| 479 | solver.SetEvaluationLimits(maxiter,maxfun) |
|---|
| 480 | |
|---|
| 481 | # Default bounds cause errors when function is not 1-dimensional |
|---|
| 482 | solver.SetStrictRanges(lower,upper) |
|---|
| 483 | |
|---|
| 484 | solver.Solve(func,termination=AveragePopulationImprovement(tolerance=feps),\ |
|---|
| 485 | EvaluationMonitor=evalmon,StepMonitor=stepmon,\ |
|---|
| 486 | ExtraArgs=args, callback=callback,\ |
|---|
| 487 | schedule=schedule, T0=T0, Tf=Tf,\ |
|---|
| 488 | boltzmann=boltzmann, learn_rate=learn_rate,\ |
|---|
| 489 | dwell=dwell, quench=quench, m=m, n=n,\ |
|---|
| 490 | maxaccept=maxaccept, disp=disp) |
|---|
| 491 | solution = solver.Solution() |
|---|
| 492 | |
|---|
| 493 | # code below here pushes output to scipy.optimize interface |
|---|
| 494 | #x = list(solver.bestSolution) |
|---|
| 495 | x = solver.bestSolution |
|---|
| 496 | fval = solver.bestEnergy |
|---|
| 497 | fcalls = len(evalmon.x) |
|---|
| 498 | iterations = len(stepmon.x) |
|---|
| 499 | allvecs = [] |
|---|
| 500 | for i in range(len(stepmon.x)): |
|---|
| 501 | #allvecs.append(list(stepmon.x[i])) |
|---|
| 502 | allvecs.append(stepmon.x[i]) |
|---|
| 503 | |
|---|
| 504 | # to be fixed |
|---|
| 505 | T = solver.T |
|---|
| 506 | accept = solver.accept |
|---|
| 507 | |
|---|
| 508 | retval = 0 |
|---|
| 509 | if (maxfun is not None) and (fcalls > maxfun): |
|---|
| 510 | retval = 1 |
|---|
| 511 | if (iterations > maxiter): |
|---|
| 512 | retval = 2 |
|---|
| 513 | if (maxaccept is not None) and (accept > maxaccept): |
|---|
| 514 | retval = 3 |
|---|
| 515 | |
|---|
| 516 | if full_output: |
|---|
| 517 | retlist = x, fval, iterations, fcalls, T, accept, retval |
|---|
| 518 | if retall: |
|---|
| 519 | retlist += (allvecs,) |
|---|
| 520 | else: |
|---|
| 521 | retlist = x, retval |
|---|
| 522 | if retall: |
|---|
| 523 | retlist = (x, allvecs) |
|---|
| 524 | |
|---|
| 525 | return retlist |
|---|
| 526 | |
|---|
| 527 | if __name__=='__main__': |
|---|
| 528 | help(__name__) |
|---|
| 529 | |
|---|
| 530 | # end of file |
|---|