This commit is contained in:
Dennis Eichhorn 2019-12-31 17:12:28 +01:00
parent 777f16f8e5
commit 41bca90452
3 changed files with 405 additions and 2 deletions

View File

@ -75,7 +75,7 @@ final class Metrics2D
}
/**
* Euclidean metric.
* Octile metric.
*
* @latex d(p, q) = \begin{cases}(\sqrt{2} - 1) \times |p_i - q_i| + |p_{i+1} - q_{i+1}|,& \text{if } |p_i - q_i| < |p_{i+1} - q_{i+1}|\\(\sqrt{2} - 1) \times |p_{i+1} - q_{i+1}| + |p_i - q_i|,&\text{if } |p_i - q_i| \geq |p_{i+1} - q_{i+1}|\end{cases}
*
@ -188,7 +188,7 @@ final class Metrics2D
*/
public static function angularSeparation(array $a, array $b) : float
{
return ($a['x'] * $b['x'] + $a['y'] * $b['y']) / pow(($a['x'] ** 2 + $a['y'] ** 2) * ($b['x'] ** 2 + $b['y'] ** 2), 1 / 2);
return ($a['x'] * $b['x'] + $a['y'] * $b['y']) / \pow(($a['x'] ** 2 + $a['y'] ** 2) * ($b['x'] ** 2 + $b['y'] ** 2), 1 / 2);
}
/**

256
Math/Topology/MetricsND.php Normal file
View File

@ -0,0 +1,256 @@
<?php
/**
* Orange Management
*
* PHP Version 7.4
*
* @package phpOMS\Math\Topology
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace phpOMS\Math\Topology;
use phpOMS\Math\Matrix\Exception\InvalidDimensionException;
/**
* Metrics.
*
* @package phpOMS\Math\Topology
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0
*/
final class MetricsND
{
/**
* Constructor
*
* @since 1.0.0
* @codeCoverageIgnore
*/
private function __construct()
{
}
/**
* Manhatten metric.
*
* @latex d(p, q) = \sum_{n=1}^N{|p_i - q_i|}
*
* @param array $a n-D array
* @param array $b n-D array
*
* @return float
*
* @since 1.0.0
*/
public static function manhattan(array $a, array $b) : float
{
if (\count($a) !== \count($b)) {
throw new InvalidDimensionException(\count($a) . 'x' . \count($b));
}
$dist = 0.0;
foreach ($a as $key => $e) {
$dist += \abs($a[$key] - $b[$key]);
}
return $dist;
}
/**
* Euclidean metric.
*
* @latex d(p, q) = \sqrt{\sum_{n=1}^N{(p_i - q_i)^2}}
*
* @param array $a n-D array
* @param array $b n-D array
*
* @return float
*
* @since 1.0.0
*/
public static function euclidean(array $a, array $b) : float
{
if (\count($a) !== \count($b)) {
throw new InvalidDimensionException(\count($a) . 'x' . \count($b));
}
$dist = 0.0;
foreach ($a as $key => $e) {
$dist += \abs($a[$key] - $b[$key]) ** 2;
}
return \sqrt($dist);
}
/**
* Chebyshev metric.
*
* @latex d(p, q) = \max_i{(|p_i - q_i|)}
*
* @param array $a n-D array
* @param array $b n-D array
*
* @return float
*
* @since 1.0.0
*/
public static function chebyshev(array $a, array $b) : float
{
if (\count($a) !== \count($b)) {
throw new InvalidDimensionException(\count($a) . 'x' . \count($b));
}
$dist = [];
foreach ($a as $key => $e) {
$dist[] = \abs($a[$key] - $b[$key]);
}
return \max($dist);
}
/**
* Minkowski metric.
*
* @latex d(p, q) = \sqrt[\lambda]{\sum_{n=1}^N{|p_i - q_i|^\lambda}}
*
* @param array $a n-D array
* @param array $b n-D array
* @param int $lambda Lambda
*
* @return float
*
* @since 1.0.0
*/
public static function minkowski(array $a, array $b, int $lambda) : float
{
if (\count($a) !== \count($b)) {
throw new InvalidDimensionException(\count($a) . 'x' . \count($b));
}
$dist = 0.0;
foreach ($a as $key => $e) {
$dist += \pow(\abs($a[$key] - $b[$key]), $lambda);
}
return \pow($dist, 1 / $lambda);
}
/**
* Canberra metric.
*
* @latex d(p, q) = \sum_{n=1}^N{\frac{|p_i - q_i|}{|p_i| + |q_i|}
*
* @param array $a n-D array
* @param array $b n-D array
*
* @return float
*
* @since 1.0.0
*/
public static function canberra(array $a, array $b) : float
{
if (\count($a) !== \count($b)) {
throw new InvalidDimensionException(\count($a) . 'x' . \count($b));
}
$dist = 0.0;
foreach ($a as $key => $e) {
$dist += \abs($a[$key] - $b[$key]) / (\abs($a[$key]) + \abs($b[$key]));
}
return $dist;
}
/**
* Bray Curtis metric.
*
* @latex d(p, q) = \frac{\sum_{n=1}^N{|p_i - q_i|}}{\sum_{n=1}^N{(p_i + q_i)}}
*
* @param array $a n-D array
* @param array $b n-D array
*
* @return float
*
* @since 1.0.0
*/
public static function brayCurtis(array $a, array $b) : float
{
if (\count($a) !== \count($b)) {
throw new InvalidDimensionException(\count($a) . 'x' . \count($b));
}
$distTop = 0.0;
$distBottom = 0.0;
foreach ($a as $key => $e) {
$distTop += \abs($a[$key] - $b[$key]);
$distBottom += $a[$key] + $b[$key];
}
return $distTop / $distBottom;
}
/**
* Angular separation metric.
*
* @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}}
*
* @param array $a n-D array
* @param array $b n-D array
*
* @return float
*
* @since 1.0.0
*/
public static function angularSeparation(array $a, array $b) : float
{
if (\count($a) !== \count($b)) {
throw new InvalidDimensionException(\count($a) . 'x' . \count($b));
}
$distTop = 0.0;
$distBottomA = 0.0;
$distBottomB = 0.0;
foreach ($a as $key => $e) {
$distTop += $a[$key] * $b[$key];
$distBottomA += $a[$key] ** 2;
$distBottomB += $b[$key] ** 2;
}
return $distTop / \pow($distBottomA * $distBottomB, 1 / 2);
}
/**
* Hamming metric.
*
* @latex d(p, q) = \sum_{n=1}^N{|p_i - q_i|}
*
* @param array $a n-D array
* @param array $b n-D array
*
* @return int
*
* @since 1.0.0
*/
public static function hamming(array $a, array $b) : int
{
if (($size = \count($a)) !== \count($b)) {
throw new InvalidDimensionException(\count($a) . 'x' . \count($b));
}
$dist = 0;
for ($i = 0; $i < $size; ++$i) {
if ($a[$i] !== $b[$i]) {
++$dist;
}
}
return $dist;
}
}

