View Javadoc
1   /*
2    * Created on Jul 14, 2007
3    *
4    * Copyright (c) 2007, The JUNG Authors 
5    *
6    * All rights reserved.
7    *
8    * This software is open-source under the BSD license; see either
9    * "license.txt" or
10   * https://github.com/jrtom/jung/blob/master/LICENSE for a description.
11   */
12  package edu.uci.ics.jung.algorithms.scoring;
13  
14  import com.google.common.base.Function;
15  import com.google.common.base.Functions;
16  
17  import edu.uci.ics.jung.graph.Hypergraph;
18  
19  /**
20   * A generalization of HITS that permits non-uniformly-distributed random jumps.
21   * The 'vertex_priors' (that is, prior probabilities for each vertex) may be
22   * thought of as the fraction of the total 'potential' (hub or authority score)
23   * that is assigned to that vertex out of the portion that is assigned according
24   * to random jumps.
25   * 
26   * @see "Algorithms for Estimating Relative Importance in Graphs by Scott White and Padhraic Smyth, 2003"
27   */
28  public class HITSWithPriors<V, E> 
29  	extends AbstractIterativeScorerWithPriors<V,E,HITS.Scores>
30  {
31      /**
32       * The sum of the potential, at each step, associated with vertices with no outedges (authority)
33       * or no inedges (hub).
34       */
35      protected HITS.Scores disappearing_potential;
36  
37      /**
38       * Creates an instance for the specified graph, edge weights, vertex prior probabilities,
39       * and random jump probability (alpha).
40       * @param g the input graph
41       * @param edge_weights the edge weights 
42       * @param vertex_priors the prior probability for each vertex
43       * @param alpha the probability of a random jump at each step
44       */
45      public HITSWithPriors(Hypergraph<V,E> g,
46              Function<E, ? extends Number> edge_weights,
47              Function<V, HITS.Scores> vertex_priors, double alpha)
48      {
49          super(g, edge_weights, vertex_priors, alpha);
50          disappearing_potential = new HITS.Scores(0,0);
51      }
52  
53      /**
54       * Creates an instance for the specified graph, vertex priors, and random
55       * jump probability (alpha).  The edge weights default to 1.0.
56       * @param g the input graph
57       * @param vertex_priors the prior probability for each vertex
58       * @param alpha the probability of a random jump at each step
59       */
60      public HITSWithPriors(Hypergraph<V,E> g, 
61            Function<V, HITS.Scores> vertex_priors, double alpha)
62      {
63      	super(g, Functions.constant(1.0), vertex_priors, alpha);
64          disappearing_potential = new HITS.Scores(0,0);
65      }
66  
67      /**
68       * Updates the value for this vertex.
69       */
70      @Override
71      protected double update(V v)
72      {
73          collectDisappearingPotential(v);
74          
75          double v_auth = 0;
76          for (E e : graph.getInEdges(v))
77          {
78          	int incident_count = getAdjustedIncidentCount(e);
79          	for (V w : graph.getIncidentVertices(e)) 
80          	{
81          		if (!w.equals(v) || hyperedges_are_self_loops) 
82          			v_auth += (getCurrentValue(w).hub * 
83          					getEdgeWeight(w,e).doubleValue() / incident_count);
84          	}
85  //            V w = graph.getOpposite(v, e);
86  //            auth += (getCurrentValue(w).hub * getEdgeWeight(w, e).doubleValue());
87          }
88          
89          double v_hub = 0;
90          for (E e : graph.getOutEdges(v))
91          {
92          	int incident_count = getAdjustedIncidentCount(e);
93          	for (V w : graph.getIncidentVertices(e)) 
94          	{
95          		if (!w.equals(v) || hyperedges_are_self_loops) 
96          			v_hub += (getCurrentValue(w).authority * 
97          					getEdgeWeight(w,e).doubleValue() / incident_count);
98          	}
99  //            V x = graph.getOpposite(v,e);
100 //            hub += (getCurrentValue(x).authority * getEdgeWeight(x, e).doubleValue()); 
101         }
102         
103         // modify total_input according to alpha
104         if (alpha > 0) 
105         {
106 	        v_auth = v_auth * (1 - alpha) + getVertexPrior(v).authority * alpha;
107 	        v_hub = v_hub * (1 - alpha) + getVertexPrior(v).hub * alpha;
108         }
109         setOutputValue(v, new HITS.Scores(v_hub, v_auth));
110 
111         return Math.max(Math.abs(getCurrentValue(v).hub - v_hub), 
112                         Math.abs(getCurrentValue(v).authority - v_auth));
113     }
114 
115     /**
116      * Code which is executed after each step.  In this case, deals with the
117      * 'disappearing potential', normalizes the scores, and then calls
118      * <code>super.afterStep()</code>.
119      * @see #collectDisappearingPotential(Object)
120      */
121     @Override
122     protected void afterStep()
123     {
124         if (disappearing_potential.hub > 0 || disappearing_potential.authority > 0)
125         {
126             for (V v : graph.getVertices())
127             {
128                 double new_hub = getOutputValue(v).hub + 
129                     (1 - alpha) * (disappearing_potential.hub * getVertexPrior(v).hub);
130                 double new_auth = getOutputValue(v).authority + 
131                     (1 - alpha) * (disappearing_potential.authority * getVertexPrior(v).authority);
132                 setOutputValue(v, new HITS.Scores(new_hub, new_auth));
133             }
134             disappearing_potential.hub = 0;
135             disappearing_potential.authority = 0;
136         }
137         
138     	normalizeScores();
139     	
140         super.afterStep();
141     }
142 
143 	/**
144 	 * Normalizes scores so that sum of their squares = 1.
145 	 * This method may be overridden so as to yield different 
146 	 * normalizations.
147 	 */
148 	protected void normalizeScores() {
149     	double hub_ssum = 0;
150     	double auth_ssum = 0;
151     	for (V v : graph.getVertices())
152     	{
153     		double hub_val = getOutputValue(v).hub;
154     		double auth_val = getOutputValue(v).authority;
155     		hub_ssum += (hub_val * hub_val);
156     		auth_ssum += (auth_val * auth_val);
157     	}
158 
159     	hub_ssum = Math.sqrt(hub_ssum);
160     	auth_ssum = Math.sqrt(auth_ssum);
161     	
162     	for (V v : graph.getVertices())
163     	{
164     		HITS.Scores values = getOutputValue(v);
165     		setOutputValue(v, new HITS.Scores(
166     				values.hub / hub_ssum,
167     				values.authority / auth_ssum));
168     	}
169 	}
170     
171 	/**
172 	 * Collects the "disappearing potential" associated with vertices that have either 
173 	 * no incoming edges, no outgoing edges, or both.  Vertices that have no incoming edges
174 	 * do not directly contribute to the hub scores of other vertices; similarly, vertices
175 	 * that have no outgoing edges do not directly contribute to the authority scores of
176 	 * other vertices.  These values are collected at each step and then distributed across all vertices
177 	 * as a part of the normalization process.  (This process is not required for, and does
178 	 * not affect, the 'sum-of-squares'-style normalization.) 
179 	 */
180     @Override
181     protected void collectDisappearingPotential(V v)
182     {
183         if (graph.outDegree(v) == 0)
184         {
185             if (isDisconnectedGraphOK())
186                 disappearing_potential.hub += getCurrentValue(v).authority;
187             else
188                 throw new IllegalArgumentException("Outdegree of " + v + " must be > 0");
189         }
190         if (graph.inDegree(v) == 0)
191         {
192             if (isDisconnectedGraphOK())
193                 disappearing_potential.authority += getCurrentValue(v).hub;
194             else
195                 throw new IllegalArgumentException("Indegree of " + v + " must be > 0");
196         }
197     }
198 
199 }