var math = require('mathjs')
var Plot = require('plotly')
function rosenbrock(x){
return (1 - x[0])**2 + 100*(x[1] - x[0]**2)**2;
}
function threeDplot(x_data, y_data, z_data, title, range){
// Display an array assert a graph.
var NotebookPlot = Plot.createPlot().constructor;
NotebookPlot.prototype._toHtml = NotebookPlot.prototype.render;
var data = [{x: x_data, y:y_data, z: z_data, type:'surface'}];
var layout = {title: title, autosize:true, autocolorscale:true}
var newPlot = Plot.createPlot(data, layout);
$$html$$ = newPlot.render()
}
range = 2
var rosenbrock_x = math.range(-range, range, .1)._data
var rosenbrock_z = [];
for (i=0; i<rosenbrock_x.length; i++){
var z_row = []
for (j=0; j<rosenbrock_x.length; j++){
z_row.push(rosenbrock([rosenbrock_x[i], rosenbrock_x[j]]));
}
rosenbrock_z.push(z_row);
}
threeDplot(rosenbrock_x, rosenbrock_x, rosenbrock_z, 'Rosenbrock function', range);
function compare_rosenbrock(a, b){
return rosenbrock(a) - rosenbrock(b);
}
function insert(arr, func_values, element, element_value){
var pos = position(element_value, func_values);
arr.splice(pos, 0, element);
func_values.splice(pos, 0, element_value);
}
function position(element, array, start, end) {
start = start || 0;
end = end || array.length;
var pivot = parseInt(start + (end - start) / 2, 10);
if (array[pivot] === element) return pivot;
if (end-start <= 1){
if (array[pivot] < element){
return pivot+1;
} else {
return pivot;
}
}
if (array[pivot] < element) {
return position(element, array, pivot, end);
} else {
return position(element, array, start, pivot);
}
}
function shrink(arr, func_values, func){
for (i=1; i<arr.length; i++){
arr[i] = math.multiply(.5, math.add(arr[i], arr[0]));
func_values[i] = func(arr[i]);
}
}
function plot_simplex(points, func_values){
var P = math.matrix(points)
var x = math.flatten(P.subset(math.index(math.range(0, P._size[0]), 0)))
var y = math.flatten(P.subset(math.index(math.range(0, P._size[0]), 1)))
var NotebookPlot = Plot.createPlot().constructor;
NotebookPlot.prototype._toHtml = NotebookPlot.prototype.render;
var data = [{x: rosenbrock_x, y:rosenbrock_x, z: rosenbrock_z, type:'surface'},
{x: x, y: y, z: func_values, type: 'scatter3d', mode: 'lines+markers'}];
var layout = {title: 'simplex', autosize:true, autocolorscale:true}
var newPlot = Plot.createPlot(data, layout);
$$html$$ = newPlot.render()
}
function NelderMeadStep(points, func_values, func){
var point = points.pop();
var mean = math.mean(points, 0);
func_values.pop();
// Reflect
var new_point = math.subtract(math.multiply(2, mean), point);
var new_value = func(new_point);
if (new_value < func_values[0]){
// If refelction worked try reflect and grow
var doubled_point = math.subtract(math.multiply(3, mean),
math.multiply(2, point));
var doubled_value = func(doubled_point);
if (doubled_value < new_value){
// If reflect and grow worked accept
insert(points, func_values, doubled_point, doubled_value);
} else {
// Else just reflect
insert(points, func_values, new_point, new_value);
}
} else if (new_value < func_values[func_values.length-1]){
// If reflect wasn't worst or best accept and move on
insert(points, func_values, new_point, new_value);
} else {
// Else try reflect and shrink
new_point = math.subtract(math.multiply(1.5, mean),
math.multiply(.5, point));
new_value = func(new_point);
if (new_value < func_values[func_values.length-1]){
insert(points, func_values, new_point, new_value);
} else {
// If reflect and shrink didn't work try just shrinking
new_point = math.multiply(.5, math.add(mean, point));
new_value = func(new_point);
if (new_value < func_values[func_values.length-1]){
insert(points, func_values, new_point, new_value);
} else {
// If nothing worked shrink the full simplex
points.push(point);
shrink(points, func_values, func);
}
}
}
}
var start_point = [-1, -1];
var stop_criterion = .001;
var history = [];
var max_num_steps = 1000;
var points = [math.add(start_point, [0.1, 0]), math.add(start_point, [0, 0.1]), start_point];
// points = math.sort(points, compare_rosenbrock);
var func_values = []
for (i=0; i<points.length; i++) func_values.push(rosenbrock(points[i]));
// func_values = math.sort(func_values);
while (func_values[0] > stop_criterion && max_num_steps > 0){
// plot_simplex(points, func_values);
NelderMeadStep(points, func_values, rosenbrock);
max_num_steps--;
// console.log(points)
console.log(func_values)
}
console.log(points[0])
function line_min(point, dir, func){
gold_ratio = (1+math.sqrt(5))/2;
var max_num_steps = 1000;
var points = [point, math.add(point, dir)];
var func_values = [func(point), func(points[1])];
if (func_values[0] < func_values[1]){
dir = math.multiply(-1, dir);
points = [points[1], points[0]];
func_values = [func_values[1], func_values[0]];
}
points.push(math.add(points[1], math.multiply(gold_ratio, dir)));
func_values.push(func(points[2]));
while (func_values[2] < func_values[1]){
points.splice(0, 1);
func_values.splice(0, 1);
points.push(math.add(points[1],
math.multiply(gold_ratio,
math.subtract(points[1], points[0]))));
func_values.push(func(points[2]));
}
while ((func_values[2] - func_values[1] > .001 ||
func_values[0] - func_values[1] > .001) &&
max_num_steps > 0) {
var new_point = math.add(points[1],
math.multiply(1/gold_ratio,
math.subtract(points[1], points[0])));
var new_value = func(new_point);
if (new_value > func_values[1]){
points = [new_point, points[1], points[0]]
func_values = [new_value, func_values[1], func_values[0]]
} else {
points = [points[1], new_point, points[2]]
func_values = [func_values[1], new_value, func_values[0]]
}
max_num_steps--;
}
return points[1];
}
function remove_biggest(D, D_contibution){
var idx = 0;
for (i=0; i<D.length; i++){
if (D_contibution[i] > D_contibution[idx]){
idx = i;
}
}
D.splice(idx, 1);
D_contibution.splice(idx, 1);
}
function dir_set(start_point, D, stop_criterion, func){
var D_contibution = math.zeros(D.length)._data;
var history = [];
var point = start_point;
var value = func(point);
while (value > stop_criterion){
var new_point = point;
for (i=0; i<D.length; i++){
new_point = line_min(new_point, D[i], func);
new_value = func(new_point);
D_contibution[i] += value - new_value;
value = new_value;
console.log(D[i], new_point, value)
}
new_dir = math.subtract(new_point, point);
remove_biggest(D, D_contibution);
D.splice(0, 0,new_dir);
D_contibution.splice(0, 0, 0);
point = new_point;
}
}
start_point = [-1, -1];
stop_criterion = .001;
max_num_steps = 1000;
func = rosenbrock;
var D = [[1, 0], [0, 1]];
dir_set(start_point, D, stop_criterion, func)
function rosenbrock_grad(x){
return [-2*(1-x[0]) + -400*(x[1] - x[0]**2)*x[0], 200*(x[1] - x[0]**2)]
}
function conjugate_grad(start_point, stop_criterion, func, grad){
var history = [];
var point = start_point;
var dir = grad(point);
while (func(point) > stop_criterion){
var old_gradient = grad(point);
point = line_min(point, dir, func);
var new_gradient = grad(point);
var gamma = math.dot(new_gradient, math.subtract(new_gradient, old_gradient)) / math.dot(old_gradient, old_gradient);
dir = math.add(new_gradient, math.multiply(gamma, dir));
console.log(point, dir);
}
}
start_point = [-1, -1];
stop_criterion = .001;
func = rosenbrock;
grad = rosenbrock_grad;
conjugate_grad(start_point, stop_criterion, func, grad)
// Standard Normal variate using Box-Muller transform.
function randn_bm(N) {
N = N || 1;
if (N == 1){
var u = 1 - Math.random(); // Subtraction to flip [0, 1) to (0, 1].
var v = 1 - Math.random();
return Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v );
} else {
var arr = [];
for (i=0; i<N; i++){
var u = 1 - Math.random();
var v = 1 - Math.random();
arr.push(Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v ));
}
}
return arr;
}
function E(S, J){
var sum = 0;
for (i=0; i<S.length; i++){
sum += J[i]*S[i]*S[(i+1)%S.length];
}
return -sum;
}
function plotHistory(history, alpha){
var NotebookPlot = Plot.createPlot().constructor;
NotebookPlot.prototype._toHtml = NotebookPlot.prototype.render;
var data = [{x: math.range(0, history.length)._data, y:history, type:'line'}];
var layout = {title: 'Simulated Annealing - parameter = ' + alpha}
var newPlot = Plot.createPlot(data, layout);
$$html$$ = newPlot.render()
}
N = 100;
S = math.ones(N)._data;
J = randn_bm(N);
max_num_steps = 1000;
minE = -math.sum(math.abs(J));
console.log(minE);
function simulated_annealing(S, J, E_func, alpha, max_num_steps, min_E){
var energy = E_func(S, J);
var t = 0;
var history = [energy];
while (energy > min_E+.001 && t < max_num_steps){
var oldS = math.clone(S);
var old_energy = energy;
var pos = math.randomInt(S.length);
S[pos] *= -1;
energy = E_func(S, J);
if (energy > old_energy) {
var r = math.random();
if (r > math.exp(-t*alpha*(energy-old_energy))){
S = oldS;
energy = old_energy;
}
}
t++;
history.push(energy);
}
return history;
}
alpha = .1;
his = simulated_annealing(S, J, E, alpha, max_num_steps, minE);
plotHistory(his, alpha);
console.log('Minimal E = ' + his[his.length-1])
alpha = .01;
his = simulated_annealing(S, J, E, alpha, max_num_steps, minE);
plotHistory(his, alpha);
console.log('Minimal E = ' + his[his.length-1])
alpha = .001;
his = simulated_annealing(S, J, E, alpha, max_num_steps, minE);
plotHistory(his, alpha);
console.log('Minimal E = ' + his[his.length-1])
population_size = 100;
var population = [];
for (i=0; i<population_size; i++){
var new_string = [];
for (j=0; j<N; j++){
r = math.random();
if (r < .5){
new_string.push(-1);
} else {
new_string.push(1);
}
}
population.push(new_string);
}
function new_element(strings, fitnesses){
idx = math.range(0, strings.length)._data;
fitnesses = math.multiply(fitnesses, 1/math.sum(fitnesses));
parents_pos = math.pickRandom(idx, 2, fitnesses);
parents = [strings[parents_pos[0]], strings[parents_pos[1]]];
crossover = math.randomInt(0, parents[0].length);
mutation = math.randomInt(0, parents[0].length);
var child = parents[0].slice(0, crossover).concat(parents[1].slice(crossover, parents[1].length));
child[mutation] *= -1;
return child;
}
function GA(strings, J, E_func, beta, max_num_steps, min_E){
var t = 0;
var history = [];
var energies = [];
var fitnesses = [];
for (l=0; l<strings.length; l++){
var energy = E_func(strings[l], J);
energies.push(energy);
fitnesses.push(math.exp(-beta*(energy-min_E)));
}
var minimal_energy = math.min(energies);
history.push(minimal_energy);
while (minimal_energy > min_E + .001 && t < max_num_steps){
var new_strings = [];
for (l=0; l<strings.length; l++){
new_strings.push(new_element(strings, fitnesses));
}
strings = new_strings;
for (l=0; l<strings.length; l++){
var energy = E_func(strings[l], J);
energies[l] = energy;
fitnesses[l] = math.exp(-beta*(energy-min_E));
}
minimal_energy = math.min(energies);
history.push(minimal_energy);
t++;
}
return history;
}
// DO NOT WORK!!!
// beta = 10;
// his = GA(population, J, E, beta, max_num_steps, minE);
// plotHistory(his, beta);
// console.log('Minimal E = ' + his[his.length-1])
beta = 1;
his = GA(population, J, E, beta, max_num_steps, minE);
plotHistory(his, beta);
console.log('Minimal E = ' + his[his.length-1])
beta = .1;
his = GA(population, J, E, beta, max_num_steps, minE);
plotHistory(his, beta);
console.log('Minimal E = ' + his[his.length-1])
beta = .01;
his = GA(population, J, E, beta, max_num_steps, minE);
plotHistory(his, beta);
console.log('Minimal E = ' + his[his.length-1])