Add phpstan strict rules (#233)

* Add phpstan strict rules

* Fix travis coveralls

* Add phpstan-phpunit strict rules

* Fix eigen decomposition test name and phpstan ingored error
This commit is contained in:
Arkadiusz Kondas 2018-02-16 07:25:24 +01:00 committed by GitHub
parent 6ac61a860c
commit 16dc16b0d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 164 additions and 71 deletions

View File

@ -41,5 +41,5 @@ after_success:
- | - |
if [[ $PHPUNIT_FLAGS != "" ]]; then if [[ $PHPUNIT_FLAGS != "" ]]; then
wget https://github.com/php-coveralls/php-coveralls/releases/download/v2.0.0/php-coveralls.phar wget https://github.com/php-coveralls/php-coveralls/releases/download/v2.0.0/php-coveralls.phar
php coveralls.phar --verbose; php php-coveralls.phar --verbose;
fi fi

View File

@ -23,7 +23,9 @@
"php": "^7.1" "php": "^7.1"
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan-phpunit": "^0.9.4",
"phpstan/phpstan-shim": "^0.9", "phpstan/phpstan-shim": "^0.9",
"phpstan/phpstan-strict-rules": "^0.9.0",
"phpunit/phpunit": "^7.0.0", "phpunit/phpunit": "^7.0.0",
"symplify/coding-standard": "^3.1", "symplify/coding-standard": "^3.1",
"symplify/easy-coding-standard": "^3.1" "symplify/easy-coding-standard": "^3.1"

91
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "3e327a50a76dd6df905cef56cbc37e02", "content-hash": "9135b3dece8c6938922f757123274e95",
"packages": [], "packages": [],
"packages-dev": [ "packages-dev": [
{ {
@ -1287,6 +1287,51 @@
], ],
"time": "2017-11-24T13:59:53+00:00" "time": "2017-11-24T13:59:53+00:00"
}, },
{
"name": "phpstan/phpstan-phpunit",
"version": "0.9.4",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git",
"reference": "852411f841a37aeca2fa5af0002b0272c485c9bf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/852411f841a37aeca2fa5af0002b0272c485c9bf",
"reference": "852411f841a37aeca2fa5af0002b0272c485c9bf",
"shasum": ""
},
"require": {
"php": "~7.0",
"phpstan/phpstan": "^0.9.1",
"phpunit/phpunit": "^6.3 || ~7.0"
},
"require-dev": {
"consistence/coding-standard": "^2.0",
"jakub-onderka/php-parallel-lint": "^0.9.2",
"phing/phing": "^2.16.0",
"phpstan/phpstan-strict-rules": "^0.9",
"satooshi/php-coveralls": "^1.0",
"slevomat/coding-standard": "^3.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.9-dev"
}
},
"autoload": {
"psr-4": {
"PHPStan\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "PHPUnit extensions and rules for PHPStan",
"time": "2018-02-02T09:45:47+00:00"
},
{ {
"name": "phpstan/phpstan-shim", "name": "phpstan/phpstan-shim",
"version": "0.9.1", "version": "0.9.1",
@ -1324,6 +1369,50 @@
"description": "PHPStan Phar distribution", "description": "PHPStan Phar distribution",
"time": "2017-12-02T20:14:45+00:00" "time": "2017-12-02T20:14:45+00:00"
}, },
{
"name": "phpstan/phpstan-strict-rules",
"version": "0.9",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
"reference": "15be9090622c6b85c079922308f831018d8d9e23"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/15be9090622c6b85c079922308f831018d8d9e23",
"reference": "15be9090622c6b85c079922308f831018d8d9e23",
"shasum": ""
},
"require": {
"php": "~7.0",
"phpstan/phpstan": "^0.9"
},
"require-dev": {
"consistence/coding-standard": "^2.0.0",
"jakub-onderka/php-parallel-lint": "^0.9.2",
"phing/phing": "^2.16.0",
"phpstan/phpstan-phpunit": "^0.9",
"phpunit/phpunit": "^6.4",
"slevomat/coding-standard": "^3.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.9-dev"
}
},
"autoload": {
"psr-4": {
"PHPStan\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Extra strict and opinionated rules for PHPStan",
"time": "2017-11-26T20:12:30+00:00"
},
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "6.0.1", "version": "6.0.1",

View File

@ -1,15 +1,17 @@
includes:
- vendor/phpstan/phpstan-strict-rules/rules.neon
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
- vendor/phpstan/phpstan-phpunit/strictRules.neon
parameters: parameters:
ignoreErrors: ignoreErrors:
- '#Phpml\\Dataset\\FilesDataset::__construct\(\) does not call parent constructor from Phpml\\Dataset\\ArrayDataset#' - '#Phpml\\Dataset\\FilesDataset::__construct\(\) does not call parent constructor from Phpml\\Dataset\\ArrayDataset#'
# mocks
- '#PHPUnit_Framework_MockObject_MockObject#'
# wide range cases # wide range cases
- '#Call to function count\(\) with argument type array<int>\|Phpml\\Clustering\\KMeans\\Point will always result in number 1#' - '#Call to function count\(\) with argument type array<int>\|Phpml\\Clustering\\KMeans\\Point will always result in number 1#'
- '#Parameter \#1 \$coordinates of class Phpml\\Clustering\\KMeans\\Point constructor expects array, array<int>\|Phpml\\Clustering\\KMeans\\Point given#' - '#Parameter \#1 \$coordinates of class Phpml\\Clustering\\KMeans\\Point constructor expects array, array<int>\|Phpml\\Clustering\\KMeans\\Point given#'
# probably known value # probably known value
- '#Variable \$j might not be defined#'
- '#Method Phpml\\Classification\\DecisionTree::getBestSplit\(\) should return Phpml\\Classification\\DecisionTree\\DecisionTreeLeaf but returns Phpml\\Classification\\DecisionTree\\DecisionTreeLeaf\|null#' - '#Method Phpml\\Classification\\DecisionTree::getBestSplit\(\) should return Phpml\\Classification\\DecisionTree\\DecisionTreeLeaf but returns Phpml\\Classification\\DecisionTree\\DecisionTreeLeaf\|null#'
- '#Call to an undefined method Phpml\\Helper\\Optimizer\\Optimizer::getCostValues\(\)#' - '#Call to an undefined method Phpml\\Helper\\Optimizer\\Optimizer::getCostValues\(\)#'

View File

@ -63,11 +63,11 @@ class Apriori implements Associator
*/ */
public function getRules(): array public function getRules(): array
{ {
if (!$this->large) { if (empty($this->large)) {
$this->large = $this->apriori(); $this->large = $this->apriori();
} }
if ($this->rules) { if (!empty($this->rules)) {
return $this->rules; return $this->rules;
} }

View File

@ -273,15 +273,15 @@ class DecisionTree implements Classifier
} }
if ($allSame || $depth >= $this->maxDepth || count($remainingTargets) === 1) { if ($allSame || $depth >= $this->maxDepth || count($remainingTargets) === 1) {
$split->isTerminal = 1; $split->isTerminal = true;
arsort($remainingTargets); arsort($remainingTargets);
$split->classValue = key($remainingTargets); $split->classValue = key($remainingTargets);
} else { } else {
if ($leftRecords) { if (!empty($leftRecords)) {
$split->leftLeaf = $this->getSplitLeaf($leftRecords, $depth + 1); $split->leftLeaf = $this->getSplitLeaf($leftRecords, $depth + 1);
} }
if ($rightRecords) { if (!empty($rightRecords)) {
$split->rightLeaf = $this->getSplitLeaf($rightRecords, $depth + 1); $split->rightLeaf = $this->getSplitLeaf($rightRecords, $depth + 1);
} }
} }
@ -312,13 +312,13 @@ class DecisionTree implements Classifier
$split->value = $baseValue; $split->value = $baseValue;
$split->giniIndex = $gini; $split->giniIndex = $gini;
$split->columnIndex = $i; $split->columnIndex = $i;
$split->isContinuous = $this->columnTypes[$i] == self::CONTINUOUS; $split->isContinuous = $this->columnTypes[$i] === self::CONTINUOUS;
$split->records = $records; $split->records = $records;
// If a numeric column is to be selected, then // If a numeric column is to be selected, then
// the original numeric value and the selected operator // the original numeric value and the selected operator
// will also be saved into the leaf for future access // will also be saved into the leaf for future access
if ($this->columnTypes[$i] == self::CONTINUOUS) { if ($this->columnTypes[$i] === self::CONTINUOUS) {
$matches = []; $matches = [];
preg_match("/^([<>=]{1,2})\s*(.*)/", (string) $split->value, $matches); preg_match("/^([<>=]{1,2})\s*(.*)/", (string) $split->value, $matches);
$split->operator = $matches[1]; $split->operator = $matches[1];
@ -349,11 +349,11 @@ class DecisionTree implements Classifier
protected function getSelectedFeatures(): array protected function getSelectedFeatures(): array
{ {
$allFeatures = range(0, $this->featureCount - 1); $allFeatures = range(0, $this->featureCount - 1);
if ($this->numUsableFeatures === 0 && !$this->selectedFeatures) { if ($this->numUsableFeatures === 0 && empty($this->selectedFeatures)) {
return $allFeatures; return $allFeatures;
} }
if ($this->selectedFeatures) { if (!empty($this->selectedFeatures)) {
return $this->selectedFeatures; return $this->selectedFeatures;
} }
@ -363,7 +363,7 @@ class DecisionTree implements Classifier
} }
shuffle($allFeatures); shuffle($allFeatures);
$selectedFeatures = array_slice($allFeatures, 0, $numFeatures, false); $selectedFeatures = array_slice($allFeatures, 0, $numFeatures);
sort($selectedFeatures); sort($selectedFeatures);
return $selectedFeatures; return $selectedFeatures;
@ -406,7 +406,7 @@ class DecisionTree implements Classifier
// all values in that column (Lower than or equal to %20 of all values) // all values in that column (Lower than or equal to %20 of all values)
$numericValues = array_filter($columnValues, 'is_numeric'); $numericValues = array_filter($columnValues, 'is_numeric');
$floatValues = array_filter($columnValues, 'is_float'); $floatValues = array_filter($columnValues, 'is_float');
if ($floatValues) { if (!empty($floatValues)) {
return false; return false;
} }
@ -433,7 +433,7 @@ class DecisionTree implements Classifier
*/ */
protected function getSplitNodesByColumn(int $column, DecisionTreeLeaf $node): array protected function getSplitNodesByColumn(int $column, DecisionTreeLeaf $node): array
{ {
if (!$node || $node->isTerminal) { if ($node->isTerminal) {
return []; return [];
} }
@ -444,11 +444,11 @@ class DecisionTree implements Classifier
$lNodes = []; $lNodes = [];
$rNodes = []; $rNodes = [];
if ($node->leftLeaf) { if ($node->leftLeaf !== null) {
$lNodes = $this->getSplitNodesByColumn($column, $node->leftLeaf); $lNodes = $this->getSplitNodesByColumn($column, $node->leftLeaf);
} }
if ($node->rightLeaf) { if ($node->rightLeaf !== null) {
$rNodes = $this->getSplitNodesByColumn($column, $node->rightLeaf); $rNodes = $this->getSplitNodesByColumn($column, $node->rightLeaf);
} }
@ -475,6 +475,6 @@ class DecisionTree implements Classifier
} }
} while ($node); } while ($node);
return $node ? $node->classValue : $this->labels[0]; return $node !== null ? $node->classValue : $this->labels[0];
} }
} }

