View Javadoc
1   /*
2    * Created on Jul 15, 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 java.util.Collection;
15  import java.util.Collections;
16  import java.util.HashMap;
17  import java.util.Map;
18  
19  import com.google.common.base.Function;
20  
21  import edu.uci.ics.jung.algorithms.scoring.util.UniformDegreeWeight;
22  import edu.uci.ics.jung.graph.Hypergraph;
23  
24  /**
25   * Assigns scores to vertices according to their 'voltage' in an approximate 
26   * solution to the Kirchoff equations.  This is accomplished by tying "source"
27   * vertices to specified positive voltages, "sink" vertices to 0 V, and 
28   * iteratively updating the voltage of each other vertex to the (weighted) 
29   * average of the voltages of its neighbors.
30   * 
31   * <p>The resultant voltages will all be in the range <code>[0, max]</code>
32   * where <code>max</code> is the largest voltage of any source vertex (in the
33   * absence of negative source voltages; see below).
34   * 
35   * <p>A few notes about this algorithm's interpretation of the graph data: 
36   * <ul>
37   * <li>Higher edge weights are interpreted as indicative of greater 
38   * influence/effect than lower edge weights.  
39   * <li>Negative edge weights (and negative "source" voltages) invalidate
40   * the interpretation of the resultant values as voltages.  However, this 
41   * algorithm will not reject graphs with negative edge weights or source voltages.
42   * <li>Parallel edges are equivalent to a single edge whose weight is the 
43   * sum of the weights on the parallel edges.
44   * <li>Current flows along undirected edges in both directions, 
45   * but only flows along directed edges in the direction of the edge.
46   * </ul>
47   *  
48   */
49  public class VoltageScorer<V, E> extends AbstractIterativeScorer<V, E, Double>
50          implements VertexScorer<V, Double>
51  {
52      protected Map<V, ? extends Number> source_voltages;
53      protected Collection<V> sinks;
54      
55      /**
56       * Creates an instance with the specified graph, edge weights, source voltages,
57       * and sinks.
58       * @param g the input graph
59       * @param edge_weights the edge weights, representing conductivity
60       * @param source_voltages the (fixed) voltage for each source
61       * @param sinks the vertices whose voltages are tied to 0
62       */
63      public VoltageScorer(Hypergraph<V, E> g, Function<? super E, ? extends Number> edge_weights, 
64              Map<V, ? extends Number> source_voltages, Collection<V> sinks)
65      {
66          super(g, edge_weights);
67          this.source_voltages = source_voltages;
68          this.sinks = sinks;
69          initialize();
70      }
71  
72      /**
73       * Creates an instance with the specified graph, edge weights, source vertices
74       * (each of whose 'voltages' are tied to 1), and sinks.
75       * @param g the input graph
76       * @param edge_weights the edge weights, representing conductivity
77       * @param sources the vertices whose voltages are tied to 1
78       * @param sinks the vertices whose voltages are tied to 0
79       */
80      public VoltageScorer(Hypergraph<V, E> g, Function<? super E, ? extends Number> edge_weights, 
81              Collection<V> sources, Collection<V> sinks)
82      {
83          super(g, edge_weights);
84          
85          Map<V, Double> unit_voltages = new HashMap<V, Double>();
86          for(V v : sources) 
87              unit_voltages.put(v, new Double(1.0));
88          this.source_voltages = unit_voltages;
89          this.sinks = sinks;
90          initialize();
91      }
92  
93      /**
94       * Creates an instance with the specified graph, source vertices
95       * (each of whose 'voltages' are tied to 1), and sinks.
96       * The outgoing edges for each vertex are assigned 
97       * weights that sum to 1.
98       * @param g the input graph
99       * @param sources the vertices whose voltages are tied to 1
100      * @param sinks the vertices whose voltages are tied to 0
101      */
102     public VoltageScorer(Hypergraph<V, E> g, Collection<V> sources, Collection<V> sinks)
103     {
104         super(g);
105         
106         Map<V, Double> unit_voltages = new HashMap<V, Double>();
107         for(V v : sources) 
108             unit_voltages.put(v, new Double(1.0));
109         this.source_voltages = unit_voltages;
110         this.sinks = sinks;
111         initialize();
112     }
113     
114     /**
115      * Creates an instance with the specified graph, source voltages,
116      * and sinks.  The outgoing edges for each vertex are assigned 
117      * weights that sum to 1.
118      * @param g the input graph
119      * @param source_voltages the (fixed) voltage for each source
120      * @param sinks the vertices whose voltages are tied to 0
121      */
122     public VoltageScorer(Hypergraph<V, E> g, Map<V, ? extends Number> source_voltages, 
123     		Collection<V> sinks)
124     {
125         super(g);
126         this.source_voltages = source_voltages;
127         this.sinks = sinks;
128         this.edge_weights = new UniformDegreeWeight<V,E>(g);
129         initialize();
130     }
131     
132     /**
133      * Creates an instance with the specified graph, edge weights, source, and
134      * sink.  The source vertex voltage is tied to 1.
135      * @param g the input graph
136      * @param edge_weights the edge weights, representing conductivity
137      * @param source the vertex whose voltage is tied to 1
138      * @param sink the vertex whose voltage is tied to 0
139      */
140     public VoltageScorer(Hypergraph<V,E> g, Function<? super E, ? extends Number> edge_weights, 
141     		V source, V sink)
142     {
143         this(g, edge_weights, Collections.singletonMap(source, 1.0), Collections.singletonList(sink));
144         initialize();
145     }
146 
147     /**
148      * Creates an instance with the specified graph, edge weights, source, and
149      * sink.  The source vertex voltage is tied to 1.
150      * The outgoing edges for each vertex are assigned 
151      * weights that sum to 1.
152      * @param g the input graph
153      * @param source the vertex whose voltage is tied to 1
154      * @param sink the vertex whose voltage is tied to 0
155      */
156     public VoltageScorer(Hypergraph<V,E> g, V source, V sink)
157     {
158         this(g, Collections.singletonMap(source, 1.0), Collections.singletonList(sink));
159         initialize();
160     }
161 
162     
163     /**
164      * Initializes the state of this instance.
165      */
166     @Override
167     public void initialize()
168     {
169         super.initialize();
170         
171         // sanity check
172         if (source_voltages.isEmpty() || sinks.isEmpty())
173             throw new IllegalArgumentException("Both sources and sinks (grounds) must be defined");
174         
175         if (source_voltages.size() + sinks.size() > graph.getVertexCount())
176             throw new IllegalArgumentException("Source/sink sets overlap, or contain vertices not in graph");
177         
178         for (Map.Entry<V, ? extends Number> entry : source_voltages.entrySet())
179         {
180             V v = entry.getKey();
181             if (sinks.contains(v))
182                 throw new IllegalArgumentException("Vertex " + v + " is incorrectly specified as both source and sink");
183             double value = entry.getValue().doubleValue();
184             if (value <= 0)
185                 throw new IllegalArgumentException("Source vertex " + v + " has negative voltage");
186         }
187         
188         // set up initial voltages
189         for (V v : graph.getVertices())
190         {
191             if (source_voltages.containsKey(v))
192                 setOutputValue(v, source_voltages.get(v).doubleValue());
193             else
194                 setOutputValue(v, 0.0);
195         }
196     }
197     
198     /**
199      * @see edu.uci.ics.jung.algorithms.scoring.AbstractIterativeScorer#update(Object)
200      */
201     @Override
202     public double update(V v)
203     {
204         // if it's a voltage source or sink, we're done
205         Number source_volts = source_voltages.get(v);
206         if (source_volts != null) 
207         {
208             setOutputValue(v, source_volts.doubleValue());
209             return 0.0;
210         }
211         if (sinks.contains(v))
212         {
213             setOutputValue(v, 0.0);
214             return 0.0;
215         }
216         
217         Collection<E> edges = graph.getInEdges(v);
218         double voltage_sum = 0;
219         double weight_sum = 0;
220         for (E e: edges)
221         {
222         	int incident_count = getAdjustedIncidentCount(e);
223         	for (V w : graph.getIncidentVertices(e)) 
224         	{
225         		if (!w.equals(v) || hyperedges_are_self_loops) 
226         		{
227         			double weight = getEdgeWeight(w,e).doubleValue() / incident_count;
228         			voltage_sum += getCurrentValue(w).doubleValue() * weight;
229         			weight_sum += weight;
230         		}
231         	}
232 //            V w = graph.getOpposite(v, e);
233 //            double weight = getEdgeWeight(w,e).doubleValue();
234 //            voltage_sum += getCurrentValue(w).doubleValue() * weight;
235 //            weight_sum += weight;
236         }
237 
238         // if either is 0, new value is 0
239         if (voltage_sum == 0 || weight_sum == 0)
240         {
241             setOutputValue(v, 0.0);
242             return getCurrentValue(v).doubleValue();
243         }
244         
245         setOutputValue(v, voltage_sum / weight_sum);
246         return Math.abs(getCurrentValue(v).doubleValue() - voltage_sum / weight_sum);
247     }
248 
249 }
250