1 /*
2  * This file is part of ELKI:
3  * Environment for Developing KDD-Applications Supported by Index-Structures
4  *
5  * Copyright (C) 2018
6  * ELKI Development Team
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Affero General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Affero General Public License for more details.
17  *
18  * You should have received a copy of the GNU Affero General Public License
19  * along with this program. If not, see <http://www.gnu.org/licenses/>.
20  */
21 package de.lmu.ifi.dbs.elki.algorithm.itemsetmining;
22 
23 import de.lmu.ifi.dbs.elki.data.BitVector;
24 import de.lmu.ifi.dbs.elki.data.SparseNumberVector;
25 import de.lmu.ifi.dbs.elki.data.type.VectorFieldTypeInformation;
26 import de.lmu.ifi.dbs.elki.utilities.datastructures.BitsUtil;
27 import de.lmu.ifi.dbs.elki.utilities.exceptions.APIViolationException;
28 
29 /**
30  * Frequent itemset.
31  *
32  * @author Erich Schubert
33  * @since 0.7.0
34  */
35 public abstract class Itemset implements Comparable<Itemset> {
36   /**
37    * Support for this itemset.
38    */
39   int support;
40 
41   /**
42    * Increase the support of the itemset.
43    *
44    * @return New support.
45    */
increaseSupport()46   public int increaseSupport() {
47     return ++support;
48   }
49 
50   /**
51    * Get item support.
52    *
53    * @return Support
54    */
getSupport()55   public int getSupport() {
56     return support;
57   }
58 
59   /**
60    * Test whether the itemset is contained in a bit vector.
61    *
62    * @param bv Bit vector
63    * @return {@code true} when the itemset is contained in this vector.
64    */
containedIn(SparseNumberVector bv)65   public boolean containedIn(SparseNumberVector bv) {
66     int i1 = this.iter(), i2 = bv.iter();
67     while(this.iterValid(i1)) {
68       if(!bv.iterValid(i2)) {
69         return false;
70       }
71       int d1 = this.iterDim(i1), d2 = bv.iterDim(i2);
72       if(d1 < d2) {
73         return false; // Missing
74       }
75       if(d1 == d2) {
76         if(bv.iterDoubleValue(i2) == 0.) {
77           return false;
78         }
79         i1 = this.iterAdvance(i1);
80       }
81       i2 = bv.iterAdvance(i2);
82     }
83     return true;
84   }
85 
86   /**
87    * Itemset length.
88    *
89    * @return Itemset length
90    */
length()91   abstract public int length();
92 
93   /**
94    * Get the items.
95    *
96    * @param i Itemset
97    * @param bits Output bitset (must be zeros)
98    * @return Output bitset
99    */
toBitset(Itemset i, long[] bits)100   public static long[] toBitset(Itemset i, long[] bits) {
101     for(int it = i.iter(); i.iterValid(it); it = i.iterAdvance(it)) {
102       BitsUtil.setI(bits, i.iterDim(it));
103     }
104     return bits;
105   }
106 
107   /**
108    * Get an iterator over items, usually the position within an array.
109    *
110    * Intended usage:
111    *
112    * <pre>
113    * {@code
114    * for (int iter = v.iter(); v.iterValid(iter); iter = v.iterAdvance(iter)) {
115    *   final int item = v.iterItem(iter);
116    *   // Do something.
117    * }
118    * }
119    * </pre>
120    *
121    * @return Iterator
122    */
iter()123   public abstract int iter();
124 
125   /**
126    * Advance the iterator to the next position.
127    *
128    * @param iter Iterator
129    * @return New iterator position
130    */
iterAdvance(int iter)131   public abstract int iterAdvance(int iter);
132 
133   /**
134    * Check if the iterator position is valid.
135    *
136    * @param iter Iterator
137    * @return {@code true} if the position is valid.
138    */
iterValid(int iter)139   public abstract boolean iterValid(int iter);
140 
141   /**
142    * Item at the iterator position.
143    *
144    * @param iter Iterator
145    * @return Current item
146    */
iterDim(int iter)147   public abstract int iterDim(int iter);
148 
149   @Override
compareTo(Itemset o)150   public int compareTo(Itemset o) {
151     // Compare by length, then lexicographical.
152     final int l1 = length(), l2 = o.length();
153     return (l1 < l2) ? -1 : (l1 > l2) ? +1 : //
154         compareLexicographical(this, o);
155   }
156 
157   @Override
equals(Object obj)158   public boolean equals(Object obj) {
159     if(obj == this) {
160       return true;
161     }
162     if(obj == null || !(obj instanceof Itemset)) {
163       return false;
164     }
165     Itemset o = (Itemset) obj;
166     int i1 = this.iter(), i2 = o.iter();
167     while(this.iterValid(i1) && o.iterValid(i2)) {
168       int v1 = this.iterDim(i1), v2 = o.iterDim(i2);
169       if(v1 != v2) {
170         return false;
171       }
172       i1 = this.iterAdvance(i1);
173       i2 = o.iterAdvance(i2);
174     }
175     return this.iterValid(i1) == o.iterValid(i2);
176   }
177 
178   /**
179    * @deprecated Itemsets MUST NOT BE USED IN HASH MAPS.
180    */
181   @Deprecated
182   @Override
hashCode()183   public int hashCode() {
184     throw new APIViolationException("Itemsets may not be used in hash maps.");
185   }
186 
187   /**
188    * Robust compare using the iterators, lexicographical only!
189    *
190    * Note: This does NOT take length into account.
191    *
192    * @param o Other itemset.
193    * @return Comparison result.
194    */
compareLexicographical(Itemset a, Itemset o)195   protected static int compareLexicographical(Itemset a, Itemset o) {
196     int i1 = a.iter(), i2 = o.iter();
197     while(a.iterValid(i1) && o.iterValid(i2)) {
198       int v1 = a.iterDim(i1), v2 = o.iterDim(i2);
199       if(v1 < v2) {
200         return -1;
201       }
202       if(v2 < v1) {
203         return +1;
204       }
205       i1 = a.iterAdvance(i1);
206       i2 = o.iterAdvance(i2);
207     }
208     return a.iterValid(i1) ? 1 : o.iterValid(i2) ? -1 : 0;
209   }
210 
211   @Override
toString()212   public String toString() {
213     return appendTo(new StringBuilder(), null).toString();
214   }
215 
216   /**
217    * Append items and support to a string buffer.
218    *
219    * @param buf Buffer
220    * @param meta Relation metadata (for labels)
221    * @return String buffer for chaining.
222    */
appendTo(StringBuilder buf, VectorFieldTypeInformation<BitVector> meta)223   public final StringBuilder appendTo(StringBuilder buf, VectorFieldTypeInformation<BitVector> meta) {
224     appendItemsTo(buf, meta);
225     return buf.append(": ").append(support);
226   }
227 
228   /**
229    * Only append the items to a string buffer.
230    *
231    * @param buf Buffer
232    * @param meta Relation metadata (for labels)
233    * @return String buffer for chaining.
234    */
appendItemsTo(StringBuilder buf, VectorFieldTypeInformation<BitVector> meta)235   public StringBuilder appendItemsTo(StringBuilder buf, VectorFieldTypeInformation<BitVector> meta) {
236     int it = this.iter();
237     if(this.iterValid(it)) {
238       while(true) {
239         int v = this.iterDim(it);
240         String lbl = (meta != null) ? meta.getLabel(v) : null;
241         if(lbl == null) {
242           buf.append(v);
243         }
244         else {
245           buf.append(lbl);
246         }
247         it = this.iterAdvance(it);
248         if(!this.iterValid(it)) {
249           break;
250         }
251         buf.append(", ");
252       }
253     }
254     return buf;
255   }
256 }
257