View Javadoc
1   /**
2    * Copyright (c) 2008, The JUNG Authors 
3    *
4    * All rights reserved.
5    *
6    * This software is open-source under the BSD license; see either
7    * "license.txt" or
8    * https://github.com/jrtom/jung/blob/master/LICENSE for a description.
9    * Created on Aug 22, 2008
10   * 
11   */
12  package edu.uci.ics.jung.algorithms.scoring;
13  
14  import com.google.common.base.Function;
15  
16  import edu.uci.ics.jung.algorithms.scoring.util.ScoringUtils;
17  import edu.uci.ics.jung.graph.Hypergraph;
18  
19  /**
20   * A special case of {@code PageRankWithPriors} in which the final scores
21   * represent a probability distribution over position assuming a random (Markovian)
22   * walk of exactly k steps, based on the initial distribution specified by the priors.
23   * 
24   * <p><b>NOTE</b>: The version of {@code KStepMarkov} in {@code algorithms.importance}
25   * (and in JUNG 1.x) is believed to be incorrect: rather than returning 
26   * a score which represents a probability distribution over position assuming
27   * a k-step random walk, it returns a score which represents the sum over all steps
28   * of the probability for each step.  If you want that behavior, set the 
29   * 'cumulative' flag as follows <i>before calling {@code evaluate()}</i>:
30   * <pre>
31   *     KStepMarkov ksm = new KStepMarkov(...);
32   *     ksm.setCumulative(true);
33   *     ksm.evaluate();
34   * </pre>
35   * 
36   * By default, the 'cumulative' flag is set to false.
37   * 
38   * NOTE: THIS CLASS IS NOT YET COMPLETE.  USE AT YOUR OWN RISK.  (The original behavior
39   * is captured by the version still available in {@code algorithms.importance}.)
40   * 
41   * @see "Algorithms for Estimating Relative Importance in Graphs by Scott White and Padhraic Smyth, 2003"
42   * @see PageRank
43   * @see PageRankWithPriors
44   */
45  public class KStepMarkov<V,E> extends PageRankWithPriors<V,E> 
46  {
47  	private boolean cumulative;
48  	
49  	/**
50  	 * Creates an instance based on the specified graph, edge weights, vertex
51  	 * priors (initial scores), and number of steps to take.
52  	 * @param graph the input graph
53  	 * @param edge_weights the edge weights (transition probabilities)
54  	 * @param vertex_priors the initial probability distribution (score assignment)
55  	 * @param steps the number of times that {@code step()} will be called by {@code evaluate}
56  	 */
57  	public KStepMarkov(Hypergraph<V,E> graph, Function<E, ? extends Number> edge_weights, 
58  					   Function<V, Double> vertex_priors, int steps)
59  	{
60  		super(graph, edge_weights, vertex_priors, 0);
61  		initialize(steps);
62  	}
63  	
64  	/**
65  	 * Creates an instance based on the specified graph, vertex
66  	 * priors (initial scores), and number of steps to take.  The edge
67  	 * weights (transition probabilities) are set to default values (a uniform
68  	 * distribution over all outgoing edges).
69  	 * @param graph the input graph
70  	 * @param vertex_priors the initial probability distribution (score assignment)
71  	 * @param steps the number of times that {@code step()} will be called by {@code evaluate}
72  	 */
73  	public KStepMarkov(Hypergraph<V,E> graph, Function<V, Double> vertex_priors, int steps)
74  	{
75  		super(graph, vertex_priors, 0);
76  		initialize(steps);
77  	}
78  	
79  	/**
80  	 * Creates an instance based on the specified graph and number of steps to 
81  	 * take.  The edge weights (transition probabilities) and vertex initial scores
82  	 * (prior probabilities) are set to default values (a uniform
83  	 * distribution over all outgoing edges, and a uniform distribution over
84  	 * all vertices, respectively).
85  	 * @param graph the input graph
86  	 * @param steps the number of times that {@code step()} will be called by {@code evaluate}
87  	 */
88  	public KStepMarkov(Hypergraph<V,E> graph, int steps)
89  	{
90  		super(graph, ScoringUtils.getUniformRootPrior(graph.getVertices()), 0);
91  		initialize(steps);
92  	}
93  	
94  	private void initialize(int steps)
95  	{
96  		this.acceptDisconnectedGraph(false);
97  		
98  		if (steps <= 0)
99  			throw new IllegalArgumentException("Number of steps must be > 0");
100 		
101 		this.max_iterations = steps;
102 		this.tolerance = -1.0;
103 		
104 		this.cumulative = false;
105 	}
106 
107 	/**
108 	 * Specifies whether this instance should assign a score to each vertex
109 	 * based on the sum over all steps of the probability for each step.
110 	 * See the class-level documentation for details.
111 	 * @param cumulative true if this instance should assign a cumulative score to each vertex
112 	 */
113 	public void setCumulative(boolean cumulative)
114 	{
115 		this.cumulative = cumulative;
116 	}
117 	
118     /**
119      * Updates the value for this vertex.  Called by <code>step()</code>.
120      */
121     @Override
122     public double update(V v)
123     {
124     	if (!cumulative)
125     		return super.update(v);
126     	
127         collectDisappearingPotential(v);
128         
129         double v_input = 0;
130         for (E e : graph.getInEdges(v))
131         {
132         	// For graphs, the code below is equivalent to 
133 //          V w = graph.getOpposite(v, e);
134 //          total_input += (getCurrentValue(w) * getEdgeWeight(w,e).doubleValue());
135         	// For hypergraphs, this divides the potential coming from w 
136         	// by the number of vertices in the connecting edge e.
137         	int incident_count = getAdjustedIncidentCount(e);
138         	for (V w : graph.getIncidentVertices(e)) 
139         	{
140         		if (!w.equals(v) || hyperedges_are_self_loops) 
141         			v_input += (getCurrentValue(w) * 
142         					getEdgeWeight(w,e).doubleValue() / incident_count);
143         	}
144         }
145         
146         // modify total_input according to alpha
147         double new_value = alpha > 0 ? 
148         		v_input * (1 - alpha) + getVertexPrior(v) * alpha :
149         		v_input;
150         setOutputValue(v, new_value + getCurrentValue(v));
151 
152         // FIXME: DO WE NEED TO CHANGE HOW DISAPPEARING IS COUNTED?  NORMALIZE?
153         
154         return Math.abs(getCurrentValue(v) - new_value);
155     }
156 
157 }