Code cleanup: moved all computations to the constructor, allowing to make

the class immutable.


git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1374308 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Gilles Sadowski 2012-08-17 15:09:59 +00:00
parent bd59fee7a6
commit 43c489d7cb
2 changed files with 43 additions and 24 deletions

View File

@ -177,20 +177,21 @@ public class HarmonicFitter extends CurveFitter<HarmonicOscillator.Parametric> {
* number of measurements.</p> * number of measurements.</p>
*/ */
public static class ParameterGuesser { public static class ParameterGuesser {
/** Sampled observations. */
private final WeightedObservedPoint[] observations;
/** Amplitude. */ /** Amplitude. */
private double a; private final double a;
/** Angular frequency. */ /** Angular frequency. */
private double omega; private final double omega;
/** Phase. */ /** Phase. */
private double phi; private final double phi;
/** /**
* Simple constructor. * Simple constructor.
* @param observations sampled observations *
* @throws NumberIsTooSmallException if the sample is too short or if * @param observations Sampled observations.
* the first guess cannot be computed. * @throws NumberIsTooSmallException if the sample is too short.
* @throws ZeroException if the abscissa range is zero.
* @throws MathIllegalStateException when the guessing procedure cannot
* produce sensible results.
*/ */
public ParameterGuesser(WeightedObservedPoint[] observations) { public ParameterGuesser(WeightedObservedPoint[] observations) {
if (observations.length < 4) { if (observations.length < 4) {
@ -198,7 +199,13 @@ public class HarmonicFitter extends CurveFitter<HarmonicOscillator.Parametric> {
observations.length, 4, true); observations.length, 4, true);
} }
this.observations = observations.clone(); final WeightedObservedPoint[] sorted = sortObservations(observations);
final double aOmega[] = guessAOmega(sorted);
a = aOmega[0];
omega = aOmega[1];
phi = guessPhi(sorted);
} }
/** /**
@ -212,16 +219,18 @@ public class HarmonicFitter extends CurveFitter<HarmonicOscillator.Parametric> {
* </ul> * </ul>
*/ */
public double[] guess() { public double[] guess() {
sortObservations();
guessAOmega();
guessPhi();
return new double[] { a, omega, phi }; return new double[] { a, omega, phi };
} }
/** /**
* Sort the observations with respect to the abscissa. * Sort the observations with respect to the abscissa.
*
* @param unsorted Input observations.
* @return the input observations, sorted.
*/ */
private void sortObservations() { private WeightedObservedPoint[] sortObservations(WeightedObservedPoint[] unsorted) {
final WeightedObservedPoint[] observations = unsorted.clone();
// Since the samples are almost always already sorted, this // Since the samples are almost always already sorted, this
// method is implemented as an insertion sort that reorders the // method is implemented as an insertion sort that reorders the
// elements in place. Insertion sort is very efficient in this case. // elements in place. Insertion sort is very efficient in this case.
@ -243,6 +252,8 @@ public class HarmonicFitter extends CurveFitter<HarmonicOscillator.Parametric> {
curr = observations[j]; curr = observations[j];
} }
} }
return observations;
} }
/** /**
@ -250,11 +261,16 @@ public class HarmonicFitter extends CurveFitter<HarmonicOscillator.Parametric> {
* This method assumes that the {@link #sortObservations()} method * This method assumes that the {@link #sortObservations()} method
* has been called previously. * has been called previously.
* *
* @param observations Observations, sorted w.r.t. abscissa.
* @throws ZeroException if the abscissa range is zero. * @throws ZeroException if the abscissa range is zero.
* @throws MathIllegalStateException when the guessing procedure cannot * @throws MathIllegalStateException when the guessing procedure cannot
* produce sensible results. * produce sensible results.
* @return the guessed amplitude (at index 0) and circular frequency
* (at index 1).
*/ */
private void guessAOmega() { private double[] guessAOmega(WeightedObservedPoint[] observations) {
final double[] aOmega = new double[2];
// initialize the sums for the linear model between the two integrals // initialize the sums for the linear model between the two integrals
double sx2 = 0; double sx2 = 0;
double sy2 = 0; double sy2 = 0;
@ -305,7 +321,7 @@ public class HarmonicFitter extends CurveFitter<HarmonicOscillator.Parametric> {
if (xRange == 0) { if (xRange == 0) {
throw new ZeroException(); throw new ZeroException();
} }
omega = 2 * Math.PI / xRange; aOmega[1] = 2 * Math.PI / xRange;
double yMin = Double.POSITIVE_INFINITY; double yMin = Double.POSITIVE_INFINITY;
double yMax = Double.NEGATIVE_INFINITY; double yMax = Double.NEGATIVE_INFINITY;
@ -318,7 +334,7 @@ public class HarmonicFitter extends CurveFitter<HarmonicOscillator.Parametric> {
yMax = y; yMax = y;
} }
} }
a = 0.5 * (yMax - yMin); aOmega[0] = 0.5 * (yMax - yMin);
} else { } else {
if (c2 == 0) { if (c2 == 0) {
// In some ill-conditioned cases (cf. MATH-844), the guesser // In some ill-conditioned cases (cf. MATH-844), the guesser
@ -326,15 +342,20 @@ public class HarmonicFitter extends CurveFitter<HarmonicOscillator.Parametric> {
throw new MathIllegalStateException(LocalizedFormats.ZERO_DENOMINATOR); throw new MathIllegalStateException(LocalizedFormats.ZERO_DENOMINATOR);
} }
a = FastMath.sqrt(c1 / c2); aOmega[0] = FastMath.sqrt(c1 / c2);
omega = FastMath.sqrt(c2 / c3); aOmega[1] = FastMath.sqrt(c2 / c3);
} }
return aOmega;
} }
/** /**
* Estimate a first guess of the phase. * Estimate a first guess of the phase.
*
* @param observations Observations, sorted w.r.t. abscissa.
* @return the guessed phase.
*/ */
private void guessPhi() { private double guessPhi(WeightedObservedPoint[] observations) {
// initialize the means // initialize the means
double fcMean = 0; double fcMean = 0;
double fsMean = 0; double fsMean = 0;
@ -356,7 +377,7 @@ public class HarmonicFitter extends CurveFitter<HarmonicOscillator.Parametric> {
fsMean += omega * currentY * sine + currentYPrime * cosine; fsMean += omega * currentY * sine + currentYPrime * cosine;
} }
phi = FastMath.atan2(-fsMean, fcMean); return FastMath.atan2(-fsMean, fcMean);
} }
} }
} }

View File

@ -191,14 +191,12 @@ public class HarmonicFitterTest {
points[i] = new WeightedObservedPoint(1, i, y[i]); points[i] = new WeightedObservedPoint(1, i, y[i]);
} }
final HarmonicFitter.ParameterGuesser guesser
= new HarmonicFitter.ParameterGuesser(points);
// The guesser fails because the function is far from an harmonic // The guesser fails because the function is far from an harmonic
// function: It is a triangular periodic function with amplitude 3 // function: It is a triangular periodic function with amplitude 3
// and period 12, and all sample points are taken at integer abscissae // and period 12, and all sample points are taken at integer abscissae
// so function values all belong to the integer subset {-3, -2, -1, 0, // so function values all belong to the integer subset {-3, -2, -1, 0,
// 1, 2, 3}. // 1, 2, 3}.
guesser.guess(); final HarmonicFitter.ParameterGuesser guesser
= new HarmonicFitter.ParameterGuesser(points);
} }
} }