/*!
*
*
* \brief Helper functions to calculate several norms and distances.
*
*
*
* \author O.Krause M.Thuma
* \date 2010-2011
*
*
* \par Copyright 1995-2017 Shark Development Team
*
*
* This file is part of Shark.
*
*
* Shark is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Shark is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Shark. If not, see .
*
*/
#ifndef SHARK_LINALG_METRICS_H
#define SHARK_LINALG_METRICS_H
#include
#include
namespace remora{
///////////////////////////////////////NORMS////////////////////////////////////////
/**
* \brief Normalized squared norm_2 (diagonal Mahalanobis).
*
* Contrary to some conventions, dimension-wise weights are considered instead of std. deviations:
* \f$ n^2(v) = \sum_i w_i v_i^2 \f$
* nb: the weights themselves are not squared, but multiplied onto the squared components
*/
template
typename VectorT::value_type diagonalMahalanobisNormSqr(
vector_expression const& vector,
vector_expression const& weights
) {
SIZE_CHECK( vector().size() == weights().size() );
return inner_prod(weights(),sqr(vector()));
}
/**
* \brief Normalized norm_2 (diagonal Mahalanobis).
*
* Contrary to some conventions, dimension-wise weights are considered instead of std. deviations:
* \f$ n^2(v) = \sqrt{\sum_i w_i v_i^2} \f$
* nb: the weights themselves are not squared, but multiplied onto the squared components
*/
template
typename VectorT::value_type diagonalMahalanobisNorm(
vector_expression const& vector,
vector_expression const& weights
) {
SIZE_CHECK( vector().size() == weights().size() );
return std::sqrt(diagonalMahalanobisNormSqr(vector,weights));
}
////////////////////////////////////////DISTANCES/////////////////////////////////////////////////
namespace detail{
/**
* \brief Normalized Euclidian squared distance (squared diagonal Mahalanobis)
* between two vectors, optimized for two Compressed arguments.
*
* Contrary to some conventions, dimension-wise weights are considered instead of std. deviations:
* \f$ d^2(v) = \sum_i w_i (x_i-z_i)^2 \f$
* NOTE: The weights themselves are not squared, but multiplied onto the squared components.
*/
template
typename VectorT::value_type diagonalMahalanobisDistanceSqr(
VectorT const& op1,
VectorU const& op2,
WeightT const& weights,
sparse_tag,
sparse_tag
){
using shark::sqr;
typename VectorT::value_type sum=0;
typename VectorT::const_iterator iter1=op1.begin();
typename VectorU::const_iterator iter2=op2.begin();
typename VectorT::const_iterator end1=op1.end();
typename VectorU::const_iterator end2=op2.end();
//be aware of empty vectors!
while(iter1 != end1 && iter2 != end2)
{
std::size_t index1=iter1.index();
std::size_t index2=iter2.index();
if(index1==index2){
sum += weights(index1) * sqr(*iter1-*iter2);
++iter1;
++iter2;
}
else if(index1
typename VectorT::value_type diagonalMahalanobisDistanceSqr(
VectorT const& op1,
VectorU const& op2,
WeightT const& weights,
sparse_tag,
dense_tag
){
std::size_t pos = 0;
typename VectorT::value_type sum=0;
auto end=op1.end();
for(auto iter=op1.begin();iter != end;++iter,++pos){
for(;pos != iter.index();++pos){
sum += weights(pos) * op2(pos) * op2(pos);
}
double diff = *iter-op2(pos);
sum += weights(pos) * diff * diff;
}
for(;pos != op2.size();++pos){
sum += weights(pos) * op2(pos) * op2(pos);
}
return sum;
}
template
typename VectorT::value_type diagonalMahalanobisDistanceSqr(
VectorT const& op1,
VectorU const& op2,
WeightT const& weights,
dense_tag arg1tag,
sparse_tag arg2tag
){
return diagonalMahalanobisDistanceSqr(op2,op1,weights,arg2tag,arg1tag);
}
template
typename VectorT::value_type diagonalMahalanobisDistanceSqr(
VectorT const& op1,
VectorU const& op2,
WeightT const& weights,
dense_tag,
dense_tag
){
return inner_prod(op1-op2,(op1-op2)*weights);
}
template
void distanceSqrBlockVector(
MatrixT const& operands,
VectorU const& op2,
Result& result
){
typedef typename Result::value_type value_type;
scalar_vector< value_type, cpu_tag > one(op2.size(),static_cast(1.0));
for(std::size_t i = 0; i != operands.size1(); ++i){
result(i) = diagonalMahalanobisDistanceSqr(
row(operands,i),op2,one,
typename MatrixT::evaluation_category::tag(),
typename VectorU::evaluation_category::tag()
);
}
}
///\brief implementation for two input blocks where at least one matrix has only a few rows
template
void distanceSqrBlockBlockRowWise(
MatrixX const& X,
MatrixY const& Y,
Result& distances
){
std::size_t sizeX=X.size1();
std::size_t sizeY=Y.size1();
if(sizeX < sizeY){//iterate over the rows of the block with less rows
for(std::size_t i = 0; i != sizeX; ++i){
auto distanceRow = row(distances,i);
distanceSqrBlockVector(
Y,row(X,i),distanceRow
);
}
}else{
for(std::size_t i = 0; i != sizeY; ++i){
auto distanceCol = column(distances,i);
distanceSqrBlockVector(
X,row(Y,i),distanceCol
);
}
}
}
///\brief implementation for two dense input blocks
template
void distanceSqrBlockBlock(
MatrixX const& X,
MatrixY const& Y,
Result& distances,
dense_tag,
dense_tag
){
typedef typename Result::value_type value_type;
std::size_t sizeX=X.size1();
std::size_t sizeY=Y.size1();
ensure_size(distances,X.size1(),Y.size1());
if(sizeX < 10 || sizeY<10){
distanceSqrBlockBlockRowWise(X,Y,distances);
return;
}
//fast blockwise iteration
//uses: (a-b)^2 = a^2 -2ab +b^2
noalias(distances) = -2*prod(X,trans(Y));
//first a^2+b^2
vector ySqr(sizeY);
for(std::size_t i = 0; i != sizeY; ++i){
ySqr(i) = norm_sqr(row(Y,i));
}
//initialize d_ij=x_i^2+y_i^2
for(std::size_t i = 0; i != sizeX; ++i){
value_type xSqr = norm_sqr(row(X,i));
noalias(row(distances,i)) += repeat(xSqr,sizeY) + ySqr;
}
}
//\brief default implementation used, when one of the arguments is not dense
template
void distanceSqrBlockBlock(
MatrixX const& X,
MatrixY const& Y,
Result& distances,
sparse_tag,
sparse_tag
){
distanceSqrBlockBlockRowWise(X,Y,distances);
}
}
/**
* \ingroup shark_globals
*
* @{
*/
/**
* \brief Normalized Euclidian squared distance (squared diagonal Mahalanobis)
* between two vectors.
*
* NOTE: The weights themselves are not squared, but multiplied onto the squared components.
*/
template
typename VectorT::value_type diagonalMahalanobisDistanceSqr(
vector_expression const& op1,
vector_expression const& op2,
vector_expression const& weights
){
SIZE_CHECK(op1().size()==op2().size());
SIZE_CHECK(op1().size()==weights().size());
//dispatch given the types of the argument
return detail::diagonalMahalanobisDistanceSqr(
op1(), op2(), weights(),
typename VectorT::evaluation_category::tag(),
typename VectorU::evaluation_category::tag()
);
}
/**
* \brief Squared distance between two vectors.
*/
template
typename VectorT::value_type distanceSqr(
vector_expression const& op1,
vector_expression const& op2
){
SIZE_CHECK(op1().size()==op2().size());
typedef typename VectorT::value_type value_type;
scalar_vector< value_type, cpu_tag > one(op1().size(),static_cast(1.0));
return diagonalMahalanobisDistanceSqr(op1,op2,one);
}
/**
* \brief Squared distance between a vector and a set of vectors and stores the result in the vector of distances
*
* The squared distance between the vector and every row-vector of the matrix is calculated.
* This can be implemented much more efficiently.
*/
template
void distanceSqr(
matrix_expression const& operands,
vector_expression const& op2,
vector_expression& distances
){
SIZE_CHECK(operands().size2()==op2().size());
ensure_size(distances,operands().size1());
detail::distanceSqrBlockVector(
operands(),op2(),distances()
);
}
/**
* \brief Squared distance between a vector and a set of vectors
*
* The squared distance between the vector and every row-vector of the matrix is calculated.
* This can be implemented much more efficiently.
*/
template
vector distanceSqr(
matrix_expression const& operands,
vector_expression const& op2
){
SIZE_CHECK(operands().size2()==op2().size());
vector distances(operands().size1());
distanceSqr(operands,op2,distances);
return distances;
}
/**
* \brief Squared distance between a vector and a set of vectors
*
* The squared distance between the vector and every row-vector of the matrix is calculated.
* This can be implemented much more efficiently.
*/
template
vector distanceSqr(
vector_expression const& op1,
matrix_expression const& operands
){
SIZE_CHECK(operands().size2()==op1().size());
vector distances(operands().size1());
distanceSqr(operands,op1,distances);
return distances;
}
/**
* \brief Squared distance between the vectors of two sets of vectors
*
* The squared distance between every row-vector of the first matrix x
* and every row-vector of the second matrix y is calculated.
* This can be implemented much more efficiently.
* The results are returned as a matrix, where the element in the i-th
* row and the j-th column is distanceSqr(x_i,y_j).
*/
template
matrix distanceSqr(
matrix_expression const& X,
matrix_expression const& Y
){
typedef matrix Matrix;
SIZE_CHECK(X().size2()==Y().size2());
std::size_t sizeX=X().size1();
std::size_t sizeY=Y().size1();
Matrix distances(sizeX, sizeY);
detail::distanceSqrBlockBlock(
X(),Y(),distances,
typename MatrixT::evaluation_category::tag(),
typename MatrixU::evaluation_category::tag()
);
return distances;
}
/**
* \brief Calculates distance between two vectors.
*/
template
typename VectorT::value_type distance(
vector_expression const& op1,
vector_expression const& op2
){
SIZE_CHECK(op1().size()==op2().size());
return std::sqrt(distanceSqr(op1,op2));
}
/**
* \brief Normalized euclidian distance (diagonal Mahalanobis) between two vectors.
*
* Contrary to some conventions, dimension-wise weights are considered instead of std. deviations:
* \f$ d(v) = \left( \sum_i w_i (x_i-z_i)^2 \right)^{1/2} \f$
* nb: the weights themselves are not squared, but multiplied onto the squared components
*/
template
typename VectorT::value_type diagonalMahalanobisDistance(
vector_expression const& op1,
vector_expression const& op2,
vector_expression const& weights
){
SIZE_CHECK(op1().size()==op2().size());
SIZE_CHECK(op1().size()==weights().size());
return std::sqrt(diagonalMahalanobisDistanceSqr(op1(), op2(), weights));
}
/** @}*/
}
#endif