/*
* File: DirectionalVectorToScalarFunction.java
* Authors: Kevin R. Dixon
* Company: Sandia National Laboratories
* Project: Cognitive Foundry
*
* Copyright October 24, 2007, Sandia Corporation. Under the terms of Contract
* DE-AC04-94AL85000, there is a non-exclusive license for use of this work by
* or on behalf of the U.S. Government. Export of this program may require a
* license from the United States Government. See CopyrightHistory.txt for
* complete details.
*
*/
package gov.sandia.cognition.learning.algorithm.minimization.line;
import gov.sandia.cognition.annotation.CodeReview;
import gov.sandia.cognition.math.matrix.Vector;
import gov.sandia.cognition.evaluator.Evaluator;
import gov.sandia.cognition.learning.data.DefaultInputOutputPair;
import gov.sandia.cognition.math.AbstractDifferentiableUnivariateScalarFunction;
import gov.sandia.cognition.util.ObjectUtil;
/**
* Maps a vector function onto a scalar one by using a
* directional vector and vector offset, and the parameter to the
* function is a scalar value along the direction from the start-point
* offset. This class also approximates the derivative by a method of forward
* differences.
* @author Kevin R. Dixon
* @since 2.0
*
*/
@CodeReview(
reviewer="Kevin R. Dixon",
date="2009-07-06",
changesNeeded=false,
comments={
"Fixed bug introduced into the numerical differentiation procedure.",
"Made clone() call super.clone().",
"Fixed the brittleness in the copy constructor."
}
)
public class DirectionalVectorToScalarFunction
extends AbstractDifferentiableUnivariateScalarFunction
{
/**
* Value used in the forward-difference derivative approximation
*/
public static final double FORWARD_DIFFERENCE = 1e-5;
/** Vector offset to scale in the specified direction */
private Vector vectorOffset;
/** Directional vector for optimization */
private Vector direction;
/**
* Function that maps a Vector onto a Double
*/
private Evaluator<? super Vector, ? extends Double> vectorScalarFunction;
/**
* Cache for the last input/output that was evaluated, so that we
* can avoid using it again in the differentiate method.
*/
private DefaultInputOutputPair<Double,Double> lastEvaluation;
/**
* Creates a new function that restricts the vectorFunction to a
* particular vector direction
*
* @param vectorScalarFunction
* Function that maps a Vector onto a Double
* @param vectorOffset offset vector from which to scale along
* direction to evaluate vectorFunction
* @param direction Direction to optimize along
*/
public DirectionalVectorToScalarFunction(
Evaluator<? super Vector, ? extends Double> vectorScalarFunction,
Vector vectorOffset,
Vector direction )
{
this.setVectorScalarFunction( vectorScalarFunction );
this.setVectorOffset( vectorOffset );
this.setDirection( direction );
this.lastEvaluation = null;
}
/**
* Copy constructor
* @param other
* DirectionalVectorToScalarFunction to copy
*/
public DirectionalVectorToScalarFunction(
DirectionalVectorToScalarFunction other )
{
this( ObjectUtil.cloneSmart( other.getVectorScalarFunction() ),
ObjectUtil.cloneSafe( other.getVectorOffset() ),
ObjectUtil.cloneSafe( other.getDirection() ) );
}
@Override
public DirectionalVectorToScalarFunction clone()
{
DirectionalVectorToScalarFunction clone =
(DirectionalVectorToScalarFunction) super.clone();
clone.setVectorScalarFunction( ObjectUtil.cloneSmart( this.getVectorScalarFunction() ) );
clone.setVectorOffset( ObjectUtil.cloneSafe( this.getVectorOffset() ) );
clone.setDirection( ObjectUtil.cloneSafe( this.getDirection() ) );
clone.lastEvaluation = null;
return clone;
}
/**
* Getter for direction
*
* @return Direction along vectorFunction
*/
public Vector getDirection()
{
return this.direction;
}
/**
* Setter for direction
*
* @param direction Direction to optimize along
*/
public void setDirection(
Vector direction )
{
this.lastEvaluation = null;
this.direction = direction;
}
/**
* Getter for vectorOffset
*
* @return Point to use as input to vectorFunction
*/
public Vector getVectorOffset()
{
return this.vectorOffset;
}
/**
* Point to use as input to vectorFunction
*
* @param vectorOffset Point to use as input to vectorFunction
*/
public void setVectorOffset(
Vector vectorOffset )
{
this.lastEvaluation = null;
this.vectorOffset = vectorOffset;
}
/**
* Transforms the scaleFactor into a multidimensional Vector using the
* direction
*
* @param scaleFactor scale factor to move along the direction from
* vectorOffset
* @return Multidimensional vector corresponding to the scale factor
* along the direction
*/
public Vector computeVector(
double scaleFactor )
{
return this.vectorOffset.plus(
this.direction.scale( scaleFactor ) );
}
/**
* Getter for vectorScalarFunction
* @return
* Function that maps a Vector onto a Double
*/
public Evaluator<? super Vector, ? extends Double> getVectorScalarFunction()
{
return this.vectorScalarFunction;
}
/**
* Setter for vectorScalarFunction
* @param vectorScalarFunction
* Function that maps a Vector onto a Double
*/
public void setVectorScalarFunction(
Evaluator<? super Vector, ? extends Double> vectorScalarFunction )
{
this.vectorScalarFunction = vectorScalarFunction;
}
/**
* Evaluates the vector function along the direction using the scale
* factor "input" and vectorOffset
*
* @param input scale factor to move along direction from
* vectorOffset
* @return vectorFunction evaluated at the Vector corresponding to the
* scale factor
*/
public double evaluate(
double input )
{
Double output;
if( this.lastEvaluation == null )
{
output = this.vectorScalarFunction.evaluate(
this.computeVector( input ) );
this.lastEvaluation =
new DefaultInputOutputPair<Double, Double>(input,output);
}
else if( this.lastEvaluation.getInput() == input )
{
return this.lastEvaluation.getOutput();
}
else
{
output = this.vectorScalarFunction.evaluate(
this.computeVector( input ) );
this.lastEvaluation.setInput(input);
this.lastEvaluation.setOutput(output);
}
return this.vectorScalarFunction.evaluate(
this.computeVector( input ) );
}
public double differentiate(
double input )
{
double output;
if( this.lastEvaluation == null )
{
output = this.evaluate(input);
this.lastEvaluation =
new DefaultInputOutputPair<Double, Double>(input,output);
}
else if( this.lastEvaluation.getInput() == input )
{
output = this.lastEvaluation.getOutput();
}
else
{
output = this.evaluate(input);
this.lastEvaluation.setInput(input);
this.lastEvaluation.setOutput(output);
}
double dx = FORWARD_DIFFERENCE;
double dy = this.evaluate( input + dx ) - output;
return dy / dx;
}
}