Skip to content

Commit 031b98b

Browse files
author
Stefan Fenn
committed
Update of coord computation, lineToPoints
1 parent 7216275 commit 031b98b

File tree

2 files changed

+265
-52
lines changed

2 files changed

+265
-52
lines changed

baysianOptimization.html

Lines changed: 254 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,37 @@
1414
<canvas id="grid" width="600px" height="600px"></canvas>
1515
<canvas id="fn2" width="600px" height="600px"></canvas>
1616
<canvas id="gp_mean" width="600px" height="600px"></canvas>
17+
<canvas id="gp_conv" width="600px" height="600px"></canvas>
18+
<canvas id="gp_acq_pi" width="600px" height="600px"></canvas>
19+
<canvas id="gp_acq_ei" width="600px" height="600px"></canvas>
20+
<canvas id="gp_acq_scaled_ei" width="600px" height="600px"></canvas>
1721
<canvas id="observations" width="600px" height="600px"></canvas>
1822
</div>
23+
<div style="width:600px;height:600px;padding-left:620px">
24+
<label>Kernel Parameter</label><input type="range" min="1" max="30" value="10"
25+
onchange="window.updateKernelParam(this.value);"></input>
26+
<br/>
27+
<button onclick="clearObservations()">Clear Observations</button>
28+
<br/>
29+
<button onclick="addRandomObservationPoint()">Add random Observation Point</button>
30+
<br/>
31+
<button onclick="addEiSuggestedObservationPoint()">Add EI suggested Observation Point</button>
32+
<br/>
33+
<button onclick="addScaledEiSuggestedObservationPoint()">Add ScaledEI suggested Observation Point</button>
34+
</div>
35+
1936

2037
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/6.6.4/math.min.js"></script>
2138
<script src="graphplot.js" type="module"></script>
2239
<script type="module">
23-
import {drawFunction, drawGrid, drawPoints, xPixToCoord, xCoordToPix} from './graphplot.js';
40+
import {clear, drawFunction, drawGrid, drawPoints, lineToPoints, xCoordToPix, xPixToCoord, yCoordToPix} from './graphplot.js';
2441

2542
const canvas = document.getElementById("grid");
2643
const config = {
27-
xMin: -0.2,
28-
xMax: 3.4,
29-
yMin: -0.2,
30-
yMax: 4.2,
44+
xMin: -1,
45+
xMax: 2,
46+
yMin: -2.2,
47+
yMax: 2.2,
3148
ctx: canvas.getContext("2d")
3249
};
3350

@@ -43,29 +60,33 @@
4360
const configGpMean = Object.assign({}, config);
4461
configGpMean.ctx = canvasGpMean.getContext("2d");
4562

63+
const canvasGpConv = document.getElementById("gp_conv");
64+
const configGpConv = Object.assign({}, config);
65+
configGpConv.ctx = canvasGpConv.getContext("2d");
66+
67+
const canvasGpAcqPi = document.getElementById("gp_acq_pi");
68+
const configGpAcqPi = Object.assign({}, config);
69+
configGpAcqPi.ctx = canvasGpAcqPi.getContext("2d");
70+
71+
const canvasGpAcqEi = document.getElementById("gp_acq_ei");
72+
const configGpAcqEi = Object.assign({}, config);
73+
configGpAcqEi.ctx = canvasGpAcqEi.getContext("2d");
74+
75+
const canvasGpAcqScaledEi = document.getElementById("gp_acq_scaled_ei");
76+
const configGpAcqScaledEi = Object.assign({}, config);
77+
configGpAcqScaledEi.ctx = canvasGpAcqScaledEi.getContext("2d");
78+
4679
drawGrid(config);
4780
const func = x => {
48-
if ((x < 0) || (x > Math.PI)) return 0;
49-
let result = 0;
50-
for (let i = 1; i <= 10; i++) {
51-
result += Math.sin(x) * Math.pow(Math.sin(i * x * x / Math.PI), 20);
52-
}
81+
let result = -x * Math.sin(10 * x);
82+
if ((x < -1) || (x > 2)) return -2;
5383
return result;
5484
};
55-
drawFunction(configF2, "#000000", func);
56-
57-
let observationsX = [0.2, 0.5, 0.9, 1.2, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.5, 2.8, 3];
58-
let observationsY = observationsX.map(x => func(x));
59-
let observations = observationsX.map(x => [x, func(x)]);
60-
drawPoints(
61-
configObs,
62-
"#0000FF",
63-
observations
64-
);
85+
drawFunction(configF2, "#606060", func);
6586

