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.randomWalk;
033
034import java.util.ArrayList;
035import java.util.Collections;
036import java.util.Iterator;
037import java.util.Comparator;
038import java.util.Random;
039
040import org.graphstream.algorithm.DynamicAlgorithm;
041import org.graphstream.graph.Edge;
042import org.graphstream.graph.Graph;
043import org.graphstream.graph.Node;
044import org.graphstream.stream.SinkAdapter;
045
046import static org.graphstream.algorithm.Toolkit.*;
047
048/**
049 * A random walk on a graph.
050 * 
051 * <h3>Idea</h3>
052 * 
053 * <p>
054 * This algorithm create a given number of entities first associated with random
055 * nodes in the graph. Then by turns, each entity chooses an edge at random and
056 * crosses it. This is iterated a given number of turns. Each time an entity
057 * crosses an edge, a count is incremented on it and each time it arrives on
058 * a node a count is counted on it.
059 * </p>
060 * 
061 * <p>
062 * You can override the entity class to provide your own behaviour for entity
063 * movement.
064 * </p>
065 *
066 * <h3>Counts on edges and nodes</h3>
067 * 
068 * <p>
069 * If the algorithm was run for an infinite number of turns, each counter would
070 * have the same value. However we can choose to stop the algorithm when needed.
071 * Furthermore the algorithm can be biased by providing each entity with a
072 * memory of the already crossed edges. It can avoid these edges when choosing
073 * at random its next edge. 
074 * </p>
075 * 
076 * <p>
077 * When an entity has no edge to choose (either because of its memory or because
078 * it reached a node that is only reachable via a one directed edge), the entity
079 * will jump randomly on another node.
080 * </p>
081 * 
082 * <p>
083 * When the number of turns
084 * awaited is reached, one can observe the counts on each edge and node. Edges
085 * and nodes that are very attractive in terms of topology should have a more
086 * important count than others.
087 * </p>
088 * 
089 * <p>
090 * This algorithm does not cope well with dynamic graphs. You can however improve
091 * this by using evaporation. When evaporation is activated, at each turn, the
092 * node and edge counts are multiplied by a number between 0 and 1. Therefore each
093 * edge or node count must be constantly updated by entities leading to a value that
094 * stabilizes in time.
095 * </p>
096 * 
097 * <h3>The basic tabu entity</h3>
098 * 
099 * <p>
100 * At each step, the default entities move from their current node to another via
101 * an edge randomly chosen. This is done in the {@link Entity#step()} method.
102 * </p>
103 * 
104 * <p>
105 * This method makes a list of all leaving edges of the current node. If the
106 * node has no leaving edge, the entity jumps to another randomly chosen node.
107 * Then an edge is chosen at random in the list of leaving edges. The edge is
108 * chosen uniformly if there are no weights on the edges, else, an edge with
109 * an higher weight has more chances to be chosen than an edge with a lower
110 * weight.
111 * </p>
112 * 
113 * <p>
114 * When crossed, if the memory is larger than 0, the edge crossed is remembered
115 * so that the entity will not choose it anew until it crosses as many edges as
116 * the memory size.
117 * </p>
118 *
119 * <h2>Usage</h2>
120 * 
121 * <p>
122 * With the default entities, you can make a node entirely tabu by putting the
123 * ``tabu`` attribute on it. No entity will traverse an edge that leads
124 * to such a node.
125 * </p>
126 *
127 * <p>
128 * You can change the default entity class either by overriding the
129 * {@link #createEntity()} method or by changing the entity class name
130 * using {@link #setEntityClass(String)}.
131 * </p>
132 * 
133 * <p>
134 * If the edges have weights, the entities can use them to favour edges
135 * with higher weights when randomly choosing them. By default the
136 * weights are searched on edges using the ``weight`` attribute. However
137 * you can override this using {@link #setWeightAttribute(String)} method.
138 * </p>
139 *
140 * <p>
141 * If you choose to have evaporation on edge counts at each turn, you can
142 * set it using {@link #setEvaporation(double)}. The evaporation is a number
143 * between 0 and 1. If set to 1 (the default), the counts are not modified,
144 * else the counts are multiplied by the evaporation at each turn.
145 * </p>
146 * 
147 * <p>
148 * To compute a turn, use the {@link #compute()} method. This will move each
149 * entity from one node to another.
150 * </p>
151 *
152 * <p>
153 * Once computed each edge and node will have an attribute ``passes`` stored
154 * on it containing the number of passage of an entity. You can change the
155 * name of this attribute using {@link #setPassesAttribute(String)}. After
156 * each computation of a turn, you can obtain the edge and nodes counts using
157 * either the passes attribute, or the utility methods {@link #getPasses(Node)}
158 * and {@link #getPasses(Edge)}.
159 * </p>
160 * 
161 * <p>
162 * You can count only the passes on the nodes or edges using the two methods
163 * {@link #computeEdgesPasses(boolean)} and {@link #computeNodePasses(boolean)}.
164 * </p>
165 * 
166 * <p>
167 * As some entities may have jumped from their node to another one chosen
168 * randomly, you can obtain the number of entities that jumped using
169 * {@link #getJumpCount()}. 
170 * </p>
171 * 
172 * <h2>Complexity</h2>
173 * 
174 * The complexity, at each turn is O(n) with n the number of entities.
175 * 
176 * <h2>Example</h2>
177 * 
178 * Here is how to compute a simple pass count for 1000 steps:
179 * 
180 * <pre>
181 * Graph graph = new MultiGraph("random walk");
182 * RandomWalk rwalk = new RandomWalk();
183 * // Populate the graph.
184 * rwalk.setEntityCount(graph.getNodeCount()/2);
185 * rwalk.init(graph);
186 * for(int i=0; i<1000; i++) {
187 *              rwalk.compute();
188 * }
189 * rwalk.terminate();
190 * for(Edge edge: graph.getEachEdge()) {
191 *              System.out.println("Edge %s counts %f%n", edge.getId(), rwalk.getPasses(edge));
192 * }
193 * </pre>
194 */
195public class RandomWalk extends SinkAdapter implements DynamicAlgorithm {
196        // Attribute
197        
198        public class Context {
199                /**
200                 * The graph.
201                 */
202                protected Graph graph;
203                
204                /**
205                 * The name of the attribute used to count the number of pass on an edge.
206                 */
207                protected String passesAttribute = "passes";
208
209                /**
210                 * The name of the attribute on edges that give their respective importance.
211                 */
212                protected String weightAttribute = null;
213
214                /**
215                 * Random number generator.
216                 */
217                protected Random random;
218
219                /**
220                 * The node tabu list.
221                 */
222                protected int entityMemory = 0;
223
224                /**
225                 * Number of entities that jump at each step.
226                 */
227                protected int jumpCount = 0, goCount = 0, waitCount = 0;
228                
229                public String getPassesAttribute() {
230                        return passesAttribute;
231                }
232                
233                public String getWeightAttribute() {
234                        return weightAttribute;
235                }
236                
237                public Random getRandom() {
238                        return random;
239                }
240                
241                public int getEntityMemory() {
242                        return entityMemory;
243                }
244        }
245
246        /**
247         * The informations shared between this class an entities.
248         */
249        protected Context context = new Context();
250
251        /**
252         * The entity class to use.
253         */
254        protected String entityClass = TabuEntity.class.getName();// "org.graphstream.algorithm.RandomWalk#TabuEntity";
255        
256        /**
257         * The set of entities travelling on the graph.
258         */
259        protected ArrayList<Entity> entities = new ArrayList<Entity>();
260
261        /**
262         * The random seed.
263         */
264        protected long randomSeed;
265
266        /**
267         * Initial count of entities.
268         */
269        protected int entityCount = 100;
270
271        /**
272         * Allow to reduce the amount counted on each edge at each turn. At each turn
273         * the edges counts are multiplied by the evaporation.
274         */
275        protected double evaporation = 1;
276
277        /**
278         * Compute counts on nodes.
279         */
280        protected boolean doNodes = true;
281
282        /**
283         * Compute counts on edges.
284         */
285        protected boolean doEdges = true;
286
287        // Constructor
288
289        /**
290         * New random walk with a new random seed (based on time), with an entity
291         * memory set to 10 nodes (tabu list), with an attributes to store passes
292         * named "passes" and no weight attribute.
293         */
294        public RandomWalk() {
295                this(System.currentTimeMillis());
296        }
297
298        /**
299         * New random walk with a given random seed, with an entity memory set to 10
300         * nodes (tabu list), with an attributes to store passes named "passes" and
301         * no weight attribute.
302         * 
303         * @param randomSeed
304         *            The random seed.
305         */
306        public RandomWalk(long randomSeed) {
307                this.randomSeed = randomSeed;
308                this.context.random = new Random(randomSeed);
309        }
310
311        /**
312         * The name of the attribute where the number of entities passes are stored
313         * (for edges and nodes).
314         * 
315         * @return A string representing the attribute name for entity passes.
316         */
317        public String getPassesAttribute() {
318                return context.passesAttribute;
319        }
320
321        /**
322         * Set the name of the entity class to use. If set to null, the default entity
323         * class will be used (RandomWalk.TabuEntity).
324         * @param name The name of the entity class to use.
325         */
326        public void setEntityClass(String name) {
327                if(name == null) {
328                        entityClass = TabuEntity.class.getName();//"org.graphstream.algorithm.RandomWalk#TabuEntity";
329                } else {
330                        entityClass = name;
331                }
332        }
333        
334        /**
335         * Set the entity memory in number of nodes remembered. This memory is used
336         * as a tabu list, that is a set of nodes not to cross.
337         * 
338         * @param size
339         *            The memory size, 0 is a valid size to disable the tabu list.
340         */
341        public void setEntityMemory(int size) {
342                if (size < 0)
343                        size = 0;
344
345                context.entityMemory = size;
346        }
347        
348        /**
349         * Set the evaporation of edge counts. This is a number between 0 and 1. If less
350         * than 1, at each turn, each edge count is multiplied by this factor. The use
351         * of evaporation allows to stabilize the counts.
352         * @param evaporation A number between 0 and 1.
353         */
354        public void setEvaporation(double evaporation) {
355                if(evaporation>=0 && evaporation<1) {
356                        this.evaporation = evaporation;
357                }
358        }
359        
360        /**
361         * The evaporation value.
362         * @return The evaporation.
363         */
364        public double getEvaporation() {
365                return evaporation;
366        }
367
368        /**
369         * The random seed used.
370         * 
371         * @return A long integer containing the random seed.
372         */
373        public long getRandomSeed() {
374                return randomSeed;
375        }
376
377        /**
378         * Number of entities.
379         * 
380         * @return The number of entities.
381         */
382        public int getEntityCount() {
383                return entities.size();
384        }
385
386        /**
387         * Number of entities that jumped instead of traversing an edge at last
388         * step. An entity executes a jump when it is blocked in a dead end (either
389         * a real one, or because of its tabu list).
390         * 
391         * @return The jump count.
392         */
393        public int getJumpCount() {
394                return context.jumpCount;
395        }
396
397        public int getWaitCount() {
398                return context.waitCount;
399        }
400
401        public int getGoCount() {
402                return context.goCount;
403        }
404
405        /**
406         * Ratio of entities that executed a jump instead of traversing an edge at
407         * last step. An entity executes a jump when it is blocked in a dead end
408         * (either a real one, or because of its tabu list).
409         * 
410         * @return The jump ratio (in [0-1]).
411         */
412        public double getJumpRatio() {
413                return (((double) context.jumpCount) / ((double) entities.size()));
414        }
415
416        /**
417         * The name of the attribute used to fetch edges importance.
418         * 
419         * @param name
420         *            A string giving the weight name.
421         */
422        public void setWeightAttribute(String name) {
423                context.weightAttribute = name;
424        }
425
426        /**
427         * Set the name of the attribute used to store the number of passes of each
428         * entity on each edge or node.
429         * 
430         * @param name
431         *            A string giving the passes name.
432         */
433        public void setPassesAttribute(String name) {
434                if (context.graph != null) {
435                        for (Edge e : context.graph.getEachEdge()) {
436                                e.addAttribute(name, e.getNumber(context.passesAttribute));
437                                e.removeAttribute(context.passesAttribute);
438                        }
439
440                        for (Node n : context.graph) {
441                                n.addAttribute(name, n.getNumber(context.passesAttribute));
442                                n.removeAttribute(context.passesAttribute);
443                        }
444                }
445
446                context.passesAttribute = name;
447        }
448        
449        /**
450         * The number of entity passage on the given edge.
451         * @param edge The edge to look at.
452         * @return The number of passes on the edge.
453         */
454        public double getPasses(Edge edge) {
455                return edge.getNumber(context.passesAttribute);
456        }
457
458        /**
459         * The number of entity passage on the given node.
460         * @param node The node to look at.
461         * @return The number of passes on the node.
462         */
463        public double getPasses(Node node) {
464                return node.getNumber(context.passesAttribute);
465        }
466
467        /**
468         * Set the number of entities which will be created at the algorithm
469         * initialization.
470         * 
471         * @param entityCount
472         */
473        public void setEntityCount(int entityCount) {
474                this.entityCount = entityCount;
475        }
476
477        /**
478         * Activate or not the counts on edges when entities cross thems.
479         * @param on If true (the default) the edges passes are counted.
480         */
481        public void computeEdgesPasses(boolean on) {
482                doEdges = on;
483        }
484
485        /**
486         * Activate or not the counts on nodes when entities cross thems.
487         * @param on If true (the default) the nodes passes are counted.
488         */
489        public void computeNodePasses(boolean on) {
490                doNodes = on;
491        }
492        
493        /**
494         * Create an entity. Override this method to create different kinds of
495         * entities or change the entity class name. The default one is the "TabuEntity".
496         * 
497         * @return The new entity.
498         * @see #setEntityClass(String)
499         */
500        public Entity createEntity() {
501                try {
502                        Object o = Class.forName(entityClass).newInstance();
503                        if(o instanceof Entity) {
504                                Entity e = (Entity) o;
505                                e.init(context, randomNode(context.graph, context.random));
506                                
507                                return e;
508                        } else {
509                                System.err.printf("Object %s  pointed at by class name '%s' does not implement Entity.%n", o.getClass().getName(), entityClass);
510                        }
511                } catch (Exception e) {
512                        System.err.printf("Error: %s%n", e.getMessage());
513                        e.printStackTrace();
514                }
515                
516                return null;
517        }
518
519        /**
520         * Initialize the algorithm for a given graph with a given entity count. The
521         * entities are created at random locations on the graph.
522         * 
523         * @param graph
524         *            The graph to explore.
525         */
526        public void init(Graph graph) {
527                if (context.graph != null)
528                        throw new RuntimeException(
529                                        "cannot begin a random walk if the previous one was not finished, use end().");
530
531                context.graph = graph;
532
533                entities.clear();
534
535                for (int i = 0; i < entityCount; i++)
536                        entities.add(createEntity());
537
538                equipGraph();
539                graph.addElementSink(this);
540        }
541
542        /**
543         * Execute one step of the algorithm. During one step, each entity choose a
544         * next edge to cross, toward a new node. The passes attribute of these edge
545         * and node are updated.
546         */
547        public void compute() {
548                context.jumpCount = 0;
549                context.goCount = 0;
550                context.waitCount = 0;
551
552                for (Entity entity : entities) {
553                        entity.step();
554                }
555
556                if(evaporation<1)
557                        evaporate();
558        }
559        
560        /**
561         * Apply evaporation on each edge.
562         */
563        protected void evaporate() {
564                for(Edge edge: context.graph.getEachEdge()) {
565                        edge.setAttribute(context.passesAttribute, edge.getNumber(context.passesAttribute)*evaporation);
566                }
567                for(Node node: context.graph) {
568                        node.setAttribute(context.passesAttribute, node.getNumber(context.passesAttribute)*evaporation);
569                }
570        }
571
572        /**
573         * End the algorithm by removing any listener on the graph and releasing
574         * memory.
575         */
576        public void terminate() {
577                entities.clear();
578                context.graph.removeElementSink(this);
579                context.graph = null;
580        }
581
582        protected void equipGraph() {
583                for (Edge e : context.graph.getEachEdge()) {
584                        e.addAttribute(context.passesAttribute, 0.0);
585                }
586
587                for (Node n : context.graph) {
588                        n.addAttribute(context.passesAttribute, 0.0);
589                }
590        }
591
592        /**
593         * Sort all edges by their "passes" attribute and return the array of sorted
594         * edges.
595         * 
596         * @return An array with all edges of the graph sorted by their number of
597         *         entity pass.
598         */
599        public ArrayList<Edge> findTheMostUsedEdges() {
600                ArrayList<Edge> edges = new ArrayList<Edge>(context.graph.getEdgeCount());
601                Iterator<? extends Edge> i = context.graph.getEdgeIterator();
602
603                while (i.hasNext()) {
604                        edges.add(i.next());
605                }
606
607                Collections.sort(edges, new Comparator<Edge>() {
608                        public int compare(Edge e1, Edge e2) {
609                                int n1 = (int) e1.getNumber(context.passesAttribute);
610                                int n2 = (int) e2.getNumber(context.passesAttribute);
611
612                                return (n1 - n2);
613                        }
614                });
615
616                return edges;
617        }
618
619        /**
620         * Sort all nodes by their "passes" attribute and return the array of sorted
621         * nodes.
622         * 
623         * @return An array with all nodes of the graph sorted by their number of
624         *         entity pass.
625         */
626        public ArrayList<Node> findTheMostUsedNodes() {
627                ArrayList<Node> nodes = new ArrayList<Node>(context.graph.getNodeCount());
628                Iterator<? extends Node> i = context.graph.getNodeIterator();
629
630                while (i.hasNext()) {
631                        nodes.add(i.next());
632                }
633
634                Collections.sort(nodes, new Comparator<Node>() {
635                        public int compare(Node e1, Node e2) {
636                                int n1 = (int) e1.getNumber(context.passesAttribute);
637                                int n2 = (int) e2.getNumber(context.passesAttribute);
638
639                                return (n1 - n2);
640                        }
641                });
642
643                return nodes;
644        }
645        // Graph listener
646
647        @Override
648        public void edgeAdded(String graphId, long timeId, String edgeId,
649                        String fromNodeId, String toNodeId, boolean directed) {
650                Edge edge = context.graph.getEdge(edgeId);
651
652                if (edge != null)
653                        edge.addAttribute(context.passesAttribute, 0.0);
654        }
655
656        @Override
657        public void nodeAdded(String graphId, long timeId, String nodeId) {
658                Node node = context.graph.getNode(nodeId);
659
660                node.addAttribute(context.passesAttribute, 0.0);
661        }
662}
663
664
665// Nested classes
666/*
667public class TabuTimedEntity extends TabuEntity {
668        protected static final float SPEED = 1000;
669
670        protected double crossing = 0;
671
672        @Override
673        public void init(Node start) {
674                super.init(start);
675        }
676
677        @Override
678        public void step() {
679                if (crossing > 0) {
680                        waitCount++;
681                        crossing -= SPEED;
682                } else {
683                        goCount++;
684                        tabuStep();
685                }
686        }
687
688        @Override
689        protected void jump() {
690                super.jump();
691                crossing = 0;
692        }
693
694        @Override
695        protected void cross(Edge edge) {
696                super.cross(edge);
697
698                float speed = 1f;
699
700                if (edge.hasLabel("SPEED_CAT")) {
701                        String s = (String) edge.getLabel("SPEED_CAT");
702                        speed = 9 - Float.parseFloat(s);
703                }
704                if (edge.hasLabel("LANE_CAT")) {
705                        String s = (String) edge.getLabel("LANE_CAT");
706                        speed *= Float.parseFloat(s);
707                }
708
709                if (speed <= 0)
710                        speed = 1f;
711
712                crossing = edgeLength(edge) / speed;
713        }
714}
715*/