if (!polyDegreeDialog.isCancelled()) {
setDegree(polyDegreeDialog.getDegree());
final AbstractLeastSquaresOptimizer optimizer = new LevenbergMarquardtOptimizer();
final PolynomialFitter fitter = new PolynomialFitter(
getDegree(), optimizer);
model = new IModel() {
boolean interrupted = false;
List<ValidObservation> fit;
List<ValidObservation> residuals;
PolynomialFunction function;
// ICoordSource coordSrc = JDCoordSource.instance;
Map<String, String> functionStrMap = new LinkedHashMap<String, String>();
double aic = Double.NaN;
double bic = Double.NaN;
@Override
public String getDescription() {
return LocaleProps
.get("MODEL_INFO_POLYNOMIAL_DEGREE_DESC")
+ degree
+ " for "
+ obs.get(0).getBand()
+ " series";
}
@Override
public List<ValidObservation> getFit() {
return fit;
}
@Override
public List<ValidObservation> getResiduals() {
return residuals;
}
@Override
public String getKind() {
return LocaleProps.get("ANALYSIS_MENU_POLYNOMIAL_FIT");
}
@Override
public List<PeriodFitParameters> getParameters() {
// None for a polynomial fit.
return null;
}
@Override
public boolean hasFuncDesc() {
return true;
}
public String toFitMetricsString() throws AlgorithmError {
String strRepr = functionStrMap
.get("MODEL_INFO_FIT_METRICS_TITLE");
// DecimalFormat fmt = NumericPrecisionPrefs
// .getOtherOutputFormat();
// List<Double> derivs = new ArrayList<Double>();
if (strRepr == null) {
// Goodness of fit.
strRepr = "RMS: "
+ NumericPrecisionPrefs
.formatOther(optimizer.getRMS());
// Akaike and Bayesean Information Criteria.
if (aic != Double.NaN && bic != Double.NaN) {
strRepr += "\nAIC: "
+ NumericPrecisionPrefs
.formatOther(aic);
strRepr += "\nBIC: "
+ NumericPrecisionPrefs
.formatOther(bic);
}
}
return strRepr;
}
// TODO: consider analytic approaches
// Return a string representation of the extreme magnitude
// (numerical opposite of magnitude) generated by the
// function.
private String toExtremumString(String titleKey,
GoalType goal) throws AlgorithmError {
double min = obs.get(0).getJD() - zeroPoint;
double max = obs.get(obs.size() - 1).getJD()
- zeroPoint;
IExtremaFinder finder = new ApacheCommonsExtremaFinder(
function, goal, min, max, zeroPoint);
finder.execute();
double extremeMag = finder.getExtremeMag();
String strRepr = String.format("JD: %s, Mag: %s",
NumericPrecisionPrefs.formatTime(finder
.getExtremeTime()),
NumericPrecisionPrefs.formatMag(extremeMag));
// Is the extremum within a reasonable range? If not,
// set it to null.
if (strRepr != null) {
if (goal == GoalType.MAXIMIZE) {
double maxMag = getNumericallyMaximumMagnitude();
if (extremeMag > maxMag) {
strRepr = null;
}
} else if (goal == GoalType.MINIMIZE) {
double minMag = getNumericallyMinimumMagnitude();
if (extremeMag < minMag) {
strRepr = null;
}
}
}
return strRepr;
}
// Return a string representation of the minimum magnitude
// (numerical maximum) generated by the function.
private String toMinimaString() throws AlgorithmError {
return toExtremumString("MODEL_INFO_MINIMA_TITLE",
GoalType.MAXIMIZE);
}
// Return a string representation of the maximum magnitude
// (numerical minimum) generated by the function.
private String toMaximaString() throws AlgorithmError {
return toExtremumString("MODEL_INFO_MAXIMA_TITLE",
GoalType.MINIMIZE);
}
// Return the magnitude that is numerically the smallest,
// rather than the minimum in terms of brightness.
private double getNumericallyMinimumMagnitude() {
double min = obs.get(0).getMag();
for (ValidObservation ob : obs) {
double mag = ob.getMag();
if (mag < min) {
min = mag;
}
}
return min;
}
// Return the magnitude that is numerically the largest,
// rather than the maximum in terms of brightness.
private double getNumericallyMaximumMagnitude() {
double max = obs.get(0).getMag();
for (ValidObservation ob : obs) {
double mag = ob.getMag();
if (mag > max) {
max = mag;
}
}
return max;
}
public String toExcelString() {
String strRepr = functionStrMap.get(LocaleProps
.get("MODEL_INFO_EXCEL_TITLE"));
if (strRepr == null) {
strRepr = "=SUM(";
double[] coeffs = function.getCoefficients();
for (int i = coeffs.length - 1; i >= 1; i--) {
strRepr += coeffs[i];
strRepr += "*(A1-" + zeroPoint + ")^" + i
+ ",\n";
}
strRepr += coeffs[0] + ")";
}
return strRepr;
}
public String toRString() {
String strRepr = functionStrMap.get(LocaleProps
.get("MODEL_INFO_R_TITLE"));
if (strRepr == null) {
strRepr = "model <- function(t) ";
double[] coeffs = function.getCoefficients();
for (int i = coeffs.length - 1; i >= 1; i--) {
strRepr += coeffs[i];
strRepr += "*(t-" + zeroPoint + ")^" + i
+ " +\n";
}
strRepr += coeffs[0];
}
return strRepr;
}
@Override
public ContinuousModelFunction getModelFunction() {
// UnivariateRealFunction func = new
// UnivariateRealFunction() {
// @Override
// public double value(double x)
// throws FunctionEvaluationException {
// double y = 0;
// double[] coeffs = function.getCoefficients();
// for (int i = coeffs.length - 1; i >= 1; i--) {
// y += coeffs[i] * Math.pow(x, i);
// }
// y += coeffs[0];
// return y;
// }
// };
return new ContinuousModelFunction(function, fit,
zeroPoint);
}
// An alternative implementation for getModelFunction() that
// uses Horner's method to avoid exponentiation.
public UnivariateRealFunction getModelFunctionHorner() {
UnivariateRealFunction func = new UnivariateRealFunction() {
@Override
public double value(double x)
throws FunctionEvaluationException {
// Compute the value of the polynomial for x via
// Horner's method.
double y = 0;
double[] coeffs = function.getCoefficients();
for (double coeff : coeffs) {
y = y * x + coeff;
}
return y;
}
};
return func;
}
@Override
public void execute() throws AlgorithmError {
for (int i = 0; i < obs.size() && !interrupted; i++) {
ValidObservation ob = obs.get(i);
fitter.addObservedPoint(1.0,
ob.getJD() - zeroPoint, ob.getMag());
}
if (!interrupted) {
try {
function = fitter.fit();
fit = new ArrayList<ValidObservation>();
residuals = new ArrayList<ValidObservation>();
double sumSqResiduals = 0;