package ca.pfv.spmf.algorithms.sequentialpatterns.clospan_AGP.items.trie; import java.util.ArrayList; import java.util.BitSet; import java.util.Collections; import java.util.LinkedList; import java.util.List; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.MutableTreeNode; import ca.pfv.spmf.algorithms.sequentialpatterns.clospan_AGP.items.abstractions.ItemAbstractionPair; import ca.pfv.spmf.algorithms.sequentialpatterns.clospan_AGP.items.patterns.Pattern; import ca.pfv.spmf.algorithms.sequentialpatterns.clospan_AGP.items.patterns.PatternCreator; /** * Class that implement a trie structure. A trie is composed of a list of * nodes children that are also the beginning of other trie structure. Those * nodes are composed of both a ItemAstractionPair object and a Trie, where the * children appear. * * The current trie is referring to a pattern that can be obtained from the root * until this one, passing by the different nodes in the way that are ancestors * of the current trie. We do not keep any trace of the parent nodes since the * whole trie will be run at the end of the algorithm, just before applying the * postprocessing step to remove the remaining non-closed frequent patterns. * * Besides, in a trie we keep some information relative to that pattern that is * referred, such as the sequences where the pattern appears, its support, and * some other information used in the key generation of the pruning methods. * * Copyright Antonio Gomariz PeƱalver 2013 * * This file is part of the SPMF DATA MINING SOFTWARE * (http://www.philippe-fournier-viger.com/spmf). * * SPMF is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * SPMF is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * SPMF. If not, see <http://www.gnu.org/licenses/>. * * @author agomariz */ public class Trie implements Comparable<Trie> { /** * List of children of the current trie */ private List<TrieNode> nodes; /** * List of sequences IDs where the pattern, to which the current trie is * referring to, appears */ private BitSet appearingIn = new BitSet(); /** * Support that the pattern, to which the current trie is referring to, has */ private int support = -1; /** * Counter that keeps the sum of all the sequence IDs that are in * appearingIn list */ private int sumSequencesIDs = -1; /** * Static field in order to generate a different identifier for all the * tries generated by the algorithm */ private static int intId = 1; /** * Trie identifier */ private int id; /** * Standard constructor of a Trie. It sets the list of nodes to empty. */ public Trie() { nodes = new ArrayList<TrieNode>(); id = intId++; } /** * Constructor of a Trie by means of a list of NodeTrie. * @param nodes List of nodes with which we want to initialize the Trie */ public Trie(List<TrieNode> nodes) { this.nodes = nodes; id = intId++; } /** * It obtain its ith trie child * @param index Child index in which we are interested * @return the ith trie child. */ public Trie getChild(int index) { return nodes.get(index).getChild(); } /** * It set a child to the Trie given as parameter * @param index Child index in which we are interested * @param child Trie that we want to insert */ public void setChild(int index, Trie child) { this.nodes.get(index).setChild(child); } /** * It gets the list of nodes associated with the Trie * @return the list of nodes */ public List<TrieNode> getNodes() { return nodes; } /** * It updates the list of nodes associated with the Trie * @param nodes the list of nodes to be used for updating */ public void setNodes(List<TrieNode> nodes) { this.nodes = nodes; } /** * It removes the ith child of the Trie. * @param index Child index in which we are interested * @return true if the node was removed, otherwise, the index was out of range and no node was removed. */ public boolean remove(int index) { //if there are some nodes and the index is not within the range of nodes if (levelSize() == 0 || index >= levelSize()) { return false; } //We remove the child pointed out by index getChild(index).removeAll(); return true; } /** * It gets the pair of the ith child * @param index Child index in which we are interested * @return the pair of the ith child */ public ItemAbstractionPair getPair(int index) { return nodes.get(index).getPair(); } /** * It gets the whole TrieNode of the ith child * @param index Child index in which we are interested * @return the trie node */ public TrieNode getNode(int index) { return nodes.get(index); } /** * It updates the whole TrieNode of the ith child * @param index Child index in which we are interested * @param node */ public void setNode(int index, TrieNode node) { nodes.set(index, node); } /** * It returns the number of children that a Trie has * @return the number of children */ public int levelSize() { if (nodes == null) { return 0; } return nodes.size(); } /** * It removes all its descendands tries and then the Trie itself. */ public void removeAll() { //If there are no nodes if (levelSize() == 0) { //We have already finish return; } //Otherwise, for each node of the Trie children for (TrieNode node : nodes) { Trie currentChild = node.getChild(); //We remove all the descendants appearing from its child if (currentChild != null) { currentChild.removeAll(); } //And we make null both its child and pair node.setChild(null); node.setPair(null); } nodes.clear(); } /* public void mergeWithTrie(TrieNode newTrie) { if (levelSize() == 0) { if (nodes == null) { nodes = new ArrayList<TrieNode>(1); } nodes.add(newTrie); } else { nodes.add(newTrie); } }*/ /** * It sorts the children by lexicographic order (given by their pair values) */ public void sort() { Collections.sort(nodes); } /** * It returns the list of sequences Ids where the pattern referred by * the Trie appears * @return the list of sequence ids */ public BitSet getAppearingIn() { return this.appearingIn; } /** * It updates the list of sequences Ids where the pattern referred by * the Trie appears * @param appearingIn The list of sequence Ids to update */ public void setAppearingIn(BitSet appearingIn) { this.appearingIn = appearingIn; } /** * Get the string representation of this Trie * @return the string representation */ @Override public String toString() { if (nodes == null) { return ""; } StringBuilder result = new StringBuilder("ID=" + id + "["); if (!nodes.isEmpty()) { for (TrieNode node : nodes) { result.append(node.getPair()).append(','); } result.deleteCharAt(result.length() - 1); } else { result.append("NULL"); } result.append(']'); return result.toString(); } /** * It gets the support of the pattern referred by the Trie. * @return the support */ public int getSupport() { if (this.support < 0) { this.support = appearingIn.cardinality(); } return this.support; } /** * It updates the support of the pattern referred by the Trie * @param support the support */ public void setSupport(int support) { this.support = support; } /** * It gets the sum of the sequence identifiers of the sequences where * the pattern, referred by the Trie, appears * @return the sum of sequence identifiers */ public int getSumIdSequences() { if (sumSequencesIDs < 0) { sumSequencesIDs = calculateSumIdSequences(); } return sumSequencesIDs; } /** * It updates the sum of the sequence identifiers of the sequences where * the pattern, referred by the Trie, appears * @param sumIdSequences Value of the sum of sequence identifiers to update */ public void setSumIdSequences(int sumIdSequences) { this.sumSequencesIDs = sumIdSequences; } /** * It calculates the sum of the sequence identifiers * @return the sum of the sequence identifiers */ private int calculateSumIdSequences() { int acum = 0; for(int i=appearingIn.nextSetBit(0);i>=0;i=appearingIn.nextSetBit(i+1)){ acum += i; } return acum; } /** * It makes a pre-order traversal from the Trie. The result is concatenate * to the prefix pattern given as parameter * @param p Prefix pattern * @return the list of patterns */ public List<Pattern> preorderTraversal(Pattern p) { List<Pattern> result = new LinkedList<Pattern>(); //If there is any node if (nodes != null) { //For each child for (TrieNode node : nodes) { Trie child = node.getChild(); /* * We concatenate the pair component of this child with the * previous prefix pattern, we set its appearances and we add it * as a element in the result list */ Pattern newPattern = PatternCreator.getInstance().concatenate(p, node.getPair()); newPattern.setAppearingIn(child.getAppearingIn()); result.add(newPattern); if (child != null) { /* * If the child is not null we make a recursive call with the * new pattern */ List<Pattern> patternsFromChild = child.preorderTraversal(newPattern); if (patternsFromChild != null) { //If we find some descendants, we add them to the result list result.addAll(patternsFromChild); } } } return result; } else { return null; } } /** * Method to display graphically the Trie by means of a TreeModel * @param model TreeModel when we want to insert the Trie nodes * @param p TreeNode for the TreeModel */ public void display(DefaultTreeModel model, MutableTreeNode p) { if (nodes != null) { //For each node for (int i = 0; i < nodes.size(); i++) { TrieNode node = nodes.get(i); Trie child = node.getChild(); //We create a new TreeNode composed of the pair and the list of appearances DefaultMutableTreeNode currentNode = new DefaultMutableTreeNode(node.getPair().toString() + child.appearingIn); //And we insert it in the TreeModel model.insertNodeInto(currentNode, p, i); //And we go on doing the same process with the child child.display(model, currentNode); } } } /** * Compare this trie to another by id * @param t the other trie * @return 0 if the id is the same, -1 if the id of this trie is smaller, otherwise 1. */ @Override public int compareTo(Trie t) { return (new Integer(this.id)).compareTo(t.id); } /** * It adds a new node to the list of nodes associated with the Trie * @param node */ public void addNode(TrieNode node) { if (nodes == null) { nodes = new ArrayList<TrieNode>(); } nodes.add(node); } }