001/*
002        AUTOMATICALLY GENERATED BY jTemp FROM
003        /Users/jsh2/Work/openimaj/target/checkout/machine-learning/clustering/src/main/jtemp/org/openimaj/ml/clustering/assignment/hard/Hierarchical#T#HardAssigner.jtemp
004*/
005/**
006 * Copyright (c) 2011, The University of Southampton and the individual contributors.
007 * All rights reserved.
008 *
009 * Redistribution and use in source and binary forms, with or without modification,
010 * are permitted provided that the following conditions are met:
011 *
012 *   *  Redistributions of source code must retain the above copyright notice,
013 *      this list of conditions and the following disclaimer.
014 *
015 *   *  Redistributions in binary form must reproduce the above copyright notice,
016 *      this list of conditions and the following disclaimer in the documentation
017 *      and/or other materials provided with the distribution.
018 *
019 *   *  Neither the name of the University of Southampton nor the names of its
020 *      contributors may be used to endorse or promote products derived from this
021 *      software without specific prior written permission.
022 *
023 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
024 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
025 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
026 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
027 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
028 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
029 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
030 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
031 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
032 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
033 */
034package org.openimaj.ml.clustering.assignment.hard;
035
036import org.openimaj.ml.clustering.assignment.HardAssigner;
037import org.openimaj.ml.clustering.assignment.soft.HierarchicalLongPathAssigner;
038import org.openimaj.ml.clustering.kmeans.HierarchicalLongKMeansResult;
039import org.openimaj.util.pair.IndependentPair;
040import org.openimaj.util.pair.IntDoublePair;
041
042/**
043 * The {@link HierarchicalLongHardAssigner} is a {@link HardAssigner} for
044 * {@link HierarchicalLongKMeansResult} instances. The assigner
045 * produces the index of the assigned leaf node as if the clusters were
046 * actually flat. 
047 * 
048 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
049 */
050public class HierarchicalLongHardAssigner implements HardAssigner<long[], double[], IntDoublePair> {
051        /**
052         * The {@link ScoringScheme} determines how the distance
053         * to a cluster is estimated from the hierarchy of k-means
054         * generated clusters.
055         * 
056         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
057         */
058        public enum ScoringScheme {
059                /**
060                 * Sum distances down the tree.
061                 * 
062                 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
063                 */
064                SUM {
065                        @Override
066                        public double computeScore(double[] weights) {
067                                double sum = 0;
068                                for (double w : weights) {
069                                        if (w < 0) break;
070                                        sum += w;
071                                }
072                                
073                                return sum;
074                        }
075                },
076                /**
077                 * Product of distances down the tree.
078                 * 
079                 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
080                 */
081                PRODUCT {
082                        @Override
083                        public double computeScore(double[] weights) {
084                                double prod = 1;
085                                for (double w : weights) {
086                                        if (w < 0) break;
087                                        prod *= w;
088                                }
089                                
090                                return prod;
091                        }
092                },
093                /**
094                 * The distance in the root cluster 
095                 * 
096                 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
097                 */
098                FIRST {
099                        @Override
100                        public double computeScore(double[] weights) {
101                                return weights[0];
102                        }
103                },
104                /**
105                 * The distance in the leaf cluster
106                 * 
107                 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
108                 */
109                LAST {
110                        @Override
111                        public double computeScore(double[] weights) {
112                                double last = -1;
113                                
114                                for (double w : weights) {
115                                        if (w < 0) break;
116                                        last = w;
117                                }
118                                
119                                return last;
120                        }
121                },
122                /**
123                 * The mean distance down the tree
124                 * 
125                 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
126                 */
127                MEAN {
128                        @Override
129                        public double computeScore(double[] weights) {
130                                double sum = 0;
131                                int count = 0;
132                                
133                                for (double w : weights) {
134                                        if (w < 0) break;
135                                        sum += w;
136                                        count++;
137                                }
138                                
139                                return sum / (double)count;
140                        }
141                }
142                ;
143                
144                protected abstract double computeScore(double[] weights); 
145        }
146        
147        protected HierarchicalLongKMeansResult result;
148        protected HierarchicalLongPathAssigner path;
149        protected ScoringScheme scorer;
150        
151        /**
152         * Construct with the given hierarchical KMeans clusterer
153         * and scoring scheme.
154         *
155         * @param result the hierarchical KMeans clusterer
156         * @param scorer the scoring scheme
157         */
158        public HierarchicalLongHardAssigner(HierarchicalLongKMeansResult result, ScoringScheme scorer) {
159                this.result = result;
160                this.scorer = scorer;
161                this.path = new HierarchicalLongPathAssigner(result);
162        }
163
164        /**
165         * Construct with the given Hierarchical KMeans clusterer
166         * and the SUM scoring scheme.
167         *
168         * @param result the hierarchical KMeans clusterer
169         */
170        public HierarchicalLongHardAssigner(HierarchicalLongKMeansResult result) {
171                this(result, ScoringScheme.SUM);
172        }
173        
174        @Override
175        public int[] assign(long[][] data) {
176                int [] asgn = new int[data.length];
177
178                for (int i=0; i<data.length; i++) {
179                        asgn[i] = result.getIndex(path.assign(data[i]));
180                }
181
182                return asgn;
183        }
184
185        @Override
186        public int assign(long[] data) {
187                return result.getIndex(path.assign(data));
188        }
189
190        @Override
191        public void assignDistance(long[][] data, int[] indices, double[] distances) {
192                int depth = result.getDepth();
193                long [][] d = new long[1][];
194                int [][] p = new int[1][depth];
195                double [][] w = new double[1][depth];
196                
197                for (int i=0; i<data.length; i++) {
198                        d[0] = data[i];
199                        
200                        path.assignWeighted(d, p, w);
201                        
202                        indices[i] = result.getIndex(p[0]);
203                        distances[i] = scorer.computeScore(w[0]);
204                }
205        }
206
207        @Override
208        public IntDoublePair assignDistance(long[] data) {
209                IndependentPair<int[], double[]> pw = path.assignWeighted(data);
210                
211                int index = result.getIndex(pw.firstObject());
212                double score = scorer.computeScore(pw.secondObject());
213                
214                return new IntDoublePair(index, score);
215        }
216        
217        @Override
218        public int size() {
219            return result.countLeafs();
220        }
221        
222        @Override
223        public int numDimensions() {
224            return result.numDimensions();
225        }
226}