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
RevLine 
[13]1package de.ugoe.cs.eventbench.models;
[1]2
[86]3import java.io.Serializable;
[361]4import java.security.InvalidParameterException;
[102]5import java.util.Collection;
[129]6import java.util.HashSet;
[9]7import java.util.LinkedHashSet;
[1]8import java.util.LinkedList;
9import java.util.List;
10
[30]11import de.ugoe.cs.util.StringTools;
12
[5]13import edu.uci.ics.jung.graph.DelegateTree;
[106]14import edu.uci.ics.jung.graph.Graph;
[5]15import edu.uci.ics.jung.graph.Tree;
16
[106]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 */
[86]31public class Trie<T> implements IDotCompatible, Serializable {
[106]32
[86]33        /**
[106]34         * <p>
[86]35         * Id for object serialization.
[106]36         * </p>
[86]37         */
38        private static final long serialVersionUID = 1L;
39
[106]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         */
[6]52        private final TrieNode<T> rootNode;
[106]53
54        /**
55         * <p>
56         * Contructor. Creates a new Trie.
57         * </p>
58         */
[6]59        public Trie() {
60                rootNode = new TrieNode<T>();
[9]61                knownSymbols = new LinkedHashSet<T>();
[6]62        }
[106]63
64        /**
65         * <p>
[361]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>
[106]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() {
[9]89                return new LinkedHashSet<T>(knownSymbols);
90        }
[106]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         */
[9]103        public void train(List<T> sequence, int maxOrder) {
[258]104                if (maxOrder < 1) {
[251]105                        return;
106                }
[9]107                IncompleteMemory<T> latestActions = new IncompleteMemory<T>(maxOrder);
[106]108                int i = 0;
109                for (T currentEvent : sequence) {
[9]110                        latestActions.add(currentEvent);
111                        knownSymbols.add(currentEvent);
112                        i++;
[106]113                        if (i >= maxOrder) {
[9]114                                add(latestActions.getLast(maxOrder));
115                        }
116                }
117                int sequenceLength = sequence.size();
[106]118                for (int j = maxOrder - 1; j > 0; j--) {
119                        add(sequence.subList(sequenceLength - j, sequenceLength));
[9]120                }
121        }
[1]122
[106]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()) {
[9]135                        knownSymbols.addAll(subsequence);
[106]136                        subsequence = new LinkedList<T>(subsequence); // defensive copy!
[1]137                        T firstSymbol = subsequence.get(0);
[6]138                        TrieNode<T> node = getChildCreate(firstSymbol);
139                        node.add(subsequence);
[1]140                }
141        }
142
[106]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) {
[6]155                return rootNode.getChildCreate(symbol);
[1]156        }
[106]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         */
[1]169        protected TrieNode<T> getChild(T symbol) {
[6]170                return rootNode.getChild(symbol);
[1]171        }
172
[106]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         */
[1]182        public int getCount(List<T> sequence) {
183                int count = 0;
184                TrieNode<T> node = find(sequence);
[106]185                if (node != null) {
[1]186                        count = node.getCount();
187                }
188                return count;
189        }
[106]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         */
[1]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);
[106]209
[1]210        }
[106]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         */
[1]223        public TrieNode<T> find(List<T> sequence) {
[106]224                if (sequence == null || sequence.isEmpty()) {
[6]225                        return rootNode;
226                }
[5]227                List<T> sequenceCopy = new LinkedList<T>(sequence);
[1]228                TrieNode<T> result = null;
[6]229                TrieNode<T> node = getChild(sequenceCopy.get(0));
[106]230                if (node != null) {
[6]231                        sequenceCopy.remove(0);
232                        result = node.find(sequenceCopy);
[1]233                }
234                return result;
235        }
[106]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         */
[102]249        public Collection<T> getFollowingSymbols(List<T> sequence) {
250                Collection<T> result = new LinkedList<T>();
[6]251                TrieNode<T> node = find(sequence);
[106]252                if (node != null) {
[6]253                        result = node.getFollowingSymbols();
[1]254                }
255                return result;
256        }
[106]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         */
[1]268        public List<T> getContextSuffix(List<T> context) {
[316]269                List<T> contextSuffix;
[361]270                if (context != null) {
[316]271                        contextSuffix = new LinkedList<T>(context); // defensive copy
272                } else {
273                        contextSuffix = new LinkedList<T>();
274                }
[1]275                boolean suffixFound = false;
[106]276
277                while (!suffixFound) {
278                        if (contextSuffix.isEmpty()) {
[1]279                                suffixFound = true; // suffix is the empty word
280                        } else {
281                                TrieNode<T> node = find(contextSuffix);
[106]282                                if (node != null) {
283                                        if (!node.getFollowingSymbols().isEmpty()) {
[1]284                                                suffixFound = true;
285                                        }
286                                }
[106]287                                if (!suffixFound) {
[5]288                                        contextSuffix.remove(0);
289                                }
[1]290                        }
291                }
[106]292
[1]293                return contextSuffix;
294        }
[106]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         */
[5]315        static public class TrieVertex {
[106]316
317                /**
318                 * <p>
319                 * Id of the vertex.
320                 * </p>
321                 */
[5]322                private String id;
[106]323
324                /**
325                 * <p>
326                 * Contructor. Creates a new TrieVertex.
327                 * </p>
328                 *
329                 * @param id
330                 *            id of the vertex
331                 */
[5]332                protected TrieVertex(String id) {
333                        this.id = id;
334                }
[106]335
336                /**
337                 * <p>
338                 * Returns the id of the vertex.
339                 * </p>
340                 *
341                 * @see java.lang.Object#toString()
342                 */
343                @Override
[5]344                public String toString() {
345                        return id;
346                }
347        }
[106]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         */
[23]356        protected Tree<TrieVertex, Edge> getGraph() {
[5]357                DelegateTree<TrieVertex, Edge> graph = new DelegateTree<TrieVertex, Edge>();
[6]358                rootNode.getGraph(null, graph);
[5]359                return graph;
360        }
[106]361
362        /*
363         * (non-Javadoc)
364         *
365         * @see de.ugoe.cs.eventbench.models.IDotCompatible#getDotRepresentation()
366         */
[30]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        }
[106]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         */
[1]383        @Override
384        public String toString() {
[6]385                return rootNode.toString();
[1]386        }
[106]387
388        /**
389         * <p>
390         * Returns the number of symbols contained in the trie.
391         * </p>
392         *
[129]393         * @return number of symbols contained in the trie
[106]394         */
[66]395        public int getNumSymbols() {
396                return knownSymbols.size();
397        }
[129]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() {
[399]409                List<TrieNode<T>> ancestors = new LinkedList<TrieNode<T>>();
[129]410                rootNode.getLeafAncestors(ancestors);
411                return ancestors.size();
412        }
[248]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        }
[258]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        }
[397]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        }
[1]472}
Note: See TracBrowser for help on using the repository browser.