/**
* Copyright (c) 2008, the JUNG Project and the Regents of the University
* of California
* All rights reserved.
*
* This software is open-source under the BSD license; see either
* "license.txt" or
* http://jung.sourceforge.net/license.txt for a description.
* Created on Aug 22, 2008
*
*/
package edu.uci.ics.jung.algorithms.scoring;
import org.apache.commons.collections15.Transformer;
import edu.uci.ics.jung.algorithms.scoring.util.ScoringUtils;
import edu.uci.ics.jung.graph.Hypergraph;
/**
* A special case of {@code PageRankWithPriors} in which the final scores
* represent a probability distribution over position assuming a random
* (Markovian) walk of exactly k steps, based on the initial distribution
* specified by the priors.
*
* <p>
* <b>NOTE</b>: The version of {@code KStepMarkov} in
* {@code algorithms.importance} (and in JUNG 1.x) is believed to be incorrect:
* rather than returning a score which represents a probability distribution
* over position assuming a k-step random walk, it returns a score which
* represents the sum over all steps of the probability for each step. If you
* want that behavior, set the 'cumulative' flag as follows <i>before calling
* {@code evaluate()}</i>:
*
* <pre>
* KStepMarkov ksm = new KStepMarkov(...);
* ksm.setCumulative(true);
* ksm.evaluate();
* </pre>
*
* By default, the 'cumulative' flag is set to false.
*
* NOTE: THIS CLASS IS NOT YET COMPLETE. USE AT YOUR OWN RISK. (The original
* behavior is captured by the version still available in
* {@code algorithms.importance}.)
*
* @see "Algorithms for Estimating Relative Importance in Graphs by Scott White and Padhraic Smyth, 2003"
* @see PageRank
* @see PageRankWithPriors
*/
public class KStepMarkov<V, E> extends PageRankWithPriors<V, E> {
private boolean cumulative;
/**
* Creates an instance based on the specified graph, edge weights, vertex
* priors (initial scores), and number of steps to take.
*
* @param graph
* the input graph
* @param edge_weights
* the edge weights (transition probabilities)
* @param vertex_priors
* the initial probability distribution (score assignment)
* @param steps
* the number of times that {@code step()} will be called by
* {@code evaluate}
*/
public KStepMarkov(Hypergraph<V, E> graph,
Transformer<E, ? extends Number> edge_weights,
Transformer<V, Double> vertex_priors, int steps) {
super(graph, edge_weights, vertex_priors, 0);
initialize(steps);
}
/**
* Creates an instance based on the specified graph, vertex priors (initial
* scores), and number of steps to take. The edge weights (transition
* probabilities) are set to default values (a uniform distribution over all
* outgoing edges).
*
* @param graph
* the input graph
* @param vertex_priors
* the initial probability distribution (score assignment)
* @param steps
* the number of times that {@code step()} will be called by
* {@code evaluate}
*/
public KStepMarkov(Hypergraph<V, E> graph,
Transformer<V, Double> vertex_priors, int steps) {
super(graph, vertex_priors, 0);
initialize(steps);
}
/**
* Creates an instance based on the specified graph and number of steps to
* take. The edge weights (transition probabilities) and vertex initial
* scores (prior probabilities) are set to default values (a uniform
* distribution over all outgoing edges, and a uniform distribution over all
* vertices, respectively).
*
* @param graph
* the input graph
* @param steps
* the number of times that {@code step()} will be called by
* {@code evaluate}
*/
public KStepMarkov(Hypergraph<V, E> graph, int steps) {
super(graph, ScoringUtils.getUniformRootPrior(graph.getVertices()), 0);
initialize(steps);
}
private void initialize(int steps) {
this.acceptDisconnectedGraph(false);
if (steps <= 0) {
throw new IllegalArgumentException("Number of steps must be > 0");
}
this.max_iterations = steps;
this.tolerance = -1.0;
this.cumulative = false;
}
/**
* Specifies whether this instance should assign a score to each vertex
* based on the
*
* @param cumulative
*/
public void setCumulative(boolean cumulative) {
this.cumulative = cumulative;
}
/**
* Updates the value for this vertex. Called by <code>step()</code>.
*/
@Override
public double update(V v) {
if (!cumulative) {
return super.update(v);
}
collectDisappearingPotential(v);
double v_input = 0;
for (E e : graph.getInEdges(v)) {
// For graphs, the code below is equivalent to
// V w = graph.getOpposite(v, e);
// total_input += (getCurrentValue(w) *
// getEdgeWeight(w,e).doubleValue());
// For hypergraphs, this divides the potential coming from w
// by the number of vertices in the connecting edge e.
int incident_count = getAdjustedIncidentCount(e);
for (V w : graph.getIncidentVertices(e)) {
if (!w.equals(v) || hyperedges_are_self_loops) {
v_input += (getCurrentValue(w)
* getEdgeWeight(w, e).doubleValue()
/ incident_count);
}
}
}
// modify total_input according to alpha
double new_value = alpha > 0
? v_input * (1 - alpha) + getVertexPrior(v) * alpha : v_input;
setOutputValue(v, new_value + getCurrentValue(v));
// FIXME: DO WE NEED TO CHANGE HOW DISAPPEARING IS COUNTED? NORMALIZE?
return Math.abs(getCurrentValue(v) - new_value);
}
}