View File

@ -0,0 +1,147 @@
<?php
/**
* Orange Management
*
* PHP Version 7.4
*
* @package tests
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace phpOMS\tests\Math\Number;
use phpOMS\Math\Topology\Metrics2D;
use phpOMS\Math\Topology\MetricsND;
/**
* @testdox phpOMS\tests\Math\Topology\MetricsNDTest: Metric/distance calculations
*
* @internal
*/
class MetricsNDTest extends \PHPUnit\Framework\TestCase
{
/**
* @testdox The manhattan distance can be calculated
* @covers phpOMS\Math\Topology\MetricsND
* @group framework
*/
public function testManhattan() : void
{
self::assertEquals(
MetricsND::manhattan(['x' => 0, 'y' => 3], ['x' => 7, 'y' => 6]),
MetricsND::manhattan(['x' => 0, 'y' => 3], ['x' => 7, 'y' => 6])
);
}
/**
* @testdox The euclidean distance can be calculated
* @covers phpOMS\Math\Topology\MetricsND
* @group framework
*/
public function testEuclidean() : void
{
self::assertEqualsWithDelta(
Metrics2D::euclidean(['x' => 0, 'y' => 3], ['x' => 7, 'y' => 6]),
MetricsND::euclidean(['x' => 0, 'y' => 3], ['x' => 7, 'y' => 6]),
0.1
);
}
/**
* @testdox The chebyshev distance can be calculated
* @covers phpOMS\Math\Topology\MetricsND
* @group framework
*/
public function testChebyshev() : void
{
self::assertEquals(
MetricsND::chebyshev(['x' => 0, 'y' => 3], ['x' => 7, 'y' => 6]),
MetricsND::chebyshev(['x' => 0, 'y' => 3], ['x' => 7, 'y' => 6])
);
}
/**
* @testdox The minkowski distance can be calculated
* @covers phpOMS\Math\Topology\MetricsND
* @group framework
*/
public function testMinkowski() : void
{
self::assertEqualsWithDelta(
Metrics2D::minkowski(['x' => 0, 'y' => 3], ['x' => 7, 'y' => 6], 3),
MetricsND::minkowski(['x' => 0, 'y' => 3], ['x' => 7, 'y' => 6], 3),
0.1
);
}
/**
* @testdox The canberra distance can be calculated
* @covers phpOMS\Math\Topology\MetricsND
* @group framework
*/
public function testCanberra() : void
{
self::assertEqualsWithDelta(
Metrics2D::canberra(['x' => 0, 'y' => 3], ['x' => 7, 'y' => 6]),
MetricsND::canberra(['x' => 0, 'y' => 3], ['x' => 7, 'y' => 6]),
0.1
);
}
/**
* @testdox The bray-curtis distance can be calculated
* @covers phpOMS\Math\Topology\MetricsND
* @group framework
*/
public function testBrayCurtis() : void
{
self::assertEqualsWithDelta(
Metrics2D::brayCurtis(['x' => 0, 'y' => 3], ['x' => 7, 'y' => 6]),
MetricsND::brayCurtis(['x' => 0, 'y' => 3], ['x' => 7, 'y' => 6]),
0.1
);
}
/**
* @testdox The angular distance can be calculated
* @covers phpOMS\Math\Topology\MetricsND
* @group framework
*/
public function testAngularSeparation() : void
{
self::assertEqualsWithDelta(
Metrics2D::angularSeparation(['x' => 0, 'y' => 3], ['x' => 7, 'y' => 6]),
MetricsND::angularSeparation(['x' => 0, 'y' => 3], ['x' => 7, 'y' => 6]),
0.1
);
}
/**
* @testdox The hamming distance can be calculated
* @covers phpOMS\Math\Topology\MetricsND
* @group framework
*/
public function testHammingDistance() : void
{
self::assertEquals(
MetricsND::hamming([1, 1, 1, 1], [0, 1, 0, 0]),
MetricsND::hamming([1, 1, 1, 1], [0, 1, 0, 0]),
);
}
/**
* @testdox Different dimension sizes for the coordinates in the hamming metric throw a InvalidDimensionException
* @covers phpOMS\Math\Topology\MetricsND
* @group framework
*/
public function testInvalidHammingDimension() : void
{
self::expectException(\phpOMS\Math\Matrix\Exception\InvalidDimensionException::class);
Metrics2D::ulam([3, 6, 4], [4, 6, 8, 3]);
}
}