/*
 * Copyright (c) 2012-2015, 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;

import org.ddogleg.optimization.functions.FunctionNtoM;
import org.ddogleg.optimization.functions.FunctionNtoMxN;
import org.ddogleg.optimization.functions.FunctionNtoN;
import org.ddogleg.optimization.functions.FunctionNtoS;
import org.ddogleg.optimization.impl.NumericalGradientForward;
import org.ddogleg.optimization.impl.NumericalJacobianForward;
import org.ejml.UtilEjml;
import org.ejml.data.DenseMatrix64F;
import org.ejml.ops.MatrixFeatures;

/**
 * Used to validate an algebraic Jacobian numerically.
 *
 * @author Peter Abeles
 */
public class DerivativeChecker {

	public static void jacobianPrint( FunctionNtoM func , FunctionNtoMxN jacobian ,
									  double param[] , double tol )
	{
		jacobianPrint(func,jacobian,param,tol,Math.sqrt(UtilEjml.EPS));
	}

	public static void jacobianPrint( FunctionNtoM func , FunctionNtoMxN jacobian ,
									  double param[] , double tol , double differenceScale )
	{
		NumericalJacobianForward numerical = new NumericalJacobianForward(func,differenceScale);

		DenseMatrix64F found = new DenseMatrix64F(func.getNumOfOutputsM(),func.getNumOfInputsN());
		DenseMatrix64F expected = new DenseMatrix64F(func.getNumOfOutputsM(),func.getNumOfInputsN());

		jacobian.process(param,found.data);
		numerical.process(param,expected.data);

		System.out.println("FOUND:");
		found.print();
		System.out.println("-----------------------------");
		System.out.println("Numerical");
		expected.print();
		
		System.out.println("-----------------------------");
		System.out.println("Large Differences");
		for( int y = 0; y < found.numRows; y++ ) {
			for( int x = 0; x < found.numCols; x++ ) {
				double diff = Math.abs(found.get(y,x)-expected.get(y,x));
				if( diff > tol ) {
//					double e = expected.get(y,x);
//					double f = found.get(y,x);
					System.out.print("1");
				} else
					System.out.print("0");
			}
			System.out.println();
		}
	}

	public static boolean jacobian( FunctionNtoM func , FunctionNtoMxN jacobian ,
									double param[] , double tol )
	{
		return jacobian(func,jacobian,param,tol,Math.sqrt(UtilEjml.EPS));
	}

	public static boolean jacobian( FunctionNtoM func , FunctionNtoMxN jacobian ,
									double param[] , double tol ,  double differenceScale )
	{
		NumericalJacobianForward numerical = new NumericalJacobianForward(func,differenceScale);

		if( numerical.getNumOfOutputsM() != jacobian.getNumOfOutputsM() )
			throw new RuntimeException("M is not equal "+numerical.getNumOfOutputsM()+"  "+jacobian.getNumOfOutputsM());

		if( numerical.getNumOfInputsN() != jacobian.getNumOfInputsN() )
			throw new RuntimeException("N is not equal: "+numerical.getNumOfInputsN()+"  "+jacobian.getNumOfInputsN());

		DenseMatrix64F found = new DenseMatrix64F(func.getNumOfOutputsM(),func.getNumOfInputsN());
		DenseMatrix64F expected = new DenseMatrix64F(func.getNumOfOutputsM(),func.getNumOfInputsN());

		jacobian.process(param,found.data);
		numerical.process(param,expected.data);

		return MatrixFeatures.isIdentical(expected,found,tol);
	}

	/**
	 * Prints out the difference using a relative error threshold
	 * @param tol fractional difference
	 */
	public static void jacobianPrintR( FunctionNtoM func , FunctionNtoMxN jacobian ,
									  double param[] , double tol )
	{
		jacobianPrint(func, jacobian, param, tol, Math.sqrt(UtilEjml.EPS));
	}

	public static void jacobianPrintR( FunctionNtoM func , FunctionNtoMxN jacobian ,
									  double param[] , double tol , double differenceScale )
	{
		NumericalJacobianForward numerical = new NumericalJacobianForward(func,differenceScale);

		DenseMatrix64F found = new DenseMatrix64F(func.getNumOfOutputsM(),func.getNumOfInputsN());
		DenseMatrix64F expected = new DenseMatrix64F(func.getNumOfOutputsM(),func.getNumOfInputsN());

		jacobian.process(param,found.data);
		numerical.process(param,expected.data);

		System.out.println("FOUND:");
		found.print();
		System.out.println("-----------------------------");
		System.out.println("Numerical");
		expected.print();

		System.out.println("-----------------------------");
		System.out.println("Large Differences");
		for( int y = 0; y < found.numRows; y++ ) {
			for( int x = 0; x < found.numCols; x++ ) {
				double f = found.get(y,x);
				double e = expected.get(y,x);

				double max = Math.max(Math.abs(f),Math.abs(e));
				if( max == 0 ) {
					max = 1;
				}

				double diff = Math.abs(f-e)/max;
				if( diff > tol ) {
					System.out.print("1");
				} else
					System.out.print("0");
			}
			System.out.println();
		}
	}

