Metrics.h
Go to the documentation of this file.
1/*!
2 *
3 *
4 * \brief Helper functions to calculate several norms and distances.
5 *
6 *
7 *
8 * \author O.Krause M.Thuma
9 * \date 2010-2011
10 *
11 *
12 * \par Copyright 1995-2017 Shark Development Team
13 *
14 * <BR><HR>
15 * This file is part of Shark.
16 * <https://shark-ml.github.io/Shark/>
17 *
18 * Shark is free software: you can redistribute it and/or modify
19 * it under the terms of the GNU Lesser General Public License as published
20 * by the Free Software Foundation, either version 3 of the License, or
21 * (at your option) any later version.
22 *
23 * Shark is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU Lesser General Public License for more details.
27 *
28 * You should have received a copy of the GNU Lesser General Public License
29 * along with Shark. If not, see <http://www.gnu.org/licenses/>.
30 *
31 */
32#ifndef SHARK_LINALG_METRICS_H
33#define SHARK_LINALG_METRICS_H
34
36#include <shark/Core/Math.h>
37namespace remora{
38
39///////////////////////////////////////NORMS////////////////////////////////////////
40
41/**
42* \brief Normalized squared norm_2 (diagonal Mahalanobis).
43*
44* Contrary to some conventions, dimension-wise weights are considered instead of std. deviations:
45* \f$ n^2(v) = \sum_i w_i v_i^2 \f$
46* nb: the weights themselves are not squared, but multiplied onto the squared components
47*/
48template<class VectorT, class WeightT, class Device>
49typename VectorT::value_type diagonalMahalanobisNormSqr(
50 vector_expression<VectorT, Device> const& vector,
51 vector_expression<WeightT, Device> const& weights
52) {
53 SIZE_CHECK( vector().size() == weights().size() );
54 return inner_prod(weights(),sqr(vector()));
55}
56
57/**
58* \brief Normalized norm_2 (diagonal Mahalanobis).
59*
60* Contrary to some conventions, dimension-wise weights are considered instead of std. deviations:
61* \f$ n^2(v) = \sqrt{\sum_i w_i v_i^2} \f$
62* nb: the weights themselves are not squared, but multiplied onto the squared components
63*/
64template<class VectorT, class WeightT, class Device>
65typename VectorT::value_type diagonalMahalanobisNorm(
66 vector_expression<VectorT, Device> const& vector,
67 vector_expression<WeightT, Device> const& weights
68) {
69 SIZE_CHECK( vector().size() == weights().size() );
70 return std::sqrt(diagonalMahalanobisNormSqr(vector,weights));
71}
72
73////////////////////////////////////////DISTANCES/////////////////////////////////////////////////
74
75namespace detail{
76 /**
77 * \brief Normalized Euclidian squared distance (squared diagonal Mahalanobis)
78 * between two vectors, optimized for two Compressed arguments.
79 *
80 * Contrary to some conventions, dimension-wise weights are considered instead of std. deviations:
81 * \f$ d^2(v) = \sum_i w_i (x_i-z_i)^2 \f$
82 * NOTE: The weights themselves are not squared, but multiplied onto the squared components.
83 */
84 template<class VectorT, class VectorU, class WeightT>
85 typename VectorT::value_type diagonalMahalanobisDistanceSqr(
86 VectorT const& op1,
87 VectorU const& op2,
88 WeightT const& weights,
89 sparse_tag,
90 sparse_tag
91 ){
92 using shark::sqr;
93 typename VectorT::value_type sum=0;
94 typename VectorT::const_iterator iter1=op1.begin();
95 typename VectorU::const_iterator iter2=op2.begin();
96 typename VectorT::const_iterator end1=op1.end();
97 typename VectorU::const_iterator end2=op2.end();
98 //be aware of empty vectors!
99 while(iter1 != end1 && iter2 != end2)
100 {
101 std::size_t index1=iter1.index();
102 std::size_t index2=iter2.index();
103 if(index1==index2){
104 sum += weights(index1) * sqr(*iter1-*iter2);
105 ++iter1;
106 ++iter2;
107 }
108 else if(index1<index2){
109 sum += weights(index1) * sqr(*iter1);
110 ++iter1;
111 }
112 else {
113 sum += weights(index2) * sqr(*iter2);
114 ++iter2;
115 }
116 }
117 while(iter1 != end1)
118 {
119 std::size_t index1=iter1.index();
120 sum += weights(index1) * sqr(*iter1);
121 ++iter1;
122 }
123 while(iter2 != end2)
124 {
125 std::size_t index2=iter2.index();
126 sum += weights(index2) * sqr(*iter2);
127 ++iter2;
128 }
129 return sum;
130 }
131
132 /**
133 * \brief Normalized Euclidian squared distance (squared diagonal Mahalanobis)
134 * between two vectors, optimized for one dense and one sparse argument
135 */
136 template<class VectorT, class VectorU, class WeightT>
137 typename VectorT::value_type diagonalMahalanobisDistanceSqr(
138 VectorT const& op1,
139 VectorU const& op2,
140 WeightT const& weights,
141 sparse_tag,
142 dense_tag
143 ){
144 std::size_t pos = 0;
145 typename VectorT::value_type sum=0;
146 auto end=op1.end();
147 for(auto iter=op1.begin();iter != end;++iter,++pos){
148 for(;pos != iter.index();++pos){
149 sum += weights(pos) * op2(pos) * op2(pos);
150 }
151 double diff = *iter-op2(pos);
152 sum += weights(pos) * diff * diff;
153 }
154 for(;pos != op2.size();++pos){
155 sum += weights(pos) * op2(pos) * op2(pos);
156 }
157 return sum;
158 }
159 template<class VectorT, class VectorU, class WeightT>
160 typename VectorT::value_type diagonalMahalanobisDistanceSqr(
161 VectorT const& op1,
162 VectorU const& op2,
163 WeightT const& weights,
164 dense_tag arg1tag,
165 sparse_tag arg2tag
166 ){
167 return diagonalMahalanobisDistanceSqr(op2,op1,weights,arg2tag,arg1tag);
168 }
169
170 template<class VectorT, class VectorU, class WeightT>
171 typename VectorT::value_type diagonalMahalanobisDistanceSqr(
172 VectorT const& op1,
173 VectorU const& op2,
174 WeightT const& weights,
175 dense_tag,
176 dense_tag
177 ){
178 return inner_prod(op1-op2,(op1-op2)*weights);
179 }
180
181
182 template<class MatrixT,class VectorU, class Result>
183 void distanceSqrBlockVector(
184 MatrixT const& operands,
185 VectorU const& op2,
186 Result& result
187 ){
188 typedef typename Result::value_type value_type;
189 scalar_vector< value_type, cpu_tag > one(op2.size(),static_cast<value_type>(1.0));
190 for(std::size_t i = 0; i != operands.size1(); ++i){
191 result(i) = diagonalMahalanobisDistanceSqr(
192 row(operands,i),op2,one,
193 typename MatrixT::evaluation_category::tag(),
194 typename VectorU::evaluation_category::tag()
195 );
196 }
197 }
198
199 ///\brief implementation for two input blocks where at least one matrix has only a few rows
200 template<class MatrixX,class MatrixY, class Result>
201 void distanceSqrBlockBlockRowWise(
202 MatrixX const& X,
203 MatrixY const& Y,
204 Result& distances
205 ){
206 std::size_t sizeX=X.size1();
207 std::size_t sizeY=Y.size1();
208 if(sizeX < sizeY){//iterate over the rows of the block with less rows
209 for(std::size_t i = 0; i != sizeX; ++i){
210 auto distanceRow = row(distances,i);
211 distanceSqrBlockVector(
212 Y,row(X,i),distanceRow
213 );
214 }
215 }else{
216 for(std::size_t i = 0; i != sizeY; ++i){
217 auto distanceCol = column(distances,i);
218 distanceSqrBlockVector(
219 X,row(Y,i),distanceCol
220 );
221 }
222 }
223 }
224
225 ///\brief implementation for two dense input blocks
226 template<class MatrixX,class MatrixY, class Result>
227 void distanceSqrBlockBlock(
228 MatrixX const& X,
229 MatrixY const& Y,
230 Result& distances,
231 dense_tag,
232 dense_tag
233 ){
234 typedef typename Result::value_type value_type;
235 std::size_t sizeX=X.size1();
236 std::size_t sizeY=Y.size1();
237 ensure_size(distances,X.size1(),Y.size1());
238 if(sizeX < 10 || sizeY<10){
239 distanceSqrBlockBlockRowWise(X,Y,distances);
240 return;
241 }
242 //fast blockwise iteration
243 //uses: (a-b)^2 = a^2 -2ab +b^2
244 noalias(distances) = -2*prod(X,trans(Y));
245 //first a^2+b^2
246 vector<value_type> ySqr(sizeY);
247 for(std::size_t i = 0; i != sizeY; ++i){
248 ySqr(i) = norm_sqr(row(Y,i));
249 }
250 //initialize d_ij=x_i^2+y_i^2
251 for(std::size_t i = 0; i != sizeX; ++i){
252 value_type xSqr = norm_sqr(row(X,i));
253 noalias(row(distances,i)) += repeat(xSqr,sizeY) + ySqr;
254 }
255 }
256 //\brief default implementation used, when one of the arguments is not dense
257 template<class MatrixX,class MatrixY,class Result>
258 void distanceSqrBlockBlock(
259 MatrixX const& X,
260 MatrixY const& Y,
261 Result& distances,
262 sparse_tag,
263 sparse_tag
264 ){
265 distanceSqrBlockBlockRowWise(X,Y,distances);
266 }
267}
268
269/**
270* \ingroup shark_globals
271*
272* @{
273*/
274
275/**
276* \brief Normalized Euclidian squared distance (squared diagonal Mahalanobis)
277* between two vectors.
278*
279* NOTE: The weights themselves are not squared, but multiplied onto the squared components.
280*/
281template<class VectorT, class VectorU, class WeightT, class Device>
282typename VectorT::value_type diagonalMahalanobisDistanceSqr(
283 vector_expression<VectorT, Device> const& op1,
284 vector_expression<VectorU, Device> const& op2,
285 vector_expression<WeightT, Device> const& weights
286){
287 SIZE_CHECK(op1().size()==op2().size());
288 SIZE_CHECK(op1().size()==weights().size());
289 //dispatch given the types of the argument
290 return detail::diagonalMahalanobisDistanceSqr(
291 op1(), op2(), weights(),
292 typename VectorT::evaluation_category::tag(),
293 typename VectorU::evaluation_category::tag()
294 );
295}
296
297/**
298* \brief Squared distance between two vectors.
299*/
300template<class VectorT,class VectorU, class Device>
301typename VectorT::value_type distanceSqr(
302 vector_expression<VectorT, Device> const& op1,
303 vector_expression<VectorU, Device> const& op2
304){
305 SIZE_CHECK(op1().size()==op2().size());
306 typedef typename VectorT::value_type value_type;
307 scalar_vector< value_type, cpu_tag > one(op1().size(),static_cast<value_type>(1.0));
308 return diagonalMahalanobisDistanceSqr(op1,op2,one);
309}
310
311/**
312* \brief Squared distance between a vector and a set of vectors and stores the result in the vector of distances
313*
314* The squared distance between the vector and every row-vector of the matrix is calculated.
315* This can be implemented much more efficiently.
316*/
317template<class MatrixT,class VectorU, class VectorR, class Device>
318void distanceSqr(
319 matrix_expression<MatrixT, Device> const& operands,
320 vector_expression<VectorU, Device> const& op2,
321 vector_expression<VectorR, Device>& distances
322){
323 SIZE_CHECK(operands().size2()==op2().size());
324 ensure_size(distances,operands().size1());
325 detail::distanceSqrBlockVector(
326 operands(),op2(),distances()
327 );
328}
329
330/**
331* \brief Squared distance between a vector and a set of vectors
332*
333* The squared distance between the vector and every row-vector of the matrix is calculated.
334* This can be implemented much more efficiently.
335*/
336template<class MatrixT,class VectorU, class Device>
337vector<typename MatrixT::value_type> distanceSqr(
338 matrix_expression<MatrixT, Device> const& operands,
339 vector_expression<VectorU, Device> const& op2
340){
341 SIZE_CHECK(operands().size2()==op2().size());
342 vector<typename MatrixT::value_type> distances(operands().size1());
343 distanceSqr(operands,op2,distances);
344 return distances;
345}
346
347/**
348* \brief Squared distance between a vector and a set of vectors
349*
350* The squared distance between the vector and every row-vector of the matrix is calculated.
351* This can be implemented much more efficiently.
352*/
353template<class MatrixT,class VectorU, class Device>
354vector<typename MatrixT::value_type> distanceSqr(
355 vector_expression<VectorU, Device> const& op1,
356 matrix_expression<MatrixT, Device> const& operands
357){
358 SIZE_CHECK(operands().size2()==op1().size());
359 vector<typename MatrixT::value_type> distances(operands().size1());
360 distanceSqr(operands,op1,distances);
361 return distances;
362}
363
364/**
365* \brief Squared distance between the vectors of two sets of vectors
366*
367* The squared distance between every row-vector of the first matrix x
368* and every row-vector of the second matrix y is calculated.
369* This can be implemented much more efficiently.
370* The results are returned as a matrix, where the element in the i-th
371* row and the j-th column is distanceSqr(x_i,y_j).
372*/
373template<class MatrixT,class MatrixU, class Device>
374matrix<typename MatrixT::value_type> distanceSqr(
375 matrix_expression<MatrixT, Device> const& X,
376 matrix_expression<MatrixU, Device> const& Y
377){
378 typedef matrix<typename MatrixT::value_type> Matrix;
379 SIZE_CHECK(X().size2()==Y().size2());
380 std::size_t sizeX=X().size1();
381 std::size_t sizeY=Y().size1();
382 Matrix distances(sizeX, sizeY);
383 detail::distanceSqrBlockBlock(
384 X(),Y(),distances,
385 typename MatrixT::evaluation_category::tag(),
386 typename MatrixU::evaluation_category::tag()
387 );
388 return distances;
389
390}
391
392
393/**
394* \brief Calculates distance between two vectors.
395*/
396template<class VectorT,class VectorU, class Device>
397typename VectorT::value_type distance(
398 vector_expression<VectorT, Device> const& op1,
399 vector_expression<VectorU, Device> const& op2
400){
401 SIZE_CHECK(op1().size()==op2().size());
402 return std::sqrt(distanceSqr(op1,op2));
403}
404
405/**
406* \brief Normalized euclidian distance (diagonal Mahalanobis) between two vectors.
407*
408* Contrary to some conventions, dimension-wise weights are considered instead of std. deviations:
409* \f$ d(v) = \left( \sum_i w_i (x_i-z_i)^2 \right)^{1/2} \f$
410* nb: the weights themselves are not squared, but multiplied onto the squared components
411*/
412template<class VectorT, class VectorU, class WeightT, class Device>
413typename VectorT::value_type diagonalMahalanobisDistance(
414 vector_expression<VectorT, Device> const& op1,
415 vector_expression<VectorU, Device> const& op2,
416 vector_expression<WeightT, Device> const& weights
417){
418 SIZE_CHECK(op1().size()==op2().size());
419 SIZE_CHECK(op1().size()==weights().size());
420 return std::sqrt(diagonalMahalanobisDistanceSqr(op1(), op2(), weights));
421}
422/** @}*/
423}
424
425#endif