package LBJ2.learn;
import java.io.PrintStream;
import java.util.Random;
import LBJ2.util.ExceptionlessInputStream;
import LBJ2.util.ExceptionlessOutputStream;
/**
* This weight vector operates similarly to its parent in the class
* hierarchy, but it halucinates (and sets) random values for weights
* corresponding to features it has never been asked about before. Thus, if
* a dot product is calculated on an empty <code>RandomWeightVector</code>,
* all the features in the feature vector will now have random weights
* associated with them in weight vector. If a
* {@link #scaledAdd(int[],double[])} is performed, any features in the
* feature vector lacking a corresponding weight in the weight vector will
* have a random one assigned before the addition is performed. This is
* usually not an issue for most algorithms, since dot products are usually
* performed before deciding how to add, which means all the weights for that
* feature vector will already be set when the addition is performed. Thus,
* it will simply appear to the algorithm that this vector had independent,
* identically distributed random values for all its dimensions when first
* created.
*
* <p> The random numbers generated by this class are Gaussian with mean 0
* and with a user-configurable standard deviation.
*
* @author Nick Rizzolo
**/
public class RandomWeightVector extends SparseWeightVector
{
/** Keeps track of how many objects of this class have been constructed. */
private static int instanceCount = 0;
/** Default value for {@link #stddev}. */
protected static final double defaultStddev = 100;
/**
* The random numbers that are generated by this class are Gaussian with
* mean 0 and standard deviation defined by this variable.
**/
protected double stddev;
/** Remembers the instance number of this instance. */
protected int instanceNumber;
/** The random number generator for this instance. */
protected Random random;
/** Sets a default standard deviation. */
public RandomWeightVector() { this(defaultStddev); }
/**
* Sets the specified standard deviation.
*
* @param s The standard deviation.
**/
public RandomWeightVector(double s) {
stddev = s;
instanceNumber = instanceCount++;
random = new Random(instanceNumber);
}
/**
* Returns the double precision value for the given feature, or
* sets a random one and returns it if one did not already exist.
*
* @param featureIndex The feature index
* @param defaultW Unused.
* @return The double precision value for the given feature.
**/
public double getWeight(int featureIndex, double defaultW) {
while (weights.size() <= featureIndex)
weights.add(random.nextGaussian() * stddev);
return weights.get(featureIndex);
}
/**
* Empties the weight map and resets the random number generator. This
* means that the same "random" values will be filled in for the weights if
* the same calls to {@link #dot(int[],double[],double)} and
* {@link #scaledAdd(int[],double[],double,double)} are made in the same
* order.
**/
public void clear() {
super.clear();
random = new Random(instanceNumber);
}
/**
* Outputs the contents of this vector into the specified
* <code>PrintStream</code>. The string representation is the same as in
* the super class, except the <code>"Begin"</code> annotation line also
* contains the value of {@link #stddev} in parentheses.
*
* @param out The stream to write to.
**/
public void write(PrintStream out) {
out.println("Begin RandomWeightVector (" + stddev + ")");
toStringJustWeights(out);
out.println("End RandomWeightVector");
}
/**
* Outputs the contents of this vector into the specified
* <code>PrintStream</code>. The string representation is the same as in
* the super class, except the <code>"Begin"</code> annotation line also
* contains the value of {@link #stddev} in parentheses.
*
* @param out The stream to write to.
* @param lex The feature lexicon.
**/
public void write(PrintStream out, Lexicon lex) {
out.println("Begin RandomWeightVector (" + stddev + ")");
toStringJustWeights(out, 0, lex);
out.println("End RandomWeightVector");
}
/**
* Writes the weight vector's internal representation in binary form.
*
* @param out The output stream.
**/
public void write(ExceptionlessOutputStream out) {
super.write(out);
out.writeDouble(stddev);
out.writeInt(instanceNumber);
// Not perfect; to preserve the current semantics of this class (which are
// also less than ideal), we should serialze the random object into the
// stream so it can continue where it left off when read back in.
}
/**
* Reads the representation of a weight vector with this object's run-time
* type from the given stream, overwriting the data in this object.
*
* <p> This method is appropriate for reading weight vectors as written by
* {@link #write(ExceptionlessOutputStream)}.
*
* @param in The input stream.
**/
public void read(ExceptionlessInputStream in) {
super.read(in);
stddev = in.readDouble();
instanceNumber = in.readInt();
random = new Random(instanceNumber);
// Not perfect; see the comment in #write(ExceptionlessOutputStream)
}
/**
* Returns a new, empty weight vector with the same parameter settings as
* this one.
*
* @return An empty weight vector.
**/
public SparseWeightVector emptyClone() {
return new RandomWeightVector(stddev);
}
}