source: trunk/EventBenchCore/src/de/ugoe/cs/eventbench/models/Trie.java @ 399

Last change on this file since 399 was 399, checked in by sherbold, 12 years ago
  • adapted implementation of de.ugoe.cs.eventbench.models.Trie.getNumLeafAncestors() and de.ugoe.cs.eventbench.TrieNode?().getLeafAncestors() as the assumptions of these methods were invalidated by the new and more precise equals functions
File size: 11.0 KB
Line 
1package de.ugoe.cs.eventbench.models;
2
3import java.io.Serializable;
4import java.security.InvalidParameterException;
5import java.util.Collection;
6import java.util.HashSet;
7import java.util.LinkedHashSet;
8import java.util.LinkedList;
9import java.util.List;
10
11import de.ugoe.cs.util.StringTools;
12
13import edu.uci.ics.jung.graph.DelegateTree;
14import edu.uci.ics.jung.graph.Graph;
15import edu.uci.ics.jung.graph.Tree;
16
17/**
18 * <p>
19 * This class implements a <it>trie</it>, i.e., a tree of sequences that the
20 * occurence of subsequences up to a predefined length. This length is the trie
21 * order.
22 * </p>
23 *
24 * @author Steffen Herbold
25 *
26 * @param <T>
27 *            Type of the symbols that are stored in the trie.
28 *
29 * @see TrieNode
30 */
31public class Trie<T> implements IDotCompatible, Serializable {
32
33        /**
34         * <p>
35         * Id for object serialization.
36         * </p>
37         */
38        private static final long serialVersionUID = 1L;
39
40        /**
41         * <p>
42         * Collection of all symbols occuring in the trie.
43         * </p>
44         */
45        private Collection<T> knownSymbols;
46
47        /**
48         * <p>
49         * Reference to the root of the trie.
50         * </p>
51         */
52        private final TrieNode<T> rootNode;
53
54        /**
55         * <p>
56         * Contructor. Creates a new Trie.
57         * </p>
58         */
59        public Trie() {
60                rootNode = new TrieNode<T>();
61                knownSymbols = new LinkedHashSet<T>();
62        }
63
64        /**
65         * <p>
66         * Copy-Constructor. Creates a new Trie as the copy of other. The other trie
67         * must noch be null.
68         * </p>
69         *
70         * @param other
71         *            Trie that is copied
72         */
73        public Trie(Trie<T> other) {
74                if (other == null) {
75                        throw new InvalidParameterException("other trie must not be null");
76                }
77                rootNode = new TrieNode<T>(other.rootNode);
78                knownSymbols = new LinkedHashSet<T>(other.knownSymbols);
79        }
80
81        /**
82         * <p>
83         * Returns a collection of all symbols occuring in the trie.
84         * </p>
85         *
86         * @return symbols occuring in the trie
87         */
88        public Collection<T> getKnownSymbols() {
89                return new LinkedHashSet<T>(knownSymbols);
90        }
91
92        /**
93         * <p>
94         * Trains the current trie using the given sequence and adds all subsequence
95         * of length {@code maxOrder}.
96         * </p>
97         *
98         * @param sequence
99         *            sequence whose subsequences are added to the trie
100         * @param maxOrder
101         *            maximum length of the subsequences added to the trie
102         */
103        public void train(List<T> sequence, int maxOrder) {
104                if (maxOrder < 1) {
105                        return;
106                }
107                IncompleteMemory<T> latestActions = new IncompleteMemory<T>(maxOrder);
108                int i = 0;
109                for (T currentEvent : sequence) {
110                        latestActions.add(currentEvent);
111                        knownSymbols.add(currentEvent);
112                        i++;
113                        if (i >= maxOrder) {
114                                add(latestActions.getLast(maxOrder));
115                        }
116                }
117                int sequenceLength = sequence.size();
118                for (int j = maxOrder - 1; j > 0; j--) {
119                        add(sequence.subList(sequenceLength - j, sequenceLength));
120                }
121        }
122
123        /**
124         * <p>
125         * Adds a given subsequence to the trie and increases the counters
126         * accordingly.
127         * </p>
128         *
129         * @param subsequence
130         *            subsequence whose counters are increased
131         * @see TrieNode#add(List)
132         */
133        protected void add(List<T> subsequence) {
134                if (subsequence != null && !subsequence.isEmpty()) {
135                        knownSymbols.addAll(subsequence);
136                        subsequence = new LinkedList<T>(subsequence); // defensive copy!
137                        T firstSymbol = subsequence.get(0);
138                        TrieNode<T> node = getChildCreate(firstSymbol);
139                        node.add(subsequence);
140                }
141        }
142
143        /**
144         * <p>
145         * Returns the child of the root node associated with the given symbol or
146         * creates it if it does not exist yet.
147         * </p>
148         *
149         * @param symbol
150         *            symbol whose node is required
151         * @return node associated with the symbol
152         * @see TrieNode#getChildCreate(Object)
153         */
154        protected TrieNode<T> getChildCreate(T symbol) {
155                return rootNode.getChildCreate(symbol);
156        }
157
158        /**
159         * <p>
160         * Returns the child of the root node associated with the given symbol or
161         * null if it does not exist.
162         * </p>
163         *
164         * @param symbol
165         *            symbol whose node is required
166         * @return node associated with the symbol; null if no such node exists
167         * @see TrieNode#getChild(Object)
168         */
169        protected TrieNode<T> getChild(T symbol) {
170                return rootNode.getChild(symbol);
171        }
172
173        /**
174         * <p>
175         * Returns the number of occurences of the given sequence.
176         * </p>
177         *
178         * @param sequence
179         *            sequence whose number of occurences is required
180         * @return number of occurences of the sequence
181         */
182        public int getCount(List<T> sequence) {
183                int count = 0;
184                TrieNode<T> node = find(sequence);
185                if (node != null) {
186                        count = node.getCount();
187                }
188                return count;
189        }
190
191        /**
192         * <p>
193         * Returns the number of occurences of the given prefix and a symbol that
194         * follows it.<br>
195         * Convenience function to simplify usage of {@link #getCount(List)}.
196         * </p>
197         *
198         * @param sequence
199         *            prefix of the sequence
200         * @param follower
201         *            suffix of the sequence
202         * @return number of occurences of the sequence
203         * @see #getCount(List)
204         */
205        public int getCount(List<T> sequence, T follower) {
206                List<T> tmpSequence = new LinkedList<T>(sequence);
207                tmpSequence.add(follower);
208                return getCount(tmpSequence);
209
210        }
211
212        /**
213         * <p>
214         * Searches the trie for a given sequence and returns the node associated
215         * with the sequence or null if no such node is found.
216         * </p>
217         *
218         * @param sequence
219         *            sequence that is searched for
220         * @return node associated with the sequence
221         * @see TrieNode#find(List)
222         */
223        public TrieNode<T> find(List<T> sequence) {
224                if (sequence == null || sequence.isEmpty()) {
225                        return rootNode;
226                }
227                List<T> sequenceCopy = new LinkedList<T>(sequence);
228                TrieNode<T> result = null;
229                TrieNode<T> node = getChild(sequenceCopy.get(0));
230                if (node != null) {
231                        sequenceCopy.remove(0);
232                        result = node.find(sequenceCopy);
233                }
234                return result;
235        }
236
237        /**
238         * <p>
239         * Returns a collection of all symbols that follow a given sequence in the
240         * trie. In case the sequence is not found or no symbols follow the sequence
241         * the result will be empty.
242         * </p>
243         *
244         * @param sequence
245         *            sequence whose followers are returned
246         * @return symbols following the given sequence
247         * @see TrieNode#getFollowingSymbols()
248         */
249        public Collection<T> getFollowingSymbols(List<T> sequence) {
250                Collection<T> result = new LinkedList<T>();
251                TrieNode<T> node = find(sequence);
252                if (node != null) {
253                        result = node.getFollowingSymbols();
254                }
255                return result;
256        }
257
258        /**
259         * <p>
260         * Returns the longest suffix of the given context that is contained in the
261         * tree and whose children are leaves.
262         * </p>
263         *
264         * @param context
265         *            context whose suffix is searched for
266         * @return longest suffix of the context
267         */
268        public List<T> getContextSuffix(List<T> context) {
269                List<T> contextSuffix;
270                if (context != null) {
271                        contextSuffix = new LinkedList<T>(context); // defensive copy
272                } else {
273                        contextSuffix = new LinkedList<T>();
274                }
275                boolean suffixFound = false;
276
277                while (!suffixFound) {
278                        if (contextSuffix.isEmpty()) {
279                                suffixFound = true; // suffix is the empty word
280                        } else {
281                                TrieNode<T> node = find(contextSuffix);
282                                if (node != null) {
283                                        if (!node.getFollowingSymbols().isEmpty()) {
284                                                suffixFound = true;
285                                        }
286                                }
287                                if (!suffixFound) {
288                                        contextSuffix.remove(0);
289                                }
290                        }
291                }
292
293                return contextSuffix;
294        }
295
296        /**
297         * <p>
298         * Helper class for graph visualization of a trie.
299         * </p>
300         *
301         * @author Steffen Herbold
302         * @version 1.0
303         */
304        static public class Edge {
305        }
306
307        /**
308         * <p>
309         * Helper class for graph visualization of a trie.
310         * </p>
311         *
312         * @author Steffen Herbold
313         * @version 1.0
314         */
315        static public class TrieVertex {
316
317                /**
318                 * <p>
319                 * Id of the vertex.
320                 * </p>
321                 */
322                private String id;
323
324                /**
325                 * <p>
326                 * Contructor. Creates a new TrieVertex.
327                 * </p>
328                 *
329                 * @param id
330                 *            id of the vertex
331                 */
332                protected TrieVertex(String id) {
333                        this.id = id;
334                }
335
336                /**
337                 * <p>
338                 * Returns the id of the vertex.
339                 * </p>
340                 *
341                 * @see java.lang.Object#toString()
342                 */
343                @Override
344                public String toString() {
345                        return id;
346                }
347        }
348
349        /**
350         * <p>
351         * Returns a {@link Graph} representation of the trie.
352         * </p>
353         *
354         * @return {@link Graph} representation of the trie
355         */
356        protected Tree<TrieVertex, Edge> getGraph() {
357                DelegateTree<TrieVertex, Edge> graph = new DelegateTree<TrieVertex, Edge>();
358                rootNode.getGraph(null, graph);
359                return graph;
360        }
361
362        /*
363         * (non-Javadoc)
364         *
365         * @see de.ugoe.cs.eventbench.models.IDotCompatible#getDotRepresentation()
366         */
367        public String getDotRepresentation() {
368                StringBuilder stringBuilder = new StringBuilder();
369                stringBuilder.append("digraph model {" + StringTools.ENDLINE);
370                rootNode.appendDotRepresentation(stringBuilder);
371                stringBuilder.append('}' + StringTools.ENDLINE);
372                return stringBuilder.toString();
373        }
374
375        /**
376         * <p>
377         * Returns the string representation of the root node.
378         * </p>
379         *
380         * @see TrieNode#toString()
381         * @see java.lang.Object#toString()
382         */
383        @Override
384        public String toString() {
385                return rootNode.toString();
386        }
387
388        /**
389         * <p>
390         * Returns the number of symbols contained in the trie.
391         * </p>
392         *
393         * @return number of symbols contained in the trie
394         */
395        public int getNumSymbols() {
396                return knownSymbols.size();
397        }
398
399        /**
400         * <p>
401         * Returns the number of trie nodes that are ancestors of a leaf. This is
402         * the equivalent to the number of states a first-order markov model would
403         * have.
404         * <p>
405         *
406         * @return number of trie nodes that are ancestors of leafs.
407         */
408        public int getNumLeafAncestors() {
409                List<TrieNode<T>> ancestors = new LinkedList<TrieNode<T>>();
410                rootNode.getLeafAncestors(ancestors);
411                return ancestors.size();
412        }
413
414        /**
415         * <p>
416         * Returns the number of trie nodes that are leafs.
417         * </p>
418         *
419         * @return number of leafs in the trie
420         */
421        public int getNumLeafs() {
422                return rootNode.getNumLeafs();
423        }
424
425        /**
426         * <p>
427         * Updates the list of known symbols by replacing it with all symbols that
428         * are found in the child nodes of the root node. This should be the same as
429         * all symbols that are contained in the trie.
430         * </p>
431         */
432        public void updateKnownSymbols() {
433                knownSymbols = new HashSet<T>();
434                for (TrieNode<T> node : rootNode.getChildren()) {
435                        knownSymbols.add(node.getSymbol());
436                }
437        }
438
439        /**
440         * <p>
441         * Two Tries are defined as equal, if their {@link #rootNode} are equal.
442         * </p>
443         *
444         * @see java.lang.Object#equals(java.lang.Object)
445         */
446        @SuppressWarnings("rawtypes")
447        @Override
448        public boolean equals(Object other) {
449                if (other == this) {
450                        return true;
451                }
452                if (other instanceof Trie) {
453                        return rootNode.equals(((Trie) other).rootNode);
454                }
455                return false;
456        }
457       
458        /*
459         * (non-Javadoc)
460         *
461         * @see java.lang.Object#hashCode()
462         */
463        @Override
464        public int hashCode() {
465                int multiplier = 17;
466                int hash = 42;
467                if (rootNode != null) {
468                        hash = multiplier * hash + rootNode.hashCode();
469                }
470                return hash;
471        }
472}
Note: See TracBrowser for help on using the repository browser.