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.database.query.knn;
22 
23 import java.util.ArrayList;
24 import java.util.List;
25 
26 import de.lmu.ifi.dbs.elki.database.ids.*;
27 import de.lmu.ifi.dbs.elki.database.relation.Relation;
28 import de.lmu.ifi.dbs.elki.index.preprocessed.knn.AbstractMaterializeKNNPreprocessor;
29 import de.lmu.ifi.dbs.elki.logging.Logging;
30 import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException;
31 
32 /**
33  * Instance for a particular database, invoking the preprocessor.
34  *
35  * @author Erich Schubert
36  * @since 0.4.0
37  *
38  * @param <O> Data object type
39  */
40 public class PreprocessorKNNQuery<O> implements KNNQuery<O> {
41   /**
42    * Class logger
43    */
44   private static final Logging LOG = Logging.getLogger(PreprocessorKNNQuery.class);
45 
46   /**
47    * The data to use for this query
48    */
49   final protected Relation<? extends O> relation;
50 
51   /**
52    * The last preprocessor result
53    */
54   final private AbstractMaterializeKNNPreprocessor<O> preprocessor;
55 
56   /**
57    * Warn only once.
58    */
59   private boolean warned = false;
60 
61   /**
62    * Constructor.
63    *
64    * @param relation Relation to query
65    * @param preprocessor Preprocessor instance to use
66    */
PreprocessorKNNQuery(Relation<? extends O> relation, AbstractMaterializeKNNPreprocessor<O> preprocessor)67   public PreprocessorKNNQuery(Relation<? extends O> relation, AbstractMaterializeKNNPreprocessor<O> preprocessor) {
68     super();
69     this.relation = relation;
70     this.preprocessor = preprocessor;
71   }
72 
73   @Override
getKNNForDBID(DBIDRef id, int k)74   public KNNList getKNNForDBID(DBIDRef id, int k) {
75     if(!warned && k > preprocessor.getK()) {
76       getLogger().warning("Requested more neighbors than preprocessed: requested " + k + " preprocessed " + preprocessor.getK(), new Throwable());
77       warned = true;
78     }
79     return preprocessor.get(id).subList(k);
80   }
81 
82   @Override
getKNNForBulkDBIDs(ArrayDBIDs ids, int k)83   public List<KNNList> getKNNForBulkDBIDs(ArrayDBIDs ids, int k) {
84     if(!warned && k > preprocessor.getK()) {
85       getLogger().warning("Requested more neighbors than preprocessed: requested " + k + " preprocessed " + preprocessor.getK(), new Throwable());
86       warned = true;
87     }
88     if(k < preprocessor.getK()) {
89       List<KNNList> result = new ArrayList<>(ids.size());
90       for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
91         result.add(preprocessor.get(iter).subList(k));
92       }
93       return result;
94     }
95     List<KNNList> result = new ArrayList<>(ids.size());
96     for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
97       result.add(preprocessor.get(iter));
98     }
99     return result;
100   }
101 
102   @Override
getKNNForObject(O obj, int k)103   public KNNList getKNNForObject(O obj, int k) {
104     throw new AbortException("Preprocessor KNN query only supports ID queries.");
105   }
106 
107   /**
108    * Get the preprocessor instance.
109    *
110    * @return preprocessor instance
111    */
getPreprocessor()112   public AbstractMaterializeKNNPreprocessor<O> getPreprocessor() {
113     return preprocessor;
114   }
115 
116   /**
117    * Get the class logger. Override when subclassing!
118    *
119    * @return Class logger.
120    */
getLogger()121   protected Logging getLogger() {
122     return LOG;
123   }
124 }
125