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 | |
---|