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.data.model;
22 
23 import de.lmu.ifi.dbs.elki.data.DoubleVector;
24 import de.lmu.ifi.dbs.elki.data.NumberVector;
25 import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
26 import de.lmu.ifi.dbs.elki.database.relation.Relation;
27 import de.lmu.ifi.dbs.elki.math.linearalgebra.Centroid;
28 import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.NumberVectorAdapter;
29 
30 /**
31  * Utility classes for dealing with cluster models.
32  *
33  * @author Erich Schubert
34  * @since 0.7.0
35  *
36  * @assoc - - - Model
37  * @assoc - - - NumberVector
38  */
39 public final class ModelUtil {
40   /**
41    * Private constructor. Static methods only.
42    */
ModelUtil()43   private ModelUtil() {
44     // Do not use.
45   }
46 
47   /**
48    * Get (and convert!) the representative vector for a cluster model.
49    *
50    * <b>Only representative-based models are supported!</b>
51    *
52    * {@code null} is returned when the model is not supported!
53    *
54    * @param model Model
55    * @param relation Data relation (for representatives specified per DBID)
56    * @param factory Vector factory, for type conversion.
57    * @return Vector of type V, {@code null} if not supported.
58    * @param <V> desired vector type
59    */
60   @SuppressWarnings("unchecked")
getPrototype(Model model, Relation<? extends V> relation, NumberVector.Factory<V> factory)61   public static <V extends NumberVector> V getPrototype(Model model, Relation<? extends V> relation, NumberVector.Factory<V> factory) {
62     // Mean model contains a numeric Vector
63     if(model instanceof MeanModel) {
64       final double[] p = ((MeanModel) model).getMean();
65       return factory.newNumberVector(p);
66     }
67     // Handle medoid models
68     if(model instanceof MedoidModel) {
69       NumberVector p = relation.get(((MedoidModel) model).getMedoid());
70       if(factory.getRestrictionClass().isInstance(p)) {
71         return (V) p;
72       }
73       return factory.newNumberVector(p, NumberVectorAdapter.STATIC);
74     }
75     if(model instanceof PrototypeModel) {
76       Object p = ((PrototypeModel<?>) model).getPrototype();
77       if(factory.getRestrictionClass().isInstance(p)) {
78         return (V) p;
79       }
80       if(p instanceof NumberVector) {
81         return factory.newNumberVector((NumberVector) p, NumberVectorAdapter.STATIC);
82       }
83       return null; // Inconvertible
84     }
85     return null;
86   }
87 
88   /**
89    * Get the representative vector for a cluster model.
90    *
91    * <b>Only representative-based models are supported!</b>
92    *
93    * {@code null} is returned when the model is not supported!
94    *
95    * @param model Model
96    * @param relation Data relation (for representatives specified per DBID)
97    * @return Some {@link NumberVector}, {@code null} if not supported.
98    */
getPrototype(Model model, Relation<? extends NumberVector> relation)99   public static NumberVector getPrototype(Model model, Relation<? extends NumberVector> relation) {
100     // Mean model contains a numeric Vector
101     if(model instanceof MeanModel) {
102       return DoubleVector.wrap(((MeanModel) model).getMean());
103     }
104     // Handle medoid models
105     if(model instanceof MedoidModel) {
106       return relation.get(((MedoidModel) model).getMedoid());
107     }
108     if(model instanceof PrototypeModel) {
109       Object p = ((PrototypeModel<?>) model).getPrototype();
110       if(p instanceof NumberVector) {
111         return (NumberVector) p;
112       }
113       return null; // Inconvertible
114     }
115     return null;
116   }
117 
118   /**
119    * Get the representative vector for a cluster model, or compute the centroid.
120    *
121    * @param model Model
122    * @param relation Data relation (for representatives specified per DBID)
123    * @param ids Cluster ids (must not be empty.
124    * @return Vector of type V, {@code null} if not supported.
125    * @param <V> desired vector type
126    */
getPrototypeOrCentroid(Model model, Relation<? extends V> relation, DBIDs ids, NumberVector.Factory<V> factory)127   public static <V extends NumberVector> V getPrototypeOrCentroid(Model model, Relation<? extends V> relation, DBIDs ids, NumberVector.Factory<V> factory) {
128     assert (ids.size() > 0);
129     V v = getPrototype(model, relation, factory);
130     return v != null ? v : factory.newNumberVector(Centroid.make(relation, ids));
131   }
132 
133   /**
134    * Get the representative vector for a cluster model, or compute the centroid.
135    *
136    * @param model Model
137    * @param relation Data relation (for representatives specified per DBID)
138    * @param ids Cluster ids (must not be empty.
139    * @return Some {@link NumberVector}.
140    */
getPrototypeOrCentroid(Model model, Relation<? extends NumberVector> relation, DBIDs ids)141   public static NumberVector getPrototypeOrCentroid(Model model, Relation<? extends NumberVector> relation, DBIDs ids) {
142     assert (ids.size() > 0);
143     NumberVector v = getPrototype(model, relation);
144     return v != null ? v : Centroid.make(relation, ids);
145   }
146 }
147