Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
77.46% covered (warning)
77.46%
55 / 71
88.89% covered (warning)
88.89%
8 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
MetricsND
77.46% covered (warning)
77.46%
55 / 71
88.89% covered (warning)
88.89%
8 / 9
45.46
0.00% covered (danger)
0.00%
0 / 1
 __construct
n/a
0 / 0
n/a
0 / 0
1
 manhattan
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 euclidean
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 cosine
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
56
 chebyshev
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 minkowski
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 canberra
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 brayCurtis
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 angularSeparation
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
3
 hamming
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2/**
3 * Jingga
4 *
5 * PHP Version 8.1
6 *
7 * @package   phpOMS\Math\Topology
8 * @copyright Dennis Eichhorn
9 * @license   OMS License 2.0
10 * @version   1.0.0
11 * @link      https://jingga.app
12 */
13declare(strict_types=1);
14
15namespace phpOMS\Math\Topology;
16
17use phpOMS\Math\Matrix\Exception\InvalidDimensionException;
18
19/**
20 * Metrics.
21 *
22 * @package phpOMS\Math\Topology
23 * @license OMS License 2.0
24 * @link    https://jingga.app
25 * @since   1.0.0
26 */
27final class MetricsND
28{
29    /**
30     * Constructor
31     *
32     * @since 1.0.0
33     * @codeCoverageIgnore
34     */
35    private function __construct()
36    {
37    }
38
39    /**
40     * Manhatten metric.
41     *
42     * @latex d(p, q) = \sum_{n=1}^N{|p_i - q_i|}
43     *
44     * @param array<int|string, int|float> $a n-D array
45     * @param array<int|string, int|float> $b n-D array
46     *
47     * @return float
48     *
49     * @throws InvalidDimensionException
50     *
51     * @since 1.0.0
52     */
53    public static function manhattan(array $a, array $b) : float
54    {
55        if (\count($a) !== \count($b)) {
56            throw new InvalidDimensionException(\count($a) . 'x' . \count($b));
57        }
58
59        $dist = 0.0;
60        foreach ($a as $key => $e) {
61            $dist += \abs($e - $b[$key]);
62        }
63
64        return $dist;
65    }
66
67    /**
68     * Euclidean metric.
69     *
70     * @latex d(p, q) = \sqrt{\sum_{n=1}^N{(p_i - q_i)^2}}
71     *
72     * @param array<int|string, int|float> $a n-D array
73     * @param array<int|string, int|float> $b n-D array
74     *
75     * @return float
76     *
77     * @throws InvalidDimensionException
78     *
79     * @since 1.0.0
80     */
81    public static function euclidean(array $a, array $b) : float
82    {
83        if (\count($a) !== \count($b)) {
84            throw new InvalidDimensionException(\count($a) . 'x' . \count($b));
85        }
86
87        $dist = 0.0;
88        foreach ($a as $key => $e) {
89            $dist += \abs($e - $b[$key]) ** 2;
90        }
91
92        return \sqrt($dist);
93    }
94
95    /**
96     * Cosine metric.
97     *
98     * @param array<int|string, int|float> $a n-D array
99     * @param array<int|string, int|float> $b n-D array
100     *
101     * @return float
102     *
103     * @throws InvalidDimensionException
104     *
105     * @since 1.0.0
106     */
107    public static function cosine(array $a, array $b) : float
108    {
109        if (($length = \count($a)) !== \count($b)) {
110            throw new InvalidDimensionException(\count($a) . 'x' . \count($b));
111        }
112
113        $dotProduct = 0;
114        for ($i = 0; $i < $length; ++$i) {
115            $dotProduct += $a[$i] * $b[$i];
116        }
117
118        $sumOfSquares = 0;
119        foreach ($a as $value) {
120            $sumOfSquares += $value * $value;
121        }
122        $magnitude1 = \sqrt($sumOfSquares);
123
124        $sumOfSquares = 0;
125        foreach ($b as $value) {
126            $sumOfSquares += $value * $value;
127        }
128        $magnitude2 = \sqrt($sumOfSquares);
129
130        if ($magnitude1 == 0 || $magnitude2 == 0) {
131            return \PHP_FLOAT_MAX;
132        }
133
134        return $dotProduct / ($magnitude1 * $magnitude2);
135    }
136
137    /**
138     * Chebyshev metric.
139     *
140     * @latex d(p, q) = \max_i{(|p_i - q_i|)}
141     *
142     * @param array<int|string, int|float> $a n-D array
143     * @param array<int|string, int|float> $b n-D array
144     *
145     * @return float
146     *
147     * @throws InvalidDimensionException
148     *
149     * @since 1.0.0
150     */
151    public static function chebyshev(array $a, array $b) : float
152    {
153        if (\count($a) !== \count($b)) {
154            throw new InvalidDimensionException(\count($a) . 'x' . \count($b));
155        }
156
157        $dist = [];
158        foreach ($a as $key => $e) {
159            $dist[] = \abs($e - $b[$key]);
160        }
161
162        return (float) \max($dist);
163    }
164
165    /**
166     * Minkowski metric.
167     *
168     * @latex d(p, q) = \sqrt[\lambda]{\sum_{n=1}^N{|p_i - q_i|^\lambda}}
169     *
170     * @param array<int|string, int|float> $a      n-D array
171     * @param array<int|string, int|float> $b      n-D array
172     * @param int                          $lambda Lambda
173     *
174     * @return float
175     *
176     * @throws InvalidDimensionException
177     *
178     * @since 1.0.0
179     */
180    public static function minkowski(array $a, array $b, int $lambda) : float
181    {
182        if (\count($a) !== \count($b)) {
183            throw new InvalidDimensionException(\count($a) . 'x' . \count($b));
184        }
185
186        $dist = 0.0;
187        foreach ($a as $key => $e) {
188            $dist += \pow(\abs($e - $b[$key]), $lambda);
189        }
190
191        return \pow($dist, 1 / $lambda);
192    }
193
194    /**
195     * Canberra metric.
196     *
197     * @latex d(p, q) = \sum_{n=1}^N{\frac{|p_i - q_i|}{|p_i| + |q_i|}
198     *
199     * @param array<int|string, int|float> $a n-D array
200     * @param array<int|string, int|float> $b n-D array
201     *
202     * @return float
203     *
204     * @throws InvalidDimensionException
205     *
206     * @since 1.0.0
207     */
208    public static function canberra(array $a, array $b) : float
209    {
210        if (\count($a) !== \count($b)) {
211            throw new InvalidDimensionException(\count($a) . 'x' . \count($b));
212        }
213
214        $dist = 0.0;
215        foreach ($a as $key => $e) {
216            $dist += \abs($e - $b[$key]) / (\abs($e) + \abs($b[$key]));
217        }
218
219        return $dist;
220    }
221
222    /**
223     * Bray Curtis metric.
224     *
225     * @latex d(p, q) = \frac{\sum_{n=1}^N{|p_i - q_i|}}{\sum_{n=1}^N{(p_i + q_i)}}
226     *
227     * @param array<int|string, int|float> $a n-D array
228     * @param array<int|string, int|float> $b n-D array
229     *
230     * @return float
231     *
232     * @throws InvalidDimensionException
233     *
234     * @since 1.0.0
235     */
236    public static function brayCurtis(array $a, array $b) : float
237    {
238        if (\count($a) !== \count($b)) {
239            throw new InvalidDimensionException(\count($a) . 'x' . \count($b));
240        }
241
242        $distTop    = 0.0;
243        $distBottom = 0.0;
244        foreach ($a as $key => $e) {
245            $distTop    += \abs($e - $b[$key]);
246            $distBottom += $e + $b[$key];
247        }
248
249        return $distTop / $distBottom;
250    }
251
252    /**
253     * Angular separation metric.
254     *
255     * @latex d(p, q) = \frac{\sum_{n=1}^N{p_i * q_i}}{\left(\sum_{n=1}^N{p_i^2} * \sum_{n=1}^N{q_i^2}\right)^\frac{1}{2}}
256     *
257     * @param array<int|string, int|float> $a n-D array
258     * @param array<int|string, int|float> $b n-D array
259     *
260     * @return float
261     *
262     * @throws InvalidDimensionException
263     *
264     * @since 1.0.0
265     */
266    public static function angularSeparation(array $a, array $b) : float
267    {
268        if (\count($a) !== \count($b)) {
269            throw new InvalidDimensionException(\count($a) . 'x' . \count($b));
270        }
271
272        $distTop     = 0.0;
273        $distBottomA = 0.0;
274        $distBottomB = 0.0;
275        foreach ($a as $key => $e) {
276            $distTop     += $e * $b[$key];
277            $distBottomA += $e ** 2;
278            $distBottomB += $b[$key] ** 2;
279        }
280
281        return $distTop / \pow($distBottomA * $distBottomB, 1 / 2);
282    }
283
284    /**
285     * Hamming metric.
286     *
287     * @latex d(p, q) = \sum_{n=1}^N{|p_i - q_i|}
288     *
289     * @param array<int|string, int|float> $a n-D array
290     * @param array<int|string, int|float> $b n-D array
291     *
292     * @return int
293     *
294     * @throws InvalidDimensionException
295     *
296     * @since 1.0.0
297     */
298    public static function hamming(array $a, array $b) : int
299    {
300        if (($size = \count($a)) !== \count($b)) {
301            throw new InvalidDimensionException(\count($a) . 'x' . \count($b));
302        }
303
304        $dist = 0;
305        for ($i = 0; $i < $size; ++$i) {
306            if ($a[$i] !== $b[$i]) {
307                ++$dist;
308            }
309        }
310
311        return $dist;
312    }
313}