edu.ucla.sspace.index.DefaultPermutationFunction Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sspace Show documentation
Show all versions of sspace Show documentation
The S-Space Package is a Natural Language Processing library for
distributional semantics representations. Distributional semantics
representations model the meaning of words, phrases, and sentences as high
dimensional vectors or probability distributions. The library includes common
algorithms such as Latent Semantic Analysis, Random Indexing, and Latent
Dirichlet Allocation. The S-Space package also includes software libraries
for matrices, vectors, graphs, and numerous clustering
algorithms.
The newest version!
/*
* Copyright 2009 David Jurgens
*
* This file is part of the S-Space package and is covered under the terms and
* conditions therein.
*
* The S-Space package is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation and distributed hereunder to you.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND NO REPRESENTATIONS OR WARRANTIES,
* EXPRESS OR IMPLIED ARE MADE. BY WAY OF EXAMPLE, BUT NOT LIMITATION, WE MAKE
* NO REPRESENTATIONS OR WARRANTIES OF MERCHANT- ABILITY OR FITNESS FOR ANY
* PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE OR DOCUMENTATION
* WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER
* RIGHTS.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package edu.ucla.sspace.index;
import edu.ucla.sspace.vector.SparseVector;
import edu.ucla.sspace.vector.TernaryVector;
import edu.ucla.sspace.vector.Vector;
import edu.ucla.sspace.vector.Vectors;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntObjectHashMap;
/**
* A generic permutation function. This class precomputes the permutations as
* necessary and only requires {@code O(k)} time to compute a single
* permutation, where {@code k} is the number of non-zero elements in the {@code
* Vector}.
*
* @author David Jurgens
*/
public class DefaultPermutationFunction
implements PermutationFunction, Serializable {
private static final long serialVersionUID = 1L;
private static final Random RANDOM = RandomIndexVectorGenerator.RANDOM;
/**
* A mapping from a distance to a corresponding permutation.
*/
private final TIntObjectMap permutationToReordering;
/**
* Creates an empty {@code DefaultPermutationFunction}.
*/
public DefaultPermutationFunction() {
permutationToReordering = new TIntObjectHashMap();
}
/**
* Returns the bijective mapping for each integer in the form of an array
* based on the the current exponent of the permutation.
*
* @param exponent the exponent for the current permutation
* @param dimensions the number of dimenensions in the index vector being
* permuted
*
* @return the mapping for each index to its new index
*/
private Function getFunction(int exponent, int dimensions) {
// Base case: we keep the same ordering. Create this function on the
// fly to save space, since the base case should rarely get called.
if (exponent == 0) {
int[] func = new int[dimensions];
for (int i = 0; i < dimensions; ++i) {
func[i] = i;
}
return new Function(func, func);
}
exponent = Math.abs(exponent);
Function function = permutationToReordering.get(exponent);
// If there wasn't a funcion for that exponent then created one by
// permuting the lower exponents value. Use recursion to access the
// lower exponents value to ensure that any non-existent lower-exponent
// functions are created along the way.
if (function == null) {
synchronized (this) {
function = permutationToReordering.get(exponent);
if (function == null) {
// lookup the prior function
int priorExponent = exponent - 1;
Function priorFunc =
getFunction(priorExponent, dimensions);
// convert to an object based array to use
// Collections.shuffle()
Integer[] objFunc = new Integer[dimensions];
for (int i = 0; i < dimensions; ++i) {
objFunc[i] = Integer.valueOf(priorFunc.forward[i]);
}
// then shuffle it to get a new permutation
java.util.List list = Arrays.asList(objFunc);
Collections.shuffle(list, RANDOM);
// convert back to a primitive array
int[] forwardMapping = new int[dimensions];
int[] backwardMapping = new int[dimensions];
for (int i = 0; i < dimensions; ++i) {
forwardMapping[i] = objFunc[i].intValue();
backwardMapping[objFunc[i].intValue()] = i;
}
System.out.printf("Forward: %s%nBackward: %s%n",
Arrays.toString(forwardMapping),
Arrays.toString(backwardMapping));
function = new Function(forwardMapping, backwardMapping);
// store it in the function map for later usee
permutationToReordering.put(exponent, function);
}
}
}
return function;
}
/**
* {@inheritDoc}
*/
public Vector permute(Vector v , int numPermutations) {
if (v instanceof TernaryVector)
return permute((TernaryVector) v, numPermutations, v.length());
Vector result = Vectors.instanceOf(v);
int[] dimensions = null;
int[] oldDims = null;
if (v instanceof SparseVector) {
oldDims = ((SparseVector) v).getNonZeroIndices();
dimensions = Arrays.copyOf(oldDims, oldDims.length);
} else {
dimensions = new int[v.length()];
for (int i = 0; i < v.length(); ++i)
dimensions[i] = i;
}
System.out.printf("input: %s%ninitial result: %s%n", v, result);
boolean isInverse = numPermutations < 0;
// NB: because we use the signum and !=, this loop will work for both
// positive and negative numbers of permutations
int totalPermutations = Math.abs(numPermutations);
// Loop through the number of permutation that we have to do and keep
// updating which indices are being assigned to which
for (int count = 1; count <= totalPermutations; ++count) {
// load the reordering funcion for this iteration of the permutation
Function function = getFunction(count, v.length());
// based on whether this is an inverse permutation, select whether
// to use the forward or backwards mapping.
int[] reordering = (isInverse)
? function.backward : function.forward;
oldDims = Arrays.copyOf(dimensions, dimensions.length);
for (int i = 0; i < oldDims.length; ++i) {
dimensions[i] = reordering[oldDims[i]];
}
}
for (int d : dimensions)
result.set(dimensions[d], v.getValue(d));
System.out.printf("input: %s%nfinal permuted result: %s%n", v, result);
return result;
}
/**
* An optimized instance of permute for TernaryVectors. In this case, only
* the positive and negative values are permuted, and a {@code
* TernaryVector} is returned.
*/
private Vector permute(TernaryVector v, int numPermutations, int length) {
int[] oldPos = v.positiveDimensions();
int[] oldNeg = v.negativeDimensions();
// create new arrays to hold the permuted locations of the vectors's
// positive and negative values.
//
// NB: we use a copy here to ensure that the function works for the 0
// permutation (i.e. effectively a no-op);
int[] positive = Arrays.copyOf(oldPos, oldPos.length);
int[] negative = Arrays.copyOf(oldNeg, oldNeg.length);
boolean isInverse = numPermutations < 0;
// NB: because we use the signum and !=, this loop will work for both
// positive and negative numbers of permutations
int totalPermutations = Math.abs(numPermutations);
for (int count = 1; count <= totalPermutations; ++count) {
// load the reordering funcion for this iteration of the permutation
Function function = getFunction(count, length);
// based on whether this is an inverse permutation, select whether
// to use the forward or backwards mapping.
int[] reordering = (isInverse)
? function.backward : function.forward;
// create a copy of the previous permuted values for positive and
// negative. We need this array because the permutation cannot be
// done in place
oldPos = Arrays.copyOf(positive, positive.length);
oldNeg = Arrays.copyOf(negative, negative.length);
// The reordering array specifies for index i the positive of i in
// the permuted array. Since the positive and negative indices are
// the only non-zero indicies, we can simply create new arrays for
// them of the same length and then set their new positions based on
// the values in the reordering array.
for (int i = 0; i < oldPos.length; ++i) {
positive[i] = reordering[oldPos[i]];
}
for (int i = 0; i < oldNeg.length; ++i) {
negative[i] = reordering[oldNeg[i]];
}
}
return new TernaryVector(length, positive, negative);
}
/**
* Returns the name of this class
*/
public String toString() {
return "DefaultPermutationFunction";
}
/**
* A bijective, invertible mapping between indices.
*/
private static class Function implements Serializable {
private static final long serialVersionUID = 1L;
private final int[] forward;
private final int[] backward;
public Function(int[] forward, int[] backward) {
this.forward = forward;
this.backward = backward;
}
}
}