Skip to content

Commit

Permalink
Improve distance performance and reduce duplication in distance class…
Browse files Browse the repository at this point in the history
…es. (#348)

* Issue #347: Reduce duplicated code.

* Issue #347: Replace array_* with regular loops for better perfomance.
  • Loading branch information
drupol authored and akondas committed Feb 6, 2019
1 parent 6844cf4 commit 4b837fa
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 79 deletions.
23 changes: 6 additions & 17 deletions src/Math/Distance/Chebyshev.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,16 @@

namespace Phpml\Math\Distance;

use Phpml\Exception\InvalidArgumentException;
use Phpml\Math\Distance;

class Chebyshev implements Distance
/**
* Class Chebyshev
*/
class Chebyshev extends Distance
{
/**
* @throws InvalidArgumentException
* {@inheritdoc}
*/
public function distance(array $a, array $b): float
{
if (count($a) !== count($b)) {
throw new InvalidArgumentException('Size of given arrays does not match');
}

$differences = [];
$count = count($a);

for ($i = 0; $i < $count; ++$i) {
$differences[] = abs($a[$i] - $b[$i]);
}

return max($differences);
return max($this->deltas($a, $b));
}
}
61 changes: 61 additions & 0 deletions src/Math/Distance/Distance.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);

namespace Phpml\Math\Distance;

use Phpml\Exception\InvalidArgumentException;
use Phpml\Math\Distance as DistanceInterface;

/**
* Class Distance
*/
abstract class Distance implements DistanceInterface
{
/**
* @var float|int
*/
public $norm;

/**
* Distance constructor.
*/
public function __construct(float $norm = 3.0)
{
$this->norm = $norm;
}

/**
* @throws InvalidArgumentException
*/
public function distance(array $a, array $b): float
{
$distance = 0;

foreach ($this->deltas($a, $b) as $delta) {
$distance += $delta ** $this->norm;
}

return $distance ** (1 / $this->norm);
}

/**
* @throws InvalidArgumentException
*/
protected function deltas(array $a, array $b): array
{
$count = count($a);

if ($count !== count($b)) {
throw new InvalidArgumentException('Size of given arrays does not match');
}

$deltas = [];

for ($i = 0; $i < $count; $i++) {
$deltas[] = abs($a[$i] - $b[$i]);
}

return $deltas;
}
}
28 changes: 11 additions & 17 deletions src/Math/Distance/Euclidean.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,25 @@

namespace Phpml\Math\Distance;

use Phpml\Exception\InvalidArgumentException;
use Phpml\Math\Distance;

class Euclidean implements Distance
/**
* Class Euclidean
*
* L^2 Metric.
*/
class Euclidean extends Distance
{
/**
* @throws InvalidArgumentException
* Euclidean constructor.
*/
public function distance(array $a, array $b): float
public function __construct()
{
if (count($a) !== count($b)) {
throw new InvalidArgumentException('Size of given arrays does not match');
}

$distance = 0;

foreach ($a as $i => $val) {
$distance += ($val - $b[$i]) ** 2;
}

return sqrt((float) $distance);
parent::__construct(2.0);
}

/**
* Square of Euclidean distance
*
* @throws \Phpml\Exception\InvalidArgumentException
*/
public function sqDistance(array $a, array $b): float
{
Expand Down
22 changes: 9 additions & 13 deletions src/Math/Distance/Manhattan.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,18 @@

namespace Phpml\Math\Distance;

use Phpml\Exception\InvalidArgumentException;
use Phpml\Math\Distance;

class Manhattan implements Distance
/**
* Class Manhattan
*
* L^1 Metric.
*/
class Manhattan extends Distance
{
/**
* @throws InvalidArgumentException
* Manhattan constructor.
*/
public function distance(array $a, array $b): float
public function __construct()
{
if (count($a) !== count($b)) {
throw new InvalidArgumentException('Size of given arrays does not match');
}

return array_sum(array_map(function ($m, $n) {
return abs($m - $n);
}, $a, $b));
parent::__construct(1.0);
}
}
38 changes: 6 additions & 32 deletions src/Math/Distance/Minkowski.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,11 @@

namespace Phpml\Math\Distance;

use Phpml\Exception\InvalidArgumentException;
use Phpml\Math\Distance;

class Minkowski implements Distance
/**
* Class Minkowski
*
* L^n Metric.
*/
class Minkowski extends Distance
{
/**
* @var float
*/
private $lambda;

public function __construct(float $lambda = 3.0)
{
$this->lambda = $lambda;
}

/**
* @throws InvalidArgumentException
*/
public function distance(array $a, array $b): float
{
if (count($a) !== count($b)) {
throw new InvalidArgumentException('Size of given arrays does not match');
}

$distance = 0;
$count = count($a);

for ($i = 0; $i < $count; ++$i) {
$distance += pow(abs($a[$i] - $b[$i]), $this->lambda);
}

return (float) pow($distance, 1 / $this->lambda);
}
}

0 comments on commit 4b837fa

Please sign in to comment.