/*
 * 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.optimization.impl;

import org.ejml.alg.dense.mult.VectorVectorMult;
import org.ejml.data.DenseMatrix64F;
import org.ejml.ops.CommonOps;
import org.ejml.ops.MatrixFeatures;
import org.ejml.ops.NormOps;
import org.junit.Test;

import static org.junit.Assert.*;

/**
 * @author Peter Abeles
 */
public class TestDoglegStepF {

	double cauchyRadius = 0.5;
	double gaussRadius = 10;
	double combinedRadius = 0.9;

	DenseMatrix64F J,x,residuals,gradient;

	public TestDoglegStepF() {
		J = new DenseMatrix64F(3,2,true,1,0.5,2,Math.sqrt(2),-2,4);

		x = new DenseMatrix64F(2,1,true,0.5,1.5);
		residuals = new DenseMatrix64F(3,1,true,-1,-2,-3);

		gradient = new DenseMatrix64F(2,1);
		CommonOps.multTransA(J, residuals, gradient);
	}

	/**
	 * Pick a step less than the Cauchy step and see if the CauchyStep produces
	 * the same output as DoglegStep
	 */
	@Test
	public void computeStep_cauchy() {
		CauchyStep cauchy = new CauchyStep();
		WrappedDog alg = new WrappedDog();
		
		cauchy.init(2, 3);
		alg.init(2,3);
		cauchy.setInputs(x, residuals, J, gradient, -1);
		alg.setInputs(x, residuals, J, gradient, -1);

		DenseMatrix64F expected = new DenseMatrix64F(2,1);
		DenseMatrix64F found = new DenseMatrix64F(2,1);

		// step less than the cauchy step
		cauchy.computeStep(cauchyRadius, expected);
		alg.computeStep(cauchyRadius,found);
		assertTrue(cauchy.isMaxStep());
		assertTrue(alg.isMaxStep());
		assertTrue(alg.calledCauchy);

		assertTrue(MatrixFeatures.isIdentical(expected, found, 1e-8));
	}

	/**
	 * Set the region so large that it will select the Gauss-Newton point.  This should should be
	 * a minimum for cost
	 */
	@Test
	public void computeStep_GaussNewton() {
		DoglegStepFtF alg = new DoglegStepFtF();

		alg.init(2, 3);
		alg.setInputs(x, residuals, J, gradient, -1);

		DenseMatrix64F step = new DenseMatrix64F(2,1);

		// give it a very large region
		alg.computeStep(gaussRadius,step);

		assertFalse(alg.isMaxStep());
		
		// should be a local max
		double a = cost(residuals, J, step, 0 , 0 );
		double b = cost(residuals, J, step, 0.01 , 0);
		double c = cost(residuals, J, step,-0.01 , 0);
		double d = cost(residuals, J, step, 0 , 0.01);
		double e = cost(residuals, J, step, 0 , -0.01);

		// should be less than all the others too
		assertTrue(a < b);
		assertTrue(a < c);
		assertTrue(a < d);
		assertTrue(a < e);
	}

	@Test
	public void computeStep_Hybrid() {
		WrappedDog alg = new WrappedDog();

		alg.init(2, 3);
		alg.setInputs(x, residuals, J, gradient, -1);

		DenseMatrix64F step = new DenseMatrix64F(2,1);

		// give it a specially selected step
		alg.computeStep(combinedRadius,step);

		// make sure the combined step was actually called
		assertTrue(alg.calledCombined);
		
		// check to see if it is along the region's radius
		assertTrue(alg.isMaxStep());
		double r = NormOps.normF(step);
		assertEquals(combinedRadius, r, 1e-8);
	}

	@Test
	public void predict_cauchy() {
		checkPredictedCost(cauchyRadius,true,false);
	}

	@Test
	public void predict_GaussNewton() {
		checkPredictedCost(gaussRadius, false, false);
	}

	@Test
	public void predict_Hybrid() {
		checkPredictedCost(combinedRadius, false, true);
	}

	private void checkPredictedCost( double radius , boolean calledCauchy , boolean calledCombined  )
	{
		double fx = VectorVectorMult.innerProd(residuals,residuals)*0.5;
		WrappedDog alg = new WrappedDog();

		alg.init(2, 3);
		alg.setInputs(x, residuals, J, gradient, fx);


		DenseMatrix64F step = new DenseMatrix64F(2,1);

		alg.computeStep(radius,step);
		assertTrue(alg.calledCauchy == calledCauchy);
		assertTrue(alg.calledCombined == calledCombined);

		// compare found to predicted cost

		// F(0) - F(h)
		double expected = fx-cost(residuals,J,step,0,0);
		assertEquals(expected,alg.predictedReduction(),1e-8);
	}
	
	public static double cost( DenseMatrix64F residuals , DenseMatrix64F J , DenseMatrix64F h , double... delta )
	{
		h = h.copy();
		for( int i = 0; i < h.numRows; i++ )
			h.data[i] += delta[i];

		DenseMatrix64F B = new DenseMatrix64F(J.numCols,J.numCols);
		CommonOps.multTransA(J,J,B);

		double left = VectorVectorMult.innerProd(residuals, residuals);
		double middle = VectorVectorMult.innerProdA(residuals, J, h);
		double right = VectorVectorMult.innerProdA(h, B, h);

		return 0.5*left + middle + 0.5*right;
	}
	
	protected  static class WrappedDog extends DoglegStepF {
		boolean calledCombined = false;
		boolean calledCauchy = false;

		@Override
		protected void cauchyStep(double regionRadius, DenseMatrix64F step) {
			super.cauchyStep(regionRadius, step);
			calledCauchy = true;
		}

		@Override
		protected void combinedStep(double regionRadius, DenseMatrix64F step) {
			super.combinedStep(regionRadius, step);
			calledCombined = true;
		}
	}
}

 

Ads help maintain this website.