package hep.aida.ref.function;
import hep.aida.IFitData;
import hep.aida.dev.IDevFitData;
import hep.aida.dev.IDevFitDataIterator;
/**
*
* @author The FreeHEP team @ SLAC
*
*/
public class NonParametricFunction extends AbstractIFunction {
private boolean mirrorLeft = false;
private boolean mirrorRight = false;
private double rho = 1;
private int nPoints = 1000;
private double[] lookupTable;
private int nEvents;
private double binWidth;
private double[] dataPts;
private double[] weights;
double upperLimit = Double.NaN;
double lowerLimit = Double.NaN;
private static final double sqrt2pi = Math.sqrt(2*Math.PI);
public NonParametricFunction(String title, IFitData data) {
super(title, data.dimension(), 1);
if ( data.dimension() != 1 )
throw new IllegalArgumentException("Only one dimensional non-parametric functions are supported!");
IDevFitDataIterator dataIter = ((IDevFitData) data).dataIterator();
lookupTable = new double[ nPoints+1 ];
nEvents = dataIter.entries();
if (mirrorLeft) nEvents += dataIter.entries();
if (mirrorRight) nEvents += dataIter.entries();
//Calculate upper and lower limits
dataIter.start();
while( dataIter.next() ) {
double x = dataIter.vars()[0];
if ( Double.isNaN(upperLimit) || x > upperLimit )
upperLimit = x;
if ( Double.isNaN(lowerLimit) || x < lowerLimit )
lowerLimit = x;
}
binWidth = (upperLimit - lowerLimit)/(nPoints-1);
dataPts = new double[nEvents];
weights = new double[nEvents];
double entries = 0;
double m = 0;
double r = 0;
int dataCount = 0;
dataIter.start();
while ( dataIter.next() ) {
double x = dataIter.vars() [0];
dataPts[dataCount] = x;
entries++;
m += x;
r += x*x;
dataCount++;
if (mirrorLeft) {
dataPts[dataCount]= 2*lowerLimit - x;
dataCount++;
}
if (mirrorRight) {
dataPts[dataCount]= 2*upperLimit - x;
dataCount++;
}
}
double mean = m/entries;
double sigma = Math.sqrt(r/entries-mean*mean);
double h = Math.pow(4./3.,0.2)*Math.pow(nEvents,-0.2)*rho;
double hmin = h*sigma*Math.sqrt(2.)/10.;
double norm = h*Math.sqrt(sigma)/(2.0*Math.sqrt(3.0));
for(int j=0; j<nEvents; ++j) {
weights[j]=norm/Math.sqrt( gauss(dataPts[j],h*sigma) );
if (weights[j]<hmin) weights[j]=hmin;
}
for (int i=0; i < nPoints+1; ++i) {
lookupTable[i]=evaluateFull( lowerLimit + i*binWidth );
}
}
public String normalizationParameter() {
return null;
}
private double gauss(double x, double sigma) {
double c = 1./(2*sigma*sigma);
double y = 0;
for (int i = 0; i<nEvents; ++i) {
double r = x - dataPts[i];
y += Math.exp(-c*r*r);
}
return y/(sigma*sqrt2pi*nEvents);
}
public double value(double[] v) {
double x = v[0];
int i = (int)Math.floor( (x - lowerLimit)/binWidth );
if (i<0)
// throw new IllegalArgumentException("Cannot have point below lower limit.");
i=0;
if (i>nPoints-1)
// throw new IllegalArgumentException("Cannot have point above upper limit.");
i=nPoints-1;
double dx = (x -(lowerLimit + i*binWidth) )/binWidth;
double val = (lookupTable[i] + dx*(lookupTable[i+1]-lookupTable[i]) );
return val;
}
private double evaluateFull( double x ) {
double y=0;
for (int i=0;i<nEvents;++i) {
double chi = (x-dataPts[i])/weights[i];
y += Math.exp(-0.5*chi*chi)/weights[i];
/*
// if mirroring the distribution across either edge of
// the range ("Boundary Kernels"), pick up the additional
// contributions
// if (_mirrorLeft) {
// chi=(x-(2*_lo-_dataPts[i]))/_weights[i];
// y+=exp(-0.5*chi*chi)/_weights[i];
// }
if (_asymLeft) {
chi=(x-(2*_lo-_dataPts[i]))/_weights[i];
y-=exp(-0.5*chi*chi)/_weights[i];
}
// if (_mirrorRight) {
// chi=(x-(2*_hi-_dataPts[i]))/_weights[i];
// y+=exp(-0.5*chi*chi)/_weights[i];
// }
if (_asymRight) {
chi=(x-(2*_hi-_dataPts[i]))/_weights[i];
y-=exp(-0.5*chi*chi)/_weights[i];
}
*/
}
return y/(sqrt2pi*nEvents);
}
}