/**
* Copyright (c) 2007, University of Pittsburgh
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the University of Pittsburgh nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
package di.uniba.it.tri.vectors;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.logging.Logger;
/**
* This class provides some standard vector methods. Many old methods have been
* removed and are now implemented in vector and util classes for real, complex,
* and binary vectors.
*/
public class VectorUtils {
@SuppressWarnings("unused")
private static final Logger logger = Logger.getLogger(VectorUtils.class.getCanonicalName());
/**
* Get nearest vector from list of candidates.
*
* @param vector The vector whose nearest neighbor is to be found.
* @param candidates The list of vectors from whoe the nearest is to be
* chosen.
* @return Integer value referencing the position in the candidate list of
* the nearest vector.
*/
public static int getNearestVector(Vector vector, Vector[] candidates) {
int nearest = 0;
double maxSim = vector.measureOverlap(candidates[0]);
for (int i = 1; i < candidates.length; ++i) {
double thisDist = vector.measureOverlap(candidates[i]);
if (thisDist > maxSim) {
maxSim = thisDist;
nearest = i;
}
}
return nearest;
}
/**
* Returns the sum overlap of the testVector with the subspace generated by
* the vectors, which are presumed to be orthogonal.
* @param testVector
* @param vectors
* @return
*/
public static double compareWithProjection(Vector testVector, ArrayList<Vector> vectors) {
float score = 0;
for (int i = 0; i < vectors.size(); ++i) {
score += Math.pow(testVector.measureOverlap(vectors.get(i)), 2);
}
return (float) Math.sqrt(score);
}
/**
* The orthogonalize function takes an array of vectors and orthogonalizes
* them using the Gram-Schmidt process. The vectors are orthogonalized in
* place, so there is no return value. Note that the output of this function
* is order dependent, in particular, the jth vector in the array will be
* made orthogonal to all the previous vectors. Since this means that the
* last vector is orthogonal to all the others, this can be used as a
* negation function to give an vector for vectors[last] NOT (vectors[0] OR
* ... OR vectors[last - 1].
*
* @param list vectors to be orthogonalized in place.
*/
public static void orthogonalizeVectors(List<Vector> list) {
switch (list.get(0).getVectorType()) {
case REAL:
RealVectorUtils.orthogonalizeVectors(list);
break;
//case BINARY:
// BinaryVectorUtils.orthogonalizeVectors(list);
// break;
default:
throw new IncompatibleVectorsException("Type not recognized: " + list.get(0).getVectorType());
}
}
/**
* Returns a superposition of the form leftWeight*left + rightWeight*right.
* @param left
* @param leftWeight
* @param rightWeight
* @param right
* @return
*/
public static Vector weightedSuperposition(
Vector left, double leftWeight, Vector right, double rightWeight) {
if ((left.getVectorType() != right.getVectorType())
|| (left.getDimension() != right.getDimension())) {
throw new IncompatibleVectorsException(
String.format("Incompatible vectors:\n%s\n%s", left.toString(), right.toString()));
}
switch (left.getVectorType()) {
case REAL:
case COMPLEX:
Vector superposition = VectorFactory.createZeroVector(
left.getVectorType(), left.getDimension());
superposition.superpose(left, leftWeight, null);
superposition.superpose(right, rightWeight, null);
superposition.normalize();
return superposition;
case BINARY:
// return BinaryVectorUtils.weightedSuperposition(
// (BinaryVector) left, leftWeight, (BinaryVector) right, rightWeight);
default:
throw new IncompatibleVectorsException("Type not recognized: " + left.getVectorType());
}
}
/**
* Generates a basic sparse vector with mainly zeros and some 1 and -1
* entries (seedLength/2 of each) each vector is an array of length
* seedLength containing 1+ the index of a non-zero value, signed according
* to whether this is a + or -1.
* <br>
* e.g. +20 would indicate a +1 in position 19, +1 would indicate a +1 in
* position 0. -20 would indicate a -1 in position 19, -1 would indicate a
* -1 in position 0.
* <br>
* The extra offset of +1 is because position 0 would be unsigned, and would
* therefore be wasted. Consequently we've chosen to make the code slightly
* more complicated to make the implementation slightly more space
* efficient.
*
* @param seedLength
* @param dimension
* @param random
* @return Sparse representation of basic ternary vector. Array of short
* signed integers, indices to the array locations where a +/-1 entry is
* located.
*/
public static short[] generateRandomVector(int seedLength, int dimension, Random random) {
boolean[] randVector = new boolean[dimension];
short[] randIndex = new short[seedLength];
int testPlace, entryCount = 0;
/* put in +1 entries */
while (entryCount < seedLength / 2) {
testPlace = random.nextInt(dimension);
if (!randVector[testPlace]) {
randVector[testPlace] = true;
randIndex[entryCount] = new Integer(testPlace + 1).shortValue();
entryCount++;
}
}
/* put in -1 entries */
while (entryCount < seedLength) {
testPlace = random.nextInt(dimension);
if (!randVector[testPlace]) {
randVector[testPlace] = true;
randIndex[entryCount] = new Integer((1 + testPlace) * -1).shortValue();
entryCount++;
}
}
return randIndex;
}
}