package userrecommender; import java.util.Calendar; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.TreeSet; import Jama.Matrix; /** * Implements the Spear Algorithm. This implementation is hidden therefore package * scope. Please use the appropriate wrapper class which transforms the given * data set into an appropriate input for this algorithm. * * @author ilire.mavriqi * */ class SpearAlgorithm { private static final int DFEAULT_NUMBER_OF_ITERATIONS = 250; private Matrix matrix; private HashMap<Integer, TreeSet<UserActivity>> activities = null; private LinkedList<Integer> uniqueUsers = null; private List<Integer> uniqueResources = null; private Map<Integer, Double> expertiseResult = null; private Map<Integer, Double> qualityResult = null; /** * creates a new Object. * * @param activities * triples (resourceID, time stamp, userID). Presumption: the * given set is already sorted by time stamp whereby the * first entry is the earliest time stamp for the given resource. * @param uniqueUsers * list of unique userIDs * @param uniqueResources * list of unique resouceIDs */ SpearAlgorithm(HashMap<Integer, TreeSet<UserActivity>> activities, LinkedList<Integer> uniqueUsers, List<Integer> uniqueResources) { this.activities = activities; this.uniqueUsers = uniqueUsers; this.uniqueResources = uniqueResources; } /** * Executes spear algorithm on the given data set by initializing the sparse * matrix and calculating the credit scores. * * @param numberOfIterations * number of iterations. Minimum number is 250. */ SpearAlgorithmResult execute(int numberOfIterations) { if (activities == null || activities.size() == 0 || uniqueUsers.size() == 0 || uniqueResources.size() == 0) { throw new IllegalArgumentException( "Invalid input data. Plase make sure that entries, unique users and unique resources do not equall zero"); } int finalNumberOfIterations = numberOfIterations > DFEAULT_NUMBER_OF_ITERATIONS ? numberOfIterations : DFEAULT_NUMBER_OF_ITERATIONS; populateMatrix(); applyCreditScores(); generateScoreVectors(finalNumberOfIterations); SpearAlgorithmResult result = new SpearAlgorithmResult(expertiseResult, qualityResult); return result; } private void generateScoreVectors(int finalNumberOfIterations) { Matrix userExpertiseVector = getInitSparseVector(uniqueUsers.size()); Matrix resourceQualityVector = getInitSparseVector ( uniqueResources.size()); for (int i = 0; i < finalNumberOfIterations; i++) { userExpertiseVector = resourceQualityVector.times((matrix).transpose()); resourceQualityVector = userExpertiseVector.times(matrix); userExpertiseVector.timesEquals(1. / getColumnSum (userExpertiseVector)); // normalise resourceQualityVector.timesEquals(1. / getColumnSum(resourceQualityVector)); // normalise System.out.println("iteration: " + i + " time: " + Calendar.getInstance().getTime() ); } expertiseResult = new HashMap<Integer, Double>(); qualityResult = new HashMap<Integer, Double>(); for (int j = 0; j < userExpertiseVector.getColumnDimension(); j++) { expertiseResult.put( uniqueUsers.get(j),userExpertiseVector.get(0,j)); } for (int j = 0; j < resourceQualityVector.getColumnDimension(); j++) { qualityResult.put(uniqueResources.get(j),resourceQualityVector.get(0,j)); } } private Matrix getInitSparseVector(int size) { Matrix spVec = new Matrix(1, size); for (int i = 0; i < spVec.getColumnDimension(); i++) { spVec.set(0, i, 1d); } return spVec; } private double getColumnSum (Matrix matrix){ double sum = 0; for(int j=0;j<matrix.getColumnDimension();j++) { sum += matrix.get(0, j); } return sum; } private void applyCreditScores() { for (int i = 0; i < matrix.getRowDimension(); i++) { for (int j = 0; j < matrix.getColumnDimension(); j++) { double creditScore = matrix.get(i, j); matrix.set(i, j, Math.sqrt(creditScore)); } } } private void populateMatrix() { matrix = new Matrix(uniqueUsers.size(), uniqueResources.size()); Iterator<Integer> resourceIterator = activities.keySet().iterator(); while (resourceIterator.hasNext()) { long lastTimeStamp = 0; double lastScore = 0d; int currentProcessedUsers = 0; Integer resourceID = resourceIterator.next(); TreeSet<UserActivity> timeUserTuple = activities.get(resourceID); for (UserActivity activity : timeUserTuple) { if (activity.getTimeStamp() == 0) { throw new IllegalArgumentException( "Invalid time stamp found for resource with id: " + resourceID + " and user id:" + activity.getUserId()); } if (activity.getTimeStamp() == lastTimeStamp) { matrix.set(uniqueUsers.indexOf(activity.getUserId()), uniqueResources.indexOf(resourceID), lastScore); } else { lastScore = activities.get(resourceID).size() - currentProcessedUsers; lastTimeStamp = activity.getTimeStamp(); matrix.set(uniqueUsers.indexOf(activity.getUserId()), uniqueResources.indexOf(resourceID), lastScore); } currentProcessedUsers += 1; } } } }