/** * 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); } }