	/**
	 * Checks the jacobian using a relative error threshold.
	 * @param tol fractional difference
	 */
	public static boolean jacobianR( FunctionNtoM func , FunctionNtoMxN jacobian ,
								 double param[] , double tol )
	{
		return jacobian(func,jacobian,param,tol,Math.sqrt(UtilEjml.EPS));
	}

	/**
	 * Checks the jacobian using a relative error threshold.
	 * @param tol fractional difference
	 */
	public static boolean jacobianR( FunctionNtoM func , FunctionNtoMxN jacobian ,
								 double param[] , double tol ,  double differenceScale )
	{
		NumericalJacobianForward numerical = new NumericalJacobianForward(func,differenceScale);

		if( numerical.getNumOfOutputsM() != jacobian.getNumOfOutputsM() )
			throw new RuntimeException("M is not equal "+numerical.getNumOfOutputsM()+"  "+jacobian.getNumOfOutputsM());

		if( numerical.getNumOfInputsN() != jacobian.getNumOfInputsN() )
			throw new RuntimeException("N is not equal: "+numerical.getNumOfInputsN()+"  "+jacobian.getNumOfInputsN());

		DenseMatrix64F found = new DenseMatrix64F(func.getNumOfOutputsM(),func.getNumOfInputsN());
		DenseMatrix64F expected = new DenseMatrix64F(func.getNumOfOutputsM(),func.getNumOfInputsN());

		jacobian.process(param, found.data);
		numerical.process(param, expected.data);

		for( int y = 0; y < found.numRows; y++ ) {
			for( int x = 0; x < found.numCols; x++ ) {
				double f = found.get(y,x);
				double e = expected.get(y,x);

				double max = Math.max(Math.abs(f),Math.abs(e));
				if( max == 0 ) {
					max = 1;
				}

				double diff = Math.abs(f-e)/max;
				if( diff > tol ) {
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * Compares the passed in gradient function to a numerical calculation.  Comparison is done using
	 * an absolute value.
	 * @return true for within tolerance and false otherwise
	 */
	public static boolean gradient( FunctionNtoS func , FunctionNtoN gradient ,
									double param[] , double tol )
	{
		return gradient(func, gradient, param, tol, Math.sqrt(UtilEjml.EPS));
	}

	public static boolean gradient( FunctionNtoS func , FunctionNtoN gradient ,
									double param[] , double tol ,  double differenceScale )
	{
		NumericalGradientForward numerical = new NumericalGradientForward(func,differenceScale);

		if( numerical.getN() != gradient.getN() )
			throw new RuntimeException("N is not equal: "+numerical.getN()
					+"  "+gradient.getN());

		int N = numerical.getN();
		double[] found = new double[N];
		double[] expected = new double[N];

		gradient.process(param, found);
		numerical.process(param,expected);

		for (int i = 0; i < N; i++) {
			if(Math.abs(found[i]-expected[i]) > tol)
				return false;
		}
		return true;
	}

	/**
	 * Compares the passed in gradient function to a numerical calculation.  Comparison is done using
	 * an absolute value.
	 * @return true for within tolerance and false otherwise
	 */
	public static boolean gradientR( FunctionNtoS func , FunctionNtoN gradient ,
									double param[] , double tol )
	{
		return gradient(func, gradient, param, tol, Math.sqrt(UtilEjml.EPS));
	}

	public static boolean gradientR( FunctionNtoS func , FunctionNtoN gradient ,
									double param[] , double tol ,  double differenceScale )
	{
		NumericalGradientForward numerical = new NumericalGradientForward(func,differenceScale);

		if( numerical.getN() != gradient.getN() )
			throw new RuntimeException("N is not equal: "+numerical.getN()
					+"  "+gradient.getN());

		int N = numerical.getN();
		double[] found = new double[N];
		double[] expected = new double[N];

		gradient.process(param, found);
		numerical.process(param,expected);

		for (int i = 0; i < N; i++) {
			double f = found[i];
			double e = expected[i];
			double max = Math.max(Math.abs(f),Math.abs(e));
			if( max == 0 ) {
				max = 1;
			}

			double diff = Math.abs(f-e)/max;
			if( diff > tol ) {
				return false;
			}
		}
		return true;
	}
}

 

Ads help maintain this website.