Gaia
frozencosineangledistance.h
1 /*
2  * Copyright (C) 2006-2013 Music Technology Group - Universitat Pompeu Fabra
3  *
4  * This file is part of Gaia
5  *
6  * Gaia is free software: you can redistribute it and/or modify it under
7  * the terms of the GNU Affero General Public License as published by the Free
8  * Software Foundation (FSF), either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14  * details.
15  *
16  * You should have received a copy of the Affero GNU General Public License
17  * version 3 along with this program. If not, see http://www.gnu.org/licenses/
18  */
19 
20 #ifndef GAIA_FROZENCOSINEANGLEDISTANCE_H
21 #define GAIA_FROZENCOSINEANGLEDISTANCE_H
22 
23 #include "frozendistance.h"
24 
25 namespace gaia2 {
26 
27 
29 public:
30  static const Real defaultUndefinedDistance = 0.0;
31 
32  FrozenCosineAngleDistance(const FrozenDataSet& dataset, int offset, int size)
33  : FrozenDistance(dataset), _offset(offset), _size(size),
34  _useDefaultValue(true), _defaultValue(defaultUndefinedDistance) {}
35 
36 
37  FrozenCosineAngleDistance(const FrozenDataSet& dataset, const QString& descriptorName)
38  : FrozenDistance(dataset) {
39  init(descriptorName);
40  }
41 
42  FrozenCosineAngleDistance(const FrozenDataSet& dataset, const std::string& descriptorName)
43  : FrozenDistance(dataset), _useDefaultValue(true), _defaultValue(defaultUndefinedDistance) {
44  init(QString::fromStdString(descriptorName));
45  }
46 
47  FrozenCosineAngleDistance(const FrozenDataSet& dataset, const char* descriptorName)
48  : FrozenDistance(dataset), _useDefaultValue(true), _defaultValue(defaultUndefinedDistance) {
49  init(QString::fromUtf8(descriptorName));
50  }
51 
52 
53  FrozenCosineAngleDistance(const FrozenDataSet& dataset, const ParameterMap& params)
54  : FrozenDistance(dataset) {
55 
56  validParams << "defaultValue";
57 
58  if (params.contains("defaultValue")) {
59  _useDefaultValue = true;
60  _defaultValue = params.value("defaultValue").toDouble();
61  }
62  else {
63  _useDefaultValue = false;
64  }
65 
66  if (params.contains("descriptorName")) {
67  init(params.value("descriptorName"));
68  }
69  else {
70  init(0, dataset.dimension());
71  }
72  }
73 
74 
75  void init(const QString& descriptorName) {
76  validParams << "descriptorName";
77 
78  QPair<int, int> pos = _dataset.descriptorLocation(descriptorName);
79  init(pos.first, pos.second - pos.first);
80  }
81 
82  void init(int offset, int size) {
83  _offset = offset;
84  _size = size;
85 
86  // precompute the norm for all the vectors in the dataset
87  _norm.resize(_dataset.rows());
88  for (int i=0; i<_dataset.rows(); i++) _norm[i] = _dataset.row(i).segment(_offset, _size).norm();
89 
90  // precompute a lookup table for the acos values
91  _acosLUT.resize(LUT_SIZE+1); // we want to allow 0 <= x <= 1 (ie: both ends included)
92  for (int i=0; i<LUT_SIZE+1; i++) {
93  _acosLUT[i] = std::acos(double(i)/LUT_SIZE) / M_PI;
94  }
95  }
96 
97  void prepare(const FrozenPoint& query) {
98  _qnorm = query.segment(_offset, _size).norm();
99  }
100 
101  // return the normalized angle (ie: alpha / M_PI)
102  Real acos(Real x) const {
103  if (x < 0) return 1.0 - acos(-x);
104  x = qMin(x, (Real)1.0);
105  return _acosLUT.at((int)(x*LUT_SIZE+0.5));
106  }
107 
108  Real operator()(int i, const FrozenPoint& query) const {
109  Real n1 = _norm[i];
110  Real n2 = _qnorm;
111 
112  static const Real epsilon = 1e-6; // arbitrary value
113  if (n1*n2 < epsilon) {
114  if (_useDefaultValue) {
115  return _defaultValue;
116  }
117  QStringList msg;
118  msg << "Could not compute cosine distance between '" << _dataset.pointName(i)
119  << "' and query point because one of the two points is null, result is undefined";
120  throw GaiaException(msg);
121  }
122 
123  Real result = _dataset.row(i).segment(_offset, _size).dot(query.segment(_offset, _size) / (n1*n2));
124  //return acos(clip(result, -1.0, 1.0)) / M_PI;
125  return acos(result);
126  }
127 
128 protected:
129  int _offset, _size;
130  bool _useDefaultValue;
131  Real _defaultValue;
132 
133  // precomputed norms for the points in the dataset on which this distance operates
134  QVector<Real> _norm;
135  Real _qnorm;
136 
137  // Lookup table for the acos function
138  static const int LUT_SIZE = 4096;
139  QVector<Real> _acosLUT;
140 };
141 
142 
143 } // namespace gaia2
144 
145 #endif // GAIA_FROZENCOSINEANGLEDISTANCE_H
Definition: frozendistance.h:33
int dimension() const
Return the number of dimensions of this dataset.
Definition: frozendataset.cpp:36
const QString & pointName(int i) const
Return the name of the point with the given index.
Definition: frozendataset.cpp:47
Main Gaia namespace, which contains all the library functions.
Definition: addfield.cpp:22
QPair< int, int > descriptorLocation(const QString &descriptorName) const
Return the column indices of beginning and end fo the region spanning the descriptor.
Definition: frozendataset.cpp:181
Definition: frozencosineangledistance.h:28
Definition: parameter.h:34
A FrozenDataSet is a dataset that has been flagged as immutable.
Definition: frozendataset.h:49
Exception class that can take up to 3 arguments of any type, which will be serialized into a QString ...
Definition: gaiaexception.h:46