66-
const kernel = function (x1, x2) {
87+
const kernelWithParameter = function (x1, x2, param) {
6788
let n = math.norm(math.subtract(x1, x2));
68-
return math.exp(-0.5 * n * n);
89+
return math.exp(-0.5 * param * n * n);
6990
}
7091

7192
const K = function (x0, x1, kernel) {
@@ -81,54 +102,237 @@
81102
}
82103

83104
const posterioriMean = function (x, x_new, sigma, y, kernel) {
84-
let scope = {
105+
const scope = {
85106
x_new: x_new,
86107
x: x,
87108
xl: x.length,
88109
sigma: sigma,
89110
y: y,
90111
K: (x1, x2) => K(x1, x2, kernel)
91-
}
112+
};
113+
//Seite 8 Conditional of a joint Gaussian
114+
//http://cs229.stanford.edu/section/more_on_gaussians.pdf
115+
//und
116+
//GP-Buch Seite 15, 16
92117
return math.evaluate('K(x_new,x) * inv(K(x,x) + sigma .* identity(xl)) * transpose(y)', scope);
93-
}
118+
};
119+
120+
const posteriorCov = function (x, x_new, sigma, kernel) {
121+
const scope = {
122+
x: x,
123+
xl: x.length,
124+
sigma: sigma,
125+
K: (x1, x2) => K(x1, x2, kernel)
126+
};
94127

95-
const zip = function (a, b) {
96-
let zip = [];
97-
for (let i = 0; i < a.length; i++) {
98-
zip.push([a[i], b[i]]);
128+
const r = [];
129+
const invSigma = math.evaluate('inv(K(x,x) + sigma .* identity(xl))', scope).valueOf();
130+
for (let x_i_new of x_new) {
131+
const scope2 = {
132+
x_new: [x_i_new],
133+
x: x,
134+
xl: x.length,
135+
sigma: sigma,
136+
K: (x1, x2) => K(x1, x2, kernel),
137+
invSigma: invSigma
138+
};
139+
const stdSqr = math.evaluate('K(x_new, x_new) - K(x_new, x) * invSigma * K(x, x_new)', scope2).valueOf()[0][0];
140+
r.push(math.abs(stdSqr));
99141
}
100-
return zip;
101-
}
142+
return r;
143+
};
102144

103-
const x_new = [];
104-
const xCoordStart = xCoordToPix(config, -0.2);
105-
const xCoordEnd = xCoordToPix(config, 3.4);
106-
for (let i = xCoordStart; i < xCoordEnd; i++) {
107-
x_new.push(xPixToCoord(config, i))
108-
}
109-
const y_new = posterioriMean(observationsX, x_new, 1e-10, observationsY, kernel).valueOf();
110-
const xy_new = zip(x_new, y_new);
111-
drawPoints(
112-
configGpMean,
113-
"#00FF00",
114-
xy_new
115-
);
116-
drawFunction(configGpMean, "#00ff00", (x)=> {
117-
const w = xCoordToPix(config, x);
118-
return y_new[w-xCoordStart];
119-
});
145+
const cdfNormal = function (x) {
146+
return (1 - math.erf(-x / Math.sqrt(2))) / 2;
147+
};
148+
149+
const pdfNormal = function (x) {
150+
const m = Math.sqrt(2 * Math.PI);
151+
const e = Math.exp(-Math.pow(x, 2) / 2);
152+
return e / m;
153+
};
154+
155+
156+
const acquisitionPI = function (mean, standardDeviation, fMax) {
157+
const pi = [];
158+
for (let w = 0; w < mean.length; w++) {
159+
pi.push(cdfNormal((mean[w] - fMax) / standardDeviation[w], 0, 1));
160+
}
161+
return pi;
162+
};
163+
164+
const acquisitionEI = function (mean, standardDeviation, fMax, xi) {
165+
const eis = [];
166+
let maxValue = 0;
167+
let minValue = 1000;
168+
169+
for (let w = 0; w < mean.length; w++) {
170+
if (standardDeviation[w] === 0) {
171+
eis.push(0);
172+
continue;
173+
}
174+
const std = standardDeviation[w];
175+
let dx = mean[w] - fMax - xi;
176+
dx = Math.max(-7, Math.min(7, dx / std)) * std;
177+
let z = dx / std;
178+
let ei = dx * cdfNormal(z) + std * pdfNormal(z);
179+
180+
if (isNaN(ei)) {
181+
ei.push(0);
182+
continue;
183+
}
184+
185+
maxValue = Math.max(maxValue, ei);
186+
minValue = Math.min(minValue, ei);
187+
eis.push(ei);
188+
}
189+
return eis.map(v => (v - minValue) / (maxValue - minValue));
190+
};
191+
192+
const acquisitionScaledEI = function (mean, standardDeviation, fMax, xi) {
193+
const scaledEis = [];
194+
let maxValue = 0;
195+
let minValue = 1000;
196+
197+
for (let w = 0; w < mean.length; w++) {
198+
if (standardDeviation[w] === 0) {
199+
scaledEis.push(0);
200+
continue;
201+
}
202+
const std = standardDeviation[w];
203+
let dx = mean[w] - fMax - xi;
204+
dx = Math.max(-5, Math.min(5, dx / std)) * std;
205+
let z = dx / std;
206+
const ei = dx * cdfNormal(z) + std * pdfNormal(z);
207+
const vi = std*std*( (z*z + 1) * cdfNormal(z) + z * pdfNormal(z) ) - ei * ei;
208+
const scaledEi = ei / math.sqrt(vi);
209+
210+
if (isNaN(scaledEi)) {
211+
scaledEis.push(0);
212+
continue;
213+
}
214+
215+
maxValue = Math.max(maxValue, scaledEi);
216+
minValue = Math.min(minValue, scaledEi);
217+
scaledEis.push(scaledEi);
218+
}
219+
return scaledEis.map(v => (v - minValue) / (maxValue - minValue));
220+
};
221+
222+
let observationsX = [-0.75, 0.1, 1.2, 1.65];
223+
let observationsY = observationsX.map(x => func(x));
224+
let observations = observationsX.map(x => [x, func(x)]);
225+
let kernelParam = 10;
226+
let acquisitionEi = [];
227+
let acquisitionScaledEi = [];
228+
229+
const drawAll = function () {
230+
drawPoints(
231+
configObs,
232+
"#0000FF",
233+
observations
234+
);
235+
236+
const kernel = (x1, x2) => kernelWithParameter(x1, x2, kernelParam);
237+
const sigma = 0.000001;
238+
const x_new = [];
239+
const xCoordStart = xCoordToPix(config, config.xMin);
240+
const xCoordEnd = xCoordToPix(config, config.xMax);
241+
for (let i = xCoordStart; i < xCoordEnd; i++) {
242+
x_new.push(xPixToCoord(config, i))
243+
}
244+
const mean = posterioriMean(observationsX, x_new, sigma, observationsY, kernel).valueOf();
245+
drawFunction(configGpMean, "#000000", (x) => {
246+
const w = xCoordToPix(config, x);
247+
return mean[w - xCoordStart];
248+
});
249+
250+
const standardDeviation = posteriorCov(observationsX, x_new, sigma, kernel);
251+
252+
const xs = [...Array(config.ctx.canvas.width).keys()];
253+
const ysU = xs.map(x => {
254+
return mean[x] + 1.96 * standardDeviation[x];
255+
});
256+
const ysL = xs.map(x => {
257+
return mean[x] - 1.96 * standardDeviation[x];
258+
});
259+
const acquisitionPi = acquisitionPI(mean, standardDeviation, Math.max(...observationsY));
260+
acquisitionEi = acquisitionEI(mean, standardDeviation, Math.max(...observationsY), 0.01);
261+
acquisitionScaledEi = acquisitionScaledEI(mean, standardDeviation, Math.max(...observationsY), 0);
262+
263+
clear(configGpConv);
264+
configGpConv.ctx.fillStyle = "#0000b050";
265+
configGpConv.ctx.lineWidth = 3;
266+
configGpConv.ctx.beginPath();
267+
lineToPoints(configGpConv, xs, ysU);
268+
lineToPoints(configGpConv, [...xs].reverse(), [...ysL].reverse());
269+
configGpConv.ctx.fill();
270+
271+
272+
drawFunction(configGpAcqEi, "#008000", (x) => {
273+
const w = xCoordToPix(config, x);
274+
return acquisitionEi[w - xCoordStart] + config.yMin+0.01;
275+
});
276+
277+
/*
278+
drawFunction(configGpAcqScaledEi, "#000080", (x) => {
279+
const w = xCoordToPix(config, x);
280+
return acquisitionScaledEi[w - xCoordStart] + config.yMin+0.01;
281+
});
282+
*/
283+
};
284+
285+
window.updateKernelParam = function (param) {
286+
kernelParam = param;
287+
drawAll();
288+
};
289+
window.addRandomObservationPoint = function () {
290+
const x = math.random() * (config.xMax - config.xMin) + config.xMin;
291+
observationsX.push(x);
292+
observationsY = observationsX.map(x => func(x));
293+
observations = observationsX.map(x => [x, func(x)]);
294+
drawAll();
295+
};
296+
window.addEiSuggestedObservationPoint = function () {
297+
const observationsW = observationsX.map(x => xCoordToPix(config, x));
298+
function argMax(array) {
299+
return array.map((y, i) => [i, y]).filter(a => !observationsW.includes(a[0])).reduce((r, a) => (a[1] > r[1] ? a : r))[0];
300+
}
301+
const x = xPixToCoord(config, argMax(acquisitionEi));
302+
observationsX.push(x);
303+
observationsY = observationsX.map(x => func(x));
304+
observations = observationsX.map(x => [x, func(x)]);
305+
drawAll();
306+
};
307+
window.addScaledEiSuggestedObservationPoint = function () {
308+
const observationsW = observationsX.map(x => xCoordToPix(config, x));
309+
function argMax(array) {
310+
return array.map((y, i) => [i, y]).filter(a => !observationsW.includes(a[0])).reduce((r, a) => (a[1] > r[1] ? a : r))[0];
311+
}
312+
const w = argMax(acquisitionScaledEi);
313+
const x = xPixToCoord(config, w);
314+
observationsX.push(x);
315+
observationsY = observationsX.map(x => func(x));
316+
observations = observationsX.map(x => [x, func(x)]);
317+
drawAll();
318+
};
319+
window.clearObservations = function () {
320+
observationsX = [];
321+
window.addRandomObservationPoint();
322+
};
120323

324+
drawAll();
121325
</script>
122326

123327

124328
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
125329
<script id="MathJax-script" async
126330
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js">
127331
</script>
128-
<div style="width:600px;padding-top:620px">
332+
<div style="width:600px;padding-top:20px">
129333
<h3>Plot of function</h3>
130334

131335
<p>
132-
\[ f(x) = \sum_{i=1}^{10}\sin(x)\ \sin(\frac{i\ x^2}{\pi})^{20} \]
336+
\[ f(x) = - x \sin(10 x) \]
133337
</p>
134338
</div>

graphplot.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,13 @@ export function drawGrid(c, xFactor, yFactor, axisColor, gridColor) {
9191
export function xCoordToPix(c, xCoord) {
9292
const xDiff = c.xMax - c.xMin;
9393
const xPixPerUnit = c.ctx.canvas.width / xDiff;
94-
return (xCoord - c.xMin) * xPixPerUnit;
94+
return Math.round((xCoord - c.xMin) * xPixPerUnit);
9595
}
9696

9797
export function yCoordToPix(c, yCoord) {
9898
const yDiff = c.yMax - c.yMin;
9999
const yPixPerUnit = c.ctx.canvas.height / yDiff;
100-
return c.ctx.canvas.height - (yCoord - c.yMin) * yPixPerUnit;
100+
return Math.round(c.ctx.canvas.height - (yCoord - c.yMin) * yPixPerUnit);
101101
}
102102

103103
export function xPixToCoord(c, xPix) {
@@ -136,6 +136,15 @@ export function drawFunction(c, strokeStyle, func) {
136136
c.ctx.stroke();
137137
}
138138

139+
export function lineToPoints(c, wPoints, yPoints) {
140+
for (let i=0; i < wPoints.length; i++) {
141+
const w = wPoints[i];
142+
const y = yPoints[i];
143+
const h = yCoordToPix(c, y);
144+
c.ctx.lineTo(w, h);
145+
}
146+
}
147+
139148
export function drawPoints(c, fillStyle, points) {
140149
clear(c);
141150
c.ctx.fillStyle = fillStyle;

0 commit comments

Comments
 (0)