| 1 | #!/usr/bin/env python |
|---|
| 2 | # |
|---|
| 3 | # Author: Mike McKerns (mmckerns @caltech and @uqfoundation) |
|---|
| 4 | # Copyright (c) 2012-2016 California Institute of Technology. |
|---|
| 5 | # License: 3-clause BSD. The full license text is available at: |
|---|
| 6 | # - http://mmckerns.github.io/project/mystic/browser/mystic/LICENSE |
|---|
| 7 | |
|---|
| 8 | def sumt(cost, solver, term, eps = 1e-4, max_SUMT_iters = 10, **kwds): |
|---|
| 9 | |
|---|
| 10 | from mystic.tools import Null |
|---|
| 11 | from numpy import inf |
|---|
| 12 | # standard solver config |
|---|
| 13 | StepMonitor=kwds.get('StepMonitor', Null) |
|---|
| 14 | EvaluationMonitor=kwds.get('EvaluationMonitor', Null) |
|---|
| 15 | sigint_callback=kwds.get('sigint_callback', None) |
|---|
| 16 | disp = kwds.get('disp',False) |
|---|
| 17 | |
|---|
| 18 | # other config |
|---|
| 19 | randomstate = kwds.get('randomstate',None) |
|---|
| 20 | |
|---|
| 21 | # SUMT penalty restart |
|---|
| 22 | F_values = [] |
|---|
| 23 | E_values = [] |
|---|
| 24 | x_values = [] |
|---|
| 25 | total_iterations = 0. |
|---|
| 26 | total_energy_history = [] |
|---|
| 27 | exitflag = 0 |
|---|
| 28 | # The outer SUMT for loop. In each iteration, an optimization will be |
|---|
| 29 | # performed. |
|---|
| 30 | for q in range(max_SUMT_iters): |
|---|
| 31 | if q == 0: |
|---|
| 32 | x0 = solver.population[0] |
|---|
| 33 | else: |
|---|
| 34 | x0 = x_values[-1] |
|---|
| 35 | |
|---|
| 36 | if disp: |
|---|
| 37 | print 'SUMT iteration %d: x = ' % q, x0 |
|---|
| 38 | |
|---|
| 39 | if randomstate: |
|---|
| 40 | import random |
|---|
| 41 | random.setstate(randomstate) |
|---|
| 42 | |
|---|
| 43 | # 'Clear' some of the attributes of the solver |
|---|
| 44 | solver.population = [[0.0 for i in range(solver.nDim)] for j in range(solver.nPop)] |
|---|
| 45 | #solver.generations = 0 |
|---|
| 46 | solver.bestEnergy = None |
|---|
| 47 | solver.bestSolution = None |
|---|
| 48 | solver.trialSolution = [0.0] * solver.nDim |
|---|
| 49 | solver.popEnergy = [solver._init_popEnergy] * solver.nPop |
|---|
| 50 | #solver.energy_history = [] |
|---|
| 51 | |
|---|
| 52 | # individual solver __init__ |
|---|
| 53 | if solver.__class__.__name__ == 'NelderMeadSimplexSolver': |
|---|
| 54 | simplex = solver.nDim + 1 |
|---|
| 55 | solver.popEnergy = [solver._init_popEnergy] * simplex |
|---|
| 56 | solver.population = [[0.0 for i in range(solver.nDim)] for j in range(simplex)] |
|---|
| 57 | elif solver.__class__.__name__ == 'PowellDirectionalSolver': |
|---|
| 58 | solver._direc = None |
|---|
| 59 | elif solver.__class__.__name__ in ['DifferentialEvolutionSolver','DifferentialEvolutionSolver2']: |
|---|
| 60 | solver.genealogy = [ [] for j in range(solver.nPop)] |
|---|
| 61 | else: pass |
|---|
| 62 | |
|---|
| 63 | # Perform the 'inner' optimization |
|---|
| 64 | solver.SetInitialPoints(x0) |
|---|
| 65 | solver.SetEvaluationMonitor(EvaluationMonitor) |
|---|
| 66 | solver.SetGenerationMonitor(StepMonitor) |
|---|
| 67 | solver.Solve(cost, term, sigint_callback=sigint_callback, **kwds) |
|---|
| 68 | x = solver.bestSolution |
|---|
| 69 | y = solver.bestEnergy |
|---|
| 70 | |
|---|
| 71 | # When a user sets maxiter and maxfun, that is actually the maxiter |
|---|
| 72 | # and maxfun *each* SUMT iteration. However, it is difficult to check |
|---|
| 73 | # total maxfun and maxiter each SUMT iteration because then what |
|---|
| 74 | # would the inner maxiter and maxfun be? Better to just leave maxiter |
|---|
| 75 | # and maxfun as limits for each inner optimization... |
|---|
| 76 | total_iterations += solver.generations |
|---|
| 77 | total_energy_history += solver.energy_history |
|---|
| 78 | |
|---|
| 79 | F_values.append(cost(x)) #XXX: cost = no penalty; y = penalty |
|---|
| 80 | E_values.append(y) |
|---|
| 81 | x_values.append(x) |
|---|
| 82 | |
|---|
| 83 | # Update penalty parameters |
|---|
| 84 | solver._penalty.store(x) #XXX: may want to combine store with iter |
|---|
| 85 | |
|---|
| 86 | # break due to the signal handler |
|---|
| 87 | if solver._EARLYEXIT: |
|---|
| 88 | exitflag = 3 |
|---|
| 89 | break |
|---|
| 90 | |
|---|
| 91 | # breaking if constraints satisfied may cause premature exit. so don't |
|---|
| 92 | # if False: |
|---|
| 93 | # exitflag = 1 |
|---|
| 94 | # break |
|---|
| 95 | |
|---|
| 96 | # break if cost(x) not improving after SUMT loop |
|---|
| 97 | if q != 0: |
|---|
| 98 | if abs(F_values[q] - F_values[q-1]) <= eps: #XXX: F or E ? |
|---|
| 99 | exitflag = 2 |
|---|
| 100 | break |
|---|
| 101 | # break for no improvement in x's after SUMT loop |
|---|
| 102 | #if linalg.norm(asarray(x_values[q])-asarray(x_values[q-1])) <= eps: |
|---|
| 103 | # exitflag = 4 |
|---|
| 104 | # break |
|---|
| 105 | |
|---|
| 106 | # Update penalty parameters |
|---|
| 107 | solver._penalty.iter() #XXX: may need 'assurance' to have iter() |
|---|
| 108 | |
|---|
| 109 | if disp: |
|---|
| 110 | if exitflag == 0: |
|---|
| 111 | print "SUMT exited because maximum number of SUMT iterations reached." |
|---|
| 112 | elif exitflag == 1: |
|---|
| 113 | print "SUMT exited because constraints were satisfied." |
|---|
| 114 | elif exitflag == 2: |
|---|
| 115 | print "SUMT exited because F values reached tolerance for improving." |
|---|
| 116 | elif exitflag == 3: |
|---|
| 117 | print "SUMT exited because signal handler 'exit' was selected." |
|---|
| 118 | #elif exitflag == 4: |
|---|
| 119 | # print "SUMT exited because x values reached tolerance for improving." |
|---|
| 120 | else: |
|---|
| 121 | print "SUMT exited for other reason." |
|---|
| 122 | print "%d SUMT iterations were performed." % (q + 1) |
|---|
| 123 | |
|---|
| 124 | # Return the last solver, which contains all of the important information |
|---|
| 125 | # First modify the solver to include total data. |
|---|
| 126 | #solver.generations = total_iterations |
|---|
| 127 | #solver.energy_history = total_energy_history |
|---|
| 128 | |
|---|
| 129 | #XXX solver.bestEnergy is not exactly cost(solver.bestSolution), |
|---|
| 130 | # since it is cost(bestSolution) with a penalty. Compensate for that |
|---|
| 131 | # here? Then, it is inconsistent with solver.energy_history, which |
|---|
| 132 | # also records cost(bestSolution) with a penalty.... |
|---|
| 133 | #solver.bestEnergy = cost(solver.bestSolution) |
|---|
| 134 | return solver |
|---|
| 135 | |
|---|