/*
 * Copyright (c) 2012-2013, Peter Abeles. All Rights Reserved.
 *
 * This file is part of DDogleg (http://ddogleg.org).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.ddogleg.fitting.modelset;

import org.ddogleg.fitting.modelset.distance.DistanceFromMeanModel;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;


/**
 * A series of tests are provided that test to see the provided model set algorithm has the expected
 * behavior.  This includes removing outliers and estimating the model parameters.  The test
 * cases tend to be fairly easy so that they will work on all model set algorithms.
 *
 * @author Peter Abeles
 */
public abstract class GenericModelMatcherTests {

	protected Random rand = new Random(0x353456);

	// how many of the points is it expected to match
	protected double minMatchFrac = 1.0;
	// how close does the parameter set need to be
	protected double parameterTol = 1e-8;
	// should it check the inlier set for accuracy
	protected boolean checkInlierSet = true;

	// mean of the true inlier set
	// this value is the best estimate that can possibly be done
	private double inlierMean;

	protected void configure(double minMatchFrac, double parameterTol, boolean checkInlierSet) {
		this.minMatchFrac = minMatchFrac;
		this.parameterTol = parameterTol;
		this.checkInlierSet = checkInlierSet;
	}

	/**
	 * Sees if it can correctly select a model set and determine the best fit parameters for
	 * a simple test case.
	 */
	@Test
	public void performSimpleModelFit() {
		double mean = 2.5;
		double tol = 0.2;

		// generate the points with a smaller tolerance to account for fitting error
		// later on.
		ModelMatcher alg = createModel(4, tol * 0.95);

		List samples = createSampleSet(100, mean, tol, 0.1);

		assertTrue(alg.process(samples));

		List matchSet = alg.getMatchSet();

		if (checkInlierSet)
			assertTrue(matchSet.size() / 90.0 >= minMatchFrac);
		assertEquals(inlierMean, alg.getModelParameters()[0], parameterTol);
	}

	/**
	 * Multiple data sets are processed in an attempt to see if it is properly reinitializing
	 * itself and returns the correct solutions.
	 */
	@Test
	public void runMultipleTimes() {
		double mean = 2.5;
		double tol = 0.2;

		// generate the points with a smaller tolerance to account for fitting error
		// later on.
		ModelMatcher alg = createModel(4, tol);

		for (int i = 0; i < 10; i++) {
			// try different sample sizes in each trial.  a bug was found once where
			// a small value of N than previous caused a problem
			int N = 200 - i * 10;
			List samples = createSampleSet(N, mean, tol * 0.90, 0.1);

			assertTrue(alg.process(samples));

			List matchSet = alg.getMatchSet();

			double foundMean = alg.getModelParameters()[0];

			if (checkInlierSet)
				assertTrue(matchSet.size() / (N * 0.9) >= minMatchFrac);
			assertEquals(inlierMean, foundMean, parameterTol);
		}
	}

	/**
	 * Creates a set of sample points that are part of the model and some outliers
	 *
	 * @param numPoints Number of sample points it will generate
	 * @param mean		Mean of the distribution
	 * @param modelDist   How close to the model do the points need to be.
	 * @param fracOutlier Fraction of the points which will be outliers.
	 * @return Set of sample points
	 */
	private List createSampleSet(int numPoints, double mean, double modelDist, double fracOutlier) {
		List ret = new ArrayList();

		double numOutlier = (int) (numPoints * fracOutlier);

		inlierMean = 0;

		for (int i = 0; i < numPoints - numOutlier; i++) {
			double d = mean + (rand.nextDouble() - 0.5) * 2.0 * modelDist;

			inlierMean += d;

			ret.add(d);
		}

		inlierMean /= ret.size();

		while (ret.size() < numPoints) {
			double d = (rand.nextDouble() - 0.5) * 200 * modelDist;

			// add a point if its sufficiently far away from the model
			if (Math.abs(d) > modelDist * 10) {
				ret.add(d);
			}
		}

		// randomize the order
		Collections.shuffle(ret, rand);

		return ret;
	}

	/**
	 * Make sure the function getInputIndex() returns the original index
	 */
	@Test
	public void checkMatchSetToInputIndex() {
		double mean = 2.5;
		double tol = 0.2;

		// generate the points with a smaller tolerance to account for fitting error
		// later on.
		ModelMatcher alg = createModel(4, tol * 0.95);

		List samples = createSampleSet(100, mean, tol, 0.1);

		assertTrue(alg.process(samples));

		List matchSet = alg.getMatchSet();

		// sanity check to make sure there is a large enough match set
		assertTrue(matchSet.size() > 20);

		int orderNotTheSame = 0;
		for( int i = 0; i < matchSet.size(); i++ ) {
			int expected = samples.indexOf(matchSet.get(i));
			int found = alg.getInputIndex(i);

			if( found != i )
				orderNotTheSame++;

			assertEquals(expected, found);
		}

		// sanity check to make sure the order has been changed
		assertTrue(orderNotTheSame != matchSet.size());
	}

	private ModelMatcher createModel(int minPoints, double fitThreshold) {
		DoubleArrayManager manager = new DoubleArrayManager(1);
		DistanceFromMeanModel dist = new DistanceFromMeanModel();
		MeanModelFitter fitter = new MeanModelFitter();

		return createModelMatcher(manager,dist, fitter,fitter, minPoints, fitThreshold);
	}

	public abstract ModelMatcher createModelMatcher(
			ModelManager manager,
			DistanceFromModel distance,
			ModelGenerator generator,
			ModelFitter fitter,
			int minPoints, double fitThreshold);


}

 

Ads help maintain this website.