001/* 002 * Copyright 2006 - 2013 003 * Stefan Balev <stefan.balev@graphstream-project.org> 004 * Julien Baudry <julien.baudry@graphstream-project.org> 005 * Antoine Dutot <antoine.dutot@graphstream-project.org> 006 * Yoann Pigné <yoann.pigne@graphstream-project.org> 007 * Guilhelm Savin <guilhelm.savin@graphstream-project.org> 008 * 009 * This file is part of GraphStream <http://graphstream-project.org>. 010 * 011 * GraphStream is a library whose purpose is to handle static or dynamic 012 * graph, create them from scratch, file or any source and display them. 013 * 014 * This program is free software distributed under the terms of two licenses, the 015 * CeCILL-C license that fits European law, and the GNU Lesser General Public 016 * License. You can use, modify and/ or redistribute the software under the terms 017 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following 018 * URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by 019 * the Free Software Foundation, either version 3 of the License, or (at your 020 * option) any later version. 021 * 022 * This program is distributed in the hope that it will be useful, but WITHOUT ANY 023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 024 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 025 * 026 * You should have received a copy of the GNU Lesser General Public License 027 * along with this program. If not, see <http://www.gnu.org/licenses/>. 028 * 029 * The fact that you are presently reading this means that you have had 030 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms. 031 */ 032package org.graphstream.algorithm.generator; 033 034import org.graphstream.graph.Node; 035import org.graphstream.stream.Pipe; 036 037/** 038 * Random Euclidean graph generator. 039 * 040 * <p> 041 * This generator creates random graphs of any size. Links of such graphs are 042 * created according to a threshold. If the Euclidean distance between two nodes 043 * is less than a given threshold, then a link is created between those 2 nodes. 044 * <p> 045 * 046 * <h2>Usage</h2> 047 * 048 * <p> 049 * Calling {@link #begin()} put one unique node in the graph, then 050 * {@link #nextEvents()} will add a new node each time it is called and connect 051 * this node to its neighbors according to the threshold planar Euclidean 052 * distance. 053 * </p> 054 * 055 * <p> 056 * This generator has the ability to add randomly chosen numerical values on 057 * arbitrary attributes on edges or nodes of the graph, and to randomly choose a 058 * direction for edges. 059 * </p> 060 * 061 * <p> 062 * A list of attributes can be given for nodes and edges. In this case each new 063 * node or edge added will have this attribute and the value will be a randomly 064 * chosen number. The range in which these numbers are chosen can be specified. 065 * </p> 066 * 067 * <p> 068 * By default, edges are not oriented. It is possible to ask orientation, in 069 * which case the direction is chosen randomly. 070 * </p> 071 * 072 * <p> 073 * By default, the graph is generated in the plane (2 dimensions) . Cartesian 074 * coordinates on nodes will be generated at random. So, each node will 075 * automatically be given two attributes: "x" and "y". If a dimension is 076 * specified, then |dimension| attributes are generated, and the 2-norm distance 077 * (<a href="http://en.wikipedia.org/wiki/Euclidean_distance">Euclidean 078 * distance</a>) is considered in that dimension between the nodes. 079 * </p> 080 * 081 * <p> 082 * If the dimension is 2, then attributes "x" and "y" are defined for each node. 083 * If dimension is 3, then attributes "x", "y" and "z" are used. For other 084 * values of dimension, |dimension| attributes are defined ("xi" with "i" \in 085 * |dimension|) . 086 * </p> 087 * 088 * <h2>Complexity</h2> 089 * 090 * For the construction of a n nodes graph, the complexity is about O(n^2). 091 * 092 * <h2>Example</h2> 093 * 094 * <pre> 095 * Graph graph = new SingleGraph("random euclidean"); 096 * Generator gen = new RandomEuclideanGenerator(); 097 * gen.addSink(graph); 098 * gen.begin(); 099 * for(int i=0; i<1000; i++) { 100 * gen.nextEvents(); 101 * } 102 * gen.end(); 103 * graph.display(false); 104 * </pre> 105 * 106 * @since June 25 2007 107 * @complexity For the construction of a n nodes graph, the complexity is about 108 * O(n^2). 109 */ 110public class RandomEuclideanGenerator extends BaseGenerator implements Pipe { 111 /** 112 * Used to generate node names. 113 */ 114 protected int nodeNames = 0; 115 116 /** 117 * The dimension of the space. 118 */ 119 protected int dimension = 2; 120 121 /** 122 * The threshold that defines whether or not a link is created between to 123 * nodes. Since the coordinate system is defined between 0 and 1, the 124 * threshold has to be set between these two bounds. 125 */ 126 protected double threshold = 0.1; 127 128 /** 129 * New random Euclidean graph generator. By default no attributes are added 130 * to nodes and edges. Dimension of the space is two. 131 */ 132 public RandomEuclideanGenerator() { 133 super(); 134 initDimension(2); 135 setUseInternalGraph(true); 136 } 137 138 /** 139 * New random Euclidean graph generator. By default no attributes are added 140 * to nodes and edges. You may also specify a dimension for the space. 141 * 142 * @param dimension 143 * The dimension of the space for the graph. By default it is 144 * two. 145 */ 146 public RandomEuclideanGenerator(int dimension) { 147 super(); 148 initDimension(dimension); 149 setUseInternalGraph(true); 150 } 151 152 /** 153 * New random Euclidean graph generator. By default no attributes are added 154 * to nodes and edges. It is possible to make edge randomly directed. You 155 * may also specify a dimension for the space. 156 * 157 * @param dimension 158 * The dimension of the space for the graph. By default it is 159 * two. 160 * @param directed 161 * If true the edges are directed. 162 * @param randomlyDirectedEdges 163 * If true edge, are directed and the direction is chosen at 164 * randomly. 165 */ 166 public RandomEuclideanGenerator(int dimension, boolean directed, 167 boolean randomlyDirectedEdges) { 168 super(directed, randomlyDirectedEdges); 169 initDimension(dimension); 170 setUseInternalGraph(true); 171 } 172 173 /** 174 * New random Euclidean graph generator. 175 * 176 * @param dimension 177 * The dimension of the space for the graph. By default it is 178 * two. 179 * @param directed 180 * If true the edges are directed. 181 * @param randomlyDirectedEdges 182 * It true, edges are directed and the direction is chosen at 183 * random. 184 * @param nodeAttribute 185 * put an attribute by that name on each node with a random 186 * numeric value. 187 * @param edgeAttribute 188 * put an attribute by that name on each edge with a random 189 * numeric value. 190 */ 191 public RandomEuclideanGenerator(int dimension, boolean directed, 192 boolean randomlyDirectedEdges, String nodeAttribute, 193 String edgeAttribute) { 194 super(directed, randomlyDirectedEdges, nodeAttribute, edgeAttribute); 195 initDimension(dimension); 196 setUseInternalGraph(true); 197 } 198 199 private void initDimension(int dimension) { 200 this.dimension = dimension; 201 super.setNodeAttributesRange(0f, 1f); 202 if (dimension > 0) { 203 if (dimension == 2) { 204 super.addNodeAttribute("x"); 205 super.addNodeAttribute("y"); 206 } else if (dimension == 3) { 207 super.addNodeAttribute("x"); 208 super.addNodeAttribute("y"); 209 super.addNodeAttribute("z"); 210 } else { 211 for (int i = 0; i < dimension; i++) 212 super.addNodeAttribute("x" + i); 213 } 214 } else 215 System.err.println("dimension has to be higher that zero"); 216 217 } 218 219 /** 220 * Start the generator. A single node is added. 221 * 222 * @see org.graphstream.algorithm.generator.Generator#begin() 223 */ 224 public void begin() { 225 String id = Integer.toString(nodeNames++); 226 227 addNode(id); 228 } 229 230 /** 231 * Step of the generator. Add a new node and connect it with some others. 232 * 233 * @see org.graphstream.algorithm.generator.Generator#nextEvents() 234 */ 235 public boolean nextEvents() { 236 String id = Integer.toString(nodeNames++); 237 238 addNode(id); 239 240 for (Node n : internalGraph.getEachNode()) { 241 if (!id.equals(n.getId()) && distance(id, n.getId()) < threshold) 242 addEdge(id + "-" + n.getId(), id, n.getId()); 243 } 244 245 return true; 246 } 247 248 /* 249 * (non-Javadoc) 250 * 251 * @see org.graphstream.algorithm.generator.Generator#end() 252 */ 253 @Override 254 public void end() { 255 super.end(); 256 } 257 258 /** 259 * Distance between two nodes. 260 * 261 * @param n1 262 * first node 263 * @param n2 264 * second node 265 * @return distance between n1 and n2 266 */ 267 private double distance(String n1, String n2) { 268 double d = 0.0; 269 270 if (dimension == 2) { 271 double x1 = internalGraph.getNode(n1).getNumber("x"); 272 double y1 = internalGraph.getNode(n1).getNumber("y"); 273 double x2 = internalGraph.getNode(n2).getNumber("x"); 274 double y2 = internalGraph.getNode(n2).getNumber("y"); 275 276 d = Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2); 277 } else if (dimension == 3) { 278 double x1 = internalGraph.getNode(n1).getNumber("x"); 279 double y1 = internalGraph.getNode(n1).getNumber("y"); 280 double x2 = internalGraph.getNode(n2).getNumber("x"); 281 double y2 = internalGraph.getNode(n2).getNumber("y"); 282 double z1 = internalGraph.getNode(n1).getNumber("z"); 283 double z2 = internalGraph.getNode(n2).getNumber("z"); 284 285 d = Math.pow(z1 - z2, 2) + Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2); 286 } else { 287 for (int i = 0; i < dimension; i++) { 288 double xi1 = internalGraph.getNode(n1).getNumber("x" + i); 289 double xi2 = internalGraph.getNode(n2).getNumber("x" + i); 290 291 d += Math.pow(xi1 - xi2, 2); 292 } 293 } 294 295 return Math.sqrt(d); 296 } 297 298 /** 299 * Set the threshold that defines whether or not a link is created between 300 * to notes. Since the coordinate system is defined between 0 and 1, the 301 * threshold has to be set between these two bounds. 302 * 303 * @param threshold 304 * The defined threshold. 305 */ 306 public void setThreshold(double threshold) { 307 if (threshold <= 1f && threshold >= 0f) 308 this.threshold = threshold; 309 } 310 311 protected void nodeAttributeHandling(String nodeId, String key, Object val) { 312 if (key != null && key.matches("x|y|z") && val instanceof Float) { 313 int i = ((int) key.charAt(0)) - (int) 'x'; 314 315 if (i < dimension) 316 internalGraph.getNode(nodeId).addAttribute(key, val); 317 } 318 } 319 320 public void edgeAttributeAdded(String sourceId, long timeId, String edgeId, 321 String attribute, Object value) { 322 } 323 324 public void edgeAttributeChanged(String sourceId, long timeId, 325 String edgeId, String attribute, Object oldValue, Object newValue) { 326 } 327 328 public void edgeAttributeRemoved(String sourceId, long timeId, 329 String edgeId, String attribute) { 330 } 331 332 public void graphAttributeAdded(String sourceId, long timeId, 333 String attribute, Object value) { 334 } 335 336 public void graphAttributeChanged(String sourceId, long timeId, 337 String attribute, Object oldValue, Object newValue) { 338 } 339 340 public void graphAttributeRemoved(String sourceId, long timeId, 341 String attribute) { 342 } 343 344 public void nodeAttributeAdded(String sourceId, long timeId, String nodeId, 345 String attribute, Object value) { 346 nodeAttributeHandling(nodeId, attribute, value); 347 } 348 349 public void nodeAttributeChanged(String sourceId, long timeId, 350 String nodeId, String attribute, Object oldValue, Object newValue) { 351 nodeAttributeHandling(nodeId, attribute, newValue); 352 } 353 354 public void nodeAttributeRemoved(String sourceId, long timeId, 355 String nodeId, String attribute) { 356 } 357 358 public void edgeAdded(String sourceId, long timeId, String edgeId, 359 String fromNodeId, String toNodeId, boolean directed) { 360 } 361 362 public void edgeRemoved(String sourceId, long timeId, String edgeId) { 363 } 364 365 public void graphCleared(String sourceId, long timeId) { 366 } 367 368 public void nodeAdded(String sourceId, long timeId, String nodeId) { 369 } 370 371 public void nodeRemoved(String sourceId, long timeId, String nodeId) { 372 } 373 374 public void stepBegins(String sourceId, long timeId, double step) { 375 } 376}