/**
* Copyright (c) 2006-2009, NEPOMUK Consortium
*
* 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 NEPOMUK Consortium 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 processing.folkrank;
import java.util.Arrays;
public class FolkRankAlgorithm {
private FolkRankResult baselineResult = null;
private FolkRankParam param = null;
private FolkRankPref pref = null;
public FolkRankAlgorithm (FolkRankParam param) {
this.param = param;
}
public void resetBaseline () {
baselineResult = null;
param = null;
}
public FolkRankResult computeFolkRank (final FolkRankData facts, final FolkRankPref pref) {
/*
* initalize baseline, if neccessary
*/
if (baselineResult == null) {
baselineResult = compute(facts, new FolkRankPref(pref.getBasePrefWeight()));
}
/*
* compute weights with preference
*/
FolkRankResult preferenceResult = compute(facts, pref);
/*
* compute difference weights
*/
double[][] baselineWeights = baselineResult.getWeights();
double[][] preferenceWeights = preferenceResult.getWeights();
/*
* for evaluation: remember weights of adapted PageRank
*/
preferenceResult.setAPRWeights(preferenceWeights);
for (int dim = 0; dim < baselineWeights.length; dim++) {
for (int item = 0; item < baselineWeights[dim].length; item++) {
preferenceWeights[dim][item] -= baselineWeights[dim][item];
}
}
return preferenceResult;
}
public FolkRankResult compute (final FolkRankData factsData, final FolkRankPref pref) {
/*
* These vectors are used to control the final weight computation:
* alpha * oldWeight + beta * newWeight + gamma * pref
* with alpha + beta + gamma = 1
*/
double alpha = param.getAlpha();
double beta = param.getBeta();
double gamma = param.getGamma();
/*
* input data
*/
int[][] facts = factsData.getFacts();
int[][] counts = factsData.getCounts();
/*
* output data
*/
//FolkRankResult result = new StandardFolkRankResult();
FolkRankResult result = new APRFolkRankResult(); // TODO: choose correct one
/*
* weight vectors
*/
double[][] weights = new double[counts.length][]; // weight
double[][] newWeights = new double[counts.length][]; // temporary weight
double[][] spread = new double[counts.length][]; // spreading weight
double[] pWeights = new double[counts.length]; // random surfer weights
double[] pWeightsSum = new double[counts.length]; // sum of random surfer + preference weights
int[][] prefNodes = pref.getPrefItems(); // nodes with preference
double[][] prefValues = pref.getPrefValues(); // preference values
/*
* allocate memory for weight vectors
*/
for (int dim = 0; dim < weights.length; dim++) {
newWeights[dim] = new double[counts[dim].length];
spread[dim] = new double[counts[dim].length];
weights[dim] = new double[counts[dim].length];
}
/*
* initialize weights, random surfer, preference sums
*/
param.getWeightInitializationStrategy().initalizeWeights(pref, weights, pWeights, pWeightsSum);
/* ********************************************************************
* main loop
* ********************************************************************/
int iter = 0;
double delta = Double.MAX_VALUE;
while (iter < param.getMaxIter() && delta > param.getEpsilon()) {
iter++;
/*
* initalize new weights with zero
*/
for (double[] newWeightsDim:newWeights) {
Arrays.fill(newWeightsDim, 0.0);
}
/*
* initalize spread
*/
for (int dim = 0; dim < spread.length; dim++) {
for (int node = 0; node < spread[dim].length; node++) {
spread[dim][node] = weights[dim][node] / counts[dim][node];
}
}
/*
* spread weight
*/
for (int fact[]: facts) {
for (int dim = 0; dim < newWeights.length; dim++) {
/*
* newWeights[dim][fact[dim]] gets weight from all other
* nodes at this hyperedge
*/
for (int dimAdd = 0; dimAdd < newWeights.length; dimAdd++) {
if (dim != dimAdd) {
/*
* get weight from surrounding nodes
*/
newWeights[dim][fact[dim]] += spread[dimAdd][fact[dimAdd]];
} else {
/*
* ignore own weight
*/
}
}
}
}
/*
* calculate new weights
*/
delta = 0.0;
double newWeight;
for (int dim = 0; dim < weights.length; dim++) {
/*
* spread preference
*/
if (prefValues != null && prefNodes != null) {
for (int node = 0; node < prefNodes[dim].length; node++) {
/*
* TODO: explain
*/
double prefSpread = (gamma / beta) * (prefValues[dim][node] / pWeightsSum[dim]);
newWeights[dim][prefNodes[dim][node]] += prefSpread;
}
}
/*
* accumulate old weight, new weight and random surfer into
* new weight
*/
double sumOld = 0;
double sumNew = 0;
double sumPref = 0;
for (int node = 0; node < weights[dim].length; node++) {
double oldW = alpha * weights[dim][node];
double newW = beta * newWeights[dim][node];
double prefW = gamma * pWeights[dim];
sumOld += oldW;
sumNew += newW;
sumPref += prefW;
newWeight = oldW +
newW +
prefW;
/*
* compute weight change
*/
delta += Math.abs(weights[dim][node] - newWeight);
weights[dim][node] = newWeight;
}
}
result.addError(delta);
} // main loop
result.setWeights(weights);
return result;
}
}