/* * Created on Jul 14, 2007 * * Copyright (c) 2007, 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. */ package edu.uci.ics.jung.algorithms.scoring; import org.apache.commons.collections15.Transformer; import org.apache.commons.collections15.functors.ConstantTransformer; import edu.uci.ics.jung.graph.Hypergraph; /** * A generalization of HITS that permits non-uniformly-distributed random jumps. * The 'vertex_priors' (that is, prior probabilities for each vertex) may be * thought of as the fraction of the total 'potential' (hub or authority score) * that is assigned to that vertex out of the portion that is assigned according * to random jumps. * * @see "Algorithms for Estimating Relative Importance in Graphs by Scott White and Padhraic Smyth, 2003" */ public class HITSWithPriors<V, E> extends AbstractIterativeScorerWithPriors<V, E, HITS.Scores> { /** * The sum of the potential, at each step, associated with vertices with no * outedges (authority) or no inedges (hub). */ protected HITS.Scores disappearing_potential; /** * Creates an instance for the specified graph, edge weights, vertex prior * probabilities, and random jump probability (alpha). * * @param g * the input graph * @param edge_weights * the edge weights * @param vertex_priors * the prior probability for each vertex * @param alpha * the probability of a random jump at each step */ public HITSWithPriors(Hypergraph<V, E> g, Transformer<E, ? extends Number> edge_weights, Transformer<V, HITS.Scores> vertex_priors, double alpha) { super(g, edge_weights, vertex_priors, alpha); disappearing_potential = new HITS.Scores(0, 0); } /** * Creates an instance for the specified graph, vertex priors, and random * jump probability (alpha). The edge weights default to 1.0. * * @param g * the input graph * @param vertex_priors * the prior probability for each vertex * @param alpha * the probability of a random jump at each step */ @SuppressWarnings("unchecked") public HITSWithPriors(Hypergraph<V, E> g, Transformer<V, HITS.Scores> vertex_priors, double alpha) { super(g, new ConstantTransformer(1.0), vertex_priors, alpha); disappearing_potential = new HITS.Scores(0, 0); } /** * Updates the value for this vertex. */ @Override protected double update(V v) { collectDisappearingPotential(v); double v_auth = 0; for (E e : graph.getInEdges(v)) { int incident_count = getAdjustedIncidentCount(e); for (V w : graph.getIncidentVertices(e)) { if (!w.equals(v) || hyperedges_are_self_loops) { v_auth += (getCurrentValue(w).hub * getEdgeWeight(w, e).doubleValue() / incident_count); } } // V w = graph.getOpposite(v, e); // auth += (getCurrentValue(w).hub * getEdgeWeight(w, // e).doubleValue()); } double v_hub = 0; for (E e : graph.getOutEdges(v)) { int incident_count = getAdjustedIncidentCount(e); for (V w : graph.getIncidentVertices(e)) { if (!w.equals(v) || hyperedges_are_self_loops) { v_hub += (getCurrentValue(w).authority * getEdgeWeight(w, e).doubleValue() / incident_count); } } // V x = graph.getOpposite(v,e); // hub += (getCurrentValue(x).authority * getEdgeWeight(x, // e).doubleValue()); } // modify total_input according to alpha if (alpha > 0) { v_auth = v_auth * (1 - alpha) + getVertexPrior(v).authority * alpha; v_hub = v_hub * (1 - alpha) + getVertexPrior(v).hub * alpha; } setOutputValue(v, new HITS.Scores(v_hub, v_auth)); return Math.max(Math.abs(getCurrentValue(v).hub - v_hub), Math.abs(getCurrentValue(v).authority - v_auth)); } /** * Code which is executed after each step. In this case, deals with the * 'disappearing potential', normalizes the scores, and then calls * <code>super.afterStep()</code>. * * @see #collectDisappearingPotential(Object) */ @Override protected void afterStep() { if (disappearing_potential.hub > 0 || disappearing_potential.authority > 0) { for (V v : graph.getVertices()) { double new_hub = getOutputValue(v).hub + (1 - alpha) * (disappearing_potential.hub * getVertexPrior(v).hub); double new_auth = getOutputValue(v).authority + (1 - alpha) * (disappearing_potential.authority * getVertexPrior(v).authority); setOutputValue(v, new HITS.Scores(new_hub, new_auth)); } disappearing_potential.hub = 0; disappearing_potential.authority = 0; } normalizeScores(); super.afterStep(); } /** * Normalizes scores so that sum of their squares = 1. This method may be * overridden so as to yield different normalizations. */ protected void normalizeScores() { double hub_ssum = 0; double auth_ssum = 0; for (V v : graph.getVertices()) { double hub_val = getOutputValue(v).hub; double auth_val = getOutputValue(v).authority; hub_ssum += (hub_val * hub_val); auth_ssum += (auth_val * auth_val); } hub_ssum = Math.sqrt(hub_ssum); auth_ssum = Math.sqrt(auth_ssum); for (V v : graph.getVertices()) { HITS.Scores values = getOutputValue(v); setOutputValue(v, new HITS.Scores(values.hub / hub_ssum, values.authority / auth_ssum)); } } /** * Collects the "disappearing potential" associated with vertices that have * either no incoming edges, no outgoing edges, or both. Vertices that have * no incoming edges do not directly contribute to the hub scores of other * vertices; similarly, vertices that have no outgoing edges do not directly * contribute to the authority scores of other vertices. These values are * collected at each step and then distributed across all vertices as a part * of the normalization process. (This process is not required for, and does * not affect, the 'sum-of-squares'-style normalization.) */ @Override protected void collectDisappearingPotential(V v) { if (graph.outDegree(v) == 0) { if (isDisconnectedGraphOK()) { disappearing_potential.hub += getCurrentValue(v).authority; } else { throw new IllegalArgumentException( "Outdegree of " + v + " must be > 0"); } } if (graph.inDegree(v) == 0) { if (isDisconnectedGraphOK()) { disappearing_potential.authority += getCurrentValue(v).hub; } else { throw new IllegalArgumentException( "Indegree of " + v + " must be > 0"); } } } }