-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathobjective_function_interface.py
219 lines (173 loc) · 9.31 KB
/
objective_function_interface.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
"""
Copyright (c) 2013, Triad National Security, LLC
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Triad National Security, LLC nor the names of its contributors may be used to endorse or
promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import inspect
class ObjectiveFunctionInterface(object):
"""
This interface must be implemented by you. This defines the objective function HS optimizes.
"""
def get_fitness(self, vector):
"""
Return the objective function value given a solution vector containing each decision variable. In practice,
vector should be a list of parameters.
For example, suppose the objective function is (-(x^2 + (y+1)^2) + 4). A possible call to fitness may look like this:
>>> print obj_fun.fitness([4, 7])
-76
"""
raise NotImplementedError(inspect.stack()[0][3])
def get_value(self, i, j=None):
"""
Get a valid value of parameter i. You can return values any way you like - uniformly at random, according to some
distribution, etc.
For example, suppose the x parameter in fitness() varies uniformly at random in the range [-1000, 1000]:
>>> print obj_fun.get_value(0)
763.406542555
>>> print obj_fun.get_value(0)
-80.8100680841
j is used only for discrete parameters in the pitch adjustment step. j maps to some value the discrete
parameter can take on. If parameter i is continuous, j should be ignored.
For example, suppose that a variable z is discrete and can take on the values [-3, -1, 0, 3, 4.5, 6.3, 8, 9, 12]. Also
suppose that z is the 3rd parameter in the objective function (i.e., i = 2).
>>> print obj_fun.get_value(2, 1)
-1
>>> print obj_fun.get_value(2, 3)
3
"""
raise NotImplementedError(inspect.stack()[0][3])
def get_index(self, i, v):
"""
Get the index of the value v of the specified parameter.
As an example, consider the variable z from get_value() above:
>>> print obj_fun.get_index(2, 6.3)
5
This will only be called for discrete variables in the pitch adjustment step. The behavior here isn't well-defined in the case
where the possible values for a variable contain non-unique elements.
For best performance, store discrete values in a sorted list that can be binary searched. Additionally, this list should not
contain any duplicate values.
"""
raise NotImplementedError(inspect.stack()[0][3])
def get_num_discrete_values(self, i):
"""
Get the number of values possible for the discrete parameter i.
As an example, consider the variables z and x from get_value() above:
>>> print get_num_discrete_values(2)
9
>>> print get_num_discrete_values(0)
inf
This will only be called for discrete variables in the pitch adjustment step. If i is a continuous variable, +inf
can be returned, but this function might not be implemented for continuous variables, so this shouldn't be
counted on.
"""
raise NotImplementedError(inspect.stack()[0][3])
def get_lower_bound(self, i):
"""
Return the lower bound of parameter i. Using the example for fitness(), the lower bound for y may be -1000.
Seeing as y is the 2nd parameter (index 1 in a 0-indexed system), this call may look like the following:
>>> print obj_fun.lower_bound(1)
-1000
This will only be called for continuous variables in the pitch adjustment step.
"""
raise NotImplementedError(inspect.stack()[0][3])
def get_upper_bound(self, i):
"""
Return the upper bound of parameter i.
This will only be called for continuous variables in the pitch adjustment step.
"""
raise NotImplementedError(inspect.stack()[0][3])
def is_variable(self, i):
"""
Return whether or not the parameter at the specified index should be varied by HS. It may be the case that HS should
only vary certain parameters while others should remain fixed. In the fitness() example, perhaps HS should only vary x.
This call may look like:
>>> print obj_fun.is_variable(0)
True
>>> print obj_fun.is_variable(1)
False
Note that if a parameter is not variable, it should still return a valid value in get_value(). This value can be constant,
but a valid value must be returned.
"""
raise NotImplementedError(inspect.stack()[0][3])
def is_discrete(self, i):
"""
Return whether or not the parameter at the specified index is a discrete parameter. Not all parameters may be continuous.
This only really matters in the pitch adjustment step of HS. Suppose that x is continuous (e.g., x varies in [-1000, 1000]),
and y is discrete (e.g., y is only allowed to take on values [-5, 3, 6, 9, 12, 45]):
>>> print obj_fun.is_discrete(0)
False
>>> print obj_fun.is_discrete(1)
True
"""
raise NotImplementedError(inspect.stack()[0][3])
def get_num_parameters(self):
"""
Return the number of parameters used by the objective function. Using the example in fitness(), this will be 2.
A sample call may look like the following:
>>> print obj_fun.get_num_parameters()
2
"""
raise NotImplementedError(inspect.stack()[0][3])
def use_random_seed(self):
"""
Return whether or not a random seed should be used. If a random seed is used, the same result will be generated each time (i.e., multiple
HS iterations will return the same best solution).
"""
raise NotImplementedError(inspect.stack()[0][3])
def get_random_seed(self):
"""
Return an optional random seed. If use_random_seed() == False, this won't be called.
"""
raise NotImplementedError(inspect.stack()[0][3])
def get_max_imp(self):
"""
Return the maximum number of improvisations. This represents the stopping criterion (i.e., the number of fitness evaluations HS
performs until search stops).
"""
raise NotImplementedError(inspect.stack()[0][3])
def get_hmcr(self):
"""
Return the harmony memory considering rate. This represents the proportion of memory consideration calls vs. random selection calls.
"""
raise NotImplementedError(inspect.stack()[0][3])
def get_par(self):
"""
Return the pitch adjusting rate. This represents how often pitch adjustment will occur if memory consideration has already been done.
"""
raise NotImplementedError(inspect.stack()[0][3])
def get_hms(self):
"""
Return the harmony memory size. This represents the size of the vector that stores previously best harmonies.
"""
raise NotImplementedError(inspect.stack()[0][3])
def get_mpai(self):
"""
Return the maximum pitch adjustment index. This determines the range from which pitch adjustment may occur for discrete variables. Also known as
discrete bandwidth.
"""
raise NotImplementedError(inspect.stack()[0][3])
def get_mpap(self):
"""
Return the maximum pitch adjustment proportion. This determines the range from which pitch adjustment may occur for continuous variables. Also known as
continuous bandwidth.
"""
raise NotImplementedError(inspect.stack()[0][3])
def maximize(self):
"""
Return True if this is a maximization problem, False if minimization problem.
"""
raise NotImplementedError(inspect.stack()[0][3])