/*
* Copyright (c) 2012 Diamond Light Source Ltd.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package uk.ac.diamond.scisoft.analysis.optimize;
import org.eclipse.january.dataset.Dataset;
import org.eclipse.january.dataset.DatasetUtils;
import Jama.Matrix;
/**
* Basic least squares optimiser, this currently works for polynomial
* functions, and not really for more complex functions.
*/
public class LeastSquares extends AbstractOptimizer {
/**
* Setup the logging facilities
*/
// private static final Logger logger = LoggerFactory.getLogger(LeastSquares.class);
/**
* Base constructor
*
* @param tolerence
* Not currently used.
*/
// TODO Add in the ability to use the tolerance parameter
public LeastSquares(@SuppressWarnings("unused") double tolerence) {
}
private double alpha(int k, int l) {
Dataset value = DatasetUtils.convertToDataset(function.calculatePartialDerivativeValues(params.get(k), coords[0]));
if (k == l) {
value.imultiply(value);
} else {
value.imultiply(function.calculatePartialDerivativeValues(params.get(l), coords[0]));
}
if (weight != null)
value.imultiply(weight);
return ((Number) value.sum()).doubleValue();
}
private double alphaPrime(int k, int l, double lambda) {
double a = alpha(k, l);
return k == l ? a * (1 + lambda) : a;
}
private Matrix evaluateMatrix(double lambda) {
Matrix mat = new Matrix(n, n);
for (int i = 0; i < n; i++) {
for (int j = 0; j <= i; j++) {
mat.set(i, j, alphaPrime(i, j, lambda));
}
}
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
mat.set(i, j, mat.get(j, i));
}
}
return mat;
}
private final static double PERT = 1e-4;
private double beta(int k) {
// do a numerical differential for the time being.
final double[] params = getParameterValues();
final double v = params[k];
double dv = (v == 0 ? 1 : v) * PERT;
params[k] = v - dv;
double chimin = calculateResidual(params);
params[k] = v + dv;
double chimax = calculateResidual(params);
params[k] = v;
setParameterValues(params);
return -0.5 * (chimax - chimin) / (2 * PERT);
}
private Matrix evaluateChiSquared() {
Matrix mat = new Matrix(n, 1);
for (int i = 0; i < n; i++) {
mat.set(i, 0, beta(i));
}
return mat;
}
private double[] solveDa(double lambda) {
Matrix A = evaluateMatrix(lambda);
Matrix B = evaluateChiSquared();
Matrix mat = A.inverse();
mat = mat.times(B);
double[] result = new double[n];
for (int i = 0; i < result.length; i++) {
result[i] = mat.get(i, 0);
}
return result;
}
@Override
void internalOptimize() {
// choose a value for lambda
optimize(0.001);
}
public void optimize(double lambda) {
double[] pvalues = getParameterValues();
// first calculate the quality of the fit.
double eval = calculateResidual(pvalues);
double[] testParams = new double[n];
for (int j = 0; j < 1; j++) {
// solve the least squares calculation
double[] dParams = solveDa(lambda);
// evaluate the new position
for (int i = 0; i < n; i++) {
testParams[i] = pvalues[i] + dParams[i];
}
double testEval = calculateResidual(testParams);
//logger.debug(testEval + "," + eval + "," + lambda + "["
// + dParams[0] + "," + dParams[1] + "," + dParams[2] + "]");
if (testEval >= eval) {
lambda *= 10;
} else {
lambda /= 10;
eval = testEval;
for (int i = 0; i < n; i++) {
pvalues[i] = testParams[i];
}
}
}
setParameterValues(pvalues);
}
}