/* * File: EvaluatorExample.java * Authors: Kevin R. Dixon * Company: Sandia National Laboratories * Project: Cognitive Foundry * * Copyright May 6, 2009, 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 examples; import gov.sandia.cognition.evaluator.Evaluator; import gov.sandia.cognition.math.matrix.Vector; import gov.sandia.cognition.math.matrix.VectorFactory; import gov.sandia.cognition.math.matrix.Vectorizable; import gov.sandia.cognition.util.AbstractCloneableSerializable; /** * This class shows how to create an Evaluator, one of the most basic * classes in the Cognitive Foundry, in addition to main points of * the important class hierarchies in the Foundry. * <BR><BR> * This is an example of one of the most fundamental types in the Foundry, * the Evaluator. An Evaluator is a simple interface that is intended * to define the functionality of a class that maps inputs onto an output, * like a mathematical "function." Much of the Foundry incorporates * Java's generics (similar to C++'s templates). In this case, the * class-generic parameters of an Evaluator define the type of the * input and output expected. * <BR><BR> * The reason for having an Evaluator interface in Java is that Java does * not allow first-level function pointers like C/C++ do. For example, in * C/C++, one can specify that a function ("foo") takes another function as * an argument ("bar" with a definable input/output definition). This * allows the function "foo" to invoke "bar". Unless we probe the dark * bowels of Java's Reflection API, Java does not have a analogous ability. * Enter the Evaluator interface. We know that a parameter to another * method of type "Evaluator<InputType,OutputType>" has a method "evaluate" * that takes a class of type "InputType" and maps that onto a class of * type "OutputType". Many mathematically oriented objects in the Foundry * implement the Evaluator interface. A quick "Find All Subtypes" found * 196 subtypes of Evaluator in the Foundry, as of this writing. * <BR><BR> * <BR><BR> * Another note about another frequently implemented/extended interface. * When Java was first created, the designers at Sun decided that objects * would not be serializable (e.g., written to disk) by default. That * objects that could be written to disk would have to implement the * Java interface "Serializable" interface explicitly. Most Java * developers have found out through experience that virtually all Java * classes should implement Serializable by default. * <BR><BR> * Perhaps the most bizarre design decision when Java was first created * involves Java's Cloneable interface. The "clone" method in Java is a * type of "smart" copying. That is, make deep copies of objects that * need to be truly copied over and otherwise just keep pointers to objects * that won't change the behavior of the encapsulating class. To support * this feature, Java has a "clone" method in the Java root class, "Object". * However, Object's clone method is protected by default and so this * automatic cloning can't be invoked by another class without the subclass * explicitly exposing the "clone" method as public. So, the developers at * Sun created the Cloneable interface. Strangely enough, they appear to * have forgotten to include the "clone" method in the Cloneable interface. * To quote Sun's Technical Review Committee: * "Unfortunately, for reasons conveniently lost in the mists of time, * the Cloneable interface does not define a clone method. This combination * results in a fair amount of confusion." * <BR> * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4098033 * <BR><BR> * As such, most Java code bases create an interface that implements * the Serializable interface and the Cloneable interface AND defines * a public "clone()" method. In the Foundry, we have created our own * obviously named CloneableSerializable interface, and its direct * cousin, the abstract class AbstractCloneableSerializable. Because * many classes want by default the ability to serialize itself and * make a smart copy of itself, we expect most classes in the * Foundry will extend AbstractCloneableSerializable as the top-level * class or implement the CloneableSerializable interface. A quick * "Find All Subtypes" found 647 subtypes of CloneableSerializable as of this * writing. * <BR><BR> * <BR><BR> * Some of the high-level tools in the Foundry make use of the Java Beans * programming style. We do not enforce this in any way, but we provide tools * that expected that Foundry classes: * <UL> * <LI>Have a public default (no-argument) constructor: * <BR>public MyClass() * <BR>{ * <BR> super(); * <BR>} * </LI> * <LI>Access to internal variables is provided by CamelCase getter/setter. * Depending on the needs of the application, the getter and setter may be * public, protected, or private. * <BR> * <BR>private int internalValue; * <BR>public int getInternalValue() * <BR>{ * <BR> return this.internalValue; * <BR>} * <BR>protected void setInternalValue( * <BR> int internalValue ) * <BR>{ * <BR> this.internalValue = internalValue; * <BR>} * </LI> * </UL> * <BR><BR>For more information on Java Beans, see the Wikipedia site: * http://en.wikipedia.org/wiki/JavaBeans * * @author Kevin R. Dixon * @since 3.0 */ public class EvaluatorExample { /** * This function transforms the Math.cos function into a first-level * object that can be passed around. This maps some type of Java Number * (Double, Integer, etc.) and turns it into a Double. * <BR> * Note that this class extends AbstractCloneableSerializable so that * it can be written to disk and cloned. * <BR> * Also note that the use of the class-parameter generic, InputType, is * pretty gratuitous: it doesn't really serve a useful purpose except * to demonstrate that we use generics. I apologize for not thinking of * a quick and easy generics-based class for this example. * * @param <InputType> Type of Java Number (Double, Integer, etc.) to expect * as input. */ public static class CosineEvaluator<InputType extends Number> extends AbstractCloneableSerializable implements Evaluator<InputType, Double> { /** * Default constructor. */ public CosineEvaluator() { super(); } /** * Evaluate method that takes the cosine of the input and returns * it as a Double. * @param input * Type of Java Number (Double, Integer, etc.) to map through Math.cos. * @return * Cosine of the input. */ public Double evaluate( InputType input) { return Math.cos( input.doubleValue() ); } } /** * This function transforms the Math.sin function into a first-level * object that can be passed around. This maps some type of Java Number * (Double, Integer, etc.) and turns it into a Double. * <BR> * Note that this class extends AbstractCloneableSerializable so that * it can be written to disk and cloned. * * @param <InputType> Type of Java Number (Double, Integer, etc.) to expect * as input. */ public static class SineEvaluator<InputType extends Number> extends AbstractCloneableSerializable implements Evaluator<InputType, Double> { /** * Default constructor. */ public SineEvaluator() { super(); } /** * Evaluate method that takes the sine of the input and returns * it as a Double. * @param input * Type of Java Number (Double, Integer, etc.) to map through Math.sin. * @return * Sine of the input. */ public Double evaluate( InputType input) { return Math.sin( input.doubleValue() ); } } /** * This class has two internal Evaluators that it uses to pass an input * to each of them, square their results, and return the sum of squares. * @param <InputType> * Type of input that my Evaluators have */ public static class FunctionSquarer<InputType extends Number> extends AbstractCloneableSerializable implements Evaluator<InputType,Double> { /** * The first function to square. */ private Evaluator<InputType,Double> f1; /** * The second function to square. */ private Evaluator<InputType,Double> f2; /** * Default constructor. */ public FunctionSquarer() { this( null, null ); } /** * Creates a new FunctionSquarer * @param f1 First function to square. * @param f2 Second function to square. */ public FunctionSquarer( final Evaluator<InputType,Double> f1, final Evaluator<InputType,Double> f2 ) { this.f1 = f1; this.f2 = f2; } /** * Returns sum of the squared outputs from each of my two Evaluators. * @param input * Input to throw through my two Evaluators, square the respective * results, then return the sum of those two squared values. * @return * Sum of the squared outputs from each of my two Evaluators. */ public Double evaluate( InputType input) { double y1 = this.getF1().evaluate(input); double y2 = this.getF2().evaluate(input); return y1*y1 + y2*y2; } /** * Getter for f1. * @return f1 */ public Evaluator<InputType, Double> getF1() { return this.f1; } /** * Setter for f1. * @param f1 f1 */ public void setF1( Evaluator<InputType, Double> f1) { this.f1 = f1; } /** * Getter for f2. * @return f2 */ public Evaluator<InputType, Double> getF2() { return this.f2; } /** * Setter for f2 * @param f2 f2 */ public void setF2( Evaluator<InputType, Double> f2) { this.f2 = f2; } } /** * Another common thing that we want to do in the Foundry is convert * an object into its parameters. This will be useful for things like * machine-learning optimization and so forth. Let's say that we want a * cosine Evaluator, but have the * magnitude and phase be parameters. * <BR> * That is, y = magnitude * cos( x + phase ). * <BR><BR> * Please note that we mean a mathematical Vector (n-dimensional group * of scalar values), not Java's useless Vector class. Please see * the example "MatrixAndVectorExample.java" for more information. */ public static class CosineEvaluatorWithMagnitudeAndPhase extends AbstractCloneableSerializable implements Evaluator<Double,Double>, Vectorizable { /** * Magnitude of the cosine. */ private double magnitude; /** * Phase of the cosine. */ private double phase; /** * Default constructor. */ public CosineEvaluatorWithMagnitudeAndPhase() { this( 1.0, 0.0 ); } /** * Creates a new CosineEvaluatorWithMagnitudeAndPhase * @param magnitude * Magnitude of the cosine. * @param phase * Phase of the cosine. */ public CosineEvaluatorWithMagnitudeAndPhase( final double magnitude, final double phase ) { this.magnitude = magnitude; this.phase = phase; } /** * Returns the cosine of the input with parameterized magnitude and * phase. * @param input * Input to the cosine * @return * magnitude * Math.cos( input + phase ) */ public Double evaluate( Double input) { return this.getMagnitude() * Math.cos( input + this.getPhase() ); } @Override public CosineEvaluatorWithMagnitudeAndPhase clone() { // If you extend AbstractCloneableSerializable, then you // should only have to copy over non-elemental objects from // the resulting super.clone() call. return (CosineEvaluatorWithMagnitudeAndPhase) super.clone(); } /** * This method returns the parameters of the class as a Vector. * @return * 2-dimensional Vector containing (magnitude,phase) */ public Vector convertToVector() { return VectorFactory.getDefault().copyValues( this.getMagnitude(), this.getPhase()); } /** * This method converts the parameters from a Vector to * (magnitude,phase). * @param parameters * 2-dimensional Vector containing (magnitude,phase) */ public void convertFromVector( Vector parameters) { if( parameters.getDimensionality() != 2 ) { throw new IllegalArgumentException( "Expected 2 parameters: [ magnitude phase ]" ); } this.setMagnitude(parameters.getElement(0)); this.setPhase(parameters.getElement(1)); } /** * Getter for magnitude. * @return Magnitude of the cosine. */ public double getMagnitude() { return this.magnitude; } /** * Setter for magnitude. * @param magnitude Magnitude of the cosine. */ public void setMagnitude( double magnitude) { this.magnitude = magnitude; } /** * Setter for phase. * @return Phase of the cosine. */ public double getPhase() { return this.phase; } /** * Setter for phase. * @param phase Phase of the cosine. */ public void setPhase( double phase) { this.phase = phase; } } /** * Entry point to the example * @param args * We don't use any command-line arguments. */ public static void main( String ... args ) { // Let's create a squarer with a cosine and sine... Does anybody // remember was cos^2 + sin^2 equals? Let's find out! CosineEvaluator<Double> cosine = new CosineEvaluator<Double>(); SineEvaluator<Double> sine = new SineEvaluator<Double>(); FunctionSquarer<Double> squarer1 = new FunctionSquarer<Double>( cosine, sine ); // Oh, yeah! cos^2 + sin^2 = 1 for any input value! Doh! final double x1 = 0.5; double y1 = squarer1.evaluate(x1); System.out.println( "x1: " + x1 + ", y1: " + y1 ); // What's the value of sin^2 + sin^2? I don't know, but for // x = 0.5, it's 0.4596976941318603. FunctionSquarer<Double> squarer2 = new FunctionSquarer<Double>( sine, sine ); double y2 = squarer2.evaluate(x1); System.out.println( "x1: " + x1 + ", y2: " + y2 ); final double magnitude1 = 10.0; final double phase1 = 2.0; CosineEvaluatorWithMagnitudeAndPhase f = new CosineEvaluatorWithMagnitudeAndPhase( magnitude1, phase1 ); // This should equal the magnitude: double y3 = f.evaluate(-phase1); System.out.println( "Magnitude: " + magnitude1 + ", y3: " + y3 ); // The parameters of the function are the (magnitude,phase) Vector: Vector parameters1 = f.convertToVector(); System.out.println( "Parameters: " + parameters1 ); // Let's change the magnitude to 0.0 parameters1.setElement(0, 0.0); // Upload the parameters back into the function... // note that the magnitude was changed to 0.0 f.convertFromVector(parameters1); System.out.println( "New magnitude: " + f.getMagnitude() ); // Get the new parameter set and fiddle with them again Vector parameters2 = f.convertToVector(); parameters2.setElement(0, magnitude1); parameters2.setElement(1, phase1*2.0); f.convertFromVector(parameters2); System.out.println( "New magnitude: " + f.getMagnitude() + ", New phase: " + f.getPhase() ); System.out.println( "Parameters: " + f.convertToVector() ); } }