/*
* BaseModelFunction.java
*
* Created on September 4, 2002, 5:26 AM
*/
package hep.aida.ref.function;
import hep.aida.IAnnotation;
import hep.aida.IFunction;
import hep.aida.IModelFunction;
import hep.aida.IRangeSet;
import hep.aida.ref.Annotation;
import hep.aida.ref.ManagedObject;
import java.util.ArrayList;
/**
*
* @author serbo
*/
public class BaseModelFunction extends ManagedObject implements IModelFunction, FunctionDispatcher {
protected String[] varNames;
protected IAnnotation annotation;
protected String codeletString;
protected String title;
protected FunctionCore function;
protected FunctionCore functionNotNormalized;
protected FunctionCore functionNormalized;
//private boolean providesNormalization;
private boolean isNormalized;
private boolean normalizationValid;
private double normalizationAmplitude;
private RangeSet[] rangeSet;
private ArrayList listeners = new ArrayList();
// One of FunctionCores can be "null"
protected BaseModelFunction() {
super(null);
}
public BaseModelFunction(String name, String tit, IFunction func) {
super(name);
IFunctionCoreNotNorm notNorm = new IFunctionCoreNotNorm(func);
init(tit, notNorm, null);
annotation = func.annotation();
setCodeletString(func.codeletString());
String[] funcVarNames = func.variableNames();
for (int i=0; i<function.dimension(); i++) {
varNames[i] = funcVarNames[i];
}
}
public BaseModelFunction(String name, String title, FunctionCore notNorm, FunctionCore norm) {
super(name);
init(title, notNorm, norm);
}
protected void init(String tit, FunctionCore notNorm, FunctionCore norm) {
if (notNorm == null && norm == null)
throw new IllegalArgumentException("Normalized and NotNormalized FunctionCores can not both be null");
annotation = new Annotation();
annotation.addItem(Annotation.titleKey,"Title", true);
if (tit != null) setTitle(tit);
codeletString = title;
functionNotNormalized = notNorm;
functionNormalized = norm;
if (notNorm != null) {
isNormalized = false;
function = functionNotNormalized;
//providesNormalization = false;
normalizationValid = true;
normalizationAmplitude = 1.;
} else {
isNormalized = true;
function = functionNormalized;
//providesNormalization = true;
normalizationValid = false;
normalizationAmplitude = 1.;
}
rangeSet = new RangeSet[function.dimension()];
varNames = new String[function.dimension()];
for (int i=0; i<function.dimension(); i++) {
varNames[i] = "x" + i;
rangeSet[i] = new RangeSet();
}
}
public FunctionCore core() {
return function;
}
public int dimension() { return function.dimension(); }
public int numberOfParameters() { return function.numberOfParameters(); }
public double functionValue(double[] var) { return function.functionValue(var); }
public final double value(double[] var) {
if (!normalizationValid) {
calculateNormalizationAmplitude();
//System.out.print(" Value = " + normalizationAmplitude * function.functionValue(var)+", var = "+var[0]);
}
double fVal = function.functionValue(var);
// System.out.println("&&& BaseModelFunction value "+fVal+" "+1./normalizationAmplitude);
double val = normalizationAmplitude * fVal;
if (isNormalized && val<0) val = 0.;
return val;
}
public IAnnotation annotation() { return annotation; }
public String variableName(int i) { return varNames[i]; }
public String[] variableNames() { return varNames; }
public String[] parameterNames() { return function.parameterNames(); }
public int indexOfParameter(String name) { return function.indexOfParameter(name); }
public void setParameters(double[] params) {
//System.out.print("\nSetting parameters: ");
//for (int i=0; i<numberOfParameters(); i++) { System.out.print(params[i]+" "); }
//System.out.print("\n");
if (isNormalized) normalizationValid = false;
function.setParameters(params);
notifyFunctionChanged(new FunctionChangedEvent( FunctionChangedEvent.PARAMETER_VALUE_CHANGED ) );
}
public void setParameter(String name, double x) throws IllegalArgumentException {
if (isNormalized) normalizationValid = false;
function.setParameter(name, x);
notifyFunctionChanged(new FunctionChangedEvent( FunctionChangedEvent.PARAMETER_VALUE_CHANGED ) );
}
public double[] parameters() { return function.parameters(); }
public double parameter(String name) {
return function.parameter(name);
}
public boolean isEqual(IFunction f) {
throw new UnsupportedOperationException("This method is not implemented yet");
}
public boolean providesGradient() { return function.providesGradient(); }
public double[] gradient(double[] x) {
double[] val = new double[dimension()];
if (function.providesGradient()) {
if (!normalizationValid) {
calculateNormalizationAmplitude();
}
val = function.gradient(x);
for (int i=0; i<val.length; i++) val[i] = normalizationAmplitude*val[i];
//System.out.print("gradient: Value = " + val[0] +", var = "+x[0]);
} else throw new UnsupportedOperationException("This function does not provide gradient");
//else return numericGradient(x);
return val;
}
public String codeletString() { return codeletString; }
public void setCodeletString(String codelet) { codeletString = codelet; }
public String normalizationParameter() {
throw new UnsupportedOperationException("This has not been implemented yet");
}
// IModelFunction methods
public boolean providesNormalization() { return function.providesNormalization(); }
public void normalize(boolean on) {
boolean notify = on != isNormalized;
if (on) {
if (functionNormalized == null)
throw new IllegalArgumentException("This function can not be converted into Normalized form!");
function = functionNormalized;
isNormalized = true;
normalizationValid = false;
normalizationAmplitude = 1.;
}
else {
if (functionNotNormalized == null)
throw new IllegalArgumentException("This function can not be converted into Not-Normalized form!");
function = functionNotNormalized;
isNormalized = false;
normalizationValid = true;
normalizationAmplitude = 1.;
}
if ( notify )
notifyFunctionChanged(new FunctionChangedEvent( FunctionChangedEvent.RANGE_CHANGED ) );
}
public boolean isNormalized() { return isNormalized; }
public double[] parameterGradient(double[] x) {
double[] val = new double[numberOfParameters()];
if (function.providesParameterGradient()) {
if (!normalizationValid) {
calculateNormalizationAmplitude();
}
val = function.parameterGradient(x);
for (int i=0; i<val.length; i++) val[i] = normalizationAmplitude*val[i];
//System.out.print("parameterGradient: Value = " + val[0] +", var = "+x[0]);
} else throw new UnsupportedOperationException("This function does not provide parameter gradient");
//else return numericParameterGradient(x);
return val;
}
public boolean providesParameterGradient() { return function.providesParameterGradient(); }
public IRangeSet normalizationRange(int iAxis) { return rangeSet[iAxis]; }
public void includeNormalizationAll() {
if (isNormalized) normalizationValid = false;
for (int i=0; i<dimension(); i++) rangeSet[i].includeAll();
notifyFunctionChanged(new FunctionChangedEvent( FunctionChangedEvent.RANGE_CHANGED ) );
}
public void excludeNormalizationAll() {
if (isNormalized) normalizationValid = false;
for (int i=0; i<dimension(); i++) rangeSet[i].excludeAll();
notifyFunctionChanged(new FunctionChangedEvent( FunctionChangedEvent.RANGE_CHANGED ) );
}
public double getNormalizationAmplitude() {
return normalizationAmplitude;
}
// Extra methods
public void calculateNormalizationAmplitude() {
normalizationValid = true;
double val = 0;
normalizationAmplitude = 1;
if (!function.providesNormalization()) {
if ( dimension() == 1 ) val = FunctionIntegrator.integralTrapezoid(this);
else val = FunctionIntegrator.integralMC(this);
}
else {
if (dimension() == 1) {
double[] xMax = rangeSet[0].upperBounds();
double[] xMin = rangeSet[0].lowerBounds();
for (int k=0; k<rangeSet[0].size(); k++) {
val += function.normalizationAmplitude(xMin, xMax);
}
} else if (dimension() == 2) {
double[] xMax = new double[2];
double[] xMin = new double[2];
double[] xMax0 = rangeSet[0].upperBounds();
double[] xMin0 = rangeSet[0].lowerBounds();
double[] xMax1 = rangeSet[1].upperBounds();
double[] xMin1 = rangeSet[1].lowerBounds();
for (int k=0; k<rangeSet[0].size(); k++) {
xMin[0] = xMin0[k];
xMax[0] = xMax0[k];
for (int j=0; j<rangeSet[1].size(); j++) {
xMin[1] = xMin1[j];
xMax[1] = xMax1[j];
val += function.normalizationAmplitude(xMin, xMax);
}
}
} else if (dimension() == 3) {
double[] xMax = new double[3];
double[] xMin = new double[3];
double[] xMax0 = rangeSet[0].upperBounds();
double[] xMin0 = rangeSet[0].lowerBounds();
double[] xMax1 = rangeSet[1].upperBounds();
double[] xMin1 = rangeSet[1].lowerBounds();
double[] xMax2 = rangeSet[2].upperBounds();
double[] xMin2 = rangeSet[2].lowerBounds();
for (int k=0; k<rangeSet[0].size(); k++) {
xMin[0] = xMin0[k];
xMax[0] = xMax0[k];
for (int j=0; j<rangeSet[1].size(); j++) {
xMin[1] = xMin0[j];
xMax[1] = xMax0[j];
val += function.normalizationAmplitude(xMin, xMax);
for (int i=0; i<rangeSet[2].size(); i++) {
xMin[2] = xMin0[i];
xMax[2] = xMax0[i];
val += function.normalizationAmplitude(xMin, xMax);
}
}
}
} else
throw new IllegalArgumentException("Temporary support only up to 3 dimensions");
}
normalizationAmplitude = 1./val;
}
public String title() {
String t = (annotation != null) ? annotation.value(Annotation.titleKey) : title;
return t;
}
public void setTitle(String t) {
title = t;
if (annotation != null) annotation.setValue(Annotation.titleKey, title);
notifyFunctionChanged(new FunctionChangedEvent( FunctionChangedEvent.TITLE_CHANGED ) );
}
public boolean setParameterNames(String[] params) {
boolean result = function.setParameterNames(params);
notifyFunctionChanged(new FunctionChangedEvent( FunctionChangedEvent.PARAMETER_NAME_CHANGED ) );
return result;
}
public double[] numericGradient(double[] x) {
throw new UnsupportedOperationException("Numeric Gradient is not implemented yet");
}
public double[] numericParameterGradient(double[] x) {
throw new UnsupportedOperationException("Numeric Parameter Gradient is not implemented yet");
}
public RangeSet[] getRangeSet() { return rangeSet; }
public String toString() {
String str = "BaseModelFunction: Title="+title()+", name="+name();
str += "\n\tDimension: "+dimension()+", number of parameters: "+numberOfParameters()+", Codelet String: " + codeletString();
str += "\n\tVariable Names: ";
String[] varNames = variableNames();
for (int i=0; i<dimension(); i++) str += varNames[i] + ", ";
str += "\t Parameters: ";
String[] parNames = parameterNames();
double[] parValues = parameters();
for (int i=0; i<numberOfParameters(); i++) str += parNames[i] + "=" + parValues[i] + ", ";
str += "\n\tProvides Gradient: " + providesGradient();
str += ", Provides Parameter Gradient: " + providesParameterGradient();
str += ", Provides Normalization: " + providesNormalization();
return str;
}
public void addFunctionListener(FunctionListener listener) {
listeners.add(listener);
}
public void removeFunctionListener(FunctionListener listener) {
listeners.remove(listener);
}
void notifyFunctionChanged(FunctionChangedEvent event) {
for ( int i = 0; i < listeners.size(); i++ )
( (FunctionListener) listeners.get(i) ).functionChanged(event);
}
}