001    // Copyright 2012, 2013 Brad Block, Pawjaw, LLC. (an Ohio Limited Liability Company)
002    // 
003    // This file is part of JFPPR. 
004    // 
005    // JFPPR is free software: you can redistribute it and/or modify
006    // it under the terms of the GNU General Public License as published by
007    // the Free Software Foundation, either version 3 of the License, or
008    // (at your option) any later version.
009    // 
010    // JFPPR is distributed in the hope that it will be useful,
011    // but WITHOUT ANY WARRANTY; without even the implied warranty of
012    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013    // GNU General Public License for more details.
014    // 
015    // You should have received a copy of the GNU General Public License
016    // along with JFPPR.  If not, see <http://www.gnu.org/licenses/>.
017    
018    package com.pawjaw.graph.fppr;
019    
020    import com.pawjaw.graph.fppr.Graph.Step;
021    import java.util.Random;
022    
023    /**
024     * Each element in the PageRank {@link Graph} is represented by an instance of
025     * this class.  Vertex elements are by the Graph instance when the Graph is
026     * instantiated.  The number of Vertex elements is specified when the Graph is
027     * instantiated.  Vertex elements are labeled by contiguous increasing integer ids
028     * starting at zero.  The process for adding outgoing edges from a Vertex is by
029     * retrieving the source Vertex from the Graph instance, then specifying a
030     * destination Vertex also retrieved from the Graph instance as well as a
031     * non-negative weight.
032     *
033     * @see Graph
034     */
035    public final class Vertex implements Comparable<Vertex> {
036        /**
037         * The id of this vertex.
038         */
039        public final int id;
040    
041        private int visits = 0;
042        private float outgoing_edge_weight_sum = 0;
043        private Segment next_segment = null;
044        private Segment last_segment = null;
045        private Segment first_segment = null;
046        private OutgoingEdge last_outgoing_edge = null;
047        private OutgoingEdge first_outgoing_edge = null;
048    
049        private static final Random R = new Random();
050    
051        protected Vertex(int id) {
052            this.id = id;
053        }
054    
055        /**
056         * Add a directed outgoing edge from this Vertex to a destination Vertex.
057         * Use the {@link Graph#vertex} method to retrieve source and destination
058         * Vertex elements.
059         * <p>Here is an example of creating a Graph with 10 Vertex
060         * elements and adding an outgoing edge from Vertex with vertex id: 7 to
061         * Vertex with vertex id: 2 with transition weight 0.5:
062         * <p><code>
063         * Graph g = new Graph(10);<br>
064         * Vertex source = g.vertex(7);<br>
065         * Vertex destination = g.vertex(2);<br>
066         * source.addOutgoingEdge(destination, 0.5);
067         * </code>
068         *
069         * @param destination the destination of this outgoing edge
070         * @param weight a non-negative transition weight for sampling walk steps
071         *
072         * @see Graph#vertex(int)
073         */
074        public void addOutgoingEdge(Vertex destination, float weight) {
075            outgoing_edge_weight_sum += weight;
076            if(first_outgoing_edge == null)
077                first_outgoing_edge = last_outgoing_edge = new OutgoingEdge(destination, weight);
078            else
079                last_outgoing_edge = last_outgoing_edge.next_outgoing_edge = new OutgoingEdge(destination, weight);
080        }
081    
082        protected void addSegment(Step step) {
083            if(first_segment == null)
084                next_segment = first_segment = last_segment = new Segment(step);
085            else
086                last_segment = last_segment.next_segment = new Segment(step);
087        }
088    
089        protected void reset() {
090            visits = 0;
091            next_segment = first_segment;
092        }
093    
094        protected void visit() {
095            visits++;
096        }
097    
098        protected int visits() {
099            return visits;
100        }
101    
102        protected Step nextSegmentStart() {
103            Segment _next_segment;
104            if(next_segment == null)
105                return null;
106            _next_segment = next_segment;
107            next_segment = next_segment.next_segment;
108            return _next_segment.segment_start;
109        }
110    
111        protected Vertex sampleNeighbor() {
112            float r = R.nextFloat() * outgoing_edge_weight_sum;
113            OutgoingEdge next_outgoing_edge = first_outgoing_edge;
114            while(next_outgoing_edge != null)
115                if((r -= next_outgoing_edge.weight) <= 0)
116                    return next_outgoing_edge.destination;
117                else
118                    next_outgoing_edge = next_outgoing_edge.next_outgoing_edge;
119            // if no neighbors, treat as if exists single loop edge back
120            return this;
121        }
122    
123        public int compareTo(Vertex o) {
124            return visits < o.visits ? 1 : -1;
125        }
126    
127        private static class OutgoingEdge {
128            float weight;
129            Vertex destination;
130            OutgoingEdge next_outgoing_edge = null;
131    
132            OutgoingEdge(Vertex destination, float weight) {
133                this.destination = destination;
134                this.weight = Math.max(0, weight);
135            }
136        }
137    
138        private static class Segment {
139            Step segment_start;
140            Segment next_segment = null;
141    
142            Segment(Step step) {
143                this.segment_start = step;
144            }
145        }
146    }