View File

@ -29,14 +29,14 @@ class DecisionTreeLeaf
public $columnIndex; public $columnIndex;
/** /**
* @var DecisionTreeLeaf * @var ?DecisionTreeLeaf
*/ */
public $leftLeaf = null; public $leftLeaf;
/** /**
* @var DecisionTreeLeaf * @var ?DecisionTreeLeaf
*/ */
public $rightLeaf = null; public $rightLeaf;
/** /**
* @var array * @var array
@ -52,7 +52,7 @@ class DecisionTreeLeaf
public $classValue = ''; public $classValue = '';
/** /**
* @var bool|int * @var bool
*/ */
public $isTerminal = false; public $isTerminal = false;
@ -103,12 +103,12 @@ class DecisionTreeLeaf
$nodeSampleCount = (float) count($this->records); $nodeSampleCount = (float) count($this->records);
$iT = $this->giniIndex; $iT = $this->giniIndex;
if ($this->leftLeaf) { if ($this->leftLeaf !== null) {
$pL = count($this->leftLeaf->records) / $nodeSampleCount; $pL = count($this->leftLeaf->records) / $nodeSampleCount;
$iT -= $pL * $this->leftLeaf->giniIndex; $iT -= $pL * $this->leftLeaf->giniIndex;
} }
if ($this->rightLeaf) { if ($this->rightLeaf !== null) {
$pR = count($this->rightLeaf->records) / $nodeSampleCount; $pR = count($this->rightLeaf->records) / $nodeSampleCount;
$iT -= $pR * $this->rightLeaf->giniIndex; $iT -= $pR * $this->rightLeaf->giniIndex;
} }
@ -140,16 +140,16 @@ class DecisionTreeLeaf
$str = "<table ><tr><td colspan=3 align=center style='border:1px solid;'>${value}</td></tr>"; $str = "<table ><tr><td colspan=3 align=center style='border:1px solid;'>${value}</td></tr>";
if ($this->leftLeaf || $this->rightLeaf) { if ($this->leftLeaf !== null || $this->rightLeaf !== null) {
$str .= '<tr>'; $str .= '<tr>';
if ($this->leftLeaf) { if ($this->leftLeaf !== null) {
$str .= '<td valign=top><b>| Yes</b><br>'.$this->leftLeaf->getHTML($columnNames).'</td>'; $str .= '<td valign=top><b>| Yes</b><br>'.$this->leftLeaf->getHTML($columnNames).'</td>';
} else { } else {
$str .= '<td></td>'; $str .= '<td></td>';
} }
$str .= '<td>&nbsp;</td>'; $str .= '<td>&nbsp;</td>';
if ($this->rightLeaf) { if ($this->rightLeaf !== null) {
$str .= '<td valign=top align=right><b>No |</b><br>'.$this->rightLeaf->getHTML($columnNames).'</td>'; $str .= '<td valign=top align=right><b>No |</b><br>'.$this->rightLeaf->getHTML($columnNames).'</td>';
} else { } else {
$str .= '<td></td>'; $str .= '<td></td>';

View File

@ -158,7 +158,7 @@ class AdaBoost implements Classifier
protected function getBestClassifier(): Classifier protected function getBestClassifier(): Classifier
{ {
$ref = new ReflectionClass($this->baseClassifier); $ref = new ReflectionClass($this->baseClassifier);
if ($this->classifierOptions) { if (!empty($this->classifierOptions)) {
$classifier = $ref->newInstanceArgs($this->classifierOptions); $classifier = $ref->newInstanceArgs($this->classifierOptions);
} else { } else {
$classifier = $ref->newInstance(); $classifier = $ref->newInstance();

View File

@ -145,7 +145,7 @@ class Bagging implements Classifier
$classifiers = []; $classifiers = [];
for ($i = 0; $i < $this->numClassifier; ++$i) { for ($i = 0; $i < $this->numClassifier; ++$i) {
$ref = new ReflectionClass($this->classifier); $ref = new ReflectionClass($this->classifier);
if ($this->classifierOptions) { if (!empty($this->classifierOptions)) {
$obj = $ref->newInstanceArgs($this->classifierOptions); $obj = $ref->newInstanceArgs($this->classifierOptions);
} else { } else {
$obj = $ref->newInstance(); $obj = $ref->newInstance();

View File

@ -42,7 +42,7 @@ class Adaline extends Perceptron
bool $normalizeInputs = true, bool $normalizeInputs = true,
int $trainingType = self::BATCH_TRAINING int $trainingType = self::BATCH_TRAINING
) { ) {
if (!in_array($trainingType, [self::BATCH_TRAINING, self::ONLINE_TRAINING])) { if (!in_array($trainingType, [self::BATCH_TRAINING, self::ONLINE_TRAINING], true)) {
throw new Exception('Adaline can only be trained with batch and online/stochastic gradient descent algorithm'); throw new Exception('Adaline can only be trained with batch and online/stochastic gradient descent algorithm');
} }

View File

@ -118,7 +118,7 @@ class DecisionStump extends WeightedClassifier
// Check the size of the weights given. // Check the size of the weights given.
// If none given, then assign 1 as a weight to each sample // If none given, then assign 1 as a weight to each sample
if ($this->weights) { if (!empty($this->weights)) {
$numWeights = count($this->weights); $numWeights = count($this->weights);
if ($numWeights != count($samples)) { if ($numWeights != count($samples)) {
throw new Exception('Number of sample weights does not match with number of samples'); throw new Exception('Number of sample weights does not match with number of samples');

View File

@ -71,13 +71,13 @@ class LogisticRegression extends Adaline
string $penalty = 'L2' string $penalty = 'L2'
) { ) {
$trainingTypes = range(self::BATCH_TRAINING, self::CONJUGATE_GRAD_TRAINING); $trainingTypes = range(self::BATCH_TRAINING, self::CONJUGATE_GRAD_TRAINING);
if (!in_array($trainingType, $trainingTypes)) { if (!in_array($trainingType, $trainingTypes, true)) {
throw new Exception('Logistic regression can only be trained with '. throw new Exception('Logistic regression can only be trained with '.
'batch (gradient descent), online (stochastic gradient descent) '. 'batch (gradient descent), online (stochastic gradient descent) '.
'or conjugate batch (conjugate gradients) algorithms'); 'or conjugate batch (conjugate gradients) algorithms');
} }
if (!in_array($cost, ['log', 'sse'])) { if (!in_array($cost, ['log', 'sse'], true)) {
throw new Exception("Logistic regression cost function can be one of the following: \n". throw new Exception("Logistic regression cost function can be one of the following: \n".
"'log' for log-likelihood and 'sse' for sum of squared errors"); "'log' for log-likelihood and 'sse' for sum of squared errors");
} }

View File

@ -97,7 +97,7 @@ class Perceptron implements Classifier, IncrementalEstimator
public function trainBinary(array $samples, array $targets, array $labels): void public function trainBinary(array $samples, array $targets, array $labels): void
{ {
if ($this->normalizer) { if ($this->normalizer !== null) {
$this->normalizer->transform($samples); $this->normalizer->transform($samples);
} }
@ -196,7 +196,7 @@ class Perceptron implements Classifier, IncrementalEstimator
*/ */
protected function checkNormalizedSample(array $sample): array protected function checkNormalizedSample(array $sample): array
{ {
if ($this->normalizer) { if ($this->normalizer !== null) {
$samples = [$sample]; $samples = [$sample];
$this->normalizer->transform($samples); $this->normalizer->transform($samples);
$sample = $samples[0]; $sample = $samples[0];

View File

@ -16,11 +16,11 @@ class MLPClassifier extends MultilayerPerceptron implements Classifier
*/ */
public function getTargetClass($target): int public function getTargetClass($target): int
{ {
if (!in_array($target, $this->classes)) { if (!in_array($target, $this->classes, true)) {
throw InvalidArgumentException::invalidTarget($target); throw InvalidArgumentException::invalidTarget($target);
} }
return array_search($target, $this->classes); return array_search($target, $this->classes, true);
} }
/** /**

View File

@ -20,7 +20,7 @@ class FuzzyCMeans implements Clusterer
/** /**
* @var array|Cluster[] * @var array|Cluster[]
*/ */
private $clusters = null; private $clusters = [];
/** /**
* @var Space * @var Space
@ -152,8 +152,7 @@ class FuzzyCMeans implements Clusterer
protected function updateClusters(): void protected function updateClusters(): void
{ {
$dim = $this->space->getDimension(); $dim = $this->space->getDimension();
if (!$this->clusters) { if (empty($this->clusters)) {
$this->clusters = [];
for ($i = 0; $i < $this->clustersNumber; ++$i) { for ($i = 0; $i < $this->clustersNumber; ++$i) {
$this->clusters[] = new Cluster($this->space, array_fill(0, $dim, 0.0)); $this->clusters[] = new Cluster($this->space, array_fill(0, $dim, 0.0));
} }

View File

@ -76,8 +76,7 @@ class Cluster extends Point implements IteratorAggregate, Countable
public function updateCentroid(): void public function updateCentroid(): void
{ {
$count = count($this->points); if (empty($this->points)) {
if (!$count) {
return; return;
} }
@ -89,6 +88,7 @@ class Cluster extends Point implements IteratorAggregate, Countable
} }
} }
$count = count($this->points);
for ($n = 0; $n < $this->dimension; ++$n) { for ($n = 0; $n < $this->dimension; ++$n) {
$this->coordinates[$n] = $centroid->coordinates[$n] / $count; $this->coordinates[$n] = $centroid->coordinates[$n] / $count;
} }

View File

@ -16,7 +16,7 @@ class Space extends SplObjectStorage
*/ */
protected $dimension; protected $dimension;
public function __construct($dimension) public function __construct(int $dimension)
{ {
if ($dimension < 1) { if ($dimension < 1) {
throw new LogicException('a space dimension cannot be null or negative'); throw new LogicException('a space dimension cannot be null or negative');
@ -75,7 +75,7 @@ class Space extends SplObjectStorage
*/ */
public function getBoundaries() public function getBoundaries()
{ {
if (!count($this)) { if (empty($this)) {
return false; return false;
} }
@ -153,8 +153,8 @@ class Space extends SplObjectStorage
$closest = $point->getClosest($clusters); $closest = $point->getClosest($clusters);
if ($closest !== $cluster) { if ($closest !== $cluster) {
isset($attach[$closest]) || $attach[$closest] = new SplObjectStorage(); $attach[$closest] ?? $attach[$closest] = new SplObjectStorage();
isset($detach[$cluster]) || $detach[$cluster] = new SplObjectStorage(); $detach[$cluster] ?? $detach[$cluster] = new SplObjectStorage();
$attach[$closest]->attach($point); $attach[$closest]->attach($point);
$detach[$cluster]->attach($point); $detach[$cluster]->attach($point);

View File

@ -58,7 +58,7 @@ class KernelPCA extends PCA
public function __construct(int $kernel = self::KERNEL_RBF, ?float $totalVariance = null, ?int $numFeatures = null, ?float $gamma = null) public function __construct(int $kernel = self::KERNEL_RBF, ?float $totalVariance = null, ?int $numFeatures = null, ?float $gamma = null)
{ {
$availableKernels = [self::KERNEL_RBF, self::KERNEL_SIGMOID, self::KERNEL_LAPLACIAN, self::KERNEL_LINEAR]; $availableKernels = [self::KERNEL_RBF, self::KERNEL_SIGMOID, self::KERNEL_LAPLACIAN, self::KERNEL_LINEAR];
if (!in_array($kernel, $availableKernels)) { if (!in_array($kernel, $availableKernels, true)) {
throw new Exception('KernelPCA can be initialized with the following kernels only: Linear, RBF, Sigmoid and Laplacian'); throw new Exception('KernelPCA can be initialized with the following kernels only: Linear, RBF, Sigmoid and Laplacian');
} }

View File

@ -130,7 +130,7 @@ class LDA extends EigenTransformerBase
$overallMean = array_fill(0, count($data[0]), 0.0); $overallMean = array_fill(0, count($data[0]), 0.0);
foreach ($data as $index => $row) { foreach ($data as $index => $row) {
$label = array_search($classes[$index], $this->labels); $label = array_search($classes[$index], $this->labels, true);
foreach ($row as $col => $val) { foreach ($row as $col => $val) {
if (!isset($means[$label][$col])) { if (!isset($means[$label][$col])) {
@ -177,7 +177,7 @@ class LDA extends EigenTransformerBase
$sW = new Matrix($s, false); $sW = new Matrix($s, false);
foreach ($data as $index => $row) { foreach ($data as $index => $row) {
$label = array_search($classes[$index], $this->labels); $label = array_search($classes[$index], $this->labels, true);
$means = $this->means[$label]; $means = $this->means[$label];
$row = $this->calculateVar($row, $means); $row = $this->calculateVar($row, $means);

View File

@ -13,9 +13,9 @@ class TfIdfTransformer implements Transformer
*/ */
private $idf = []; private $idf = [];
public function __construct(?array $samples = null) public function __construct(array $samples = [])
{ {
if ($samples) { if (!empty($samples)) {
$this->fit($samples); $this->fit($samples);
} }
} }

View File

@ -123,7 +123,7 @@ class TokenCountVectorizer implements Transformer
private function isStopWord(string $token): bool private function isStopWord(string $token): bool
{ {
return $this->stopWords && $this->stopWords->isStopWord($token); return $this->stopWords !== null && $this->stopWords->isStopWord($token);
} }
private function updateFrequency(string $token): void private function updateFrequency(string $token): void

View File

@ -274,6 +274,7 @@ class EigenvalueDecomposition
} }
// Accumulate transformations. // Accumulate transformations.
$j = 0;
for ($i = 0; $i < $this->n - 1; ++$i) { for ($i = 0; $i < $this->n - 1; ++$i) {
$this->V[$this->n - 1][$i] = $this->V[$i][$i]; $this->V[$this->n - 1][$i] = $this->V[$i][$i];
$this->V[$i][$i] = 1.0; $this->V[$i][$i] = 1.0;

View File

@ -105,7 +105,7 @@ class Matrix
*/ */
public function getDeterminant() public function getDeterminant()
{ {
if ($this->determinant) { if ($this->determinant !== null) {
return $this->determinant; return $this->determinant;
} }

View File

@ -50,7 +50,7 @@ class Mean
$values = array_count_values($numbers); $values = array_count_values($numbers);
return array_search(max($values), $values); return array_search(max($values), $values, true);
} }
/** /**

View File

@ -57,7 +57,7 @@ class ClassificationReport
public function __construct(array $actualLabels, array $predictedLabels, int $average = self::MACRO_AVERAGE) public function __construct(array $actualLabels, array $predictedLabels, int $average = self::MACRO_AVERAGE)
{ {
$averagingMethods = range(self::MICRO_AVERAGE, self::WEIGHTED_AVERAGE); $averagingMethods = range(self::MICRO_AVERAGE, self::WEIGHTED_AVERAGE);
if (!in_array($average, $averagingMethods)) { if (!in_array($average, $averagingMethods, true)) {
throw new InvalidArgumentException('Averaging method must be MICRO_AVERAGE, MACRO_AVERAGE or WEIGHTED_AVERAGE'); throw new InvalidArgumentException('Averaging method must be MICRO_AVERAGE, MACRO_AVERAGE or WEIGHTED_AVERAGE');
} }

View File

@ -6,9 +6,9 @@ namespace Phpml\Metric;
class ConfusionMatrix class ConfusionMatrix
{ {
public static function compute(array $actualLabels, array $predictedLabels, ?array $labels = null): array public static function compute(array $actualLabels, array $predictedLabels, array $labels = []): array
{ {
$labels = $labels ? array_flip($labels) : self::getUniqueLabels($actualLabels); $labels = !empty($labels) ? array_flip($labels) : self::getUniqueLabels($actualLabels);
$matrix = self::generateMatrixWithZeros($labels); $matrix = self::generateMatrixWithZeros($labels);
foreach ($actualLabels as $index => $actual) { foreach ($actualLabels as $index => $actual) {

View File

@ -19,7 +19,7 @@ class Layer
*/ */
public function __construct(int $nodesNumber = 0, string $nodeClass = Neuron::class, ?ActivationFunction $activationFunction = null) public function __construct(int $nodesNumber = 0, string $nodeClass = Neuron::class, ?ActivationFunction $activationFunction = null)
{ {
if (!in_array(Node::class, class_implements($nodeClass))) { if (!in_array(Node::class, class_implements($nodeClass), true)) {
throw InvalidArgumentException::invalidLayerNodeClass(); throw InvalidArgumentException::invalidLayerNodeClass();
} }

View File

@ -41,7 +41,7 @@ class Normalizer implements Preprocessor
*/ */
public function __construct(int $norm = self::NORM_L2) public function __construct(int $norm = self::NORM_L2)
{ {
if (!in_array($norm, [self::NORM_L1, self::NORM_L2, self::NORM_STD])) { if (!in_array($norm, [self::NORM_L1, self::NORM_L2, self::NORM_STD], true)) {
throw NormalizerException::unknownNorm(); throw NormalizerException::unknownNorm();
} }

View File

@ -42,7 +42,7 @@ class DataTransformer
$results = []; $results = [];
foreach (explode(PHP_EOL, $rawPredictions) as $result) { foreach (explode(PHP_EOL, $rawPredictions) as $result) {
if (isset($result[0])) { if (isset($result[0])) {
$results[] = array_search($result, $numericLabels); $results[] = array_search((int) $result, $numericLabels, true);
} }
} }
@ -61,7 +61,7 @@ class DataTransformer
$columnLabels = []; $columnLabels = [];
foreach ($headerColumns as $numericLabel) { foreach ($headerColumns as $numericLabel) {
$columnLabels[] = array_search($numericLabel, $numericLabels); $columnLabels[] = array_search((int) $numericLabel, $numericLabels, true);
} }
$results = []; $results = [];

View File

@ -149,7 +149,7 @@ class SupportVectorMachine
$this->samples = array_merge($this->samples, $samples); $this->samples = array_merge($this->samples, $samples);
$this->targets = array_merge($this->targets, $targets); $this->targets = array_merge($this->targets, $targets);
$trainingSet = DataTransformer::trainingSet($this->samples, $this->targets, in_array($this->type, [Type::EPSILON_SVR, Type::NU_SVR])); $trainingSet = DataTransformer::trainingSet($this->samples, $this->targets, in_array($this->type, [Type::EPSILON_SVR, Type::NU_SVR], true));
file_put_contents($trainingSetFileName = $this->varPath.uniqid('phpml', true), $trainingSet); file_put_contents($trainingSetFileName = $this->varPath.uniqid('phpml', true), $trainingSet);
$modelFileName = $trainingSetFileName.'-model'; $modelFileName = $trainingSetFileName.'-model';
@ -182,7 +182,7 @@ class SupportVectorMachine
{ {
$predictions = $this->runSvmPredict($samples, false); $predictions = $this->runSvmPredict($samples, false);
if (in_array($this->type, [Type::C_SVC, Type::NU_SVC])) { if (in_array($this->type, [Type::C_SVC, Type::NU_SVC], true)) {
$predictions = DataTransformer::predictions($predictions, $this->targets); $predictions = DataTransformer::predictions($predictions, $this->targets);
} else { } else {
$predictions = explode(PHP_EOL, trim($predictions)); $predictions = explode(PHP_EOL, trim($predictions));
@ -208,7 +208,7 @@ class SupportVectorMachine
$predictions = $this->runSvmPredict($samples, true); $predictions = $this->runSvmPredict($samples, true);
if (in_array($this->type, [Type::C_SVC, Type::NU_SVC])) { if (in_array($this->type, [Type::C_SVC, Type::NU_SVC], true)) {
$predictions = DataTransformer::probabilities($predictions, $this->targets); $predictions = DataTransformer::probabilities($predictions, $this->targets);
} else { } else {
$predictions = explode(PHP_EOL, trim($predictions)); $predictions = explode(PHP_EOL, trim($predictions));

View File

@ -16,7 +16,7 @@ class FuzzyCMeansTest extends TestCase
$clusters = $fcm->cluster($samples); $clusters = $fcm->cluster($samples);
$this->assertCount(2, $clusters); $this->assertCount(2, $clusters);
foreach ($samples as $index => $sample) { foreach ($samples as $index => $sample) {
if (in_array($sample, $clusters[0]) || in_array($sample, $clusters[1])) { if (in_array($sample, $clusters[0], true) || in_array($sample, $clusters[1], true)) {
unset($samples[$index]); unset($samples[$index]);
} }
} }

View File

@ -20,7 +20,7 @@ class KMeansTest extends TestCase
$this->assertCount(2, $clusters); $this->assertCount(2, $clusters);
foreach ($samples as $index => $sample) { foreach ($samples as $index => $sample) {
if (in_array($sample, $clusters[0]) || in_array($sample, $clusters[1])) { if (in_array($sample, $clusters[0], true) || in_array($sample, $clusters[1], true)) {
unset($samples[$index]); unset($samples[$index]);
} }
} }

View File

@ -8,7 +8,7 @@ use Phpml\Math\LinearAlgebra\EigenvalueDecomposition;
use Phpml\Math\Matrix; use Phpml\Math\Matrix;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
class EigenDecompositionTest extends TestCase class EigenvalueDecompositionTest extends TestCase
{ {
public function testSymmetricMatrixEigenPairs(): void public function testSymmetricMatrixEigenPairs(): void
{ {