package org.antlr.codebuff;

import java.util.Arrays;
import java.util.function.Function;

public class Optimizer {
	public static final int LEARNING_RATE = 10;
	public static final double h = 0.0001;
	public static final double PRECISION = 0.0000001; // can't be too small as f(x)-f(xprev) prec is low

	/** Use a simple gradient descent approach to find weights */
	/*
	LEARNING_RATE = 10
	h = 0.0001
	PRECISION = 0.0000001 # can't be too small as f(x)-f(xprev) prec is low
	def f(x): return np.cos(3*np.pi*x) / x
	x0s = [runif_(.1,1.2), runif_(.1,1.2)] # random starting positions
	tracex = minimize(f, x0s[0], LEARNING_RATE, h, PRECISION)
	 */
	public static double[] minimize(Function<double[], Double> f, double[] x0, double eta, double h, double precision) {
		double[] x = Arrays.copyOf(x0, x0.length);
		double[] prev_x = null;
		while ( true ) {
			prev_x = x;
			double finite_diff = f.apply(vector_scalar_add(x,h)) - f.apply(x);	// division by h rolls into learning rate
			x = vector_scalar_sub(x, eta * finite_diff);                 		// decelerates x jump as it flattens out
			// print "f(%1.12f) = %1.12f" % (x, f(x)),
			double delta = f.apply(x) - f.apply(prev_x);
			// print ", delta = %1.20f" % delta
			// stop when small change in vertical but not heading down
			if ( delta >= 0 && Math.abs(delta) < precision ) {
				break;
			}
		}
		return x;
	}

	public static double cost(double[] parameters) { return 0.0; }

	public static double[] vector_scalar_add(double[] x, double v) {
		double[] y = new double[x.length];
		for (int i = 0; i<x.length; i++) {
			y[i] = x[i] + v;
		}
		return y;
	}

	public static double[] vector_scalar_sub(double[] x, double v) {
		double[] y = new double[x.length];
		for (int i = 0; i<x.length; i++) {
			y[i] = x[i] - v;
		}
		return y;
	}

	public static void main(String[] args) {
//		minimize(Optimizer::cost, x0s[0], LEARNING_RATE, h, PRECISION);
	}
}

@@ -1364,24 +1364,1077 @@
 turn x;%0A%09%7D%0A%0A
+%09public static double%5B%5D minimize2(Function%3Cdouble%5B%5D, Double%3E f, double%5B%5D x0, double eta, double h, double precision) %7B%0A%09%09double%5B%5D x = Arrays.copyOf(x0, x0.length);%0A%09%09double%5B%5D prev_x = null;%0A%09%09while ( true ) %7B%0A%09%09%09prev_x = x;%0A%0A%09%09%09double%5B%5D finite_diff = finiteDifferenceForAllParameter(f, x, h);%0A%0A%09%09%09for (int i=0; i%3Cx.length; i++) %7B%0A%09%09%09%09x%5Bi%5D = x%5Bi%5D - eta * finite_diff%5Bi%5D;%0A%09%09%09%7D%0A%0A%09%09%09// print %22f(%251.12f) = %251.12f%22 %25 (x, f(x)),%0A%09%09%09double delta = f.apply(x) - f.apply(prev_x);%0A%09%09%09// print %22, delta = %251.20f%22 %25 delta%0A%09%09%09// stop when small change in vertical but not heading down%0A%09%09%09if ( delta %3E= 0 && Math.abs(delta) %3C precision ) %7B%0A%09%09%09%09break;%0A%09%09%09%7D%0A%09%09%7D%0A%09%09return x;%0A%09%7D%0A%0A%09// calculate finite difference for each parameter independently%0A%09public static double%5B%5D finiteDifferenceForAllParameter(Function%3Cdouble%5B%5D, Double%3E f, double%5B%5D x, double h) %7B%0A%09%09double%5B%5D finiteDifferences = new double%5Bx.length%5D;%0A%09%09for (int i=0; i%3Cx.length; i++) %7B%0A%09%09%09double%5B%5D newX = x;%0A%09%09%09newX%5Bi%5D += h;%0A%09%09%09finiteDifferences%5Bi%5D = f.apply(newX) - f.apply(x);%0A%09%09%7D%0A%09%09return finiteDifferences;%0A%09%7D%0A%0A
 %09public stat

add new minimize function to optimizer