1 /**
2  * @file core/metrics/lmetric_impl.hpp
3  * @author Ryan Curtin
4  *
5  * Implementation of template specializations of LMetric class.
6  *
7  * mlpack is free software; you may redistribute it and/or modify it under the
8  * terms of the 3-clause BSD license.  You should have received a copy of the
9  * 3-clause BSD license along with mlpack.  If not, see
10  * http://www.opensource.org/licenses/BSD-3-Clause for more information.
11  */
12 #ifndef MLPACK_CORE_METRICS_LMETRIC_IMPL_HPP
13 #define MLPACK_CORE_METRICS_LMETRIC_IMPL_HPP
14 
15 // In case it hasn't been included.
16 #include "lmetric.hpp"
17 
18 namespace mlpack {
19 namespace metric {
20 
21 // Unspecialized implementation.  This should almost never be used...
22 template<int Power, bool TakeRoot>
23 template<typename VecTypeA, typename VecTypeB>
Evaluate(const VecTypeA & a,const VecTypeB & b)24 typename VecTypeA::elem_type LMetric<Power, TakeRoot>::Evaluate(
25     const VecTypeA& a,
26     const VecTypeB& b)
27 {
28   typename VecTypeA::elem_type sum = 0;
29   for (size_t i = 0; i < a.n_elem; ++i)
30     sum += std::pow(fabs(a[i] - b[i]), Power);
31 
32   if (!TakeRoot) // The compiler should optimize this correctly at compile-time.
33     return sum;
34 
35   return std::pow(sum, (1.0 / Power));
36 }
37 
38 // L1-metric specializations; the root doesn't matter.
39 template<>
40 template<typename VecTypeA, typename VecTypeB>
Evaluate(const VecTypeA & a,const VecTypeB & b)41 typename VecTypeA::elem_type LMetric<1, true>::Evaluate(
42     const VecTypeA& a,
43     const VecTypeB& b)
44 {
45   return arma::accu(abs(a - b));
46 }
47 
48 template<>
49 template<typename VecTypeA, typename VecTypeB>
Evaluate(const VecTypeA & a,const VecTypeB & b)50 typename VecTypeA::elem_type LMetric<1, false>::Evaluate(
51     const VecTypeA& a,
52     const VecTypeB& b)
53 {
54   return arma::accu(abs(a - b));
55 }
56 
57 // L2-metric specializations.
58 template<>
59 template<typename VecTypeA, typename VecTypeB>
Evaluate(const VecTypeA & a,const VecTypeB & b)60 typename VecTypeA::elem_type LMetric<2, true>::Evaluate(
61     const VecTypeA& a,
62     const VecTypeB& b)
63 {
64   return arma::norm(a - b, 2);
65 }
66 
67 template<>
68 template<typename VecTypeA, typename VecTypeB>
Evaluate(const VecTypeA & a,const VecTypeB & b)69 typename VecTypeA::elem_type LMetric<2, false>::Evaluate(
70     const VecTypeA& a,
71     const VecTypeB& b)
72 {
73   return accu(arma::square(a - b));
74 }
75 
76 // L3-metric specialization (not very likely to be used, but just in case).
77 template<>
78 template<typename VecTypeA, typename VecTypeB>
Evaluate(const VecTypeA & a,const VecTypeB & b)79 typename VecTypeA::elem_type LMetric<3, true>::Evaluate(
80     const VecTypeA& a,
81     const VecTypeB& b)
82 {
83   typename VecTypeA::elem_type sum = 0;
84   for (size_t i = 0; i < a.n_elem; ++i)
85     sum += std::pow(fabs(a[i] - b[i]), 3.0);
86 
87   return std::pow(arma::accu(arma::pow(arma::abs(a - b), 3.0)), 1.0 / 3.0);
88 }
89 
90 template<>
91 template<typename VecTypeA, typename VecTypeB>
Evaluate(const VecTypeA & a,const VecTypeB & b)92 typename VecTypeA::elem_type LMetric<3, false>::Evaluate(
93     const VecTypeA& a,
94     const VecTypeB& b)
95 {
96   return arma::accu(arma::pow(arma::abs(a - b), 3.0));
97 }
98 
99 // L-infinity (Chebyshev distance) specialization
100 template<>
101 template<typename VecTypeA, typename VecTypeB>
Evaluate(const VecTypeA & a,const VecTypeB & b)102 typename VecTypeA::elem_type LMetric<INT_MAX, false>::Evaluate(
103     const VecTypeA& a,
104     const VecTypeB& b)
105 {
106   return arma::as_scalar(arma::max(arma::abs(a - b)));
107 }
108 
109 } // namespace metric
110 } // namespace mlpack
111 
112 #endif
113