-
Notifications
You must be signed in to change notification settings - Fork 38
/
Simple.py
159 lines (121 loc) · 5.38 KB
/
Simple.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
from heapq import heappush, heappop, heappushpop
import numpy
import math
import time
import matplotlib.pyplot as plotter
CAPACITY_INCREMENT = 1000
class _Simplex:
def __init__(self, pointIndices, testCoords, contentFractions, objectiveScore, opportunityCost, contentFraction, difference):
self.pointIndices = pointIndices
self.testCoords = testCoords
self.contentFractions = contentFractions
self.contentFraction = contentFraction
self.__objectiveScore = objectiveScore
self.__opportunityCost = opportunityCost
self.update(difference)
def update(self, difference):
self.acquisitionValue = -(self.__objectiveScore + (self.__opportunityCost * difference))
self.difference = difference
def __eq__(self, other):
return self.acquisitionValue == other.acquisitionValue
def __lt__(self, other):
return self.acquisitionValue < other.acquisitionValue
class SimpleTuner:
def __init__(self, cornerPoints, objectiveFunction, exploration_preference=0.15):
self.__cornerPoints = cornerPoints
self.__numberOfVertices = len(cornerPoints)
self.queue = []
self.capacity = self.__numberOfVertices + CAPACITY_INCREMENT
self.testPoints = numpy.empty((self.capacity, self.__numberOfVertices))
self.objective = objectiveFunction
self.iterations = 0
self.maxValue = None
self.minValue = None
self.bestCoords = []
self.opportunityCostFactor = exploration_preference #/ self.__numberOfVertices
def optimize(self, maxSteps=10):
for step in range(maxSteps):
#print(self.maxValue, self.iterations, self.bestCoords)
if len(self.queue) > 0:
targetSimplex = self.__getNextSimplex()
newPointIndex = self.__testCoords(targetSimplex.testCoords)
for i in range(0, self.__numberOfVertices):
tempIndex = targetSimplex.pointIndices[i]
targetSimplex.pointIndices[i] = newPointIndex
newContentFraction = targetSimplex.contentFraction * targetSimplex.contentFractions[i]
newSimplex = self.__makeSimplex(targetSimplex.pointIndices, newContentFraction)
heappush(self.queue, newSimplex)
targetSimplex.pointIndices[i] = tempIndex
else:
testPoint = self.__cornerPoints[self.iterations]
testPoint.append(0)
testPoint = numpy.array(testPoint, dtype=numpy.float64)
self.__testCoords(testPoint)
if self.iterations == (self.__numberOfVertices - 1):
initialSimplex = self.__makeSimplex(numpy.arange(self.__numberOfVertices, dtype=numpy.intp), 1)
heappush(self.queue, initialSimplex)
self.iterations += 1
def get_best(self):
return (self.maxValue, self.bestCoords[0:-1])
def __getNextSimplex(self):
targetSimplex = heappop(self.queue)
currentDifference = self.maxValue - self.minValue
while currentDifference > targetSimplex.difference:
targetSimplex.update(currentDifference)
# if greater than because heapq is in ascending order
if targetSimplex.acquisitionValue > self.queue[0].acquisitionValue:
targetSimplex = heappushpop(self.queue, targetSimplex)
return targetSimplex
def __testCoords(self, testCoords):
objectiveValue = self.objective(testCoords[0:-1])
if self.maxValue == None or objectiveValue > self.maxValue:
self.maxValue = objectiveValue
self.bestCoords = testCoords
if self.minValue == None: self.minValue = objectiveValue
elif objectiveValue < self.minValue:
self.minValue = objectiveValue
testCoords[-1] = objectiveValue
if self.capacity == self.iterations:
self.capacity += CAPACITY_INCREMENT
self.testPoints.resize((self.capacity, self.__numberOfVertices))
newPointIndex = self.iterations
self.testPoints[newPointIndex] = testCoords
return newPointIndex
def __makeSimplex(self, pointIndices, contentFraction):
vertexMatrix = self.testPoints[pointIndices]
coordMatrix = vertexMatrix[:, 0:-1]
barycenterLocation = numpy.sum(vertexMatrix, axis=0) / self.__numberOfVertices
differences = coordMatrix - barycenterLocation[0:-1]
distances = numpy.sqrt(numpy.sum(differences * differences, axis=1))
totalDistance = numpy.sum(distances)
barycentricTestCoords = distances / totalDistance
euclideanTestCoords = vertexMatrix.T.dot(barycentricTestCoords)
vertexValues = vertexMatrix[:,-1]
testpointDifferences = coordMatrix - euclideanTestCoords[0:-1]
testPointDistances = numpy.sqrt(numpy.sum(testpointDifferences * testpointDifferences, axis=1))
inverseDistances = 1 / testPointDistances
inverseSum = numpy.sum(inverseDistances)
interpolatedValue = inverseDistances.dot(vertexValues) / inverseSum
currentDifference = self.maxValue - self.minValue
opportunityCost = self.opportunityCostFactor * math.log(contentFraction, self.__numberOfVertices)
return _Simplex(pointIndices.copy(), euclideanTestCoords, barycentricTestCoords, interpolatedValue, opportunityCost, contentFraction, currentDifference)
def plot(self):
if self.__numberOfVertices != 3: raise RuntimeError('Plotting only supported in 2D')
matrix = self.testPoints[0:self.iterations, :]
x = matrix[:,0].flat
y = matrix[:,1].flat
z = matrix[:,2].flat
coords = []
acquisitions = []
for triangle in self.queue:
coords.append(triangle.pointIndices)
acquisitions.append(-1 * triangle.acquisitionValue)
plotter.figure()
plotter.tricontourf(x, y, coords, z)
plotter.triplot(x, y, coords, color='white', lw=0.5)
plotter.colorbar()
plotter.figure()
plotter.tripcolor(x, y, coords, acquisitions)
plotter.triplot(x, y, coords, color='white', lw=0.5)
plotter.colorbar()
plotter.show()