View Javadoc
1   /*
2   * Copyright (c) 2003, 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   */
10  package edu.uci.ics.jung.algorithms.layout;
11  
12  import java.awt.geom.Point2D;
13  import java.util.ArrayList;
14  import java.util.Collection;
15  import java.util.ConcurrentModificationException;
16  import java.util.List;
17  
18  import com.google.common.cache.CacheBuilder;
19  import com.google.common.cache.CacheLoader;
20  import com.google.common.cache.LoadingCache;
21  
22  import edu.uci.ics.jung.algorithms.layout.util.RandomLocationTransformer;
23  import edu.uci.ics.jung.algorithms.util.IterativeContext;
24  import edu.uci.ics.jung.graph.Graph;
25  
26  /**
27   * Implements a self-organizing map layout algorithm, based on Meyer's
28   * self-organizing graph methods.
29   *
30   * @author Yan Biao Boey
31   */
32  public class ISOMLayout<V, E> extends AbstractLayout<V,E> implements IterativeContext {
33  
34      protected LoadingCache<V, ISOMVertexData> isomVertexData =
35      	CacheBuilder.newBuilder().build(new CacheLoader<V, ISOMVertexData>() {
36  	    	public ISOMVertexData load(V vertex) {
37  	    		return new ISOMVertexData();
38  	    	}
39      });
40  
41  	private int maxEpoch;
42  	private int epoch;
43  
44  	private int radiusConstantTime;
45  	private int radius;
46  	private int minRadius;
47  
48  	private double adaption;
49  	private double initialAdaption;
50  	private double minAdaption;
51  
52      protected GraphElementAccessor<V,E> elementAccessor =
53      	new RadiusGraphElementAccessor<V,E>();
54  
55  	private double coolingFactor;
56  
57  	private List<V> queue = new ArrayList<V>();
58  	private String status = null;
59  
60  	/**
61  	 * @return the current number of epochs and execution status, as a string.
62  	 */
63  	public String getStatus() {
64  		return status;
65  	}
66  
67  	public ISOMLayout(Graph<V,E> g) {
68  		super(g);
69  	}
70  
71  	public void initialize() {
72  
73  		setInitializer(new RandomLocationTransformer<V>(getSize()));
74  		maxEpoch = 2000;
75  		epoch = 1;
76  
77  		radiusConstantTime = 100;
78  		radius = 5;
79  		minRadius = 1;
80  
81  		initialAdaption = 90.0D / 100.0D;
82  		adaption = initialAdaption;
83  		minAdaption = 0;
84  
85  		//factor = 0; //Will be set later on
86  		coolingFactor = 2;
87  
88  		//temperature = 0.03;
89  		//initialJumpRadius = 100;
90  		//jumpRadius = initialJumpRadius;
91  
92  		//delay = 100;
93  	}
94  
95  
96  	/**
97  	* Advances the current positions of the graph elements.
98  	*/
99  	public void step() {
100 		status = "epoch: " + epoch + "; ";
101 		if (epoch < maxEpoch) {
102 			adjust();
103 			updateParameters();
104 			status += " status: running";
105 		} else {
106 			status += "adaption: " + adaption + "; ";
107 			status += "status: done";
108 //			done = true;
109 		}
110 	}
111 
112 	private synchronized void adjust() {
113 		//Generate random position in graph space
114 		Point2D tempXYD = new Point2D.Double();
115 
116 		// creates a new XY data location
117         tempXYD.setLocation(10 + Math.random() * getSize().getWidth(),
118                 10 + Math.random() * getSize().getHeight());
119 
120 		//Get closest vertex to random position
121 		V winner = elementAccessor.getVertex(this, tempXYD.getX(), tempXYD.getY());
122 
123 		while(true) {
124 		    try {
125 		    	for(V v : getGraph().getVertices()) {
126 		            ISOMVertexData ivd = getISOMVertexData(v);
127 		            ivd.distance = 0;
128 		            ivd.visited = false;
129 		        }
130 		        break;
131 		    } catch(ConcurrentModificationException cme) {}
132         }
133 		adjustVertex(winner, tempXYD);
134 	}
135 
136 	private synchronized void updateParameters() {
137 		epoch++;
138 		double factor = Math.exp(-1 * coolingFactor * (1.0 * epoch / maxEpoch));
139 		adaption = Math.max(minAdaption, factor * initialAdaption);
140 		//jumpRadius = (int) factor * jumpRadius;
141 		//temperature = factor * temperature;
142 		if ((radius > minRadius) && (epoch % radiusConstantTime == 0)) {
143 			radius--;
144 		}
145 	}
146 
147 	private synchronized void adjustVertex(V v, Point2D tempXYD) {
148 		queue.clear();
149 		ISOMVertexData ivd = getISOMVertexData(v);
150 		ivd.distance = 0;
151 		ivd.visited = true;
152 		queue.add(v);
153 		V current;
154 
155 		while (!queue.isEmpty()) {
156 			current = queue.remove(0);
157 			ISOMVertexData currData = getISOMVertexData(current);
158 			Point2D currXYData = apply(current);
159 
160 			double dx = tempXYD.getX() - currXYData.getX();
161 			double dy = tempXYD.getY() - currXYData.getY();
162 			double factor = adaption / Math.pow(2, currData.distance);
163 
164 			currXYData.setLocation(currXYData.getX()+(factor*dx), currXYData.getY()+(factor*dy));
165 
166 			if (currData.distance < radius) {
167 			    Collection<V> s = getGraph().getNeighbors(current);
168 			    while(true) {
169 			        try {
170 			        	for(V child : s) {
171 			                ISOMVertexData childData = getISOMVertexData(child);
172 			                if (childData != null && !childData.visited) {
173 			                    childData.visited = true;
174 			                    childData.distance = currData.distance + 1;
175 			                    queue.add(child);
176 			                }
177 			            }
178 			            break;
179 			        } catch(ConcurrentModificationException cme) {}
180 			    }
181 			}
182 		}
183 	}
184 
185 	protected ISOMVertexData getISOMVertexData(V v) {
186 		return isomVertexData.getUnchecked(v);
187 	}
188 
189 	/**
190 	 * This one is an incremental visualization.
191 	 * @return <code>true</code> is the layout algorithm is incremental, <code>false</code> otherwise
192 	 */
193 	public boolean isIncremental() {
194 		return true;
195 	}
196 
197 	/**
198 	 * Returns <code>true</code> if the vertex positions are no longer being
199 	 * updated.  Currently <code>ISOMLayout</code> stops updating vertex
200 	 * positions after a certain number of iterations have taken place.
201 	 * @return <code>true</code> if the vertex position updates have stopped,
202 	 * <code>false</code> otherwise
203 	 */
204 	public boolean done() {
205 		return epoch >= maxEpoch;
206 	}
207 
208 	protected static class ISOMVertexData {
209 		int distance;
210 		boolean visited;
211 
212 		protected ISOMVertexData() {
213 		    distance = 0;
214 		    visited = false;
215 		}
216 	}
217 
218 	/**
219 	 * Resets the layout iteration count to 0, which allows the layout algorithm to
220 	 * continue updating vertex positions.
221 	 */
222 	public void reset() {
223 		epoch = 0;
224 	}
225 }