Update phpstan to 0.10.5 (#320)

This commit is contained in:
Marcin Michalski 2018-10-28 07:44:52 +01:00 committed by Arkadiusz Kondas
parent d9b85e841f
commit 53c5a6b9e5
131 changed files with 1010 additions and 974 deletions

View File

@ -19,6 +19,7 @@ This changelog references the relevant changes done in PHP-ML library.
* fix SVM locale (non-locale aware) (#288) * fix SVM locale (non-locale aware) (#288)
* typo, tests, code styles and documentation fixes (#265, #261, #254, #253, #251, #250, #248, #245, #243) * typo, tests, code styles and documentation fixes (#265, #261, #254, #253, #251, #250, #248, #245, #243)
* change [MLPClassifier] return labels in output (#315) * change [MLPClassifier] return labels in output (#315)
* enhancement Update phpstan to 0.10.5 (#320)
* 0.6.2 (2018-02-22) * 0.6.2 (2018-02-22)
* Fix Apriori array keys (#238) * Fix Apriori array keys (#238)

View File

@ -24,9 +24,9 @@
}, },
"require-dev": { "require-dev": {
"phpbench/phpbench": "^0.14.0", "phpbench/phpbench": "^0.14.0",
"phpstan/phpstan-phpunit": "^0.9.4", "phpstan/phpstan-phpunit": "^0.10",
"phpstan/phpstan-shim": "^0.9", "phpstan/phpstan-shim": "^0.10",
"phpstan/phpstan-strict-rules": "^0.9.0", "phpstan/phpstan-strict-rules": "^0.10",
"phpunit/phpunit": "^7.0.0", "phpunit/phpunit": "^7.0.0",
"symplify/coding-standard": "^5.1", "symplify/coding-standard": "^5.1",
"symplify/easy-coding-standard": "^5.1" "symplify/easy-coding-standard": "^5.1"

128
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": "cb4240c977f956be78a7fa686c77d0f2", "content-hash": "9ec1ca6b843d05e0870bd777026d7a8b",
"packages": [], "packages": [],
"packages-dev": [ "packages-dev": [
{ {
@ -1511,83 +1511,42 @@
], ],
"time": "2018-08-05T17:53:17+00:00" "time": "2018-08-05T17:53:17+00:00"
}, },
{
"name": "phpstan/phpdoc-parser",
"version": "0.3",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "ed3223362174b8067729930439e139794e9e514a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/ed3223362174b8067729930439e139794e9e514a",
"reference": "ed3223362174b8067729930439e139794e9e514a",
"shasum": ""
},
"require": {
"php": "~7.1"
},
"require-dev": {
"consistence/coding-standard": "^2.0.0",
"jakub-onderka/php-parallel-lint": "^0.9.2",
"phing/phing": "^2.16.0",
"phpstan/phpstan": "^0.10@dev",
"phpunit/phpunit": "^6.3",
"slevomat/coding-standard": "^3.3.0",
"symfony/process": "^3.4 || ^4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.3-dev"
}
},
"autoload": {
"psr-4": {
"PHPStan\\PhpDocParser\\": [
"src/"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"time": "2018-06-20T17:48:01+00:00"
},
{ {
"name": "phpstan/phpstan-phpunit", "name": "phpstan/phpstan-phpunit",
"version": "0.9.4", "version": "0.10",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git", "url": "https://github.com/phpstan/phpstan-phpunit.git",
"reference": "852411f841a37aeca2fa5af0002b0272c485c9bf" "reference": "6feecc7faae187daa6be44140cd0f1ba210e6aa0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/852411f841a37aeca2fa5af0002b0272c485c9bf", "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/6feecc7faae187daa6be44140cd0f1ba210e6aa0",
"reference": "852411f841a37aeca2fa5af0002b0272c485c9bf", "reference": "6feecc7faae187daa6be44140cd0f1ba210e6aa0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "~7.0", "nikic/php-parser": "^4.0",
"phpstan/phpstan": "^0.9.1", "php": "~7.1",
"phpunit/phpunit": "^6.3 || ~7.0" "phpstan/phpstan": "^0.10"
},
"conflict": {
"phpunit/phpunit": "<7.0"
}, },
"require-dev": { "require-dev": {
"consistence/coding-standard": "^2.0", "consistence/coding-standard": "^3.0.1",
"jakub-onderka/php-parallel-lint": "^0.9.2", "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4",
"jakub-onderka/php-parallel-lint": "^1.0",
"phing/phing": "^2.16.0", "phing/phing": "^2.16.0",
"phpstan/phpstan-strict-rules": "^0.9", "phpstan/phpstan-strict-rules": "^0.10",
"phpunit/phpunit": "^7.0",
"satooshi/php-coveralls": "^1.0", "satooshi/php-coveralls": "^1.0",
"slevomat/coding-standard": "^3.3.0" "slevomat/coding-standard": "^4.5.2"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "0.9-dev" "dev-master": "0.10-dev"
} }
}, },
"autoload": { "autoload": {
@ -1600,26 +1559,28 @@
"MIT" "MIT"
], ],
"description": "PHPUnit extensions and rules for PHPStan", "description": "PHPUnit extensions and rules for PHPStan",
"time": "2018-02-02T09:45:47+00:00" "time": "2018-06-22T18:12:17+00:00"
}, },
{ {
"name": "phpstan/phpstan-shim", "name": "phpstan/phpstan-shim",
"version": "0.9.2", "version": "0.10.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan-shim.git", "url": "https://github.com/phpstan/phpstan-shim.git",
"reference": "e4720fb2916be05de02869780072253e7e0e8a75" "reference": "a274185548d140a7f48cc1eed5b94f3a9068c674"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-shim/zipball/e4720fb2916be05de02869780072253e7e0e8a75", "url": "https://api.github.com/repos/phpstan/phpstan-shim/zipball/a274185548d140a7f48cc1eed5b94f3a9068c674",
"reference": "e4720fb2916be05de02869780072253e7e0e8a75", "reference": "a274185548d140a7f48cc1eed5b94f3a9068c674",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "~7.0" "php": "~7.1"
}, },
"replace": { "replace": {
"nikic/php-parser": "^4.0.2",
"phpstan/phpdoc-parser": "^0.3",
"phpstan/phpstan": "self.version" "phpstan/phpstan": "self.version"
}, },
"bin": [ "bin": [
@ -1629,46 +1590,53 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "0.9-dev" "dev-master": "0.10-dev"
} }
}, },
"autoload": {
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
"MIT" "MIT"
], ],
"description": "PHPStan Phar distribution", "description": "PHPStan Phar distribution",
"time": "2018-01-28T14:29:27+00:00" "time": "2018-10-20T17:45:03+00:00"
}, },
{ {
"name": "phpstan/phpstan-strict-rules", "name": "phpstan/phpstan-strict-rules",
"version": "0.9", "version": "0.10.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git", "url": "https://github.com/phpstan/phpstan-strict-rules.git",
"reference": "15be9090622c6b85c079922308f831018d8d9e23" "reference": "18c0b6e8899606b127c680402ab473a7b67166db"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/15be9090622c6b85c079922308f831018d8d9e23", "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/18c0b6e8899606b127c680402ab473a7b67166db",
"reference": "15be9090622c6b85c079922308f831018d8d9e23", "reference": "18c0b6e8899606b127c680402ab473a7b67166db",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "~7.0", "nikic/php-parser": "^4.0",
"phpstan/phpstan": "^0.9" "php": "~7.1",
"phpstan/phpstan": "^0.10"
}, },
"require-dev": { "require-dev": {
"consistence/coding-standard": "^2.0.0", "consistence/coding-standard": "^3.0.1",
"jakub-onderka/php-parallel-lint": "^0.9.2", "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4",
"jakub-onderka/php-parallel-lint": "^1.0",
"phing/phing": "^2.16.0", "phing/phing": "^2.16.0",
"phpstan/phpstan-phpunit": "^0.9", "phpstan/phpstan-phpunit": "^0.10",
"phpunit/phpunit": "^6.4", "phpunit/phpunit": "^7.0",
"slevomat/coding-standard": "^3.3.0" "slevomat/coding-standard": "^4.5.2"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "0.9-dev" "dev-master": "0.10-dev"
} }
}, },
"autoload": { "autoload": {
@ -1681,7 +1649,7 @@
"MIT" "MIT"
], ],
"description": "Extra strict and opinionated rules for PHPStan", "description": "Extra strict and opinionated rules for PHPStan",
"time": "2017-11-26T20:12:30+00:00" "time": "2018-07-06T20:36:44+00:00"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",

View File

@ -2,14 +2,13 @@ includes:
- vendor/phpstan/phpstan-strict-rules/rules.neon - vendor/phpstan/phpstan-strict-rules/rules.neon
- vendor/phpstan/phpstan-phpunit/extension.neon - vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon - vendor/phpstan/phpstan-phpunit/rules.neon
- vendor/phpstan/phpstan-phpunit/strictRules.neon
parameters: parameters:
ignoreErrors: ignoreErrors:
- '#Property Phpml\\Clustering\\KMeans\\Cluster\:\:\$points \(iterable\<Phpml\\Clustering\\KMeans\\Point\>\&SplObjectStorage\) does not accept SplObjectStorage#'
- '#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#'
# 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#'
- '#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

View File

@ -64,11 +64,11 @@ class Apriori implements Associator
*/ */
public function getRules(): array public function getRules(): array
{ {
if (empty($this->large)) { if (count($this->large) === 0) {
$this->large = $this->apriori(); $this->large = $this->apriori();
} }
if (!empty($this->rules)) { if (count($this->rules) > 0) {
return $this->rules; return $this->rules;
} }
@ -89,7 +89,7 @@ class Apriori implements Associator
$L = []; $L = [];
$items = $this->frequent($this->items()); $items = $this->frequent($this->items());
for ($k = 1; !empty($items); ++$k) { for ($k = 1; isset($items[0]); ++$k) {
$L[$k] = $items; $L[$k] = $items;
$items = $this->frequent($this->candidates($items)); $items = $this->frequent($this->candidates($items));
} }
@ -118,7 +118,7 @@ class Apriori implements Associator
*/ */
private function generateAllRules(): void private function generateAllRules(): void
{ {
for ($k = 2; !empty($this->large[$k]); ++$k) { for ($k = 2; isset($this->large[$k]); ++$k) {
foreach ($this->large[$k] as $frequent) { foreach ($this->large[$k] as $frequent) {
$this->generateRules($frequent); $this->generateRules($frequent);
} }
@ -241,7 +241,7 @@ class Apriori implements Associator
continue; continue;
} }
foreach ((array) $this->samples as $sample) { foreach ($this->samples as $sample) {
if ($this->subset($sample, $candidate)) { if ($this->subset($sample, $candidate)) {
$candidates[] = $candidate; $candidates[] = $candidate;
@ -316,7 +316,7 @@ class Apriori implements Associator
*/ */
private function subset(array $set, array $subset): bool private function subset(array $set, array $subset): bool
{ {
return !array_diff($subset, array_intersect($subset, $set)); return count(array_diff($subset, array_intersect($subset, $set))) === 0;
} }
/** /**

View File

@ -249,7 +249,7 @@ class DecisionTree implements Classifier
foreach ($records as $recordNo) { foreach ($records as $recordNo) {
// Check if the previous record is the same with the current one // Check if the previous record is the same with the current one
$record = $this->samples[$recordNo]; $record = $this->samples[$recordNo];
if ($prevRecord && $prevRecord != $record) { if ($prevRecord !== null && $prevRecord != $record) {
$allSame = false; $allSame = false;
} }
@ -275,13 +275,13 @@ class DecisionTree implements Classifier
if ($allSame || $depth >= $this->maxDepth || count($remainingTargets) === 1) { if ($allSame || $depth >= $this->maxDepth || count($remainingTargets) === 1) {
$split->isTerminal = true; $split->isTerminal = true;
arsort($remainingTargets); arsort($remainingTargets);
$split->classValue = key($remainingTargets); $split->classValue = (string) key($remainingTargets);
} else { } else {
if (!empty($leftRecords)) { if (isset($leftRecords[0])) {
$split->leftLeaf = $this->getSplitLeaf($leftRecords, $depth + 1); $split->leftLeaf = $this->getSplitLeaf($leftRecords, $depth + 1);
} }
if (!empty($rightRecords)) { if (isset($rightRecords[0])) {
$split->rightLeaf = $this->getSplitLeaf($rightRecords, $depth + 1); $split->rightLeaf = $this->getSplitLeaf($rightRecords, $depth + 1);
} }
} }
@ -292,8 +292,10 @@ class DecisionTree implements Classifier
protected function getBestSplit(array $records): DecisionTreeLeaf protected function getBestSplit(array $records): DecisionTreeLeaf
{ {
$targets = array_intersect_key($this->targets, array_flip($records)); $targets = array_intersect_key($this->targets, array_flip($records));
$samples = array_intersect_key($this->samples, array_flip($records)); $samples = (array) array_combine(
$samples = array_combine($records, $this->preprocess($samples)); $records,
$this->preprocess(array_intersect_key($this->samples, array_flip($records)))
);
$bestGiniVal = 1; $bestGiniVal = 1;
$bestSplit = null; $bestSplit = null;
$features = $this->getSelectedFeatures(); $features = $this->getSelectedFeatures();
@ -306,6 +308,10 @@ class DecisionTree implements Classifier
$counts = array_count_values($colValues); $counts = array_count_values($colValues);
arsort($counts); arsort($counts);
$baseValue = key($counts); $baseValue = key($counts);
if ($baseValue === null) {
continue;
}
$gini = $this->getGiniIndex($baseValue, $colValues, $targets); $gini = $this->getGiniIndex($baseValue, $colValues, $targets);
if ($bestSplit === null || $bestGiniVal > $gini) { if ($bestSplit === null || $bestGiniVal > $gini) {
$split = new DecisionTreeLeaf(); $split = new DecisionTreeLeaf();
@ -349,11 +355,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 && empty($this->selectedFeatures)) { if ($this->numUsableFeatures === 0 && count($this->selectedFeatures) === 0) {
return $allFeatures; return $allFeatures;
} }
if (!empty($this->selectedFeatures)) { if (count($this->selectedFeatures) > 0) {
return $this->selectedFeatures; return $this->selectedFeatures;
} }
@ -406,7 +412,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 (!empty($floatValues)) { if (count($floatValues) > 0) {
return false; return false;
} }
@ -463,7 +469,7 @@ class DecisionTree implements Classifier
$node = $this->tree; $node = $this->tree;
do { do {
if ($node->isTerminal) { if ($node->isTerminal) {
break; return $node->classValue;
} }
if ($node->evaluate($sample)) { if ($node->evaluate($sample)) {
@ -473,6 +479,6 @@ class DecisionTree implements Classifier
} }
} while ($node); } while ($node);
return $node !== null ? $node->classValue : $this->labels[0]; return $this->labels[0];
} }
} }

View File

@ -119,7 +119,7 @@ class DecisionTreeLeaf
/** /**
* Returns HTML representation of the node including children nodes * Returns HTML representation of the node including children nodes
*/ */
public function getHTML($columnNames = null): string public function getHTML(?array $columnNames = null): string
{ {
if ($this->isTerminal) { if ($this->isTerminal) {
$value = "<b>${this}->classValue</b>"; $value = "<b>${this}->classValue</b>";
@ -131,7 +131,7 @@ class DecisionTreeLeaf
$col = "col_$this->columnIndex"; $col = "col_$this->columnIndex";
} }
if (!preg_match('/^[<>=]{1,2}/', (string) $value)) { if ((bool) preg_match('/^[<>=]{1,2}/', (string) $value) === false) {
$value = "=${value}"; $value = "=${value}";
} }

View File

@ -100,7 +100,7 @@ class AdaBoost implements Classifier
{ {
// Initialize usual variables // Initialize usual variables
$this->labels = array_keys(array_count_values($targets)); $this->labels = array_keys(array_count_values($targets));
if (count($this->labels) != 2) { if (count($this->labels) !== 2) {
throw new InvalidArgumentException('AdaBoost is a binary classifier and can classify between two classes only'); throw new InvalidArgumentException('AdaBoost is a binary classifier and can classify between two classes only');
} }
@ -159,13 +159,10 @@ class AdaBoost implements Classifier
protected function getBestClassifier(): Classifier protected function getBestClassifier(): Classifier
{ {
$ref = new ReflectionClass($this->baseClassifier); $ref = new ReflectionClass($this->baseClassifier);
if (!empty($this->classifierOptions)) { /** @var Classifier $classifier */
$classifier = $ref->newInstanceArgs($this->classifierOptions); $classifier = count($this->classifierOptions) === 0 ? $ref->newInstance() : $ref->newInstanceArgs($this->classifierOptions);
} else {
$classifier = $ref->newInstance();
}
if (is_subclass_of($classifier, WeightedClassifier::class)) { if ($classifier instanceof WeightedClassifier) {
$classifier->setSampleWeights($this->weights); $classifier->setSampleWeights($this->weights);
$classifier->train($this->samples, $this->targets); $classifier->train($this->samples, $this->targets);
} else { } else {

View File

@ -51,16 +51,6 @@ class Bagging implements Classifier
*/ */
protected $subsetRatio = 0.7; protected $subsetRatio = 0.7;
/**
* @var array
*/
private $targets = [];
/**
* @var array
*/
private $samples = [];
/** /**
* Creates an ensemble classifier with given number of base classifiers * Creates an ensemble classifier with given number of base classifiers
* Default number of base classifiers is 50. * Default number of base classifiers is 50.
@ -146,11 +136,8 @@ 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 (!empty($this->classifierOptions)) { /** @var Classifier $obj */
$obj = $ref->newInstanceArgs($this->classifierOptions); $obj = count($this->classifierOptions) === 0 ? $ref->newInstance() : $ref->newInstanceArgs($this->classifierOptions);
} else {
$obj = $ref->newInstance();
}
$classifiers[] = $this->initSingleClassifier($obj); $classifiers[] = $this->initSingleClassifier($obj);
} }

View File

@ -45,8 +45,7 @@ class KNearestNeighbors implements Classifier
protected function predictSample(array $sample) protected function predictSample(array $sample)
{ {
$distances = $this->kNeighborsDistances($sample); $distances = $this->kNeighborsDistances($sample);
$predictions = (array) array_combine(array_values($this->targets), array_fill(0, count($this->targets), 0));
$predictions = array_combine(array_values($this->targets), array_fill(0, count($this->targets), 0));
foreach (array_keys($distances) as $index) { foreach (array_keys($distances) as $index) {
++$predictions[$this->targets[$index]]; ++$predictions[$this->targets[$index]];

View File

@ -55,7 +55,7 @@ class Adaline extends Perceptron
* Adapts the weights with respect to given samples and targets * Adapts the weights with respect to given samples and targets
* by use of gradient descent learning rule * by use of gradient descent learning rule
*/ */
protected function runTraining(array $samples, array $targets) protected function runTraining(array $samples, array $targets): void
{ {
// The cost function is the sum of squares // The cost function is the sum of squares
$callback = function ($weights, $sample, $target) { $callback = function ($weights, $sample, $target) {
@ -70,6 +70,6 @@ class Adaline extends Perceptron
$isBatch = $this->trainingType == self::BATCH_TRAINING; $isBatch = $this->trainingType == self::BATCH_TRAINING;
return parent::runGradientDescent($samples, $targets, $callback, $isBatch); parent::runGradientDescent($samples, $targets, $callback, $isBatch);
} }
} }

View File

@ -119,13 +119,13 @@ 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 (!empty($this->weights)) { if (count($this->weights) === 0) {
$this->weights = array_fill(0, count($samples), 1);
} else {
$numWeights = count($this->weights); $numWeights = count($this->weights);
if ($numWeights != count($samples)) { if ($numWeights !== count($samples)) {
throw new InvalidArgumentException('Number of sample weights does not match with number of samples'); throw new InvalidArgumentException('Number of sample weights does not match with number of samples');
} }
} else {
$this->weights = array_fill(0, count($samples), 1);
} }
// Determine type of each column as either "continuous" or "nominal" // Determine type of each column as either "continuous" or "nominal"
@ -134,7 +134,7 @@ class DecisionStump extends WeightedClassifier
// Try to find the best split in the columns of the dataset // Try to find the best split in the columns of the dataset
// by calculating error rate for each split point in each column // by calculating error rate for each split point in each column
$columns = range(0, count($samples[0]) - 1); $columns = range(0, count($samples[0]) - 1);
if ($this->givenColumnIndex != self::AUTO_SELECT) { if ($this->givenColumnIndex !== self::AUTO_SELECT) {
$columns = [$this->givenColumnIndex]; $columns = [$this->givenColumnIndex];
} }
@ -184,7 +184,7 @@ class DecisionStump extends WeightedClassifier
// the average value for the cut point // the average value for the cut point
$threshold = array_sum($values) / (float) count($values); $threshold = array_sum($values) / (float) count($values);
[$errorRate, $prob] = $this->calculateErrorRate($targets, $threshold, $operator, $values); [$errorRate, $prob] = $this->calculateErrorRate($targets, $threshold, $operator, $values);
if ($split === [] || $errorRate < $split['trainingErrorRate']) { if (!isset($split['trainingErrorRate']) || $errorRate < $split['trainingErrorRate']) {
$split = [ $split = [
'value' => $threshold, 'value' => $threshold,
'operator' => $operator, 'operator' => $operator,
@ -224,8 +224,7 @@ class DecisionStump extends WeightedClassifier
foreach (['=', '!='] as $operator) { foreach (['=', '!='] as $operator) {
foreach ($distinctVals as $val) { foreach ($distinctVals as $val) {
[$errorRate, $prob] = $this->calculateErrorRate($targets, $val, $operator, $values); [$errorRate, $prob] = $this->calculateErrorRate($targets, $val, $operator, $values);
if (!isset($split['trainingErrorRate']) || $split['trainingErrorRate'] < $errorRate) {
if ($split === [] || $split['trainingErrorRate'] < $errorRate) {
$split = [ $split = [
'value' => $val, 'value' => $val,
'operator' => $operator, 'operator' => $operator,

View File

@ -60,11 +60,6 @@ class Perceptron implements Classifier, IncrementalEstimator
*/ */
protected $enableEarlyStop = true; protected $enableEarlyStop = true;
/**
* @var array
*/
protected $costValues = [];
/** /**
* Initalize a perceptron classifier with given learning rate and maximum * Initalize a perceptron classifier with given learning rate and maximum
* number of iterations used while training the perceptron * number of iterations used while training the perceptron
@ -156,7 +151,7 @@ class Perceptron implements Classifier, IncrementalEstimator
* Trains the perceptron model with Stochastic Gradient Descent optimization * Trains the perceptron model with Stochastic Gradient Descent optimization
* to get the correct set of weights * to get the correct set of weights
*/ */
protected function runTraining(array $samples, array $targets) protected function runTraining(array $samples, array $targets): void
{ {
// The cost function is the sum of squares // The cost function is the sum of squares
$callback = function ($weights, $sample, $target) { $callback = function ($weights, $sample, $target) {
@ -176,7 +171,7 @@ class Perceptron implements Classifier, IncrementalEstimator
* Executes a Gradient Descent algorithm for * Executes a Gradient Descent algorithm for
* the given cost function * the given cost function
*/ */
protected function runGradientDescent(array $samples, array $targets, Closure $gradientFunc, bool $isBatch = false) protected function runGradientDescent(array $samples, array $targets, Closure $gradientFunc, bool $isBatch = false): void
{ {
$class = $isBatch ? GD::class : StochasticGD::class; $class = $isBatch ? GD::class : StochasticGD::class;

View File

@ -108,8 +108,7 @@ class FuzzyCMeans implements Clusterer
$column = array_column($this->membership, $k); $column = array_column($this->membership, $k);
arsort($column); arsort($column);
reset($column); reset($column);
$i = key($column); $cluster = $this->clusters[key($column)];
$cluster = $this->clusters[$i];
$cluster->attach(new Point($this->samples[$k])); $cluster->attach(new Point($this->samples[$k]));
} }
@ -152,7 +151,7 @@ class FuzzyCMeans implements Clusterer
protected function updateClusters(): void protected function updateClusters(): void
{ {
$dim = $this->space->getDimension(); $dim = $this->space->getDimension();
if (empty($this->clusters)) { if (count($this->clusters) === 0) {
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));
} }
@ -171,11 +170,11 @@ class FuzzyCMeans implements Clusterer
} }
} }
protected function getMembershipRowTotal(int $row, int $col, bool $multiply) protected function getMembershipRowTotal(int $row, int $col, bool $multiply): float
{ {
$sum = 0.0; $sum = 0.0;
for ($k = 0; $k < $this->sampleCount; ++$k) { for ($k = 0; $k < $this->sampleCount; ++$k) {
$val = pow($this->membership[$row][$k], $this->fuzziness); $val = $this->membership[$row][$k] ** $this->fuzziness;
if ($multiply) { if ($multiply) {
$val *= $this->samples[$k][$col]; $val *= $this->samples[$k][$col];
} }
@ -211,7 +210,7 @@ class FuzzyCMeans implements Clusterer
$this->samples[$col] $this->samples[$col]
); );
$val = pow($dist1 / $dist2, 2.0 / ($this->fuzziness - 1)); $val = ($dist1 / $dist2) ** 2.0 / ($this->fuzziness - 1);
$sum += $val; $sum += $val;
} }
@ -223,7 +222,7 @@ class FuzzyCMeans implements Clusterer
* and all cluster centers. This method returns the summation of all * and all cluster centers. This method returns the summation of all
* these distances * these distances
*/ */
protected function getObjective() protected function getObjective(): float
{ {
$sum = 0.0; $sum = 0.0;
$distance = new Euclidean(); $distance = new Euclidean();

View File

@ -4,12 +4,11 @@ declare(strict_types=1);
namespace Phpml\Clustering\KMeans; namespace Phpml\Clustering\KMeans;
use Countable;
use IteratorAggregate; use IteratorAggregate;
use LogicException; use LogicException;
use SplObjectStorage; use SplObjectStorage;
class Cluster extends Point implements IteratorAggregate, Countable class Cluster extends Point implements IteratorAggregate
{ {
/** /**
* @var Space * @var Space
@ -32,10 +31,10 @@ class Cluster extends Point implements IteratorAggregate, Countable
{ {
$points = []; $points = [];
foreach ($this->points as $point) { foreach ($this->points as $point) {
if (!empty($point->label)) { if (count($point->label) === 0) {
$points[$point->label] = $point->toArray();
} else {
$points[] = $point->toArray(); $points[] = $point->toArray();
} else {
$points[$point->label] = $point->toArray();
} }
} }
@ -106,10 +105,7 @@ class Cluster extends Point implements IteratorAggregate, Countable
return $this->points; return $this->points;
} }
/** public function count(): int
* @return mixed
*/
public function count()
{ {
return count($this->points); return count($this->points);
} }

View File

@ -6,7 +6,7 @@ namespace Phpml\Clustering\KMeans;
use ArrayAccess; use ArrayAccess;
class Point implements ArrayAccess class Point implements ArrayAccess, \Countable
{ {
/** /**
* @var int * @var int
@ -23,6 +23,9 @@ class Point implements ArrayAccess
*/ */
protected $label; protected $label;
/**
* @param mixed $label
*/
public function __construct(array $coordinates, $label = null) public function __construct(array $coordinates, $label = null)
{ {
$this->dimension = count($coordinates); $this->dimension = count($coordinates);
@ -36,7 +39,7 @@ class Point implements ArrayAccess
} }
/** /**
* @return int|mixed * @return float|int
*/ */
public function getDistanceWith(self $point, bool $precise = true) public function getDistanceWith(self $point, bool $precise = true)
{ {
@ -50,9 +53,9 @@ class Point implements ArrayAccess
} }
/** /**
* @return mixed * @param Point[] $points
*/ */
public function getClosest(array $points) public function getClosest(array $points): ?self
{ {
$minPoint = null; $minPoint = null;
@ -114,4 +117,9 @@ class Point implements ArrayAccess
{ {
unset($this->coordinates[$offset]); unset($this->coordinates[$offset]);
} }
public function count(): int
{
return count($this->coordinates);
}
} }

View File

@ -28,6 +28,8 @@ class Space extends SplObjectStorage
public function toArray(): array public function toArray(): array
{ {
$points = []; $points = [];
/** @var Point $point */
foreach ($this as $point) { foreach ($this as $point) {
$points[] = $point->toArray(); $points[] = $point->toArray();
} }
@ -35,9 +37,12 @@ class Space extends SplObjectStorage
return ['points' => $points]; return ['points' => $points];
} }
/**
* @param mixed $label
*/
public function newPoint(array $coordinates, $label = null): Point public function newPoint(array $coordinates, $label = null): Point
{ {
if (count($coordinates) != $this->dimension) { if (count($coordinates) !== $this->dimension) {
throw new LogicException('('.implode(',', $coordinates).') is not a point of this space'); throw new LogicException('('.implode(',', $coordinates).') is not a point of this space');
} }
@ -45,7 +50,8 @@ class Space extends SplObjectStorage
} }
/** /**
* @param null $data * @param mixed $label
* @param mixed $data
*/ */
public function addPoint(array $coordinates, $label = null, $data = null): void public function addPoint(array $coordinates, $label = null, $data = null): void
{ {
@ -53,8 +59,8 @@ class Space extends SplObjectStorage
} }
/** /**
* @param Point $point * @param object $point
* @param null $data * @param mixed $data
*/ */
public function attach($point, $data = null): void public function attach($point, $data = null): void
{ {
@ -82,10 +88,16 @@ class Space extends SplObjectStorage
$min = $this->newPoint(array_fill(0, $this->dimension, null)); $min = $this->newPoint(array_fill(0, $this->dimension, null));
$max = $this->newPoint(array_fill(0, $this->dimension, null)); $max = $this->newPoint(array_fill(0, $this->dimension, null));
/** @var self $point */
foreach ($this as $point) { foreach ($this as $point) {
for ($n = 0; $n < $this->dimension; ++$n) { for ($n = 0; $n < $this->dimension; ++$n) {
($min[$n] > $point[$n] || $min[$n] === null) && $min[$n] = $point[$n]; if ($min[$n] === null || $min[$n] > $point[$n]) {
($max[$n] < $point[$n] || $max[$n] === null) && $max[$n] = $point[$n]; $min[$n] = $point[$n];
}
if ($max[$n] === null || $max[$n] < $point[$n]) {
$max[$n] = $point[$n];
}
} }
} }
@ -141,7 +153,10 @@ class Space extends SplObjectStorage
return $clusters; return $clusters;
} }
protected function iterate($clusters): bool /**
* @param Cluster[] $clusters
*/
protected function iterate(array $clusters): bool
{ {
$convergence = true; $convergence = true;
@ -164,10 +179,12 @@ class Space extends SplObjectStorage
} }
} }
/** @var Cluster $cluster */
foreach ($attach as $cluster) { foreach ($attach as $cluster) {
$cluster->attachAll($attach[$cluster]); $cluster->attachAll($attach[$cluster]);
} }
/** @var Cluster $cluster */
foreach ($detach as $cluster) { foreach ($detach as $cluster) {
$cluster->detachAll($detach[$cluster]); $cluster->detachAll($detach[$cluster]);
} }
@ -179,23 +196,36 @@ class Space extends SplObjectStorage
return $convergence; return $convergence;
} }
/**
* @return Cluster[]
*/
protected function initializeKMPPClusters(int $clustersNumber): array protected function initializeKMPPClusters(int $clustersNumber): array
{ {
$clusters = []; $clusters = [];
$this->rewind(); $this->rewind();
$clusters[] = new Cluster($this, $this->current()->getCoordinates()); /** @var Point $current */
$current = $this->current();
$clusters[] = new Cluster($this, $current->getCoordinates());
$distances = new SplObjectStorage(); $distances = new SplObjectStorage();
for ($i = 1; $i < $clustersNumber; ++$i) { for ($i = 1; $i < $clustersNumber; ++$i) {
$sum = 0; $sum = 0;
/** @var Point $point */
foreach ($this as $point) { foreach ($this as $point) {
$distance = $point->getDistanceWith($point->getClosest($clusters)); $closest = $point->getClosest($clusters);
if ($closest === null) {
continue;
}
$distance = $point->getDistanceWith($closest);
$sum += $distances[$point] = $distance; $sum += $distances[$point] = $distance;
} }
$sum = random_int(0, (int) $sum); $sum = random_int(0, (int) $sum);
/** @var Point $point */
foreach ($this as $point) { foreach ($this as $point) {
$sum -= $distances[$point]; $sum -= $distances[$point];
@ -212,6 +242,9 @@ class Space extends SplObjectStorage
return $clusters; return $clusters;
} }
/**
* @return Cluster[]
*/
private function initializeRandomClusters(int $clustersNumber): array private function initializeRandomClusters(int $clustersNumber): array
{ {
$clusters = []; $clusters = [];

View File

@ -60,7 +60,7 @@ abstract class Split
return $this->testLabels; return $this->testLabels;
} }
abstract protected function splitDataset(Dataset $dataset, float $testSize); abstract protected function splitDataset(Dataset $dataset, float $testSize): void;
protected function seedGenerator(?int $seed = null): void protected function seedGenerator(?int $seed = null): void
{ {

View File

@ -27,6 +27,7 @@ class StratifiedRandomSplit extends RandomSplit
$samples = $dataset->getSamples(); $samples = $dataset->getSamples();
$uniqueTargets = array_unique($targets); $uniqueTargets = array_unique($targets);
/** @var array $split */
$split = array_combine($uniqueTargets, array_fill(0, count($uniqueTargets), [])); $split = array_combine($uniqueTargets, array_fill(0, count($uniqueTargets), []));
foreach ($samples as $key => $sample) { foreach ($samples as $key => $sample) {

View File

@ -29,14 +29,14 @@ class CsvDataset extends ArrayDataset
if ($headingRow) { if ($headingRow) {
$data = fgetcsv($handle, $maxLineLength, $delimiter); $data = fgetcsv($handle, $maxLineLength, $delimiter);
$this->columnNames = array_slice($data, 0, $features); $this->columnNames = array_slice((array) $data, 0, $features);
} else { } else {
$this->columnNames = range(0, $features - 1); $this->columnNames = range(0, $features - 1);
} }
$samples = $targets = []; $samples = $targets = [];
while (($data = fgetcsv($handle, $maxLineLength, $delimiter)) !== false) { while (($data = fgetcsv($handle, $maxLineLength, $delimiter)) !== false) {
$samples[] = array_slice($data, 0, $features); $samples[] = array_slice((array) $data, 0, $features);
$targets[] = $data[$features]; $targets[] = $data[$features];
} }

View File

@ -23,8 +23,8 @@ class SvmDataset extends ArrayDataset
$samples = []; $samples = [];
$targets = []; $targets = [];
$maxIndex = 0; $maxIndex = 0;
while (($line = fgets($handle)) !== false) { while (false !== $line = fgets($handle)) {
[$sample, $target, $maxIndex] = self::processLine($line, $maxIndex); [$sample, $target, $maxIndex] = self::processLine((string) $line, $maxIndex);
$samples[] = $sample; $samples[] = $sample;
$targets[] = $target; $targets[] = $target;
} }
@ -38,6 +38,9 @@ class SvmDataset extends ArrayDataset
return [$samples, $targets]; return [$samples, $targets];
} }
/**
* @return resource
*/
private static function openFile(string $filePath) private static function openFile(string $filePath)
{ {
if (!file_exists($filePath)) { if (!file_exists($filePath)) {

View File

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Phpml\DimensionReduction; namespace Phpml\DimensionReduction;
use Closure; use Closure;
use Exception;
use Phpml\Exception\InvalidArgumentException; use Phpml\Exception\InvalidArgumentException;
use Phpml\Exception\InvalidOperationException; use Phpml\Exception\InvalidOperationException;
use Phpml\Math\Distance\Euclidean; use Phpml\Math\Distance\Euclidean;
@ -59,8 +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]; if (!in_array($kernel, [self::KERNEL_RBF, self::KERNEL_SIGMOID, self::KERNEL_LAPLACIAN, self::KERNEL_LINEAR], true)) {
if (!in_array($kernel, $availableKernels, true)) {
throw new InvalidArgumentException('KernelPCA can be initialized with the following kernels only: Linear, RBF, Sigmoid and Laplacian'); throw new InvalidArgumentException('KernelPCA can be initialized with the following kernels only: Linear, RBF, Sigmoid and Laplacian');
} }
@ -190,7 +188,7 @@ class KernelPCA extends PCA
return function ($x, $y) { return function ($x, $y) {
$res = Matrix::dot($x, $y)[0] + 1.0; $res = Matrix::dot($x, $y)[0] + 1.0;
return tanh($this->gamma * $res); return tanh((float) $this->gamma * $res);
}; };
case self::KERNEL_LAPLACIAN: case self::KERNEL_LAPLACIAN:
@ -203,7 +201,7 @@ class KernelPCA extends PCA
default: default:
// Not reached // Not reached
throw new Exception(sprintf('KernelPCA initialized with invalid kernel: %d', $this->kernel)); throw new InvalidArgumentException(sprintf('KernelPCA initialized with invalid kernel: %d', $this->kernel));
} }
} }

View File

@ -115,7 +115,7 @@ class PCA extends EigenTransformerBase
*/ */
protected function normalize(array $data, int $n): array protected function normalize(array $data, int $n): array
{ {
if (empty($this->means)) { if (count($this->means) === 0) {
$this->calculateMeans($data, $n); $this->calculateMeans($data, $n);
} }

View File

@ -6,7 +6,7 @@ namespace Phpml;
interface Estimator interface Estimator
{ {
public function train(array $samples, array $targets); public function train(array $samples, array $targets): void;
/** /**
* @return mixed * @return mixed

View File

@ -15,7 +15,7 @@ class TfIdfTransformer implements Transformer
public function __construct(array $samples = []) public function __construct(array $samples = [])
{ {
if (!empty($samples)) { if (count($samples) > 0) {
$this->fit($samples); $this->fit($samples);
} }
} }

View File

@ -43,7 +43,7 @@ final class SelectKBest implements Transformer
public function fit(array $samples, ?array $targets = null): void public function fit(array $samples, ?array $targets = null): void
{ {
if ($targets === null || empty($targets)) { if ($targets === null || count($targets) === 0) {
throw new InvalidArgumentException('The array has zero elements'); throw new InvalidArgumentException('The array has zero elements');
} }

View File

@ -51,18 +51,13 @@ trait OneVsRest
protected function trainByLabel(array $samples, array $targets, array $allLabels = []): void protected function trainByLabel(array $samples, array $targets, array $allLabels = []): void
{ {
// Overwrites the current value if it exist. $allLabels must be provided for each partialTrain run. // Overwrites the current value if it exist. $allLabels must be provided for each partialTrain run.
if (!empty($allLabels)) { $this->allLabels = count($allLabels) === 0 ? array_keys(array_count_values($targets)) : $allLabels;
$this->allLabels = $allLabels;
} else {
$this->allLabels = array_keys(array_count_values($targets));
}
sort($this->allLabels, SORT_STRING); sort($this->allLabels, SORT_STRING);
// If there are only two targets, then there is no need to perform OvR // If there are only two targets, then there is no need to perform OvR
if (count($this->allLabels) == 2) { if (count($this->allLabels) === 2) {
// Init classifier if required. // Init classifier if required.
if (empty($this->classifiers)) { if (count($this->classifiers) === 0) {
$this->classifiers[0] = $this->getClassifierCopy(); $this->classifiers[0] = $this->getClassifierCopy();
} }
@ -72,7 +67,7 @@ trait OneVsRest
foreach ($this->allLabels as $label) { foreach ($this->allLabels as $label) {
// Init classifier if required. // Init classifier if required.
if (empty($this->classifiers[$label])) { if (!isset($this->classifiers[$label])) {
$this->classifiers[$label] = $this->getClassifierCopy(); $this->classifiers[$label] = $this->getClassifierCopy();
} }
@ -92,10 +87,8 @@ trait OneVsRest
/** /**
* Returns an instance of the current class after cleaning up OneVsRest stuff. * Returns an instance of the current class after cleaning up OneVsRest stuff.
*
* @return Classifier|OneVsRest
*/ */
protected function getClassifierCopy() protected function getClassifierCopy(): Classifier
{ {
// Clone the current classifier, so that // Clone the current classifier, so that
// we don't mess up its variables while training // we don't mess up its variables while training
@ -111,7 +104,7 @@ trait OneVsRest
*/ */
protected function predictSample(array $sample) protected function predictSample(array $sample)
{ {
if (count($this->allLabels) == 2) { if (count($this->allLabels) === 2) {
return $this->classifiers[0]->predictSampleBinary($sample); return $this->classifiers[0]->predictSampleBinary($sample);
} }

View File

@ -91,7 +91,7 @@ class ConjugateGradient extends GD
{ {
[$cost] = parent::gradient($theta); [$cost] = parent::gradient($theta);
return array_sum($cost) / $this->sampleCount; return array_sum($cost) / (int) $this->sampleCount;
} }
/** /**

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Phpml\Helper\Optimizer; namespace Phpml\Helper\Optimizer;
use Closure; use Closure;
use Phpml\Exception\InvalidOperationException;
/** /**
* Batch version of Gradient Descent to optimize the weights * Batch version of Gradient Descent to optimize the weights
@ -59,6 +60,10 @@ class GD extends StochasticGD
$gradient = []; $gradient = [];
$totalPenalty = 0; $totalPenalty = 0;
if ($this->gradientCb === null) {
throw new InvalidOperationException('Gradient callback is not defined');
}
foreach ($this->samples as $index => $sample) { foreach ($this->samples as $index => $sample) {
$target = $this->targets[$index]; $target = $this->targets[$index];

View File

@ -37,9 +37,9 @@ abstract class Optimizer
} }
} }
public function setTheta(array $theta) public function setTheta(array $theta): self
{ {
if (count($theta) != $this->dimensions) { if (count($theta) !== $this->dimensions) {
throw new InvalidArgumentException(sprintf('Number of values in the weights array should be %s', $this->dimensions)); throw new InvalidArgumentException(sprintf('Number of values in the weights array should be %s', $this->dimensions));
} }
@ -52,5 +52,5 @@ abstract class Optimizer
* Executes the optimization with the given samples & targets * Executes the optimization with the given samples & targets
* and returns the weights * and returns the weights
*/ */
abstract public function runOptimization(array $samples, array $targets, Closure $gradientCb); abstract public function runOptimization(array $samples, array $targets, Closure $gradientCb): array;
} }

View File

@ -6,6 +6,7 @@ namespace Phpml\Helper\Optimizer;
use Closure; use Closure;
use Phpml\Exception\InvalidArgumentException; use Phpml\Exception\InvalidArgumentException;
use Phpml\Exception\InvalidOperationException;
/** /**
* Stochastic Gradient Descent optimization method * Stochastic Gradient Descent optimization method
@ -34,7 +35,7 @@ class StochasticGD extends Optimizer
* *
* @var \Closure|null * @var \Closure|null
*/ */
protected $gradientCb = null; protected $gradientCb;
/** /**
* Maximum number of iterations used to train the model * Maximum number of iterations used to train the model
@ -89,9 +90,9 @@ class StochasticGD extends Optimizer
$this->dimensions = $dimensions; $this->dimensions = $dimensions;
} }
public function setTheta(array $theta) public function setTheta(array $theta): Optimizer
{ {
if (count($theta) != $this->dimensions + 1) { if (count($theta) !== $this->dimensions + 1) {
throw new InvalidArgumentException(sprintf('Number of values in the weights array should be %s', $this->dimensions + 1)); throw new InvalidArgumentException(sprintf('Number of values in the weights array should be %s', $this->dimensions + 1));
} }
@ -156,7 +157,7 @@ class StochasticGD extends Optimizer
* The cost function to minimize and the gradient of the function are to be * The cost function to minimize and the gradient of the function are to be
* handled by the callback function provided as the third parameter of the method. * handled by the callback function provided as the third parameter of the method.
*/ */
public function runOptimization(array $samples, array $targets, Closure $gradientCb): ?array public function runOptimization(array $samples, array $targets, Closure $gradientCb): array
{ {
$this->samples = $samples; $this->samples = $samples;
$this->targets = $targets; $this->targets = $targets;
@ -175,7 +176,7 @@ class StochasticGD extends Optimizer
// Save the best theta in the "pocket" so that // Save the best theta in the "pocket" so that
// any future set of theta worse than this will be disregarded // any future set of theta worse than this will be disregarded
if ($bestTheta == null || $cost <= $bestScore) { if ($bestTheta === null || $cost <= $bestScore) {
$bestTheta = $theta; $bestTheta = $theta;
$bestScore = $cost; $bestScore = $cost;
} }
@ -210,6 +211,10 @@ class StochasticGD extends Optimizer
$jValue = 0.0; $jValue = 0.0;
$theta = $this->theta; $theta = $this->theta;
if ($this->gradientCb === null) {
throw new InvalidOperationException('Gradient callback is not defined');
}
foreach ($this->samples as $index => $sample) { foreach ($this->samples as $index => $sample) {
$target = $this->targets[$index]; $target = $this->targets[$index];
@ -254,7 +259,7 @@ class StochasticGD extends Optimizer
// Check if the last two cost values are almost the same // Check if the last two cost values are almost the same
$costs = array_slice($this->costValues, -2); $costs = array_slice($this->costValues, -2);
if (count($costs) == 2 && abs($costs[1] - $costs[0]) < $this->threshold) { if (count($costs) === 2 && abs($costs[1] - $costs[0]) < $this->threshold) {
return true; return true;
} }

View File

@ -6,5 +6,5 @@ namespace Phpml;
interface IncrementalEstimator interface IncrementalEstimator
{ {
public function partialTrain(array $samples, array $targets, array $labels = []); public function partialTrain(array $samples, array $targets, array $labels = []): void;
} }

View File

@ -9,6 +9,9 @@ use Phpml\Exception\InvalidArgumentException;
class Comparison class Comparison
{ {
/** /**
* @param mixed $a
* @param mixed $b
*
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public static function compare($a, $b, string $operator): bool public static function compare($a, $b, string $operator): bool

View File

@ -3,25 +3,25 @@
declare(strict_types=1); declare(strict_types=1);
/** /**
* Class to obtain eigenvalues and eigenvectors of a real matrix. * Class to obtain eigenvalues and eigenvectors of a real matrix.
* *
* If A is symmetric, then A = V*D*V' where the eigenvalue matrix D * If A is symmetric, then A = V*D*V' where the eigenvalue matrix D
* is diagonal and the eigenvector matrix V is orthogonal (i.e. * is diagonal and the eigenvector matrix V is orthogonal (i.e.
* A = V.times(D.times(V.transpose())) and V.times(V.transpose()) * A = V.times(D.times(V.transpose())) and V.times(V.transpose())
* equals the identity matrix). * equals the identity matrix).
* *
* If A is not symmetric, then the eigenvalue matrix D is block diagonal * If A is not symmetric, then the eigenvalue matrix D is block diagonal
* with the real eigenvalues in 1-by-1 blocks and any complex eigenvalues, * with the real eigenvalues in 1-by-1 blocks and any complex eigenvalues,
* lambda + i*mu, in 2-by-2 blocks, [lambda, mu; -mu, lambda]. The * lambda + i*mu, in 2-by-2 blocks, [lambda, mu; -mu, lambda]. The
* columns of V represent the eigenvectors in the sense that A*V = V*D, * columns of V represent the eigenvectors in the sense that A*V = V*D,
* i.e. A.times(V) equals V.times(D). The matrix V may be badly * i.e. A.times(V) equals V.times(D). The matrix V may be badly
* conditioned, or even singular, so the validity of the equation * conditioned, or even singular, so the validity of the equation
* A = V*D*inverse(V) depends upon V.cond(). * A = V*D*inverse(V) depends upon V.cond().
* *
* @author Paul Meagher * @author Paul Meagher
* @license PHP v3.0 * @license PHP v3.0
* *
* @version 1.1 * @version 1.1
* *
* Slightly changed to adapt the original code to PHP-ML library * Slightly changed to adapt the original code to PHP-ML library
* @date 2017/04/11 * @date 2017/04/11
@ -36,83 +36,79 @@ use Phpml\Math\Matrix;
class EigenvalueDecomposition class EigenvalueDecomposition
{ {
/** /**
* Row and column dimension (square matrix). * Row and column dimension (square matrix).
* *
* @var int * @var int
*/ */
private $n; private $n;
/** /**
* Internal symmetry flag. * Arrays for internal storage of eigenvalues.
* *
* @var bool * @var array
*/
private $symmetric;
/**
* Arrays for internal storage of eigenvalues.
*
* @var array
*/ */
private $d = []; private $d = [];
/**
* @var array
*/
private $e = []; private $e = [];
/** /**
* Array for internal storage of eigenvectors. * Array for internal storage of eigenvectors.
* *
* @var array * @var array
*/ */
private $V = []; private $V = [];
/** /**
* Array for internal storage of nonsymmetric Hessenberg form. * Array for internal storage of nonsymmetric Hessenberg form.
* *
* @var array * @var array
*/ */
private $H = []; private $H = [];
/** /**
* Working storage for nonsymmetric algorithm. * Working storage for nonsymmetric algorithm.
* *
* @var array * @var array
*/ */
private $ort = []; private $ort = [];
/** /**
* Used for complex scalar division. * Used for complex scalar division.
* *
* @var float * @var float
*/ */
private $cdivr; private $cdivr;
/**
* @var float
*/
private $cdivi; private $cdivi;
private $A;
/** /**
* Constructor: Check for symmetry, then construct the eigenvalue decomposition * Constructor: Check for symmetry, then construct the eigenvalue decomposition
*/ */
public function __construct(array $Arg) public function __construct(array $arg)
{ {
$this->A = $Arg; $this->n = count($arg[0]);
$this->n = count($Arg[0]); $symmetric = true;
$this->symmetric = true;
for ($j = 0; ($j < $this->n) & $this->symmetric; ++$j) { for ($j = 0; ($j < $this->n) & $symmetric; ++$j) {
for ($i = 0; ($i < $this->n) & $this->symmetric; ++$i) { for ($i = 0; ($i < $this->n) & $symmetric; ++$i) {
$this->symmetric = ($this->A[$i][$j] == $this->A[$j][$i]); $symmetric = $arg[$i][$j] == $arg[$j][$i];
} }
} }
if ($this->symmetric) { if ($symmetric) {
$this->V = $this->A; $this->V = $arg;
// Tridiagonalize. // Tridiagonalize.
$this->tred2(); $this->tred2();
// Diagonalize. // Diagonalize.
$this->tql2(); $this->tql2();
} else { } else {
$this->H = $this->A; $this->H = $arg;
$this->ort = []; $this->ort = [];
// Reduce to Hessenberg form. // Reduce to Hessenberg form.
$this->orthes(); $this->orthes();
@ -148,7 +144,7 @@ class EigenvalueDecomposition
} }
/** /**
* Return the real parts of the eigenvalues<br> * Return the real parts of the eigenvalues<br>
* d = real(diag(D)); * d = real(diag(D));
*/ */
public function getRealEigenvalues(): array public function getRealEigenvalues(): array
@ -157,7 +153,7 @@ class EigenvalueDecomposition
} }
/** /**
* Return the imaginary parts of the eigenvalues <br> * Return the imaginary parts of the eigenvalues <br>
* d = imag(diag(D)) * d = imag(diag(D))
*/ */
public function getImagEigenvalues(): array public function getImagEigenvalues(): array
@ -166,7 +162,7 @@ class EigenvalueDecomposition
} }
/** /**
* Return the block diagonal eigenvalue matrix * Return the block diagonal eigenvalue matrix
*/ */
public function getDiagonalEigenvalues(): array public function getDiagonalEigenvalues(): array
{ {
@ -187,7 +183,7 @@ class EigenvalueDecomposition
} }
/** /**
* Symmetric Householder reduction to tridiagonal form. * Symmetric Householder reduction to tridiagonal form.
*/ */
private function tred2(): void private function tred2(): void
{ {
@ -308,12 +304,12 @@ class EigenvalueDecomposition
} }
/** /**
* Symmetric tridiagonal QL algorithm. * Symmetric tridiagonal QL algorithm.
* *
* This is derived from the Algol procedures tql2, by * This is derived from the Algol procedures tql2, by
* Bowdler, Martin, Reinsch, and Wilkinson, Handbook for * Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
* Auto. Comp., Vol.ii-Linear Algebra, and the corresponding * Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
* Fortran subroutine in EISPACK. * Fortran subroutine in EISPACK.
*/ */
private function tql2(): void private function tql2(): void
{ {
@ -341,10 +337,7 @@ class EigenvalueDecomposition
// If m == l, $this->d[l] is an eigenvalue, // If m == l, $this->d[l] is an eigenvalue,
// otherwise, iterate. // otherwise, iterate.
if ($m > $l) { if ($m > $l) {
$iter = 0;
do { do {
// Could check iteration count here.
++$iter;
// Compute implicit shift // Compute implicit shift
$g = $this->d[$l]; $g = $this->d[$l];
$p = ($this->d[$l + 1] - $g) / (2.0 * $this->e[$l]); $p = ($this->d[$l + 1] - $g) / (2.0 * $this->e[$l]);
@ -423,12 +416,12 @@ class EigenvalueDecomposition
} }
/** /**
* Nonsymmetric reduction to Hessenberg form. * Nonsymmetric reduction to Hessenberg form.
* *
* This is derived from the Algol procedures orthes and ortran, * This is derived from the Algol procedures orthes and ortran,
* by Martin and Wilkinson, Handbook for Auto. Comp., * by Martin and Wilkinson, Handbook for Auto. Comp.,
* Vol.ii-Linear Algebra, and the corresponding * Vol.ii-Linear Algebra, and the corresponding
* Fortran subroutines in EISPACK. * Fortran subroutines in EISPACK.
*/ */
private function orthes(): void private function orthes(): void
{ {
@ -541,12 +534,12 @@ class EigenvalueDecomposition
} }
/** /**
* Nonsymmetric reduction from Hessenberg to real Schur form. * Nonsymmetric reduction from Hessenberg to real Schur form.
* *
* Code is derived from the Algol procedure hqr2, * Code is derived from the Algol procedure hqr2,
* by Martin and Wilkinson, Handbook for Auto. Comp., * by Martin and Wilkinson, Handbook for Auto. Comp.,
* Vol.ii-Linear Algebra, and the corresponding * Vol.ii-Linear Algebra, and the corresponding
* Fortran subroutine in EISPACK. * Fortran subroutine in EISPACK.
*/ */
private function hqr2(): void private function hqr2(): void
{ {
@ -911,7 +904,7 @@ class EigenvalueDecomposition
$y = $this->H[$i + 1][$i]; $y = $this->H[$i + 1][$i];
$vr = ($this->d[$i] - $p) * ($this->d[$i] - $p) + $this->e[$i] * $this->e[$i] - $q * $q; $vr = ($this->d[$i] - $p) * ($this->d[$i] - $p) + $this->e[$i] * $this->e[$i] - $q * $q;
$vi = ($this->d[$i] - $p) * 2.0 * $q; $vi = ($this->d[$i] - $p) * 2.0 * $q;
if ($vr == 0.0 & $vi == 0.0) { if ($vr == 0.0 && $vi == 0.0) {
$vr = $eps * $norm * (abs($w) + abs($q) + abs($x) + abs($y) + abs($z)); $vr = $eps * $norm * (abs($w) + abs($q) + abs($x) + abs($y) + abs($z));
} }
@ -943,7 +936,7 @@ class EigenvalueDecomposition
// Vectors of isolated roots // Vectors of isolated roots
for ($i = 0; $i < $nn; ++$i) { for ($i = 0; $i < $nn; ++$i) {
if ($i < $low | $i > $high) { if ($i < $low || $i > $high) {
for ($j = $i; $j < $nn; ++$j) { for ($j = $i; $j < $nn; ++$j) {
$this->V[$i][$j] = $this->H[$i][$j]; $this->V[$i][$j] = $this->H[$i][$j];
} }

View File

@ -80,7 +80,7 @@ class LUDecomposition
*/ */
public function __construct(Matrix $A) public function __construct(Matrix $A)
{ {
if ($A->getRows() != $A->getColumns()) { if ($A->getRows() !== $A->getColumns()) {
throw new MatrixException('Matrix is not square matrix'); throw new MatrixException('Matrix is not square matrix');
} }
@ -118,7 +118,7 @@ class LUDecomposition
// Find pivot and exchange if necessary. // Find pivot and exchange if necessary.
$p = $j; $p = $j;
for ($i = $j + 1; $i < $this->m; ++$i) { for ($i = $j + 1; $i < $this->m; ++$i) {
if (abs($LUcolj[$i]) > abs($LUcolj[$p])) { if (abs($LUcolj[$i] ?? 0) > abs($LUcolj[$p] ?? 0)) {
$p = $i; $p = $i;
} }
} }
@ -204,7 +204,7 @@ class LUDecomposition
* *
* @see getPivot * @see getPivot
*/ */
public function getDoublePivot() public function getDoublePivot(): array
{ {
return $this->getPivot(); return $this->getPivot();
} }

View File

@ -89,7 +89,7 @@ class Matrix
/** /**
* @throws MatrixException * @throws MatrixException
*/ */
public function getColumnValues($column): array public function getColumnValues(int $column): array
{ {
if ($column >= $this->columns) { if ($column >= $this->columns) {
throw new MatrixException('Column out of range'); throw new MatrixException('Column out of range');
@ -125,7 +125,7 @@ class Matrix
public function transpose(): self public function transpose(): self
{ {
if ($this->rows == 1) { if ($this->rows === 1) {
$matrix = array_map(function ($el) { $matrix = array_map(function ($el) {
return [$el]; return [$el];
}, $this->matrix[0]); }, $this->matrix[0]);
@ -138,7 +138,7 @@ class Matrix
public function multiply(self $matrix): self public function multiply(self $matrix): self
{ {
if ($this->columns != $matrix->getRows()) { if ($this->columns !== $matrix->getRows()) {
throw new InvalidArgumentException('Inconsistent matrix supplied'); throw new InvalidArgumentException('Inconsistent matrix supplied');
} }
@ -166,6 +166,9 @@ class Matrix
return new self($product, false); return new self($product, false);
} }
/**
* @param float|int $value
*/
public function divideByScalar($value): self public function divideByScalar($value): self
{ {
$newMatrix = []; $newMatrix = [];
@ -178,6 +181,9 @@ class Matrix
return new self($newMatrix, false); return new self($newMatrix, false);
} }
/**
* @param float|int $value
*/
public function multiplyByScalar($value): self public function multiplyByScalar($value): self
{ {
$newMatrix = []; $newMatrix = [];

View File

@ -14,7 +14,7 @@ class Product
$product = 0; $product = 0;
foreach ($a as $index => $value) { foreach ($a as $index => $value) {
if (is_numeric($value) && is_numeric($b[$index])) { if (is_numeric($value) && is_numeric($b[$index])) {
$product += $value * $b[$index]; $product += (float) $value * (float) $b[$index];
} }
} }

View File

@ -131,7 +131,7 @@ class Set implements IteratorAggregate
*/ */
public function containsAll(array $elements): bool public function containsAll(array $elements): bool
{ {
return !array_diff($elements, $this->elements); return count(array_diff($elements, $this->elements)) === 0;
} }
/** /**
@ -149,7 +149,7 @@ class Set implements IteratorAggregate
public function isEmpty(): bool public function isEmpty(): bool
{ {
return $this->cardinality() == 0; return $this->cardinality() === 0;
} }
public function cardinality(): int public function cardinality(): int

View File

@ -31,7 +31,7 @@ final class ANOVA
$samplesPerClass = array_map(function (array $class): int { $samplesPerClass = array_map(function (array $class): int {
return count($class); return count($class);
}, $samples); }, $samples);
$allSamples = array_sum($samplesPerClass); $allSamples = (int) array_sum($samplesPerClass);
$ssAllSamples = self::sumOfSquaresPerFeature($samples); $ssAllSamples = self::sumOfSquaresPerFeature($samples);
$sumSamples = self::sumOfFeaturesPerClass($samples); $sumSamples = self::sumOfFeaturesPerClass($samples);
$squareSumSamples = self::sumOfSquares($sumSamples); $squareSumSamples = self::sumOfSquares($sumSamples);

View File

@ -15,11 +15,11 @@ class Covariance
*/ */
public static function fromXYArrays(array $x, array $y, bool $sample = true, ?float $meanX = null, ?float $meanY = null): float public static function fromXYArrays(array $x, array $y, bool $sample = true, ?float $meanX = null, ?float $meanY = null): float
{ {
if (empty($x) || empty($y)) { $n = count($x);
if ($n === 0 || count($y) === 0) {
throw new InvalidArgumentException('The array has zero elements'); throw new InvalidArgumentException('The array has zero elements');
} }
$n = count($x);
if ($sample && $n === 1) { if ($sample && $n === 1) {
throw new InvalidArgumentException('The array must have at least 2 elements'); throw new InvalidArgumentException('The array must have at least 2 elements');
} }
@ -53,7 +53,7 @@ class Covariance
*/ */
public static function fromDataset(array $data, int $i, int $k, bool $sample = true, ?float $meanX = null, ?float $meanY = null): float public static function fromDataset(array $data, int $i, int $k, bool $sample = true, ?float $meanX = null, ?float $meanY = null): float
{ {
if (empty($data)) { if (count($data) === 0) {
throw new InvalidArgumentException('The array has zero elements'); throw new InvalidArgumentException('The array has zero elements');
} }
@ -87,7 +87,7 @@ class Covariance
// with a slight cost of CPU utilization. // with a slight cost of CPU utilization.
$sum = 0.0; $sum = 0.0;
foreach ($data as $row) { foreach ($data as $row) {
$val = []; $val = [0, 0];
foreach ($row as $index => $col) { foreach ($row as $index => $col) {
if ($index == $i) { if ($index == $i) {
$val[0] = $col - $meanX; $val[0] = $col - $meanX;

View File

@ -58,7 +58,7 @@ class Mean
*/ */
private static function checkArrayLength(array $array): void private static function checkArrayLength(array $array): void
{ {
if (empty($array)) { if (count($array) === 0) {
throw new InvalidArgumentException('The array has zero elements'); throw new InvalidArgumentException('The array has zero elements');
} }
} }

View File

@ -13,12 +13,11 @@ class StandardDeviation
*/ */
public static function population(array $numbers, bool $sample = true): float public static function population(array $numbers, bool $sample = true): float
{ {
if (empty($numbers)) { $n = count($numbers);
if ($n === 0) {
throw new InvalidArgumentException('The array has zero elements'); throw new InvalidArgumentException('The array has zero elements');
} }
$n = count($numbers);
if ($sample && $n === 1) { if ($sample && $n === 1) {
throw new InvalidArgumentException('The array must have at least 2 elements'); throw new InvalidArgumentException('The array must have at least 2 elements');
} }
@ -33,7 +32,7 @@ class StandardDeviation
--$n; --$n;
} }
return sqrt((float) ($carry / $n)); return sqrt($carry / $n);
} }
/** /**
@ -44,7 +43,7 @@ class StandardDeviation
*/ */
public static function sumOfSquares(array $numbers): float public static function sumOfSquares(array $numbers): float
{ {
if (empty($numbers)) { if (count($numbers) === 0) {
throw new InvalidArgumentException('The array has zero elements'); throw new InvalidArgumentException('The array has zero elements');
} }

View File

@ -142,9 +142,9 @@ class ClassificationReport
private function computeMicroAverage(): void private function computeMicroAverage(): void
{ {
$truePositive = array_sum($this->truePositive); $truePositive = (int) array_sum($this->truePositive);
$falsePositive = array_sum($this->falsePositive); $falsePositive = (int) array_sum($this->falsePositive);
$falseNegative = array_sum($this->falseNegative); $falseNegative = (int) array_sum($this->falseNegative);
$precision = $this->computePrecision($truePositive, $falsePositive); $precision = $this->computePrecision($truePositive, $falsePositive);
$recall = $this->computeRecall($truePositive, $falseNegative); $recall = $this->computeRecall($truePositive, $falseNegative);
@ -227,6 +227,6 @@ class ClassificationReport
$labels = array_values(array_unique(array_merge($actualLabels, $predictedLabels))); $labels = array_values(array_unique(array_merge($actualLabels, $predictedLabels)));
sort($labels); sort($labels);
return array_combine($labels, array_fill(0, count($labels), 0)); return (array) array_combine($labels, array_fill(0, count($labels), 0));
} }
} }

View File

@ -8,13 +8,13 @@ class ConfusionMatrix
{ {
public static function compute(array $actualLabels, array $predictedLabels, array $labels = []): array public static function compute(array $actualLabels, array $predictedLabels, array $labels = []): array
{ {
$labels = !empty($labels) ? array_flip($labels) : self::getUniqueLabels($actualLabels); $labels = count($labels) === 0 ? self::getUniqueLabels($actualLabels) : array_flip($labels);
$matrix = self::generateMatrixWithZeros($labels); $matrix = self::generateMatrixWithZeros($labels);
foreach ($actualLabels as $index => $actual) { foreach ($actualLabels as $index => $actual) {
$predicted = $predictedLabels[$index]; $predicted = $predictedLabels[$index];
if (!isset($labels[$actual]) || !isset($labels[$predicted])) { if (!isset($labels[$actual], $labels[$predicted])) {
continue; continue;
} }

View File

@ -16,7 +16,7 @@ class ModelManager
} }
$serialized = serialize($estimator); $serialized = serialize($estimator);
if (empty($serialized)) { if (!isset($serialized[0])) {
throw new SerializeException(sprintf('Class "%s" can not be serialized.', gettype($estimator))); throw new SerializeException(sprintf('Class "%s" can not be serialized.', gettype($estimator)));
} }
@ -32,7 +32,7 @@ class ModelManager
throw new FileException(sprintf('File "%s" can\'t be open.', basename($filepath))); throw new FileException(sprintf('File "%s" can\'t be open.', basename($filepath)));
} }
$object = unserialize(file_get_contents($filepath)); $object = unserialize((string) file_get_contents($filepath), [Estimator::class]);
if ($object === false) { if ($object === false) {
throw new SerializeException(sprintf('"%s" can not be unserialized.', basename($filepath))); throw new SerializeException(sprintf('"%s" can not be unserialized.', basename($filepath)));
} }

View File

@ -13,7 +13,7 @@ interface Network
public function getOutput(): array; public function getOutput(): array;
public function addLayer(Layer $layer); public function addLayer(Layer $layer): void;
/** /**
* @return Layer[] * @return Layer[]

View File

@ -61,7 +61,7 @@ abstract class MultilayerPerceptron extends LayeredNetwork implements Estimator,
*/ */
public function __construct(int $inputLayerFeatures, array $hiddenLayers, array $classes, int $iterations = 10000, ?ActivationFunction $activationFunction = null, float $learningRate = 1) public function __construct(int $inputLayerFeatures, array $hiddenLayers, array $classes, int $iterations = 10000, ?ActivationFunction $activationFunction = null, float $learningRate = 1)
{ {
if (empty($hiddenLayers)) { if (count($hiddenLayers) === 0) {
throw new InvalidArgumentException('Provide at least 1 hidden layer'); throw new InvalidArgumentException('Provide at least 1 hidden layer');
} }
@ -95,7 +95,7 @@ abstract class MultilayerPerceptron extends LayeredNetwork implements Estimator,
*/ */
public function partialTrain(array $samples, array $targets, array $classes = []): void public function partialTrain(array $samples, array $targets, array $classes = []): void
{ {
if (!empty($classes) && array_values($classes) !== $this->classes) { if (count($classes) > 0 && array_values($classes) !== $this->classes) {
// We require the list of classes in the constructor. // We require the list of classes in the constructor.
throw new InvalidArgumentException( throw new InvalidArgumentException(
'The provided classes don\'t match the classes provided in the constructor' 'The provided classes don\'t match the classes provided in the constructor'
@ -126,7 +126,7 @@ abstract class MultilayerPerceptron extends LayeredNetwork implements Estimator,
/** /**
* @param mixed $target * @param mixed $target
*/ */
abstract protected function trainSample(array $sample, $target); abstract protected function trainSample(array $sample, $target): void;
/** /**
* @return mixed * @return mixed

View File

@ -49,6 +49,6 @@ class Synapse
protected function generateRandomWeight(): float protected function generateRandomWeight(): float
{ {
return 1 / random_int(5, 25) * (random_int(0, 1) ? -1 : 1); return (1 / random_int(5, 25) * random_int(0, 1)) > 0 ? -1 : 1;
} }
} }

View File

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace Phpml\NeuralNetwork;
interface Training
{
public function train(array $samples, array $targets);
}

View File

@ -26,7 +26,7 @@ class DataTransformer
public static function testSet(array $samples): string public static function testSet(array $samples): string
{ {
if (empty($samples)) { if (count($samples) === 0) {
throw new InvalidArgumentException('The array has zero elements'); throw new InvalidArgumentException('The array has zero elements');
} }
@ -62,7 +62,7 @@ class DataTransformer
$predictions = explode(PHP_EOL, trim($rawPredictions)); $predictions = explode(PHP_EOL, trim($rawPredictions));
$header = array_shift($predictions); $header = array_shift($predictions);
$headerColumns = explode(' ', $header); $headerColumns = explode(' ', (string) $header);
array_shift($headerColumns); array_shift($headerColumns);
$columnLabels = []; $columnLabels = [];

View File

@ -165,7 +165,7 @@ class SupportVectorMachine
); );
} }
$this->model = file_get_contents($modelFileName); $this->model = (string) file_get_contents($modelFileName);
unlink($modelFileName); unlink($modelFileName);
} }
@ -241,7 +241,7 @@ class SupportVectorMachine
unlink($testSetFileName); unlink($testSetFileName);
unlink($modelFileName); unlink($modelFileName);
$predictions = file_get_contents($outputFileName); $predictions = (string) file_get_contents($outputFileName);
unlink($outputFileName); unlink($outputFileName);

View File

@ -4,10 +4,17 @@ declare(strict_types=1);
namespace Phpml\Tokenization; namespace Phpml\Tokenization;
use Phpml\Exception\InvalidArgumentException;
class WhitespaceTokenizer implements Tokenizer class WhitespaceTokenizer implements Tokenizer
{ {
public function tokenize(string $text): array public function tokenize(string $text): array
{ {
return preg_split('/[\pZ\pC]+/u', $text, -1, PREG_SPLIT_NO_EMPTY); $substrings = preg_split('/[\pZ\pC]+/u', $text, -1, PREG_SPLIT_NO_EMPTY);
if ($substrings === false) {
throw new InvalidArgumentException('preg_split failed on: '.$text);
}
return $substrings;
} }
} }

View File

@ -11,6 +11,9 @@ use ReflectionClass;
class AprioriTest extends TestCase class AprioriTest extends TestCase
{ {
/**
* @var array
*/
private $sampleGreek = [ private $sampleGreek = [
['alpha', 'beta', 'epsilon'], ['alpha', 'beta', 'epsilon'],
['alpha', 'beta', 'theta'], ['alpha', 'beta', 'theta'],
@ -18,6 +21,9 @@ class AprioriTest extends TestCase
['alpha', 'beta', 'theta'], ['alpha', 'beta', 'theta'],
]; ];
/**
* @var array
*/
private $sampleChars = [ private $sampleChars = [
['E', 'D', 'N', 'E+N', 'EN'], ['E', 'D', 'N', 'E+N', 'EN'],
['E', 'R', 'N', 'E+R', 'E+N', 'ER', 'EN'], ['E', 'R', 'N', 'E+R', 'E+N', 'ER', 'EN'],
@ -31,6 +37,9 @@ class AprioriTest extends TestCase
['N'], ['N'],
]; ];
/**
* @var array
*/
private $sampleBasket = [ private $sampleBasket = [
[1, 2, 3, 4], [1, 2, 3, 4],
[1, 2, 4], [1, 2, 4],
@ -48,16 +57,16 @@ class AprioriTest extends TestCase
$predicted = $apriori->predict([['alpha', 'epsilon'], ['beta', 'theta']]); $predicted = $apriori->predict([['alpha', 'epsilon'], ['beta', 'theta']]);
$this->assertCount(2, $predicted); self::assertCount(2, $predicted);
$this->assertEquals([['beta']], $predicted[0]); self::assertEquals([['beta']], $predicted[0]);
$this->assertEquals([['alpha']], $predicted[1]); self::assertEquals([['alpha']], $predicted[1]);
} }
public function testPowerSet(): void public function testPowerSet(): void
{ {
$apriori = new Apriori(); $apriori = new Apriori();
$this->assertCount(8, self::invoke($apriori, 'powerSet', [['a', 'b', 'c']])); self::assertCount(8, self::invoke($apriori, 'powerSet', [['a', 'b', 'c']]));
} }
public function testApriori(): void public function testApriori(): void
@ -67,13 +76,13 @@ class AprioriTest extends TestCase
$L = $apriori->apriori(); $L = $apriori->apriori();
$this->assertCount(4, $L[2]); self::assertCount(4, $L[2]);
$this->assertTrue(self::invoke($apriori, 'contains', [$L[2], [1, 2]])); self::assertTrue(self::invoke($apriori, 'contains', [$L[2], [1, 2]]));
$this->assertFalse(self::invoke($apriori, 'contains', [$L[2], [1, 3]])); self::assertFalse(self::invoke($apriori, 'contains', [$L[2], [1, 3]]));
$this->assertFalse(self::invoke($apriori, 'contains', [$L[2], [1, 4]])); self::assertFalse(self::invoke($apriori, 'contains', [$L[2], [1, 4]]));
$this->assertTrue(self::invoke($apriori, 'contains', [$L[2], [2, 3]])); self::assertTrue(self::invoke($apriori, 'contains', [$L[2], [2, 3]]));
$this->assertTrue(self::invoke($apriori, 'contains', [$L[2], [2, 4]])); self::assertTrue(self::invoke($apriori, 'contains', [$L[2], [2, 4]]));
$this->assertTrue(self::invoke($apriori, 'contains', [$L[2], [3, 4]])); self::assertTrue(self::invoke($apriori, 'contains', [$L[2], [3, 4]]));
} }
public function testAprioriEmpty(): void public function testAprioriEmpty(): void
@ -85,7 +94,7 @@ class AprioriTest extends TestCase
$L = $apriori->apriori(); $L = $apriori->apriori();
$this->assertEmpty($L); self::assertEmpty($L);
} }
public function testAprioriSingleItem(): void public function testAprioriSingleItem(): void
@ -97,8 +106,8 @@ class AprioriTest extends TestCase
$L = $apriori->apriori(); $L = $apriori->apriori();
$this->assertEquals([1], array_keys($L)); self::assertEquals([1], array_keys($L));
$this->assertEquals([['a']], $L[1]); self::assertEquals([['a']], $L[1]);
} }
public function testAprioriL3(): void public function testAprioriL3(): void
@ -110,7 +119,7 @@ class AprioriTest extends TestCase
$L = $apriori->apriori(); $L = $apriori->apriori();
$this->assertEquals([['a', 'b', 'c']], $L[3]); self::assertEquals([['a', 'b', 'c']], $L[3]);
} }
public function testGetRules(): void public function testGetRules(): void
@ -118,7 +127,7 @@ class AprioriTest extends TestCase
$apriori = new Apriori(0.4, 0.8); $apriori = new Apriori(0.4, 0.8);
$apriori->train($this->sampleChars, []); $apriori->train($this->sampleChars, []);
$this->assertCount(19, $apriori->getRules()); self::assertCount(19, $apriori->getRules());
} }
public function testGetRulesSupportAndConfidence(): void public function testGetRulesSupportAndConfidence(): void
@ -130,14 +139,14 @@ class AprioriTest extends TestCase
$rules = $apriori->getRules(); $rules = $apriori->getRules();
$this->assertCount(4, $rules); self::assertCount(4, $rules);
$this->assertContains([ self::assertContains([
Apriori::ARRAY_KEY_ANTECEDENT => ['a'], Apriori::ARRAY_KEY_ANTECEDENT => ['a'],
Apriori::ARRAY_KEY_CONSEQUENT => ['b'], Apriori::ARRAY_KEY_CONSEQUENT => ['b'],
Apriori::ARRAY_KEY_SUPPORT => 0.5, Apriori::ARRAY_KEY_SUPPORT => 0.5,
Apriori::ARRAY_KEY_CONFIDENCE => 0.5, Apriori::ARRAY_KEY_CONFIDENCE => 0.5,
], $rules); ], $rules);
$this->assertContains([ self::assertContains([
Apriori::ARRAY_KEY_ANTECEDENT => ['b'], Apriori::ARRAY_KEY_ANTECEDENT => ['b'],
Apriori::ARRAY_KEY_CONSEQUENT => ['a'], Apriori::ARRAY_KEY_CONSEQUENT => ['a'],
Apriori::ARRAY_KEY_SUPPORT => 0.5, Apriori::ARRAY_KEY_SUPPORT => 0.5,
@ -149,14 +158,14 @@ class AprioriTest extends TestCase
{ {
$apriori = new Apriori(); $apriori = new Apriori();
$this->assertCount(6, self::invoke($apriori, 'antecedents', [['a', 'b', 'c']])); self::assertCount(6, self::invoke($apriori, 'antecedents', [['a', 'b', 'c']]));
} }
public function testItems(): void public function testItems(): void
{ {
$apriori = new Apriori(); $apriori = new Apriori();
$apriori->train($this->sampleGreek, []); $apriori->train($this->sampleGreek, []);
$this->assertCount(4, self::invoke($apriori, 'items', [])); self::assertCount(4, self::invoke($apriori, 'items', []));
} }
public function testFrequent(): void public function testFrequent(): void
@ -164,8 +173,8 @@ class AprioriTest extends TestCase
$apriori = new Apriori(0.51); $apriori = new Apriori(0.51);
$apriori->train($this->sampleGreek, []); $apriori->train($this->sampleGreek, []);
$this->assertCount(0, self::invoke($apriori, 'frequent', [[['epsilon'], ['theta']]])); self::assertCount(0, self::invoke($apriori, 'frequent', [[['epsilon'], ['theta']]]));
$this->assertCount(2, self::invoke($apriori, 'frequent', [[['alpha'], ['beta']]])); self::assertCount(2, self::invoke($apriori, 'frequent', [[['alpha'], ['beta']]]));
} }
public function testCandidates(): void public function testCandidates(): void
@ -175,10 +184,10 @@ class AprioriTest extends TestCase
$candidates = self::invoke($apriori, 'candidates', [[['alpha'], ['beta'], ['theta']]]); $candidates = self::invoke($apriori, 'candidates', [[['alpha'], ['beta'], ['theta']]]);
$this->assertCount(3, $candidates); self::assertCount(3, $candidates);
$this->assertEquals(['alpha', 'beta'], $candidates[0]); self::assertEquals(['alpha', 'beta'], $candidates[0]);
$this->assertEquals(['alpha', 'theta'], $candidates[1]); self::assertEquals(['alpha', 'theta'], $candidates[1]);
$this->assertEquals(['beta', 'theta'], $candidates[2]); self::assertEquals(['beta', 'theta'], $candidates[2]);
} }
public function testConfidence(): void public function testConfidence(): void
@ -186,8 +195,8 @@ class AprioriTest extends TestCase
$apriori = new Apriori(); $apriori = new Apriori();
$apriori->train($this->sampleGreek, []); $apriori->train($this->sampleGreek, []);
$this->assertEquals(0.5, self::invoke($apriori, 'confidence', [['alpha', 'beta', 'theta'], ['alpha', 'beta']])); self::assertEquals(0.5, self::invoke($apriori, 'confidence', [['alpha', 'beta', 'theta'], ['alpha', 'beta']]));
$this->assertEquals(1, self::invoke($apriori, 'confidence', [['alpha', 'beta'], ['alpha']])); self::assertEquals(1, self::invoke($apriori, 'confidence', [['alpha', 'beta'], ['alpha']]));
} }
public function testSupport(): void public function testSupport(): void
@ -195,8 +204,8 @@ class AprioriTest extends TestCase
$apriori = new Apriori(); $apriori = new Apriori();
$apriori->train($this->sampleGreek, []); $apriori->train($this->sampleGreek, []);
$this->assertEquals(1.0, self::invoke($apriori, 'support', [['alpha', 'beta']])); self::assertEquals(1.0, self::invoke($apriori, 'support', [['alpha', 'beta']]));
$this->assertEquals(0.5, self::invoke($apriori, 'support', [['epsilon']])); self::assertEquals(0.5, self::invoke($apriori, 'support', [['epsilon']]));
} }
public function testFrequency(): void public function testFrequency(): void
@ -204,35 +213,35 @@ class AprioriTest extends TestCase
$apriori = new Apriori(); $apriori = new Apriori();
$apriori->train($this->sampleGreek, []); $apriori->train($this->sampleGreek, []);
$this->assertEquals(4, self::invoke($apriori, 'frequency', [['alpha', 'beta']])); self::assertEquals(4, self::invoke($apriori, 'frequency', [['alpha', 'beta']]));
$this->assertEquals(2, self::invoke($apriori, 'frequency', [['epsilon']])); self::assertEquals(2, self::invoke($apriori, 'frequency', [['epsilon']]));
} }
public function testContains(): void public function testContains(): void
{ {
$apriori = new Apriori(); $apriori = new Apriori();
$this->assertTrue(self::invoke($apriori, 'contains', [[['a'], ['b']], ['a']])); self::assertTrue(self::invoke($apriori, 'contains', [[['a'], ['b']], ['a']]));
$this->assertTrue(self::invoke($apriori, 'contains', [[[1, 2]], [1, 2]])); self::assertTrue(self::invoke($apriori, 'contains', [[[1, 2]], [1, 2]]));
$this->assertFalse(self::invoke($apriori, 'contains', [[['a'], ['b']], ['c']])); self::assertFalse(self::invoke($apriori, 'contains', [[['a'], ['b']], ['c']]));
} }
public function testSubset(): void public function testSubset(): void
{ {
$apriori = new Apriori(); $apriori = new Apriori();
$this->assertTrue(self::invoke($apriori, 'subset', [['a', 'b'], ['a']])); self::assertTrue(self::invoke($apriori, 'subset', [['a', 'b'], ['a']]));
$this->assertTrue(self::invoke($apriori, 'subset', [['a'], ['a']])); self::assertTrue(self::invoke($apriori, 'subset', [['a'], ['a']]));
$this->assertFalse(self::invoke($apriori, 'subset', [['a'], ['a', 'b']])); self::assertFalse(self::invoke($apriori, 'subset', [['a'], ['a', 'b']]));
} }
public function testEquals(): void public function testEquals(): void
{ {
$apriori = new Apriori(); $apriori = new Apriori();
$this->assertTrue(self::invoke($apriori, 'equals', [['a'], ['a']])); self::assertTrue(self::invoke($apriori, 'equals', [['a'], ['a']]));
$this->assertFalse(self::invoke($apriori, 'equals', [['a'], []])); self::assertFalse(self::invoke($apriori, 'equals', [['a'], []]));
$this->assertFalse(self::invoke($apriori, 'equals', [['a'], ['b', 'a']])); self::assertFalse(self::invoke($apriori, 'equals', [['a'], ['b', 'a']]));
} }
public function testSaveAndRestore(): void public function testSaveAndRestore(): void
@ -243,14 +252,14 @@ class AprioriTest extends TestCase
$testSamples = [['alpha', 'epsilon'], ['beta', 'theta']]; $testSamples = [['alpha', 'epsilon'], ['beta', 'theta']];
$predicted = $classifier->predict($testSamples); $predicted = $classifier->predict($testSamples);
$filename = 'apriori-test-'.random_int(100, 999).'-'.uniqid(); $filename = 'apriori-test-'.random_int(100, 999).'-'.uniqid('', false);
$filepath = tempnam(sys_get_temp_dir(), $filename); $filepath = (string) tempnam(sys_get_temp_dir(), $filename);
$modelManager = new ModelManager(); $modelManager = new ModelManager();
$modelManager->saveToFile($classifier, $filepath); $modelManager->saveToFile($classifier, $filepath);
$restoredClassifier = $modelManager->restoreFromFile($filepath); $restoredClassifier = $modelManager->restoreFromFile($filepath);
$this->assertEquals($classifier, $restoredClassifier); self::assertEquals($classifier, $restoredClassifier);
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples)); self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
} }
/** /**
@ -261,7 +270,7 @@ class AprioriTest extends TestCase
* *
* @return mixed * @return mixed
*/ */
private static function invoke(&$object, string $method, array $params = []) private static function invoke(Apriori $object, string $method, array $params = [])
{ {
$reflection = new ReflectionClass(get_class($object)); $reflection = new ReflectionClass(get_class($object));
$method = $reflection->getMethod($method); $method = $reflection->getMethod($method);

View File

@ -21,7 +21,10 @@ class DecisionTreeLeafTest extends TestCase
$leaf->rightLeaf = $rightLeaf; $leaf->rightLeaf = $rightLeaf;
$this->assertEquals('<table ><tr><td colspan=3 align=center style=\'border:1px solid;\'><b>col_0 =1</b><br>Gini: 0.00</td></tr><tr><td></td><td>&nbsp;</td><td valign=top align=right><b>No |</b><br><table ><tr><td colspan=3 align=center style=\'border:1px solid;\'><b>col_1 <= 2</b><br>Gini: 0.00</td></tr></table></td></tr></table>', $leaf->getHTML()); self::assertEquals(
'<table ><tr><td colspan=3 align=center style=\'border:1px solid;\'><b>col_0 =1</b><br>Gini: 0.00</td></tr><tr><td></td><td>&nbsp;</td><td valign=top align=right><b>No |</b><br><table ><tr><td colspan=3 align=center style=\'border:1px solid;\'><b>col_1 <= 2</b><br>Gini: 0.00</td></tr></table></td></tr></table>',
$leaf->getHTML()
);
} }
public function testNodeImpurityDecreaseShouldBeZeroWhenLeafIsTerminal(): void public function testNodeImpurityDecreaseShouldBeZeroWhenLeafIsTerminal(): void
@ -29,7 +32,7 @@ class DecisionTreeLeafTest extends TestCase
$leaf = new DecisionTreeLeaf(); $leaf = new DecisionTreeLeaf();
$leaf->isTerminal = true; $leaf->isTerminal = true;
$this->assertEquals(0.0, $leaf->getNodeImpurityDecrease(1)); self::assertEquals(0.0, $leaf->getNodeImpurityDecrease(1));
} }
public function testNodeImpurityDecrease(): void public function testNodeImpurityDecrease(): void
@ -45,6 +48,6 @@ class DecisionTreeLeafTest extends TestCase
$leaf->rightLeaf->records = []; $leaf->rightLeaf->records = [];
$leaf->rightLeaf->giniIndex = 0.3; $leaf->rightLeaf->giniIndex = 0.3;
$this->assertSame(0.75, $leaf->getNodeImpurityDecrease(2)); self::assertSame(0.75, $leaf->getNodeImpurityDecrease(2));
} }
} }

View File

@ -10,6 +10,9 @@ use PHPUnit\Framework\TestCase;
class DecisionTreeTest extends TestCase class DecisionTreeTest extends TestCase
{ {
/**
* @var array
*/
private $data = [ private $data = [
['sunny', 85, 85, 'false', 'Dont_play'], ['sunny', 85, 85, 'false', 'Dont_play'],
['sunny', 80, 90, 'true', 'Dont_play'], ['sunny', 80, 90, 'true', 'Dont_play'],
@ -27,26 +30,27 @@ class DecisionTreeTest extends TestCase
['rain', 71, 80, 'true', 'Dont_play'], ['rain', 71, 80, 'true', 'Dont_play'],
]; ];
/**
* @var array
*/
private $extraData = [ private $extraData = [
['scorching', 90, 95, 'false', 'Dont_play'], ['scorching', 90, 95, 'false', 'Dont_play'],
['scorching', 100, 93, 'true', 'Dont_play'], ['scorching', 100, 93, 'true', 'Dont_play'],
]; ];
public function testPredictSingleSample() public function testPredictSingleSample(): void
{ {
[$data, $targets] = $this->getData($this->data); [$data, $targets] = $this->getData($this->data);
$classifier = new DecisionTree(5); $classifier = new DecisionTree(5);
$classifier->train($data, $targets); $classifier->train($data, $targets);
$this->assertEquals('Dont_play', $classifier->predict(['sunny', 78, 72, 'false'])); self::assertEquals('Dont_play', $classifier->predict(['sunny', 78, 72, 'false']));
$this->assertEquals('Play', $classifier->predict(['overcast', 60, 60, 'false'])); self::assertEquals('Play', $classifier->predict(['overcast', 60, 60, 'false']));
$this->assertEquals('Dont_play', $classifier->predict(['rain', 60, 60, 'true'])); self::assertEquals('Dont_play', $classifier->predict(['rain', 60, 60, 'true']));
[$data, $targets] = $this->getData($this->extraData); [$data, $targets] = $this->getData($this->extraData);
$classifier->train($data, $targets); $classifier->train($data, $targets);
$this->assertEquals('Dont_play', $classifier->predict(['scorching', 95, 90, 'true'])); self::assertEquals('Dont_play', $classifier->predict(['scorching', 95, 90, 'true']));
$this->assertEquals('Play', $classifier->predict(['overcast', 60, 60, 'false'])); self::assertEquals('Play', $classifier->predict(['overcast', 60, 60, 'false']));
return $classifier;
} }
public function testSaveAndRestore(): void public function testSaveAndRestore(): void
@ -58,14 +62,14 @@ class DecisionTreeTest extends TestCase
$testSamples = [['sunny', 78, 72, 'false'], ['overcast', 60, 60, 'false']]; $testSamples = [['sunny', 78, 72, 'false'], ['overcast', 60, 60, 'false']];
$predicted = $classifier->predict($testSamples); $predicted = $classifier->predict($testSamples);
$filename = 'decision-tree-test-'.random_int(100, 999).'-'.uniqid(); $filename = 'decision-tree-test-'.random_int(100, 999).'-'.uniqid('', false);
$filepath = tempnam(sys_get_temp_dir(), $filename); $filepath = (string) tempnam(sys_get_temp_dir(), $filename);
$modelManager = new ModelManager(); $modelManager = new ModelManager();
$modelManager->saveToFile($classifier, $filepath); $modelManager->saveToFile($classifier, $filepath);
$restoredClassifier = $modelManager->restoreFromFile($filepath); $restoredClassifier = $modelManager->restoreFromFile($filepath);
$this->assertEquals($classifier, $restoredClassifier); self::assertEquals($classifier, $restoredClassifier);
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples)); self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
} }
public function testTreeDepth(): void public function testTreeDepth(): void
@ -73,10 +77,10 @@ class DecisionTreeTest extends TestCase
[$data, $targets] = $this->getData($this->data); [$data, $targets] = $this->getData($this->data);
$classifier = new DecisionTree(5); $classifier = new DecisionTree(5);
$classifier->train($data, $targets); $classifier->train($data, $targets);
$this->assertTrue($classifier->actualDepth <= 5); self::assertTrue($classifier->actualDepth <= 5);
} }
private function getData($input) private function getData(array $input): array
{ {
$targets = array_column($input, 4); $targets = array_column($input, 4);
array_walk($input, function (&$v): void { array_walk($input, function (&$v): void {

View File

@ -37,27 +37,27 @@ class AdaBoostTest extends TestCase
$targets = [0, 0, 0, 1, 1, 1]; $targets = [0, 0, 0, 1, 1, 1];
$classifier = new AdaBoost(); $classifier = new AdaBoost();
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
$this->assertEquals(0, $classifier->predict([0.1, 0.2])); self::assertEquals(0, $classifier->predict([0.1, 0.2]));
$this->assertEquals(0, $classifier->predict([0.1, 0.99])); self::assertEquals(0, $classifier->predict([0.1, 0.99]));
$this->assertEquals(1, $classifier->predict([1.1, 0.8])); self::assertEquals(1, $classifier->predict([1.1, 0.8]));
// OR problem // OR problem
$samples = [[0, 0], [0.1, 0.2], [0.2, 0.1], [1, 0], [0, 1], [1, 1]]; $samples = [[0, 0], [0.1, 0.2], [0.2, 0.1], [1, 0], [0, 1], [1, 1]];
$targets = [0, 0, 0, 1, 1, 1]; $targets = [0, 0, 0, 1, 1, 1];
$classifier = new AdaBoost(); $classifier = new AdaBoost();
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
$this->assertEquals(0, $classifier->predict([0.1, 0.2])); self::assertEquals(0, $classifier->predict([0.1, 0.2]));
$this->assertEquals(1, $classifier->predict([0.1, 0.99])); self::assertEquals(1, $classifier->predict([0.1, 0.99]));
$this->assertEquals(1, $classifier->predict([1.1, 0.8])); self::assertEquals(1, $classifier->predict([1.1, 0.8]));
// XOR problem // XOR problem
$samples = [[0.1, 0.2], [1., 1.], [0.9, 0.8], [0., 1.], [1., 0.], [0.2, 0.8]]; $samples = [[0.1, 0.2], [1., 1.], [0.9, 0.8], [0., 1.], [1., 0.], [0.2, 0.8]];
$targets = [0, 0, 0, 1, 1, 1]; $targets = [0, 0, 0, 1, 1, 1];
$classifier = new AdaBoost(5); $classifier = new AdaBoost(5);
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
$this->assertEquals(0, $classifier->predict([0.1, 0.1])); self::assertEquals(0, $classifier->predict([0.1, 0.1]));
$this->assertEquals(1, $classifier->predict([0, 0.999])); self::assertEquals(1, $classifier->predict([0, 0.999]));
$this->assertEquals(0, $classifier->predict([1.1, 0.8])); self::assertEquals(0, $classifier->predict([1.1, 0.8]));
} }
public function testSaveAndRestore(): void public function testSaveAndRestore(): void
@ -70,13 +70,13 @@ class AdaBoostTest extends TestCase
$testSamples = [[0, 1], [1, 1], [0.2, 0.1]]; $testSamples = [[0, 1], [1, 1], [0.2, 0.1]];
$predicted = $classifier->predict($testSamples); $predicted = $classifier->predict($testSamples);
$filename = 'adaboost-test-'.random_int(100, 999).'-'.uniqid(); $filename = 'adaboost-test-'.random_int(100, 999).'-'.uniqid('', false);
$filepath = tempnam(sys_get_temp_dir(), $filename); $filepath = (string) tempnam(sys_get_temp_dir(), $filename);
$modelManager = new ModelManager(); $modelManager = new ModelManager();
$modelManager->saveToFile($classifier, $filepath); $modelManager->saveToFile($classifier, $filepath);
$restoredClassifier = $modelManager->restoreFromFile($filepath); $restoredClassifier = $modelManager->restoreFromFile($filepath);
$this->assertEquals($classifier, $restoredClassifier); self::assertEquals($classifier, $restoredClassifier);
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples)); self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
} }
} }

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Phpml\Tests\Classification\Ensemble; namespace Phpml\Tests\Classification\Ensemble;
use Phpml\Classification\Classifier;
use Phpml\Classification\DecisionTree; use Phpml\Classification\DecisionTree;
use Phpml\Classification\Ensemble\Bagging; use Phpml\Classification\Ensemble\Bagging;
use Phpml\Classification\NaiveBayes; use Phpml\Classification\NaiveBayes;
@ -13,6 +14,9 @@ use PHPUnit\Framework\TestCase;
class BaggingTest extends TestCase class BaggingTest extends TestCase
{ {
/**
* @var array
*/
private $data = [ private $data = [
['sunny', 85, 85, 'false', 'Dont_play'], ['sunny', 85, 85, 'false', 'Dont_play'],
['sunny', 80, 90, 'true', 'Dont_play'], ['sunny', 80, 90, 'true', 'Dont_play'],
@ -30,6 +34,9 @@ class BaggingTest extends TestCase
['rain', 71, 80, 'true', 'Dont_play'], ['rain', 71, 80, 'true', 'Dont_play'],
]; ];
/**
* @var array
*/
private $extraData = [ private $extraData = [
['scorching', 90, 95, 'false', 'Dont_play'], ['scorching', 90, 95, 'false', 'Dont_play'],
['scorching', 0, 0, 'false', 'Dont_play'], ['scorching', 0, 0, 'false', 'Dont_play'],
@ -49,14 +56,14 @@ class BaggingTest extends TestCase
$classifier = $this->getClassifier(); $classifier = $this->getClassifier();
// Testing with default options // Testing with default options
$classifier->train($data, $targets); $classifier->train($data, $targets);
$this->assertEquals('Dont_play', $classifier->predict(['sunny', 78, 72, 'false'])); self::assertEquals('Dont_play', $classifier->predict(['sunny', 78, 72, 'false']));
$this->assertEquals('Play', $classifier->predict(['overcast', 60, 60, 'false'])); self::assertEquals('Play', $classifier->predict(['overcast', 60, 60, 'false']));
$this->assertEquals('Dont_play', $classifier->predict(['rain', 60, 60, 'true'])); self::assertEquals('Dont_play', $classifier->predict(['rain', 60, 60, 'true']));
[$data, $targets] = $this->getData($this->extraData); [$data, $targets] = $this->getData($this->extraData);
$classifier->train($data, $targets); $classifier->train($data, $targets);
$this->assertEquals('Dont_play', $classifier->predict(['scorching', 95, 90, 'true'])); self::assertEquals('Dont_play', $classifier->predict(['scorching', 95, 90, 'true']));
$this->assertEquals('Play', $classifier->predict(['overcast', 60, 60, 'false'])); self::assertEquals('Play', $classifier->predict(['overcast', 60, 60, 'false']));
} }
public function testSaveAndRestore(): void public function testSaveAndRestore(): void
@ -68,14 +75,14 @@ class BaggingTest extends TestCase
$testSamples = [['sunny', 78, 72, 'false'], ['overcast', 60, 60, 'false']]; $testSamples = [['sunny', 78, 72, 'false'], ['overcast', 60, 60, 'false']];
$predicted = $classifier->predict($testSamples); $predicted = $classifier->predict($testSamples);
$filename = 'bagging-test-'.random_int(100, 999).'-'.uniqid(); $filename = 'bagging-test-'.random_int(100, 999).'-'.uniqid('', false);
$filepath = tempnam(sys_get_temp_dir(), $filename); $filepath = (string) tempnam(sys_get_temp_dir(), $filename);
$modelManager = new ModelManager(); $modelManager = new ModelManager();
$modelManager->saveToFile($classifier, $filepath); $modelManager->saveToFile($classifier, $filepath);
$restoredClassifier = $modelManager->restoreFromFile($filepath); $restoredClassifier = $modelManager->restoreFromFile($filepath);
$this->assertEquals($classifier, $restoredClassifier); self::assertEquals($classifier, $restoredClassifier);
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples)); self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
} }
public function testBaseClassifiers(): void public function testBaseClassifiers(): void
@ -94,12 +101,15 @@ class BaggingTest extends TestCase
foreach ($testData as $test) { foreach ($testData as $test) {
$result = $classifier->predict($test); $result = $classifier->predict($test);
$baseResult = $classifier->predict($test); $baseResult = $classifier->predict($test);
$this->assertEquals($result, $baseResult); self::assertEquals($result, $baseResult);
} }
} }
} }
protected function getClassifier($numBaseClassifiers = 50) /**
* @return Bagging
*/
protected function getClassifier(int $numBaseClassifiers = 50): Classifier
{ {
$classifier = new Bagging($numBaseClassifiers); $classifier = new Bagging($numBaseClassifiers);
$classifier->setSubsetRatio(1.0); $classifier->setSubsetRatio(1.0);
@ -108,7 +118,7 @@ class BaggingTest extends TestCase
return $classifier; return $classifier;
} }
protected function getAvailableBaseClassifiers() protected function getAvailableBaseClassifiers(): array
{ {
return [ return [
DecisionTree::class => ['depth' => 5], DecisionTree::class => ['depth' => 5],
@ -116,7 +126,7 @@ class BaggingTest extends TestCase
]; ];
} }
private function getData($input) private function getData(array $input): array
{ {
// Populating input data to a size large enough // Populating input data to a size large enough
// for base classifiers that they can work with a subset of it // for base classifiers that they can work with a subset of it

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Phpml\Tests\Classification\Ensemble; namespace Phpml\Tests\Classification\Ensemble;
use Phpml\Classification\Classifier;
use Phpml\Classification\DecisionTree; use Phpml\Classification\DecisionTree;
use Phpml\Classification\Ensemble\RandomForest; use Phpml\Classification\Ensemble\RandomForest;
use Phpml\Classification\NaiveBayes; use Phpml\Classification\NaiveBayes;
@ -47,7 +48,10 @@ class RandomForestTest extends BaggingTest
$classifier->setFeatureSubsetRatio('pow'); $classifier->setFeatureSubsetRatio('pow');
} }
protected function getClassifier($numBaseClassifiers = 50) /**
* @return RandomForest
*/
protected function getClassifier(int $numBaseClassifiers = 50): Classifier
{ {
$classifier = new RandomForest($numBaseClassifiers); $classifier = new RandomForest($numBaseClassifiers);
$classifier->setFeatureSubsetRatio('log'); $classifier->setFeatureSubsetRatio('log');
@ -55,7 +59,7 @@ class RandomForestTest extends BaggingTest
return $classifier; return $classifier;
} }
protected function getAvailableBaseClassifiers() protected function getAvailableBaseClassifiers(): array
{ {
return [DecisionTree::class => ['depth' => 5]]; return [DecisionTree::class => ['depth' => 5]];
} }

View File

@ -19,15 +19,15 @@ class KNearestNeighborsTest extends TestCase
$classifier = new KNearestNeighbors(); $classifier = new KNearestNeighbors();
$classifier->train($samples, $labels); $classifier->train($samples, $labels);
$this->assertEquals('b', $classifier->predict([3, 2])); self::assertEquals('b', $classifier->predict([3, 2]));
$this->assertEquals('b', $classifier->predict([5, 1])); self::assertEquals('b', $classifier->predict([5, 1]));
$this->assertEquals('b', $classifier->predict([4, 3])); self::assertEquals('b', $classifier->predict([4, 3]));
$this->assertEquals('b', $classifier->predict([4, -5])); self::assertEquals('b', $classifier->predict([4, -5]));
$this->assertEquals('a', $classifier->predict([2, 3])); self::assertEquals('a', $classifier->predict([2, 3]));
$this->assertEquals('a', $classifier->predict([1, 2])); self::assertEquals('a', $classifier->predict([1, 2]));
$this->assertEquals('a', $classifier->predict([1, 5])); self::assertEquals('a', $classifier->predict([1, 5]));
$this->assertEquals('a', $classifier->predict([3, 10])); self::assertEquals('a', $classifier->predict([3, 10]));
} }
public function testPredictArrayOfSamples(): void public function testPredictArrayOfSamples(): void
@ -42,7 +42,7 @@ class KNearestNeighborsTest extends TestCase
$classifier->train($trainSamples, $trainLabels); $classifier->train($trainSamples, $trainLabels);
$predicted = $classifier->predict($testSamples); $predicted = $classifier->predict($testSamples);
$this->assertEquals($testLabels, $predicted); self::assertEquals($testLabels, $predicted);
} }
public function testPredictArrayOfSamplesUsingChebyshevDistanceMetric(): void public function testPredictArrayOfSamplesUsingChebyshevDistanceMetric(): void
@ -57,7 +57,7 @@ class KNearestNeighborsTest extends TestCase
$classifier->train($trainSamples, $trainLabels); $classifier->train($trainSamples, $trainLabels);
$predicted = $classifier->predict($testSamples); $predicted = $classifier->predict($testSamples);
$this->assertEquals($testLabels, $predicted); self::assertEquals($testLabels, $predicted);
} }
public function testSaveAndRestore(): void public function testSaveAndRestore(): void
@ -73,12 +73,12 @@ class KNearestNeighborsTest extends TestCase
$predicted = $classifier->predict($testSamples); $predicted = $classifier->predict($testSamples);
$filename = 'knearest-neighbors-test-'.random_int(100, 999).'-'.uniqid('', false); $filename = 'knearest-neighbors-test-'.random_int(100, 999).'-'.uniqid('', false);
$filepath = tempnam(sys_get_temp_dir(), $filename); $filepath = (string) tempnam(sys_get_temp_dir(), $filename);
$modelManager = new ModelManager(); $modelManager = new ModelManager();
$modelManager->saveToFile($classifier, $filepath); $modelManager->saveToFile($classifier, $filepath);
$restoredClassifier = $modelManager->restoreFromFile($filepath); $restoredClassifier = $modelManager->restoreFromFile($filepath);
$this->assertEquals($classifier, $restoredClassifier); self::assertEquals($classifier, $restoredClassifier);
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples)); self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
} }
} }

View File

@ -31,18 +31,18 @@ class AdalineTest extends TestCase
$targets = [0, 0, 0, 1]; $targets = [0, 0, 0, 1];
$classifier = new Adaline(); $classifier = new Adaline();
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
$this->assertEquals(0, $classifier->predict([0.1, 0.2])); self::assertEquals(0, $classifier->predict([0.1, 0.2]));
$this->assertEquals(0, $classifier->predict([0.1, 0.99])); self::assertEquals(0, $classifier->predict([0.1, 0.99]));
$this->assertEquals(1, $classifier->predict([1.1, 0.8])); self::assertEquals(1, $classifier->predict([1.1, 0.8]));
// OR problem // OR problem
$samples = [[0, 0], [1, 0], [0, 1], [1, 1]]; $samples = [[0, 0], [1, 0], [0, 1], [1, 1]];
$targets = [0, 1, 1, 1]; $targets = [0, 1, 1, 1];
$classifier = new Adaline(); $classifier = new Adaline();
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
$this->assertEquals(0, $classifier->predict([0.1, 0.2])); self::assertEquals(0, $classifier->predict([0.1, 0.2]));
$this->assertEquals(1, $classifier->predict([0.1, 0.99])); self::assertEquals(1, $classifier->predict([0.1, 0.99]));
$this->assertEquals(1, $classifier->predict([1.1, 0.8])); self::assertEquals(1, $classifier->predict([1.1, 0.8]));
// By use of One-v-Rest, Adaline can perform multi-class classification // By use of One-v-Rest, Adaline can perform multi-class classification
// The samples should be separable by lines perpendicular to the dimensions // The samples should be separable by lines perpendicular to the dimensions
@ -55,15 +55,15 @@ class AdalineTest extends TestCase
$classifier = new Adaline(); $classifier = new Adaline();
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
$this->assertEquals(0, $classifier->predict([0.5, 0.5])); self::assertEquals(0, $classifier->predict([0.5, 0.5]));
$this->assertEquals(1, $classifier->predict([6.0, 5.0])); self::assertEquals(1, $classifier->predict([6.0, 5.0]));
$this->assertEquals(2, $classifier->predict([3.0, 9.5])); self::assertEquals(2, $classifier->predict([3.0, 9.5]));
// Extra partial training should lead to the same results. // Extra partial training should lead to the same results.
$classifier->partialTrain([[0, 1], [1, 0]], [0, 0], [0, 1, 2]); $classifier->partialTrain([[0, 1], [1, 0]], [0, 0], [0, 1, 2]);
$this->assertEquals(0, $classifier->predict([0.5, 0.5])); self::assertEquals(0, $classifier->predict([0.5, 0.5]));
$this->assertEquals(1, $classifier->predict([6.0, 5.0])); self::assertEquals(1, $classifier->predict([6.0, 5.0]));
$this->assertEquals(2, $classifier->predict([3.0, 9.5])); self::assertEquals(2, $classifier->predict([3.0, 9.5]));
// Train should clear previous data. // Train should clear previous data.
$samples = [ $samples = [
@ -73,9 +73,9 @@ class AdalineTest extends TestCase
]; ];
$targets = [2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1]; $targets = [2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1];
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
$this->assertEquals(2, $classifier->predict([0.5, 0.5])); self::assertEquals(2, $classifier->predict([0.5, 0.5]));
$this->assertEquals(0, $classifier->predict([6.0, 5.0])); self::assertEquals(0, $classifier->predict([6.0, 5.0]));
$this->assertEquals(1, $classifier->predict([3.0, 9.5])); self::assertEquals(1, $classifier->predict([3.0, 9.5]));
} }
public function testSaveAndRestore(): void public function testSaveAndRestore(): void
@ -89,12 +89,12 @@ class AdalineTest extends TestCase
$predicted = $classifier->predict($testSamples); $predicted = $classifier->predict($testSamples);
$filename = 'adaline-test-'.random_int(100, 999).'-'.uniqid('', false); $filename = 'adaline-test-'.random_int(100, 999).'-'.uniqid('', false);
$filepath = tempnam(sys_get_temp_dir(), $filename); $filepath = (string) tempnam(sys_get_temp_dir(), $filename);
$modelManager = new ModelManager(); $modelManager = new ModelManager();
$modelManager->saveToFile($classifier, $filepath); $modelManager->saveToFile($classifier, $filepath);
$restoredClassifier = $modelManager->restoreFromFile($filepath); $restoredClassifier = $modelManager->restoreFromFile($filepath);
$this->assertEquals($classifier, $restoredClassifier); self::assertEquals($classifier, $restoredClassifier);
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples)); self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
} }
} }

View File

@ -23,7 +23,7 @@ class DecisionStumpTest extends TestCase
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
} }
public function testPredictSingleSample() public function testPredictSingleSample(): void
{ {
// Samples should be separable with a line perpendicular // Samples should be separable with a line perpendicular
// to any dimension given in the dataset // to any dimension given in the dataset
@ -33,20 +33,20 @@ class DecisionStumpTest extends TestCase
$targets = [0, 0, 1, 1]; $targets = [0, 0, 1, 1];
$classifier = new DecisionStump(); $classifier = new DecisionStump();
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
$this->assertEquals(0, $classifier->predict([0.1, 0.2])); self::assertEquals(0, $classifier->predict([0.1, 0.2]));
$this->assertEquals(0, $classifier->predict([1.1, 0.2])); self::assertEquals(0, $classifier->predict([1.1, 0.2]));
$this->assertEquals(1, $classifier->predict([0.1, 0.99])); self::assertEquals(1, $classifier->predict([0.1, 0.99]));
$this->assertEquals(1, $classifier->predict([1.1, 0.8])); self::assertEquals(1, $classifier->predict([1.1, 0.8]));
// Then: vertical test // Then: vertical test
$samples = [[0, 0], [1, 0], [0, 1], [1, 1]]; $samples = [[0, 0], [1, 0], [0, 1], [1, 1]];
$targets = [0, 1, 0, 1]; $targets = [0, 1, 0, 1];
$classifier = new DecisionStump(); $classifier = new DecisionStump();
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
$this->assertEquals(0, $classifier->predict([0.1, 0.2])); self::assertEquals(0, $classifier->predict([0.1, 0.2]));
$this->assertEquals(0, $classifier->predict([0.1, 1.1])); self::assertEquals(0, $classifier->predict([0.1, 1.1]));
$this->assertEquals(1, $classifier->predict([1.0, 0.99])); self::assertEquals(1, $classifier->predict([1.0, 0.99]));
$this->assertEquals(1, $classifier->predict([1.1, 0.1])); self::assertEquals(1, $classifier->predict([1.1, 0.1]));
// By use of One-v-Rest, DecisionStump can perform multi-class classification // By use of One-v-Rest, DecisionStump can perform multi-class classification
// The samples should be separable by lines perpendicular to the dimensions // The samples should be separable by lines perpendicular to the dimensions
@ -59,11 +59,9 @@ class DecisionStumpTest extends TestCase
$classifier = new DecisionStump(); $classifier = new DecisionStump();
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
$this->assertEquals(0, $classifier->predict([0.5, 0.5])); self::assertEquals(0, $classifier->predict([0.5, 0.5]));
$this->assertEquals(1, $classifier->predict([6.0, 5.0])); self::assertEquals(1, $classifier->predict([6.0, 5.0]));
$this->assertEquals(2, $classifier->predict([3.5, 9.5])); self::assertEquals(2, $classifier->predict([3.5, 9.5]));
return $classifier;
} }
public function testSaveAndRestore(): void public function testSaveAndRestore(): void
@ -76,13 +74,13 @@ class DecisionStumpTest extends TestCase
$testSamples = [[0, 1], [1, 1], [0.2, 0.1]]; $testSamples = [[0, 1], [1, 1], [0.2, 0.1]];
$predicted = $classifier->predict($testSamples); $predicted = $classifier->predict($testSamples);
$filename = 'dstump-test-'.random_int(100, 999).'-'.uniqid(); $filename = 'dstump-test-'.random_int(100, 999).'-'.uniqid('', false);
$filepath = tempnam(sys_get_temp_dir(), $filename); $filepath = (string) tempnam(sys_get_temp_dir(), $filename);
$modelManager = new ModelManager(); $modelManager = new ModelManager();
$modelManager->saveToFile($classifier, $filepath); $modelManager->saveToFile($classifier, $filepath);
$restoredClassifier = $modelManager->restoreFromFile($filepath); $restoredClassifier = $modelManager->restoreFromFile($filepath);
$this->assertEquals($classifier, $restoredClassifier); self::assertEquals($classifier, $restoredClassifier);
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples)); self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
} }
} }

View File

@ -64,8 +64,8 @@ class LogisticRegressionTest extends TestCase
$targets = [0, 0, 0, 1, 0, 1]; $targets = [0, 0, 0, 1, 0, 1];
$classifier = new LogisticRegression(); $classifier = new LogisticRegression();
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
$this->assertEquals(0, $classifier->predict([0.1, 0.1])); self::assertEquals(0, $classifier->predict([0.1, 0.1]));
$this->assertEquals(1, $classifier->predict([0.9, 0.9])); self::assertEquals(1, $classifier->predict([0.9, 0.9]));
} }
public function testPredictSingleSampleWithBatchTraining(): void public function testPredictSingleSampleWithBatchTraining(): void
@ -83,8 +83,8 @@ class LogisticRegressionTest extends TestCase
'L2' 'L2'
); );
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
$this->assertEquals(0, $classifier->predict([0.1, 0.1])); self::assertEquals(0, $classifier->predict([0.1, 0.1]));
$this->assertEquals(1, $classifier->predict([0.9, 0.9])); self::assertEquals(1, $classifier->predict([0.9, 0.9]));
} }
public function testPredictSingleSampleWithOnlineTraining(): void public function testPredictSingleSampleWithOnlineTraining(): void
@ -102,8 +102,8 @@ class LogisticRegressionTest extends TestCase
'' ''
); );
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
$this->assertEquals(0, $classifier->predict([0.1, 0.1])); self::assertEquals(0, $classifier->predict([0.1, 0.1]));
$this->assertEquals(1, $classifier->predict([0.9, 0.9])); self::assertEquals(1, $classifier->predict([0.9, 0.9]));
} }
public function testPredictSingleSampleWithSSECost(): void public function testPredictSingleSampleWithSSECost(): void
@ -118,8 +118,8 @@ class LogisticRegressionTest extends TestCase
'L2' 'L2'
); );
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
$this->assertEquals(0, $classifier->predict([0.1, 0.1])); self::assertEquals(0, $classifier->predict([0.1, 0.1]));
$this->assertEquals(1, $classifier->predict([0.9, 0.9])); self::assertEquals(1, $classifier->predict([0.9, 0.9]));
} }
public function testPredictSingleSampleWithoutPenalty(): void public function testPredictSingleSampleWithoutPenalty(): void
@ -134,8 +134,8 @@ class LogisticRegressionTest extends TestCase
'' ''
); );
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
$this->assertEquals(0, $classifier->predict([0.1, 0.1])); self::assertEquals(0, $classifier->predict([0.1, 0.1]));
$this->assertEquals(1, $classifier->predict([0.9, 0.9])); self::assertEquals(1, $classifier->predict([0.9, 0.9]));
} }
public function testPredictMultiClassSample(): void public function testPredictMultiClassSample(): void
@ -151,9 +151,9 @@ class LogisticRegressionTest extends TestCase
$classifier = new LogisticRegression(); $classifier = new LogisticRegression();
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
$this->assertEquals(0, $classifier->predict([0.5, 0.5])); self::assertEquals(0, $classifier->predict([0.5, 0.5]));
$this->assertEquals(1, $classifier->predict([6.0, 5.0])); self::assertEquals(1, $classifier->predict([6.0, 5.0]));
$this->assertEquals(2, $classifier->predict([3.0, 9.5])); self::assertEquals(2, $classifier->predict([3.0, 9.5]));
} }
public function testPredictProbabilitySingleSample(): void public function testPredictProbabilitySingleSample(): void
@ -171,13 +171,13 @@ class LogisticRegressionTest extends TestCase
$zero = $method->invoke($predictor, [0.1, 0.1], 0); $zero = $method->invoke($predictor, [0.1, 0.1], 0);
$one = $method->invoke($predictor, [0.1, 0.1], 1); $one = $method->invoke($predictor, [0.1, 0.1], 1);
$this->assertEquals(1, $zero + $one, '', 1e-6); self::assertEquals(1, $zero + $one, '', 1e-6);
$this->assertTrue($zero > $one); self::assertTrue($zero > $one);
$zero = $method->invoke($predictor, [0.9, 0.9], 0); $zero = $method->invoke($predictor, [0.9, 0.9], 0);
$one = $method->invoke($predictor, [0.9, 0.9], 1); $one = $method->invoke($predictor, [0.9, 0.9], 1);
$this->assertEquals(1, $zero + $one, '', 1e-6); self::assertEquals(1, $zero + $one, '', 1e-6);
$this->assertTrue($zero < $one); self::assertTrue($zero < $one);
} }
public function testPredictProbabilityMultiClassSample(): void public function testPredictProbabilityMultiClassSample(): void
@ -213,10 +213,10 @@ class LogisticRegressionTest extends TestCase
$two = $method->invoke($predictor, [3.0, 9.5], 2); $two = $method->invoke($predictor, [3.0, 9.5], 2);
$not_two = $method->invoke($predictor, [3.0, 9.5], 'not_2'); $not_two = $method->invoke($predictor, [3.0, 9.5], 'not_2');
$this->assertEquals(1, $zero + $not_zero, '', 1e-6); self::assertEquals(1, $zero + $not_zero, '', 1e-6);
$this->assertEquals(1, $one + $not_one, '', 1e-6); self::assertEquals(1, $one + $not_one, '', 1e-6);
$this->assertEquals(1, $two + $not_two, '', 1e-6); self::assertEquals(1, $two + $not_two, '', 1e-6);
$this->assertTrue($zero < $two); self::assertTrue($zero < $two);
$this->assertTrue($one < $two); self::assertTrue($one < $two);
} }
} }

View File

@ -33,9 +33,9 @@ class PerceptronTest extends TestCase
$classifier = new Perceptron(0.001, 5000); $classifier = new Perceptron(0.001, 5000);
$classifier->setEarlyStop(false); $classifier->setEarlyStop(false);
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
$this->assertEquals(0, $classifier->predict([0.1, 0.2])); self::assertEquals(0, $classifier->predict([0.1, 0.2]));
$this->assertEquals(0, $classifier->predict([0, 1])); self::assertEquals(0, $classifier->predict([0, 1]));
$this->assertEquals(1, $classifier->predict([1.1, 0.8])); self::assertEquals(1, $classifier->predict([1.1, 0.8]));
// OR problem // OR problem
$samples = [[0.1, 0.1], [0.4, 0.], [0., 0.3], [1, 0], [0, 1], [1, 1]]; $samples = [[0.1, 0.1], [0.4, 0.], [0., 0.3], [1, 0], [0, 1], [1, 1]];
@ -43,9 +43,9 @@ class PerceptronTest extends TestCase
$classifier = new Perceptron(0.001, 5000, false); $classifier = new Perceptron(0.001, 5000, false);
$classifier->setEarlyStop(false); $classifier->setEarlyStop(false);
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
$this->assertEquals(0, $classifier->predict([0., 0.])); self::assertEquals(0, $classifier->predict([0., 0.]));
$this->assertEquals(1, $classifier->predict([0.1, 0.99])); self::assertEquals(1, $classifier->predict([0.1, 0.99]));
$this->assertEquals(1, $classifier->predict([1.1, 0.8])); self::assertEquals(1, $classifier->predict([1.1, 0.8]));
// By use of One-v-Rest, Perceptron can perform multi-class classification // By use of One-v-Rest, Perceptron can perform multi-class classification
// The samples should be separable by lines perpendicular to the dimensions // The samples should be separable by lines perpendicular to the dimensions
@ -59,15 +59,15 @@ class PerceptronTest extends TestCase
$classifier = new Perceptron(); $classifier = new Perceptron();
$classifier->setEarlyStop(false); $classifier->setEarlyStop(false);
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
$this->assertEquals(0, $classifier->predict([0.5, 0.5])); self::assertEquals(0, $classifier->predict([0.5, 0.5]));
$this->assertEquals(1, $classifier->predict([6.0, 5.0])); self::assertEquals(1, $classifier->predict([6.0, 5.0]));
$this->assertEquals(2, $classifier->predict([3.0, 9.5])); self::assertEquals(2, $classifier->predict([3.0, 9.5]));
// Extra partial training should lead to the same results. // Extra partial training should lead to the same results.
$classifier->partialTrain([[0, 1], [1, 0]], [0, 0], [0, 1, 2]); $classifier->partialTrain([[0, 1], [1, 0]], [0, 0], [0, 1, 2]);
$this->assertEquals(0, $classifier->predict([0.5, 0.5])); self::assertEquals(0, $classifier->predict([0.5, 0.5]));
$this->assertEquals(1, $classifier->predict([6.0, 5.0])); self::assertEquals(1, $classifier->predict([6.0, 5.0]));
$this->assertEquals(2, $classifier->predict([3.0, 9.5])); self::assertEquals(2, $classifier->predict([3.0, 9.5]));
// Train should clear previous data. // Train should clear previous data.
$samples = [ $samples = [
@ -77,9 +77,9 @@ class PerceptronTest extends TestCase
]; ];
$targets = [2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1]; $targets = [2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1];
$classifier->train($samples, $targets); $classifier->train($samples, $targets);
$this->assertEquals(2, $classifier->predict([0.5, 0.5])); self::assertEquals(2, $classifier->predict([0.5, 0.5]));
$this->assertEquals(0, $classifier->predict([6.0, 5.0])); self::assertEquals(0, $classifier->predict([6.0, 5.0]));
$this->assertEquals(1, $classifier->predict([3.0, 9.5])); self::assertEquals(1, $classifier->predict([3.0, 9.5]));
} }
public function testSaveAndRestore(): void public function testSaveAndRestore(): void
@ -93,12 +93,12 @@ class PerceptronTest extends TestCase
$predicted = $classifier->predict($testSamples); $predicted = $classifier->predict($testSamples);
$filename = 'perceptron-test-'.random_int(100, 999).'-'.uniqid('', false); $filename = 'perceptron-test-'.random_int(100, 999).'-'.uniqid('', false);
$filepath = tempnam(sys_get_temp_dir(), $filename); $filepath = (string) tempnam(sys_get_temp_dir(), $filename);
$modelManager = new ModelManager(); $modelManager = new ModelManager();
$modelManager->saveToFile($classifier, $filepath); $modelManager->saveToFile($classifier, $filepath);
$restoredClassifier = $modelManager->restoreFromFile($filepath); $restoredClassifier = $modelManager->restoreFromFile($filepath);
$this->assertEquals($classifier, $restoredClassifier); self::assertEquals($classifier, $restoredClassifier);
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples)); self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
} }
} }

View File

@ -21,21 +21,21 @@ class MLPClassifierTest extends TestCase
{ {
$mlp = new MLPClassifier(2, [2], [0, 1]); $mlp = new MLPClassifier(2, [2], [0, 1]);
$this->assertCount(3, $mlp->getLayers()); self::assertCount(3, $mlp->getLayers());
$layers = $mlp->getLayers(); $layers = $mlp->getLayers();
// input layer // input layer
$this->assertCount(3, $layers[0]->getNodes()); self::assertCount(3, $layers[0]->getNodes());
$this->assertNotContainsOnly(Neuron::class, $layers[0]->getNodes()); self::assertNotContainsOnly(Neuron::class, $layers[0]->getNodes());
// hidden layer // hidden layer
$this->assertCount(3, $layers[1]->getNodes()); self::assertCount(3, $layers[1]->getNodes());
$this->assertNotContainsOnly(Neuron::class, $layers[1]->getNodes()); self::assertNotContainsOnly(Neuron::class, $layers[1]->getNodes());
// output layer // output layer
$this->assertCount(2, $layers[2]->getNodes()); self::assertCount(2, $layers[2]->getNodes());
$this->assertContainsOnly(Neuron::class, $layers[2]->getNodes()); self::assertContainsOnly(Neuron::class, $layers[2]->getNodes());
} }
public function testSynapsesGeneration(): void public function testSynapsesGeneration(): void
@ -46,11 +46,11 @@ class MLPClassifierTest extends TestCase
foreach ($layers[1]->getNodes() as $node) { foreach ($layers[1]->getNodes() as $node) {
if ($node instanceof Neuron) { if ($node instanceof Neuron) {
$synapses = $node->getSynapses(); $synapses = $node->getSynapses();
$this->assertCount(3, $synapses); self::assertCount(3, $synapses);
$synapsesNodes = $this->getSynapsesNodes($synapses); $synapsesNodes = $this->getSynapsesNodes($synapses);
foreach ($layers[0]->getNodes() as $prevNode) { foreach ($layers[0]->getNodes() as $prevNode) {
$this->assertContains($prevNode, $synapsesNodes); self::assertContains($prevNode, $synapsesNodes);
} }
} }
} }
@ -65,10 +65,10 @@ class MLPClassifierTest extends TestCase
['a', 'b', 'a', 'b'] ['a', 'b', 'a', 'b']
); );
$this->assertEquals('a', $network->predict([1, 0])); self::assertEquals('a', $network->predict([1, 0]));
$this->assertEquals('b', $network->predict([0, 1])); self::assertEquals('b', $network->predict([0, 1]));
$this->assertEquals('a', $network->predict([1, 1])); self::assertEquals('a', $network->predict([1, 1]));
$this->assertEquals('b', $network->predict([0, 0])); self::assertEquals('b', $network->predict([0, 0]));
} }
public function testBackpropagationTrainingReset(): void public function testBackpropagationTrainingReset(): void
@ -80,16 +80,16 @@ class MLPClassifierTest extends TestCase
['a', 'b'] ['a', 'b']
); );
$this->assertEquals('a', $network->predict([1, 0])); self::assertEquals('a', $network->predict([1, 0]));
$this->assertEquals('b', $network->predict([0, 1])); self::assertEquals('b', $network->predict([0, 1]));
$network->train( $network->train(
[[1, 0], [0, 1]], [[1, 0], [0, 1]],
['b', 'a'] ['b', 'a']
); );
$this->assertEquals('b', $network->predict([1, 0])); self::assertEquals('b', $network->predict([1, 0]));
$this->assertEquals('a', $network->predict([0, 1])); self::assertEquals('a', $network->predict([0, 1]));
} }
public function testBackpropagationPartialTraining(): void public function testBackpropagationPartialTraining(): void
@ -101,18 +101,18 @@ class MLPClassifierTest extends TestCase
['a', 'b'] ['a', 'b']
); );
$this->assertEquals('a', $network->predict([1, 0])); self::assertEquals('a', $network->predict([1, 0]));
$this->assertEquals('b', $network->predict([0, 1])); self::assertEquals('b', $network->predict([0, 1]));
$network->partialTrain( $network->partialTrain(
[[1, 1], [0, 0]], [[1, 1], [0, 0]],
['a', 'b'] ['a', 'b']
); );
$this->assertEquals('a', $network->predict([1, 0])); self::assertEquals('a', $network->predict([1, 0]));
$this->assertEquals('b', $network->predict([0, 1])); self::assertEquals('b', $network->predict([0, 1]));
$this->assertEquals('a', $network->predict([1, 1])); self::assertEquals('a', $network->predict([1, 1]));
$this->assertEquals('b', $network->predict([0, 0])); self::assertEquals('b', $network->predict([0, 0]));
} }
public function testBackpropagationLearningMultilayer(): void public function testBackpropagationLearningMultilayer(): void
@ -124,10 +124,10 @@ class MLPClassifierTest extends TestCase
['a', 'b', 'a', 'c'] ['a', 'b', 'a', 'c']
); );
$this->assertEquals('a', $network->predict([1, 0, 0, 0, 0])); self::assertEquals('a', $network->predict([1, 0, 0, 0, 0]));
$this->assertEquals('b', $network->predict([0, 1, 1, 0, 0])); self::assertEquals('b', $network->predict([0, 1, 1, 0, 0]));
$this->assertEquals('a', $network->predict([1, 1, 1, 1, 1])); self::assertEquals('a', $network->predict([1, 1, 1, 1, 1]));
$this->assertEquals('c', $network->predict([0, 0, 0, 0, 0])); self::assertEquals('c', $network->predict([0, 0, 0, 0, 0]));
} }
public function testBackpropagationLearningMulticlass(): void public function testBackpropagationLearningMulticlass(): void
@ -139,11 +139,11 @@ class MLPClassifierTest extends TestCase
['a', 'b', 'a', 'a', 4] ['a', 'b', 'a', 'a', 4]
); );
$this->assertEquals('a', $network->predict([1, 0, 0, 0, 0])); self::assertEquals('a', $network->predict([1, 0, 0, 0, 0]));
$this->assertEquals('b', $network->predict([0, 1, 0, 0, 0])); self::assertEquals('b', $network->predict([0, 1, 0, 0, 0]));
$this->assertEquals('a', $network->predict([0, 0, 1, 1, 0])); self::assertEquals('a', $network->predict([0, 0, 1, 1, 0]));
$this->assertEquals('a', $network->predict([1, 1, 1, 1, 1])); self::assertEquals('a', $network->predict([1, 1, 1, 1, 1]));
$this->assertEquals(4, $network->predict([0, 0, 0, 0, 0])); self::assertEquals(4, $network->predict([0, 0, 0, 0, 0]));
} }
/** /**
@ -157,10 +157,10 @@ class MLPClassifierTest extends TestCase
['a', 'b', 'a', 'a'] ['a', 'b', 'a', 'a']
); );
$this->assertEquals('a', $network->predict([1, 0, 0, 0, 0])); self::assertEquals('a', $network->predict([1, 0, 0, 0, 0]));
$this->assertEquals('b', $network->predict([0, 1, 0, 0, 0])); self::assertEquals('b', $network->predict([0, 1, 0, 0, 0]));
$this->assertEquals('a', $network->predict([0, 0, 1, 1, 0])); self::assertEquals('a', $network->predict([0, 0, 1, 1, 0]));
$this->assertEquals('a', $network->predict([1, 1, 1, 1, 1])); self::assertEquals('a', $network->predict([1, 1, 1, 1, 1]));
} }
public function activationFunctionsProvider(): array public function activationFunctionsProvider(): array
@ -184,13 +184,13 @@ class MLPClassifierTest extends TestCase
$predicted = $classifier->predict($testSamples); $predicted = $classifier->predict($testSamples);
$filename = 'perceptron-test-'.random_int(100, 999).'-'.uniqid('', false); $filename = 'perceptron-test-'.random_int(100, 999).'-'.uniqid('', false);
$filepath = tempnam(sys_get_temp_dir(), $filename); $filepath = (string) tempnam(sys_get_temp_dir(), $filename);
$modelManager = new ModelManager(); $modelManager = new ModelManager();
$modelManager->saveToFile($classifier, $filepath); $modelManager->saveToFile($classifier, $filepath);
$restoredClassifier = $modelManager->restoreFromFile($filepath); $restoredClassifier = $modelManager->restoreFromFile($filepath);
$this->assertEquals($classifier, $restoredClassifier); self::assertEquals($classifier, $restoredClassifier);
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples)); self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
} }
public function testSaveAndRestoreWithPartialTraining(): void public function testSaveAndRestoreWithPartialTraining(): void
@ -201,11 +201,11 @@ class MLPClassifierTest extends TestCase
['a', 'b'] ['a', 'b']
); );
$this->assertEquals('a', $network->predict([1, 0])); self::assertEquals('a', $network->predict([1, 0]));
$this->assertEquals('b', $network->predict([0, 1])); self::assertEquals('b', $network->predict([0, 1]));
$filename = 'perceptron-test-'.random_int(100, 999).'-'.uniqid('', false); $filename = 'perceptron-test-'.random_int(100, 999).'-'.uniqid('', false);
$filepath = tempnam(sys_get_temp_dir(), $filename); $filepath = (string) tempnam(sys_get_temp_dir(), $filename);
$modelManager = new ModelManager(); $modelManager = new ModelManager();
$modelManager->saveToFile($network, $filepath); $modelManager->saveToFile($network, $filepath);
@ -216,10 +216,10 @@ class MLPClassifierTest extends TestCase
['a', 'b'] ['a', 'b']
); );
$this->assertEquals('a', $restoredNetwork->predict([1, 0])); self::assertEquals('a', $restoredNetwork->predict([1, 0]));
$this->assertEquals('b', $restoredNetwork->predict([0, 1])); self::assertEquals('b', $restoredNetwork->predict([0, 1]));
$this->assertEquals('a', $restoredNetwork->predict([1, 1])); self::assertEquals('a', $restoredNetwork->predict([1, 1]));
$this->assertEquals('b', $restoredNetwork->predict([0, 0])); self::assertEquals('b', $restoredNetwork->predict([0, 0]));
} }
public function testThrowExceptionOnInvalidLayersNumber(): void public function testThrowExceptionOnInvalidLayersNumber(): void
@ -249,7 +249,7 @@ class MLPClassifierTest extends TestCase
{ {
$output = (new MLPClassifier(2, [2, 2], ['T', 'F']))->getOutput(); $output = (new MLPClassifier(2, [2, 2], ['T', 'F']))->getOutput();
$this->assertEquals(['T', 'F'], array_keys($output)); self::assertEquals(['T', 'F'], array_keys($output));
} }
private function getSynapsesNodes(array $synapses): array private function getSynapsesNodes(array $synapses): array

View File

@ -18,9 +18,9 @@ class NaiveBayesTest extends TestCase
$classifier = new NaiveBayes(); $classifier = new NaiveBayes();
$classifier->train($samples, $labels); $classifier->train($samples, $labels);
$this->assertEquals('a', $classifier->predict([3, 1, 1])); self::assertEquals('a', $classifier->predict([3, 1, 1]));
$this->assertEquals('b', $classifier->predict([1, 4, 1])); self::assertEquals('b', $classifier->predict([1, 4, 1]));
$this->assertEquals('c', $classifier->predict([1, 1, 6])); self::assertEquals('c', $classifier->predict([1, 1, 6]));
} }
public function testPredictArrayOfSamples(): void public function testPredictArrayOfSamples(): void
@ -35,7 +35,7 @@ class NaiveBayesTest extends TestCase
$classifier->train($trainSamples, $trainLabels); $classifier->train($trainSamples, $trainLabels);
$predicted = $classifier->predict($testSamples); $predicted = $classifier->predict($testSamples);
$this->assertEquals($testLabels, $predicted); self::assertEquals($testLabels, $predicted);
// Feed an extra set of training data. // Feed an extra set of training data.
$samples = [[1, 1, 6]]; $samples = [[1, 1, 6]];
@ -44,7 +44,7 @@ class NaiveBayesTest extends TestCase
$testSamples = [[1, 1, 6], [5, 1, 1]]; $testSamples = [[1, 1, 6], [5, 1, 1]];
$testLabels = ['d', 'a']; $testLabels = ['d', 'a'];
$this->assertEquals($testLabels, $classifier->predict($testSamples)); self::assertEquals($testLabels, $classifier->predict($testSamples));
} }
public function testSaveAndRestore(): void public function testSaveAndRestore(): void
@ -59,13 +59,13 @@ class NaiveBayesTest extends TestCase
$predicted = $classifier->predict($testSamples); $predicted = $classifier->predict($testSamples);
$filename = 'naive-bayes-test-'.random_int(100, 999).'-'.uniqid('', false); $filename = 'naive-bayes-test-'.random_int(100, 999).'-'.uniqid('', false);
$filepath = tempnam(sys_get_temp_dir(), $filename); $filepath = (string) tempnam(sys_get_temp_dir(), $filename);
$modelManager = new ModelManager(); $modelManager = new ModelManager();
$modelManager->saveToFile($classifier, $filepath); $modelManager->saveToFile($classifier, $filepath);
$restoredClassifier = $modelManager->restoreFromFile($filepath); $restoredClassifier = $modelManager->restoreFromFile($filepath);
$this->assertEquals($classifier, $restoredClassifier); self::assertEquals($classifier, $restoredClassifier);
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples)); self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
} }
public function testPredictSimpleNumericLabels(): void public function testPredictSimpleNumericLabels(): void
@ -76,9 +76,9 @@ class NaiveBayesTest extends TestCase
$classifier = new NaiveBayes(); $classifier = new NaiveBayes();
$classifier->train($samples, $labels); $classifier->train($samples, $labels);
$this->assertEquals('1996', $classifier->predict([3, 1, 1])); self::assertEquals('1996', $classifier->predict([3, 1, 1]));
$this->assertEquals('1997', $classifier->predict([1, 4, 1])); self::assertEquals('1997', $classifier->predict([1, 4, 1]));
$this->assertEquals('1998', $classifier->predict([1, 1, 6])); self::assertEquals('1998', $classifier->predict([1, 1, 6]));
} }
public function testPredictArrayOfSamplesNumericalLabels(): void public function testPredictArrayOfSamplesNumericalLabels(): void
@ -93,7 +93,7 @@ class NaiveBayesTest extends TestCase
$classifier->train($trainSamples, $trainLabels); $classifier->train($trainSamples, $trainLabels);
$predicted = $classifier->predict($testSamples); $predicted = $classifier->predict($testSamples);
$this->assertEquals($testLabels, $predicted); self::assertEquals($testLabels, $predicted);
// Feed an extra set of training data. // Feed an extra set of training data.
$samples = [[1, 1, 6]]; $samples = [[1, 1, 6]];
@ -102,7 +102,7 @@ class NaiveBayesTest extends TestCase
$testSamples = [[1, 1, 6], [5, 1, 1]]; $testSamples = [[1, 1, 6], [5, 1, 1]];
$testLabels = ['1999', '1996']; $testLabels = ['1999', '1996'];
$this->assertEquals($testLabels, $classifier->predict($testSamples)); self::assertEquals($testLabels, $classifier->predict($testSamples));
} }
public function testSaveAndRestoreNumericLabels(): void public function testSaveAndRestoreNumericLabels(): void
@ -117,12 +117,12 @@ class NaiveBayesTest extends TestCase
$predicted = $classifier->predict($testSamples); $predicted = $classifier->predict($testSamples);
$filename = 'naive-bayes-test-'.random_int(100, 999).'-'.uniqid('', false); $filename = 'naive-bayes-test-'.random_int(100, 999).'-'.uniqid('', false);
$filepath = tempnam(sys_get_temp_dir(), $filename); $filepath = (string) tempnam(sys_get_temp_dir(), $filename);
$modelManager = new ModelManager(); $modelManager = new ModelManager();
$modelManager->saveToFile($classifier, $filepath); $modelManager->saveToFile($classifier, $filepath);
$restoredClassifier = $modelManager->restoreFromFile($filepath); $restoredClassifier = $modelManager->restoreFromFile($filepath);
$this->assertEquals($classifier, $restoredClassifier); self::assertEquals($classifier, $restoredClassifier);
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples)); self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
} }
} }

View File

@ -19,15 +19,15 @@ class SVCTest extends TestCase
$classifier = new SVC(Kernel::LINEAR, $cost = 1000); $classifier = new SVC(Kernel::LINEAR, $cost = 1000);
$classifier->train($samples, $labels); $classifier->train($samples, $labels);
$this->assertEquals('b', $classifier->predict([3, 2])); self::assertEquals('b', $classifier->predict([3, 2]));
$this->assertEquals('b', $classifier->predict([5, 1])); self::assertEquals('b', $classifier->predict([5, 1]));
$this->assertEquals('b', $classifier->predict([4, 3])); self::assertEquals('b', $classifier->predict([4, 3]));
$this->assertEquals('b', $classifier->predict([4, -5])); self::assertEquals('b', $classifier->predict([4, -5]));
$this->assertEquals('a', $classifier->predict([2, 3])); self::assertEquals('a', $classifier->predict([2, 3]));
$this->assertEquals('a', $classifier->predict([1, 2])); self::assertEquals('a', $classifier->predict([1, 2]));
$this->assertEquals('a', $classifier->predict([1, 5])); self::assertEquals('a', $classifier->predict([1, 5]));
$this->assertEquals('a', $classifier->predict([3, 10])); self::assertEquals('a', $classifier->predict([3, 10]));
} }
public function testPredictArrayOfSamplesWithLinearKernel(): void public function testPredictArrayOfSamplesWithLinearKernel(): void
@ -42,7 +42,7 @@ class SVCTest extends TestCase
$classifier->train($trainSamples, $trainLabels); $classifier->train($trainSamples, $trainLabels);
$predictions = $classifier->predict($testSamples); $predictions = $classifier->predict($testSamples);
$this->assertEquals($testLabels, $predictions); self::assertEquals($testLabels, $predictions);
} }
public function testSaveAndRestore(): void public function testSaveAndRestore(): void
@ -57,14 +57,14 @@ class SVCTest extends TestCase
$classifier->train($trainSamples, $trainLabels); $classifier->train($trainSamples, $trainLabels);
$predicted = $classifier->predict($testSamples); $predicted = $classifier->predict($testSamples);
$filepath = tempnam(sys_get_temp_dir(), uniqid('svc-test', true)); $filepath = (string) tempnam(sys_get_temp_dir(), uniqid('svc-test', true));
$modelManager = new ModelManager(); $modelManager = new ModelManager();
$modelManager->saveToFile($classifier, $filepath); $modelManager->saveToFile($classifier, $filepath);
$restoredClassifier = $modelManager->restoreFromFile($filepath); $restoredClassifier = $modelManager->restoreFromFile($filepath);
$this->assertEquals($classifier, $restoredClassifier); self::assertEquals($classifier, $restoredClassifier);
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples)); self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
$this->assertEquals($predicted, $testLabels); self::assertEquals($predicted, $testLabels);
} }
public function testWithNonDotDecimalLocale(): void public function testWithNonDotDecimalLocale(): void
@ -81,8 +81,8 @@ class SVCTest extends TestCase
$classifier = new SVC(Kernel::LINEAR, $cost = 1000); $classifier = new SVC(Kernel::LINEAR, $cost = 1000);
$classifier->train($trainSamples, $trainLabels); $classifier->train($trainSamples, $trainLabels);
$this->assertEquals($classifier->predict($testSamples), $testLabels); self::assertEquals($classifier->predict($testSamples), $testLabels);
setlocale(LC_NUMERIC, $currentLocale); setlocale(LC_NUMERIC, (string) $currentLocale);
} }
} }

View File

@ -19,7 +19,7 @@ class DBSCANTest extends TestCase
$dbscan = new DBSCAN($epsilon = 2, $minSamples = 3); $dbscan = new DBSCAN($epsilon = 2, $minSamples = 3);
$this->assertEquals($clustered, $dbscan->cluster($samples)); self::assertEquals($clustered, $dbscan->cluster($samples));
$samples = [[1, 1], [6, 6], [1, -1], [5, 6], [-1, -1], [7, 8], [-1, 1], [7, 7]]; $samples = [[1, 1], [6, 6], [1, -1], [5, 6], [-1, -1], [7, 8], [-1, 1], [7, 7]];
$clustered = [ $clustered = [
@ -29,7 +29,7 @@ class DBSCANTest extends TestCase
$dbscan = new DBSCAN($epsilon = 3, $minSamples = 4); $dbscan = new DBSCAN($epsilon = 3, $minSamples = 4);
$this->assertEquals($clustered, $dbscan->cluster($samples)); self::assertEquals($clustered, $dbscan->cluster($samples));
} }
public function testDBSCANSamplesClusteringAssociative(): void public function testDBSCANSamplesClusteringAssociative(): void
@ -57,7 +57,7 @@ class DBSCANTest extends TestCase
$dbscan = new DBSCAN($epsilon = 3, $minSamples = 2); $dbscan = new DBSCAN($epsilon = 3, $minSamples = 2);
$this->assertEquals($clustered, $dbscan->cluster($samples)); self::assertEquals($clustered, $dbscan->cluster($samples));
} }
public function testClusterEpsilonSmall(): void public function testClusterEpsilonSmall(): void
@ -68,7 +68,7 @@ class DBSCANTest extends TestCase
$dbscan = new DBSCAN($epsilon = 0.5, $minSamples = 2); $dbscan = new DBSCAN($epsilon = 0.5, $minSamples = 2);
$this->assertEquals($clustered, $dbscan->cluster($samples)); self::assertEquals($clustered, $dbscan->cluster($samples));
} }
public function testClusterEpsilonBoundary(): void public function testClusterEpsilonBoundary(): void
@ -79,7 +79,7 @@ class DBSCANTest extends TestCase
$dbscan = new DBSCAN($epsilon = 1.0, $minSamples = 2); $dbscan = new DBSCAN($epsilon = 1.0, $minSamples = 2);
$this->assertEquals($clustered, $dbscan->cluster($samples)); self::assertEquals($clustered, $dbscan->cluster($samples));
} }
public function testClusterEpsilonLarge(): void public function testClusterEpsilonLarge(): void
@ -91,6 +91,6 @@ class DBSCANTest extends TestCase
$dbscan = new DBSCAN($epsilon = 1.5, $minSamples = 2); $dbscan = new DBSCAN($epsilon = 1.5, $minSamples = 2);
$this->assertEquals($clustered, $dbscan->cluster($samples)); self::assertEquals($clustered, $dbscan->cluster($samples));
} }
} }

View File

@ -10,40 +10,40 @@ use PHPUnit\Framework\TestCase;
class FuzzyCMeansTest extends TestCase class FuzzyCMeansTest extends TestCase
{ {
public function testFCMSamplesClustering() public function testFCMSamplesClustering(): void
{ {
$samples = [[1, 1], [8, 7], [1, 2], [7, 8], [2, 1], [8, 9]]; $samples = [[1, 1], [8, 7], [1, 2], [7, 8], [2, 1], [8, 9]];
$fcm = new FuzzyCMeans(2); $fcm = new FuzzyCMeans(2);
$clusters = $fcm->cluster($samples); $clusters = $fcm->cluster($samples);
$this->assertCount(2, $clusters); self::assertCount(2, $clusters);
foreach ($samples as $index => $sample) { foreach ($samples as $index => $sample) {
if (in_array($sample, $clusters[0], true) || in_array($sample, $clusters[1], true)) { if (in_array($sample, $clusters[0], true) || in_array($sample, $clusters[1], true)) {
unset($samples[$index]); unset($samples[$index]);
} }
} }
$this->assertCount(0, $samples); self::assertCount(0, $samples);
return $fcm;
} }
public function testMembershipMatrix(): void public function testMembershipMatrix(): void
{ {
$fcm = $this->testFCMSamplesClustering(); $fcm = new FuzzyCMeans(2);
$fcm->cluster([[1, 1], [8, 7], [1, 2], [7, 8], [2, 1], [8, 9]]);
$clusterCount = 2; $clusterCount = 2;
$sampleCount = 6; $sampleCount = 6;
$matrix = $fcm->getMembershipMatrix(); $matrix = $fcm->getMembershipMatrix();
$this->assertCount($clusterCount, $matrix); self::assertCount($clusterCount, $matrix);
foreach ($matrix as $row) { foreach ($matrix as $row) {
$this->assertCount($sampleCount, $row); self::assertCount($sampleCount, $row);
} }
// Transpose of the matrix // Transpose of the matrix
array_unshift($matrix, null); array_unshift($matrix, null);
$matrix = call_user_func_array('array_map', $matrix); $matrix = array_map(...$matrix);
// All column totals should be equal to 1 (100% membership) // All column totals should be equal to 1 (100% membership)
foreach ($matrix as $col) { foreach ($matrix as $col) {
$this->assertEquals(1, array_sum($col)); self::assertEquals(1, array_sum($col));
} }
} }

View File

@ -26,7 +26,7 @@ class ClusterTest extends TestCase
$cluster = new Cluster(new Space(2), [1, 2]); $cluster = new Cluster(new Space(2), [1, 2]);
$cluster->attach(new Point([1, 1])); $cluster->attach(new Point([1, 1]));
$this->assertSame([ self::assertSame([
'centroid' => [1, 2], 'centroid' => [1, 2],
'points' => [ 'points' => [
[1, 1], [1, 1],
@ -42,8 +42,8 @@ class ClusterTest extends TestCase
$detachedPoint = $cluster->detach($point); $detachedPoint = $cluster->detach($point);
$this->assertSame($detachedPoint, $point); self::assertSame($detachedPoint, $point);
$this->assertNotContains($point, $cluster->getPoints()); self::assertNotContains($point, $cluster->getPoints());
$this->assertCount(1, $cluster); self::assertCount(1, $cluster);
} }
} }

View File

@ -17,7 +17,7 @@ class KMeansTest extends TestCase
$kmeans = new KMeans(2); $kmeans = new KMeans(2);
$clusters = $kmeans->cluster($samples); $clusters = $kmeans->cluster($samples);
$this->assertCount(2, $clusters); self::assertCount(2, $clusters);
foreach ($samples as $index => $sample) { foreach ($samples as $index => $sample) {
if (in_array($sample, $clusters[0], true) || in_array($sample, $clusters[1], true)) { if (in_array($sample, $clusters[0], true) || in_array($sample, $clusters[1], true)) {
@ -25,7 +25,7 @@ class KMeansTest extends TestCase
} }
} }
$this->assertCount(0, $samples); self::assertCount(0, $samples);
} }
public function testKMeansSamplesLabeledClustering(): void public function testKMeansSamplesLabeledClustering(): void
@ -42,16 +42,16 @@ class KMeansTest extends TestCase
$kmeans = new KMeans(2); $kmeans = new KMeans(2);
$clusters = $kmeans->cluster($samples); $clusters = $kmeans->cluster($samples);
$this->assertCount(2, $clusters); self::assertCount(2, $clusters);
foreach ($samples as $index => $sample) { foreach ($samples as $index => $sample) {
if (in_array($sample, $clusters[0], true) || in_array($sample, $clusters[1], true)) { if (in_array($sample, $clusters[0], true) || in_array($sample, $clusters[1], true)) {
$this->assertArrayHasKey($index, $clusters[0] + $clusters[1]); self::assertArrayHasKey($index, $clusters[0] + $clusters[1]);
unset($samples[$index]); unset($samples[$index]);
} }
} }
$this->assertCount(0, $samples); self::assertCount(0, $samples);
} }
public function testKMeansInitializationMethods(): void public function testKMeansInitializationMethods(): void
@ -71,11 +71,11 @@ class KMeansTest extends TestCase
$kmeans = new KMeans(4, KMeans::INIT_KMEANS_PLUS_PLUS); $kmeans = new KMeans(4, KMeans::INIT_KMEANS_PLUS_PLUS);
$clusters = $kmeans->cluster($samples); $clusters = $kmeans->cluster($samples);
$this->assertCount(4, $clusters); self::assertCount(4, $clusters);
$kmeans = new KMeans(4, KMeans::INIT_RANDOM); $kmeans = new KMeans(4, KMeans::INIT_RANDOM);
$clusters = $kmeans->cluster($samples); $clusters = $kmeans->cluster($samples);
$this->assertCount(4, $clusters); self::assertCount(4, $clusters);
} }
public function testThrowExceptionOnInvalidClusterNumber(): void public function testThrowExceptionOnInvalidClusterNumber(): void

View File

@ -32,13 +32,13 @@ class RandomSplitTest extends TestCase
$randomSplit = new RandomSplit($dataset, 0.5); $randomSplit = new RandomSplit($dataset, 0.5);
$this->assertCount(2, $randomSplit->getTestSamples()); self::assertCount(2, $randomSplit->getTestSamples());
$this->assertCount(2, $randomSplit->getTrainSamples()); self::assertCount(2, $randomSplit->getTrainSamples());
$randomSplit2 = new RandomSplit($dataset, 0.25); $randomSplit2 = new RandomSplit($dataset, 0.25);
$this->assertCount(1, $randomSplit2->getTestSamples()); self::assertCount(1, $randomSplit2->getTestSamples());
$this->assertCount(3, $randomSplit2->getTrainSamples()); self::assertCount(3, $randomSplit2->getTrainSamples());
} }
public function testDatasetRandomSplitWithSameSeed(): void public function testDatasetRandomSplitWithSameSeed(): void
@ -53,10 +53,10 @@ class RandomSplitTest extends TestCase
$randomSplit1 = new RandomSplit($dataset, 0.5, $seed); $randomSplit1 = new RandomSplit($dataset, 0.5, $seed);
$randomSplit2 = new RandomSplit($dataset, 0.5, $seed); $randomSplit2 = new RandomSplit($dataset, 0.5, $seed);
$this->assertEquals($randomSplit1->getTestLabels(), $randomSplit2->getTestLabels()); self::assertEquals($randomSplit1->getTestLabels(), $randomSplit2->getTestLabels());
$this->assertEquals($randomSplit1->getTestSamples(), $randomSplit2->getTestSamples()); self::assertEquals($randomSplit1->getTestSamples(), $randomSplit2->getTestSamples());
$this->assertEquals($randomSplit1->getTrainLabels(), $randomSplit2->getTrainLabels()); self::assertEquals($randomSplit1->getTrainLabels(), $randomSplit2->getTrainLabels());
$this->assertEquals($randomSplit1->getTrainSamples(), $randomSplit2->getTrainSamples()); self::assertEquals($randomSplit1->getTrainSamples(), $randomSplit2->getTrainSamples());
} }
public function testDatasetRandomSplitWithDifferentSeed(): void public function testDatasetRandomSplitWithDifferentSeed(): void
@ -69,10 +69,10 @@ class RandomSplitTest extends TestCase
$randomSplit1 = new RandomSplit($dataset, 0.5, 4321); $randomSplit1 = new RandomSplit($dataset, 0.5, 4321);
$randomSplit2 = new RandomSplit($dataset, 0.5, 1234); $randomSplit2 = new RandomSplit($dataset, 0.5, 1234);
$this->assertNotEquals($randomSplit1->getTestLabels(), $randomSplit2->getTestLabels()); self::assertNotEquals($randomSplit1->getTestLabels(), $randomSplit2->getTestLabels());
$this->assertNotEquals($randomSplit1->getTestSamples(), $randomSplit2->getTestSamples()); self::assertNotEquals($randomSplit1->getTestSamples(), $randomSplit2->getTestSamples());
$this->assertNotEquals($randomSplit1->getTrainLabels(), $randomSplit2->getTrainLabels()); self::assertNotEquals($randomSplit1->getTrainLabels(), $randomSplit2->getTrainLabels());
$this->assertNotEquals($randomSplit1->getTrainSamples(), $randomSplit2->getTrainSamples()); self::assertNotEquals($randomSplit1->getTrainSamples(), $randomSplit2->getTrainSamples());
} }
public function testRandomSplitCorrectSampleAndLabelPosition(): void public function testRandomSplitCorrectSampleAndLabelPosition(): void
@ -84,9 +84,9 @@ class RandomSplitTest extends TestCase
$randomSplit = new RandomSplit($dataset, 0.5); $randomSplit = new RandomSplit($dataset, 0.5);
$this->assertEquals($randomSplit->getTestSamples()[0][0], $randomSplit->getTestLabels()[0]); self::assertEquals($randomSplit->getTestSamples()[0][0], $randomSplit->getTestLabels()[0]);
$this->assertEquals($randomSplit->getTestSamples()[1][0], $randomSplit->getTestLabels()[1]); self::assertEquals($randomSplit->getTestSamples()[1][0], $randomSplit->getTestLabels()[1]);
$this->assertEquals($randomSplit->getTrainSamples()[0][0], $randomSplit->getTrainLabels()[0]); self::assertEquals($randomSplit->getTrainSamples()[0][0], $randomSplit->getTrainLabels()[0]);
$this->assertEquals($randomSplit->getTrainSamples()[1][0], $randomSplit->getTrainLabels()[1]); self::assertEquals($randomSplit->getTrainSamples()[1][0], $randomSplit->getTrainLabels()[1]);
} }
} }

View File

@ -19,13 +19,13 @@ class StratifiedRandomSplitTest extends TestCase
$split = new StratifiedRandomSplit($dataset, 0.5); $split = new StratifiedRandomSplit($dataset, 0.5);
$this->assertEquals(2, $this->countSamplesByTarget($split->getTestLabels(), 'a')); self::assertEquals(2, $this->countSamplesByTarget($split->getTestLabels(), 'a'));
$this->assertEquals(2, $this->countSamplesByTarget($split->getTestLabels(), 'b')); self::assertEquals(2, $this->countSamplesByTarget($split->getTestLabels(), 'b'));
$split = new StratifiedRandomSplit($dataset, 0.25); $split = new StratifiedRandomSplit($dataset, 0.25);
$this->assertEquals(1, $this->countSamplesByTarget($split->getTestLabels(), 'a')); self::assertEquals(1, $this->countSamplesByTarget($split->getTestLabels(), 'a'));
$this->assertEquals(1, $this->countSamplesByTarget($split->getTestLabels(), 'b')); self::assertEquals(1, $this->countSamplesByTarget($split->getTestLabels(), 'b'));
} }
public function testDatasetStratifiedRandomSplitWithEvenDistributionAndNumericTargets(): void public function testDatasetStratifiedRandomSplitWithEvenDistributionAndNumericTargets(): void
@ -37,16 +37,19 @@ class StratifiedRandomSplitTest extends TestCase
$split = new StratifiedRandomSplit($dataset, 0.5); $split = new StratifiedRandomSplit($dataset, 0.5);
$this->assertEquals(2, $this->countSamplesByTarget($split->getTestLabels(), 1)); self::assertEquals(2, $this->countSamplesByTarget($split->getTestLabels(), 1));
$this->assertEquals(2, $this->countSamplesByTarget($split->getTestLabels(), 2)); self::assertEquals(2, $this->countSamplesByTarget($split->getTestLabels(), 2));
$split = new StratifiedRandomSplit($dataset, 0.25); $split = new StratifiedRandomSplit($dataset, 0.25);
$this->assertEquals(1, $this->countSamplesByTarget($split->getTestLabels(), 1)); self::assertEquals(1, $this->countSamplesByTarget($split->getTestLabels(), 1));
$this->assertEquals(1, $this->countSamplesByTarget($split->getTestLabels(), 2)); self::assertEquals(1, $this->countSamplesByTarget($split->getTestLabels(), 2));
} }
private function countSamplesByTarget($splitTargets, $countTarget): int /**
* @param string|int $countTarget
*/
private function countSamplesByTarget(array $splitTargets, $countTarget): int
{ {
$count = 0; $count = 0;
foreach ($splitTargets as $target) { foreach ($splitTargets as $target) {

View File

@ -23,8 +23,8 @@ class ArrayDatasetTest extends TestCase
$labels = ['a', 'a', 'b', 'b'] $labels = ['a', 'a', 'b', 'b']
); );
$this->assertEquals($samples, $dataset->getSamples()); self::assertEquals($samples, $dataset->getSamples());
$this->assertEquals($labels, $dataset->getTargets()); self::assertEquals($labels, $dataset->getTargets());
} }
public function testRemoveColumns(): void public function testRemoveColumns(): void
@ -35,6 +35,6 @@ class ArrayDatasetTest extends TestCase
); );
$dataset->removeColumns([0, 2]); $dataset->removeColumns([0, 2]);
$this->assertEquals([[2, 4], [3, 5], [4, 6], [5, 7]], $dataset->getSamples()); self::assertEquals([[2, 4], [3, 5], [4, 6], [5, 7]], $dataset->getSamples());
} }
} }

View File

@ -22,8 +22,8 @@ class CsvDatasetTest extends TestCase
$dataset = new CsvDataset($filePath, 2, true); $dataset = new CsvDataset($filePath, 2, true);
$this->assertCount(10, $dataset->getSamples()); self::assertCount(10, $dataset->getSamples());
$this->assertCount(10, $dataset->getTargets()); self::assertCount(10, $dataset->getTargets());
} }
public function testSampleCsvDatasetWithoutHeaderRow(): void public function testSampleCsvDatasetWithoutHeaderRow(): void
@ -32,8 +32,8 @@ class CsvDatasetTest extends TestCase
$dataset = new CsvDataset($filePath, 2, false); $dataset = new CsvDataset($filePath, 2, false);
$this->assertCount(11, $dataset->getSamples()); self::assertCount(11, $dataset->getSamples());
$this->assertCount(11, $dataset->getTargets()); self::assertCount(11, $dataset->getTargets());
} }
public function testLongCsvDataset(): void public function testLongCsvDataset(): void
@ -42,7 +42,7 @@ class CsvDatasetTest extends TestCase
$dataset = new CsvDataset($filePath, 1000, false); $dataset = new CsvDataset($filePath, 1000, false);
$this->assertCount(1000, $dataset->getSamples()[0]); self::assertCount(1000, $dataset->getSamples()[0]);
$this->assertEquals('label', $dataset->getTargets()[0]); self::assertEquals('label', $dataset->getTargets()[0]);
} }
} }

View File

@ -14,10 +14,10 @@ class GlassDatasetTest extends TestCase
$glass = new GlassDataset(); $glass = new GlassDataset();
// whole dataset // whole dataset
$this->assertCount(214, $glass->getSamples()); self::assertCount(214, $glass->getSamples());
$this->assertCount(214, $glass->getTargets()); self::assertCount(214, $glass->getTargets());
// one sample features count // one sample features count
$this->assertCount(9, $glass->getSamples()[0]); self::assertCount(9, $glass->getSamples()[0]);
} }
} }

View File

@ -14,10 +14,10 @@ class IrisDatasetTest extends TestCase
$iris = new IrisDataset(); $iris = new IrisDataset();
// whole dataset // whole dataset
$this->assertCount(150, $iris->getSamples()); self::assertCount(150, $iris->getSamples());
$this->assertCount(150, $iris->getTargets()); self::assertCount(150, $iris->getTargets());
// one sample features count // one sample features count
$this->assertCount(4, $iris->getSamples()[0]); self::assertCount(4, $iris->getSamples()[0]);
} }
} }

View File

@ -14,10 +14,10 @@ class WineDatasetTest extends TestCase
$wine = new WineDataset(); $wine = new WineDataset();
// whole dataset // whole dataset
$this->assertCount(178, $wine->getSamples()); self::assertCount(178, $wine->getSamples());
$this->assertCount(178, $wine->getTargets()); self::assertCount(178, $wine->getTargets());
// one sample features count // one sample features count
$this->assertCount(13, $wine->getSamples()[0]); self::assertCount(13, $wine->getSamples()[0]);
} }
} }

View File

@ -22,22 +22,22 @@ class FilesDatasetTest extends TestCase
$dataset = new FilesDataset($rootPath); $dataset = new FilesDataset($rootPath);
$this->assertCount(50, $dataset->getSamples()); self::assertCount(50, $dataset->getSamples());
$this->assertCount(50, $dataset->getTargets()); self::assertCount(50, $dataset->getTargets());
$targets = ['business', 'entertainment', 'politics', 'sport', 'tech']; $targets = ['business', 'entertainment', 'politics', 'sport', 'tech'];
$this->assertEquals($targets, array_values(array_unique($dataset->getTargets()))); self::assertEquals($targets, array_values(array_unique($dataset->getTargets())));
$firstSample = file_get_contents($rootPath.'/business/001.txt'); $firstSample = file_get_contents($rootPath.'/business/001.txt');
$this->assertEquals($firstSample, $dataset->getSamples()[0][0]); self::assertEquals($firstSample, $dataset->getSamples()[0][0]);
$firstTarget = 'business'; $firstTarget = 'business';
$this->assertEquals($firstTarget, $dataset->getTargets()[0]); self::assertEquals($firstTarget, $dataset->getTargets()[0]);
$lastSample = file_get_contents($rootPath.'/tech/010.txt'); $lastSample = file_get_contents($rootPath.'/tech/010.txt');
$this->assertEquals($lastSample, $dataset->getSamples()[49][0]); self::assertEquals($lastSample, $dataset->getSamples()[49][0]);
$lastTarget = 'tech'; $lastTarget = 'tech';
$this->assertEquals($lastTarget, $dataset->getTargets()[49]); self::assertEquals($lastTarget, $dataset->getTargets()[49]);
} }
} }

View File

@ -21,8 +21,8 @@ class SvmDatasetTest extends TestCase
$expectedTargets = [ $expectedTargets = [
]; ];
$this->assertEquals($expectedSamples, $dataset->getSamples()); self::assertEquals($expectedSamples, $dataset->getSamples());
$this->assertEquals($expectedTargets, $dataset->getTargets()); self::assertEquals($expectedTargets, $dataset->getTargets());
} }
public function testSvmDataset1x1(): void public function testSvmDataset1x1(): void
@ -37,8 +37,8 @@ class SvmDatasetTest extends TestCase
0, 0,
]; ];
$this->assertEquals($expectedSamples, $dataset->getSamples()); self::assertEquals($expectedSamples, $dataset->getSamples());
$this->assertEquals($expectedTargets, $dataset->getTargets()); self::assertEquals($expectedTargets, $dataset->getTargets());
} }
public function testSvmDataset3x1(): void public function testSvmDataset3x1(): void
@ -57,8 +57,8 @@ class SvmDatasetTest extends TestCase
1, 1,
]; ];
$this->assertEquals($expectedSamples, $dataset->getSamples()); self::assertEquals($expectedSamples, $dataset->getSamples());
$this->assertEquals($expectedTargets, $dataset->getTargets()); self::assertEquals($expectedTargets, $dataset->getTargets());
} }
public function testSvmDataset3x4(): void public function testSvmDataset3x4(): void
@ -77,8 +77,8 @@ class SvmDatasetTest extends TestCase
0, 0,
]; ];
$this->assertEquals($expectedSamples, $dataset->getSamples()); self::assertEquals($expectedSamples, $dataset->getSamples());
$this->assertEquals($expectedTargets, $dataset->getTargets()); self::assertEquals($expectedTargets, $dataset->getTargets());
} }
public function testSvmDatasetSparse(): void public function testSvmDatasetSparse(): void
@ -95,8 +95,8 @@ class SvmDatasetTest extends TestCase
1, 1,
]; ];
$this->assertEquals($expectedSamples, $dataset->getSamples()); self::assertEquals($expectedSamples, $dataset->getSamples());
$this->assertEquals($expectedTargets, $dataset->getTargets()); self::assertEquals($expectedTargets, $dataset->getTargets());
} }
public function testSvmDatasetComments(): void public function testSvmDatasetComments(): void
@ -113,8 +113,8 @@ class SvmDatasetTest extends TestCase
1, 1,
]; ];
$this->assertEquals($expectedSamples, $dataset->getSamples()); self::assertEquals($expectedSamples, $dataset->getSamples());
$this->assertEquals($expectedTargets, $dataset->getTargets()); self::assertEquals($expectedTargets, $dataset->getTargets());
} }
public function testSvmDatasetTabs(): void public function testSvmDatasetTabs(): void
@ -129,8 +129,8 @@ class SvmDatasetTest extends TestCase
1, 1,
]; ];
$this->assertEquals($expectedSamples, $dataset->getSamples()); self::assertEquals($expectedSamples, $dataset->getSamples());
$this->assertEquals($expectedTargets, $dataset->getTargets()); self::assertEquals($expectedTargets, $dataset->getTargets());
} }
public function testSvmDatasetMissingFile(): void public function testSvmDatasetMissingFile(): void

View File

@ -40,7 +40,7 @@ class KernelPCATest extends TestCase
// during the calculation of eigenValues, we have to compare // during the calculation of eigenValues, we have to compare
// absolute value of the values // absolute value of the values
array_map(function ($val1, $val2) use ($epsilon): void { array_map(function ($val1, $val2) use ($epsilon): void {
$this->assertEquals(abs($val1), abs($val2), '', $epsilon); self::assertEquals(abs($val1), abs($val2), '', $epsilon);
}, $transformed, $reducedData); }, $transformed, $reducedData);
// Fitted KernelPCA object can also transform an arbitrary sample of the // Fitted KernelPCA object can also transform an arbitrary sample of the
@ -48,7 +48,7 @@ class KernelPCATest extends TestCase
$newData = [1.25, 2.25]; $newData = [1.25, 2.25];
$newTransformed = [0.18956227539216]; $newTransformed = [0.18956227539216];
$newTransformed2 = $kpca->transform($newData); $newTransformed2 = $kpca->transform($newData);
$this->assertEquals(abs($newTransformed[0]), abs($newTransformed2[0]), '', $epsilon); self::assertEquals(abs($newTransformed[0]), abs($newTransformed2[0]), '', $epsilon);
} }
public function testKernelPCAThrowWhenKernelInvalid(): void public function testKernelPCAThrowWhenKernelInvalid(): void

View File

@ -51,7 +51,7 @@ class LDATest extends TestCase
// absolute value of the values // absolute value of the values
$row1 = array_map('abs', $row1); $row1 = array_map('abs', $row1);
$row2 = array_map('abs', $row2); $row2 = array_map('abs', $row2);
$this->assertEquals($row1, $row2, '', $epsilon); self::assertEquals($row1, $row2, '', $epsilon);
}; };
array_map($check, $control, $transformed2); array_map($check, $control, $transformed2);

View File

@ -42,7 +42,7 @@ class PCATest extends TestCase
// during the calculation of eigenValues, we have to compare // during the calculation of eigenValues, we have to compare
// absolute value of the values // absolute value of the values
array_map(function ($val1, $val2) use ($epsilon): void { array_map(function ($val1, $val2) use ($epsilon): void {
$this->assertEquals(abs($val1), abs($val2), '', $epsilon); self::assertEquals(abs($val1), abs($val2), '', $epsilon);
}, $transformed, $reducedData); }, $transformed, $reducedData);
// Test fitted PCA object to transform an arbitrary sample of the // Test fitted PCA object to transform an arbitrary sample of the
@ -52,7 +52,7 @@ class PCATest extends TestCase
$newRow2 = $pca->transform($row); $newRow2 = $pca->transform($row);
array_map(function ($val1, $val2) use ($epsilon): void { array_map(function ($val1, $val2) use ($epsilon): void {
$this->assertEquals(abs($val1), abs($val2), '', $epsilon); self::assertEquals(abs($val1), abs($val2), '', $epsilon);
}, $newRow, $newRow2); }, $newRow, $newRow2);
} }
} }

View File

@ -14,13 +14,13 @@ class StopWordsTest extends TestCase
{ {
$stopWords = new StopWords(['lorem', 'ipsum', 'dolor']); $stopWords = new StopWords(['lorem', 'ipsum', 'dolor']);
$this->assertTrue($stopWords->isStopWord('lorem')); self::assertTrue($stopWords->isStopWord('lorem'));
$this->assertTrue($stopWords->isStopWord('ipsum')); self::assertTrue($stopWords->isStopWord('ipsum'));
$this->assertTrue($stopWords->isStopWord('dolor')); self::assertTrue($stopWords->isStopWord('dolor'));
$this->assertFalse($stopWords->isStopWord('consectetur')); self::assertFalse($stopWords->isStopWord('consectetur'));
$this->assertFalse($stopWords->isStopWord('adipiscing')); self::assertFalse($stopWords->isStopWord('adipiscing'));
$this->assertFalse($stopWords->isStopWord('amet')); self::assertFalse($stopWords->isStopWord('amet'));
} }
public function testThrowExceptionOnInvalidLanguage(): void public function testThrowExceptionOnInvalidLanguage(): void
@ -33,23 +33,23 @@ class StopWordsTest extends TestCase
{ {
$stopWords = StopWords::factory('English'); $stopWords = StopWords::factory('English');
$this->assertTrue($stopWords->isStopWord('again')); self::assertTrue($stopWords->isStopWord('again'));
$this->assertFalse($stopWords->isStopWord('strategy')); self::assertFalse($stopWords->isStopWord('strategy'));
} }
public function testPolishStopWords(): void public function testPolishStopWords(): void
{ {
$stopWords = StopWords::factory('Polish'); $stopWords = StopWords::factory('Polish');
$this->assertTrue($stopWords->isStopWord('wam')); self::assertTrue($stopWords->isStopWord('wam'));
$this->assertFalse($stopWords->isStopWord('transhumanizm')); self::assertFalse($stopWords->isStopWord('transhumanizm'));
} }
public function testFrenchStopWords(): void public function testFrenchStopWords(): void
{ {
$stopWords = StopWords::factory('French'); $stopWords = StopWords::factory('French');
$this->assertTrue($stopWords->isStopWord('alors')); self::assertTrue($stopWords->isStopWord('alors'));
$this->assertFalse($stopWords->isStopWord('carte')); self::assertFalse($stopWords->isStopWord('carte'));
} }
} }

View File

@ -54,6 +54,6 @@ class TfIdfTransformerTest extends TestCase
$transformer = new TfIdfTransformer($samples); $transformer = new TfIdfTransformer($samples);
$transformer->transform($samples); $transformer->transform($samples);
$this->assertEquals($tfIdfSamples, $samples, '', 0.001); self::assertEquals($tfIdfSamples, $samples, '', 0.001);
} }
} }

View File

@ -74,10 +74,10 @@ class TokenCountVectorizerTest extends TestCase
$vectorizer = new TokenCountVectorizer(new WhitespaceTokenizer()); $vectorizer = new TokenCountVectorizer(new WhitespaceTokenizer());
$vectorizer->fit($samples); $vectorizer->fit($samples);
$this->assertSame($vocabulary, $vectorizer->getVocabulary()); self::assertSame($vocabulary, $vectorizer->getVocabulary());
$vectorizer->transform($samples); $vectorizer->transform($samples);
$this->assertSame($tokensCounts, $samples); self::assertSame($tokensCounts, $samples);
} }
public function testTransformationWithMinimumDocumentTokenCountFrequency(): void public function testTransformationWithMinimumDocumentTokenCountFrequency(): void
@ -132,10 +132,10 @@ class TokenCountVectorizerTest extends TestCase
$vectorizer = new TokenCountVectorizer(new WhitespaceTokenizer(), null, 0.5); $vectorizer = new TokenCountVectorizer(new WhitespaceTokenizer(), null, 0.5);
$vectorizer->fit($samples); $vectorizer->fit($samples);
$this->assertSame($vocabulary, $vectorizer->getVocabulary()); self::assertSame($vocabulary, $vectorizer->getVocabulary());
$vectorizer->transform($samples); $vectorizer->transform($samples);
$this->assertSame($tokensCounts, $samples); self::assertSame($tokensCounts, $samples);
// word at least once in all samples // word at least once in all samples
$samples = [ $samples = [
@ -184,7 +184,7 @@ class TokenCountVectorizerTest extends TestCase
$vectorizer->fit($samples); $vectorizer->fit($samples);
$vectorizer->transform($samples); $vectorizer->transform($samples);
$this->assertSame($tokensCounts, $samples); self::assertSame($tokensCounts, $samples);
} }
public function testTransformationWithStopWords(): void public function testTransformationWithStopWords(): void
@ -246,9 +246,9 @@ class TokenCountVectorizerTest extends TestCase
$vectorizer = new TokenCountVectorizer(new WhitespaceTokenizer(), $stopWords); $vectorizer = new TokenCountVectorizer(new WhitespaceTokenizer(), $stopWords);
$vectorizer->fit($samples); $vectorizer->fit($samples);
$this->assertSame($vocabulary, $vectorizer->getVocabulary()); self::assertSame($vocabulary, $vectorizer->getVocabulary());
$vectorizer->transform($samples); $vectorizer->transform($samples);
$this->assertSame($tokensCounts, $samples); self::assertSame($tokensCounts, $samples);
} }
} }

View File

@ -33,7 +33,7 @@ class ConjugateGradientTest extends TestCase
$theta = $optimizer->runOptimization($samples, $targets, $callback); $theta = $optimizer->runOptimization($samples, $targets, $callback);
$this->assertEquals([-1, 2], $theta, '', 0.1); self::assertEquals([-1, 2], $theta, '', 0.1);
} }
public function testRunOptimizationWithCustomInitialTheta(): void public function testRunOptimizationWithCustomInitialTheta(): void
@ -61,7 +61,7 @@ class ConjugateGradientTest extends TestCase
$theta = $optimizer->runOptimization($samples, $targets, $callback); $theta = $optimizer->runOptimization($samples, $targets, $callback);
$this->assertEquals([-1.087708, 2.212034], $theta, '', 0.000001); self::assertEquals([-1.087708, 2.212034], $theta, '', 0.000001);
} }
public function testRunOptimization2Dim(): void public function testRunOptimization2Dim(): void
@ -89,7 +89,7 @@ class ConjugateGradientTest extends TestCase
$theta = $optimizer->runOptimization($samples, $targets, $callback); $theta = $optimizer->runOptimization($samples, $targets, $callback);
$this->assertEquals([-1, 2, -3], $theta, '', 0.1); self::assertEquals([-1, 2, -3], $theta, '', 0.1);
} }
public function testThrowExceptionOnInvalidTheta(): void public function testThrowExceptionOnInvalidTheta(): void

View File

@ -32,7 +32,7 @@ class GDTest extends TestCase
$theta = $optimizer->runOptimization($samples, $targets, $callback); $theta = $optimizer->runOptimization($samples, $targets, $callback);
$this->assertEquals([-1, 2], $theta, '', 0.1); self::assertEquals([-1, 2], $theta, '', 0.1);
} }
public function testRunOptimization2Dim(): void public function testRunOptimization2Dim(): void
@ -60,6 +60,6 @@ class GDTest extends TestCase
$theta = $optimizer->runOptimization($samples, $targets, $callback); $theta = $optimizer->runOptimization($samples, $targets, $callback);
$this->assertEquals([-1, 2, -3], $theta, '', 0.1); self::assertEquals([-1, 2, -3], $theta, '', 0.1);
} }
} }

View File

@ -26,9 +26,9 @@ class OptimizerTest extends TestCase
$optimizer = $this->getMockForAbstractClass(Optimizer::class, [2]); $optimizer = $this->getMockForAbstractClass(Optimizer::class, [2]);
$object = $optimizer->setTheta([0.3, 1]); $object = $optimizer->setTheta([0.3, 1]);
$theta = $this->getObjectAttribute($optimizer, 'theta'); $theta = self::getObjectAttribute($optimizer, 'theta');
$this->assertSame($object, $optimizer); self::assertSame($object, $optimizer);
$this->assertSame([0.3, 1], $theta); self::assertSame([0.3, 1], $theta);
} }
} }

View File

@ -32,7 +32,7 @@ class StochasticGDTest extends TestCase
$theta = $optimizer->runOptimization($samples, $targets, $callback); $theta = $optimizer->runOptimization($samples, $targets, $callback);
$this->assertEquals([-1, 2], $theta, '', 0.1); self::assertEquals([-1, 2], $theta, '', 0.1);
} }
public function testRunOptimization2Dim(): void public function testRunOptimization2Dim(): void
@ -60,6 +60,6 @@ class StochasticGDTest extends TestCase
$theta = $optimizer->runOptimization($samples, $targets, $callback); $theta = $optimizer->runOptimization($samples, $targets, $callback);
$this->assertEquals([-1, 2, -3], $theta, '', 0.1); self::assertEquals([-1, 2, -3], $theta, '', 0.1);
} }
} }

View File

@ -20,7 +20,7 @@ class ComparisonTest extends TestCase
{ {
$result = Comparison::compare($a, $b, $operator); $result = Comparison::compare($a, $b, $operator);
$this->assertEquals($expected, $result); self::assertEquals($expected, $result);
} }
public function testThrowExceptionWhenOperatorIsInvalid(): void public function testThrowExceptionWhenOperatorIsInvalid(): void

View File

@ -36,7 +36,7 @@ class ChebyshevTest extends TestCase
$expectedDistance = 2; $expectedDistance = 2;
$actualDistance = $this->distanceMetric->distance($a, $b); $actualDistance = $this->distanceMetric->distance($a, $b);
$this->assertEquals($expectedDistance, $actualDistance); self::assertEquals($expectedDistance, $actualDistance);
} }
public function testCalculateDistanceForTwoDimensions(): void public function testCalculateDistanceForTwoDimensions(): void
@ -47,7 +47,7 @@ class ChebyshevTest extends TestCase
$expectedDistance = 2; $expectedDistance = 2;
$actualDistance = $this->distanceMetric->distance($a, $b); $actualDistance = $this->distanceMetric->distance($a, $b);
$this->assertEquals($expectedDistance, $actualDistance); self::assertEquals($expectedDistance, $actualDistance);
} }
public function testCalculateDistanceForThreeDimensions(): void public function testCalculateDistanceForThreeDimensions(): void
@ -58,6 +58,6 @@ class ChebyshevTest extends TestCase
$expectedDistance = 5; $expectedDistance = 5;
$actualDistance = $this->distanceMetric->distance($a, $b); $actualDistance = $this->distanceMetric->distance($a, $b);
$this->assertEquals($expectedDistance, $actualDistance); self::assertEquals($expectedDistance, $actualDistance);
} }
} }

View File

@ -36,7 +36,7 @@ class EuclideanTest extends TestCase
$expectedDistance = 2; $expectedDistance = 2;
$actualDistance = $this->distanceMetric->distance($a, $b); $actualDistance = $this->distanceMetric->distance($a, $b);
$this->assertEquals($expectedDistance, $actualDistance); self::assertEquals($expectedDistance, $actualDistance);
} }
public function testCalculateDistanceForTwoDimensions(): void public function testCalculateDistanceForTwoDimensions(): void
@ -47,7 +47,7 @@ class EuclideanTest extends TestCase
$expectedDistance = 2.2360679774998; $expectedDistance = 2.2360679774998;
$actualDistance = $this->distanceMetric->distance($a, $b); $actualDistance = $this->distanceMetric->distance($a, $b);
$this->assertEquals($expectedDistance, $actualDistance); self::assertEquals($expectedDistance, $actualDistance);
} }
public function testCalculateDistanceForThreeDimensions(): void public function testCalculateDistanceForThreeDimensions(): void
@ -58,6 +58,6 @@ class EuclideanTest extends TestCase
$expectedDistance = 6.7082039324993694; $expectedDistance = 6.7082039324993694;
$actualDistance = $this->distanceMetric->distance($a, $b); $actualDistance = $this->distanceMetric->distance($a, $b);
$this->assertEquals($expectedDistance, $actualDistance); self::assertEquals($expectedDistance, $actualDistance);
} }
} }

View File

@ -36,7 +36,7 @@ class ManhattanTest extends TestCase
$expectedDistance = 2; $expectedDistance = 2;
$actualDistance = $this->distanceMetric->distance($a, $b); $actualDistance = $this->distanceMetric->distance($a, $b);
$this->assertEquals($expectedDistance, $actualDistance); self::assertEquals($expectedDistance, $actualDistance);
} }
public function testCalculateDistanceForTwoDimensions(): void public function testCalculateDistanceForTwoDimensions(): void
@ -47,7 +47,7 @@ class ManhattanTest extends TestCase
$expectedDistance = 3; $expectedDistance = 3;
$actualDistance = $this->distanceMetric->distance($a, $b); $actualDistance = $this->distanceMetric->distance($a, $b);
$this->assertEquals($expectedDistance, $actualDistance); self::assertEquals($expectedDistance, $actualDistance);
} }
public function testCalculateDistanceForThreeDimensions(): void public function testCalculateDistanceForThreeDimensions(): void
@ -58,6 +58,6 @@ class ManhattanTest extends TestCase
$expectedDistance = 11; $expectedDistance = 11;
$actualDistance = $this->distanceMetric->distance($a, $b); $actualDistance = $this->distanceMetric->distance($a, $b);
$this->assertEquals($expectedDistance, $actualDistance); self::assertEquals($expectedDistance, $actualDistance);
} }
} }

View File

@ -36,7 +36,7 @@ class MinkowskiTest extends TestCase
$expectedDistance = 2; $expectedDistance = 2;
$actualDistance = $this->distanceMetric->distance($a, $b); $actualDistance = $this->distanceMetric->distance($a, $b);
$this->assertEquals($expectedDistance, $actualDistance); self::assertEquals($expectedDistance, $actualDistance);
} }
public function testCalculateDistanceForTwoDimensions(): void public function testCalculateDistanceForTwoDimensions(): void
@ -47,7 +47,7 @@ class MinkowskiTest extends TestCase
$expectedDistance = 2.080; $expectedDistance = 2.080;
$actualDistance = $this->distanceMetric->distance($a, $b); $actualDistance = $this->distanceMetric->distance($a, $b);
$this->assertEquals($expectedDistance, $actualDistance, '', $delta = 0.001); self::assertEquals($expectedDistance, $actualDistance, '', $delta = 0.001);
} }
public function testCalculateDistanceForThreeDimensions(): void public function testCalculateDistanceForThreeDimensions(): void
@ -58,7 +58,7 @@ class MinkowskiTest extends TestCase
$expectedDistance = 5.819; $expectedDistance = 5.819;
$actualDistance = $this->distanceMetric->distance($a, $b); $actualDistance = $this->distanceMetric->distance($a, $b);
$this->assertEquals($expectedDistance, $actualDistance, '', $delta = 0.001); self::assertEquals($expectedDistance, $actualDistance, '', $delta = 0.001);
} }
public function testCalculateDistanceForThreeDimensionsWithDifferentLambda(): void public function testCalculateDistanceForThreeDimensionsWithDifferentLambda(): void
@ -71,6 +71,6 @@ class MinkowskiTest extends TestCase
$expectedDistance = 5.300; $expectedDistance = 5.300;
$actualDistance = $distanceMetric->distance($a, $b); $actualDistance = $distanceMetric->distance($a, $b);
$this->assertEquals($expectedDistance, $actualDistance, '', $delta = 0.001); self::assertEquals($expectedDistance, $actualDistance, '', $delta = 0.001);
} }
} }

View File

@ -13,14 +13,14 @@ class RBFTest extends TestCase
{ {
$rbf = new RBF($gamma = 0.001); $rbf = new RBF($gamma = 0.001);
$this->assertEquals(1, $rbf->compute([1, 2], [1, 2])); self::assertEquals(1, $rbf->compute([1, 2], [1, 2]));
$this->assertEquals(0.97336, $rbf->compute([1, 2, 3], [4, 5, 6]), '', $delta = 0.0001); self::assertEquals(0.97336, $rbf->compute([1, 2, 3], [4, 5, 6]), '', $delta = 0.0001);
$this->assertEquals(0.00011, $rbf->compute([4, 5], [1, 100]), '', $delta = 0.0001); self::assertEquals(0.00011, $rbf->compute([4, 5], [1, 100]), '', $delta = 0.0001);
$rbf = new RBF($gamma = 0.2); $rbf = new RBF($gamma = 0.2);
$this->assertEquals(1, $rbf->compute([1, 2], [1, 2])); self::assertEquals(1, $rbf->compute([1, 2], [1, 2]));
$this->assertEquals(0.00451, $rbf->compute([1, 2, 3], [4, 5, 6]), '', $delta = 0.0001); self::assertEquals(0.00451, $rbf->compute([1, 2, 3], [4, 5, 6]), '', $delta = 0.0001);
$this->assertEquals(0, $rbf->compute([4, 5], [1, 100])); self::assertEquals(0, $rbf->compute([4, 5], [1, 100]));
} }
} }

View File

@ -34,8 +34,7 @@ final class LUDecompositionTest extends TestCase
$lu = new LUDecomposition(new Matrix([[1, 2], [3, 4]])); $lu = new LUDecomposition(new Matrix([[1, 2], [3, 4]]));
$L = $lu->getL(); $L = $lu->getL();
$this->assertInstanceOf(Matrix::class, $L); self::assertSame([[1.0, 0.0], [0.3333333333333333, 1.0]], $L->toArray());
$this->assertSame([[1.0, 0.0], [0.3333333333333333, 1.0]], $L->toArray());
} }
public function testUpperTriangularFactor(): void public function testUpperTriangularFactor(): void
@ -43,7 +42,6 @@ final class LUDecompositionTest extends TestCase
$lu = new LUDecomposition(new Matrix([[1, 2], [3, 4]])); $lu = new LUDecomposition(new Matrix([[1, 2], [3, 4]]));
$U = $lu->getU(); $U = $lu->getU();
$this->assertInstanceOf(Matrix::class, $U); self::assertSame([[3.0, 4.0], [0.0, 0.6666666666666667]], $U->toArray());
$this->assertSame([[3.0, 4.0], [0.0, 0.6666666666666667]], $U->toArray());
} }
} }

View File

@ -22,11 +22,10 @@ class MatrixTest extends TestCase
$flatArray = [1, 2, 3, 4]; $flatArray = [1, 2, 3, 4];
$matrix = Matrix::fromFlatArray($flatArray); $matrix = Matrix::fromFlatArray($flatArray);
$this->assertInstanceOf(Matrix::class, $matrix); self::assertEquals([[1], [2], [3], [4]], $matrix->toArray());
$this->assertEquals([[1], [2], [3], [4]], $matrix->toArray()); self::assertEquals(4, $matrix->getRows());
$this->assertEquals(4, $matrix->getRows()); self::assertEquals(1, $matrix->getColumns());
$this->assertEquals(1, $matrix->getColumns()); self::assertEquals($flatArray, $matrix->getColumnValues(0));
$this->assertEquals($flatArray, $matrix->getColumnValues(0));
} }
public function testThrowExceptionOnInvalidColumnNumber(): void public function testThrowExceptionOnInvalidColumnNumber(): void
@ -51,7 +50,7 @@ class MatrixTest extends TestCase
[4, 2, 1], [4, 2, 1],
[5, 6, 7], [5, 6, 7],
]); ]);
$this->assertEquals(-3, $matrix->getDeterminant()); self::assertEquals(-3, $matrix->getDeterminant());
$matrix = new Matrix([ $matrix = new Matrix([
[1, 2, 3, 3, 2, 1], [1, 2, 3, 3, 2, 1],
@ -61,7 +60,7 @@ class MatrixTest extends TestCase
[1 / 4, 4, 1, 0, 2, 3 / 7], [1 / 4, 4, 1, 0, 2, 3 / 7],
[1, 8, 7, 5, 4, 4 / 5], [1, 8, 7, 5, 4, 4 / 5],
]); ]);
$this->assertEquals(1116.5035, $matrix->getDeterminant(), '', $delta = 0.0001); self::assertEquals(1116.5035, $matrix->getDeterminant(), '', $delta = 0.0001);
} }
public function testMatrixTranspose(): void public function testMatrixTranspose(): void
@ -78,7 +77,7 @@ class MatrixTest extends TestCase
[3, 1, 7], [3, 1, 7],
]; ];
$this->assertEquals($transposedMatrix, $matrix->transpose()->toArray()); self::assertEquals($transposedMatrix, $matrix->transpose()->toArray());
} }
public function testThrowExceptionOnMultiplyWhenInconsistentMatrixSupplied(): void public function testThrowExceptionOnMultiplyWhenInconsistentMatrixSupplied(): void
@ -107,7 +106,7 @@ class MatrixTest extends TestCase
[139, 154], [139, 154],
]; ];
$this->assertEquals($product, $matrix1->multiply($matrix2)->toArray()); self::assertEquals($product, $matrix1->multiply($matrix2)->toArray());
} }
public function testDivideByScalar(): void public function testDivideByScalar(): void
@ -122,7 +121,7 @@ class MatrixTest extends TestCase
[1, 5, 10], [1, 5, 10],
]; ];
$this->assertEquals($quotient, $matrix->divideByScalar(2)->toArray()); self::assertEquals($quotient, $matrix->divideByScalar(2)->toArray());
} }
public function testThrowExceptionWhenInverseIfArrayIsNotSquare(): void public function testThrowExceptionWhenInverseIfArrayIsNotSquare(): void
@ -158,7 +157,7 @@ class MatrixTest extends TestCase
[-1 / 2, 1 / 2, -1 / 2], [-1 / 2, 1 / 2, -1 / 2],
]; ];
$this->assertEquals($inverseMatrix, $matrix->inverse()->toArray(), '', $delta = 0.0001); self::assertEquals($inverseMatrix, $matrix->inverse()->toArray(), '', $delta = 0.0001);
} }
public function testCrossOutMatrix(): void public function testCrossOutMatrix(): void
@ -174,14 +173,14 @@ class MatrixTest extends TestCase
[1, 1], [1, 1],
]; ];
$this->assertEquals($crossOuted, $matrix->crossOut(1, 1)->toArray()); self::assertEquals($crossOuted, $matrix->crossOut(1, 1)->toArray());
} }
public function testToScalar(): void public function testToScalar(): void
{ {
$matrix = new Matrix([[1, 2, 3], [3, 2, 3]]); $matrix = new Matrix([[1, 2, 3], [3, 2, 3]]);
$this->assertEquals($matrix->toScalar(), 1); self::assertEquals($matrix->toScalar(), 1);
} }
public function testMultiplyByScalar(): void public function testMultiplyByScalar(): void
@ -196,7 +195,7 @@ class MatrixTest extends TestCase
[-4, -20, -40], [-4, -20, -40],
]; ];
$this->assertEquals($result, $matrix->multiplyByScalar(-2)->toArray()); self::assertEquals($result, $matrix->multiplyByScalar(-2)->toArray());
} }
public function testAdd(): void public function testAdd(): void
@ -208,7 +207,7 @@ class MatrixTest extends TestCase
$m1 = new Matrix($array1); $m1 = new Matrix($array1);
$m2 = new Matrix($array2); $m2 = new Matrix($array2);
$this->assertEquals($result, $m1->add($m2)->toArray()[0]); self::assertEquals($result, $m1->add($m2)->toArray()[0]);
} }
public function testSubtract(): void public function testSubtract(): void
@ -220,7 +219,7 @@ class MatrixTest extends TestCase
$m1 = new Matrix($array1); $m1 = new Matrix($array1);
$m2 = new Matrix($array2); $m2 = new Matrix($array2);
$this->assertEquals($result, $m1->subtract($m2)->toArray()[0]); self::assertEquals($result, $m1->subtract($m2)->toArray()[0]);
} }
public function testTransposeArray(): void public function testTransposeArray(): void
@ -235,7 +234,7 @@ class MatrixTest extends TestCase
[1, 2], [1, 2],
]; ];
$this->assertEquals($transposed, Matrix::transposeArray($array)); self::assertEquals($transposed, Matrix::transposeArray($array));
} }
public function testDot(): void public function testDot(): void
@ -244,12 +243,12 @@ class MatrixTest extends TestCase
$vect2 = [3, 3, 3]; $vect2 = [3, 3, 3];
$dot = [18]; $dot = [18];
$this->assertEquals($dot, Matrix::dot($vect1, $vect2)); self::assertEquals($dot, Matrix::dot($vect1, $vect2));
$matrix1 = [[1, 1], [2, 2]]; $matrix1 = [[1, 1], [2, 2]];
$matrix2 = [[3, 3], [3, 3], [3, 3]]; $matrix2 = [[3, 3], [3, 3], [3, 3]];
$dot = [6, 12]; $dot = [6, 12];
$this->assertEquals($dot, Matrix::dot($matrix2, $matrix1)); self::assertEquals($dot, Matrix::dot($matrix2, $matrix1));
} }
/** /**
@ -257,12 +256,10 @@ class MatrixTest extends TestCase
*/ */
public function testFrobeniusNorm(array $matrix, float $norm): void public function testFrobeniusNorm(array $matrix, float $norm): void
{ {
$matrix = new Matrix($matrix); self::assertEquals($norm, (new Matrix($matrix))->frobeniusNorm(), '', 0.0001);
$this->assertEquals($norm, $matrix->frobeniusNorm(), '', 0.0001);
} }
public function dataProviderForFrobeniusNorm() public function dataProviderForFrobeniusNorm(): array
{ {
return [ return [
[ [

View File

@ -12,11 +12,11 @@ class ProductTest extends TestCase
{ {
public function testScalarProduct(): void public function testScalarProduct(): void
{ {
$this->assertEquals(10, Product::scalar([2, 3], [-1, 4])); self::assertEquals(10, Product::scalar([2, 3], [-1, 4]));
$this->assertEquals(-0.1, Product::scalar([1, 4, 1], [-2, 0.5, -0.1])); self::assertEquals(-0.1, Product::scalar([1, 4, 1], [-2, 0.5, -0.1]));
$this->assertEquals(8, Product::scalar([2], [4])); self::assertEquals(8, Product::scalar([2], [4]));
//test for non numeric values //test for non numeric values
$this->assertEquals(0, Product::scalar(['', null, [], new stdClass()], [null])); self::assertEquals(0, Product::scalar(['', null, [], new stdClass()], [null]));
} }
} }

View File

@ -13,84 +13,79 @@ class SetTest extends TestCase
{ {
$union = Set::union(new Set([3, 1]), new Set([3, 2, 2])); $union = Set::union(new Set([3, 1]), new Set([3, 2, 2]));
$this->assertInstanceOf(Set::class, $union); self::assertEquals(new Set([1, 2, 3]), $union);
$this->assertEquals(new Set([1, 2, 3]), $union); self::assertEquals(3, $union->cardinality());
$this->assertEquals(3, $union->cardinality());
} }
public function testIntersection(): void public function testIntersection(): void
{ {
$intersection = Set::intersection(new Set(['C', 'A']), new Set(['B', 'C'])); $intersection = Set::intersection(new Set(['C', 'A']), new Set(['B', 'C']));
$this->assertInstanceOf(Set::class, $intersection); self::assertEquals(new Set(['C']), $intersection);
$this->assertEquals(new Set(['C']), $intersection); self::assertEquals(1, $intersection->cardinality());
$this->assertEquals(1, $intersection->cardinality());
} }
public function testDifference(): void public function testDifference(): void
{ {
$difference = Set::difference(new Set(['C', 'A', 'B']), new Set(['A'])); $difference = Set::difference(new Set(['C', 'A', 'B']), new Set(['A']));
$this->assertInstanceOf(Set::class, $difference); self::assertEquals(new Set(['B', 'C']), $difference);
$this->assertEquals(new Set(['B', 'C']), $difference); self::assertEquals(2, $difference->cardinality());
$this->assertEquals(2, $difference->cardinality());
} }
public function testPower(): void public function testPower(): void
{ {
$power = Set::power(new Set(['A', 'B'])); $power = Set::power(new Set(['A', 'B']));
$this->assertInternalType('array', $power); self::assertEquals([new Set(), new Set(['A']), new Set(['B']), new Set(['A', 'B'])], $power);
$this->assertEquals([new Set(), new Set(['A']), new Set(['B']), new Set(['A', 'B'])], $power); self::assertCount(4, $power);
$this->assertCount(4, $power);
} }
public function testCartesian(): void public function testCartesian(): void
{ {
$cartesian = Set::cartesian(new Set(['A']), new Set([1, 2])); $cartesian = Set::cartesian(new Set(['A']), new Set([1, 2]));
$this->assertInternalType('array', $cartesian); self::assertEquals([new Set(['A', 1]), new Set(['A', 2])], $cartesian);
$this->assertEquals([new Set(['A', 1]), new Set(['A', 2])], $cartesian); self::assertCount(2, $cartesian);
$this->assertCount(2, $cartesian);
} }
public function testContains(): void public function testContains(): void
{ {
$set = new Set(['B', 'A', 2, 1]); $set = new Set(['B', 'A', 2, 1]);
$this->assertTrue($set->contains('B')); self::assertTrue($set->contains('B'));
$this->assertTrue($set->containsAll(['A', 'B'])); self::assertTrue($set->containsAll(['A', 'B']));
$this->assertFalse($set->contains('C')); self::assertFalse($set->contains('C'));
$this->assertFalse($set->containsAll(['A', 'B', 'C'])); self::assertFalse($set->containsAll(['A', 'B', 'C']));
} }
public function testRemove(): void public function testRemove(): void
{ {
$set = new Set(['B', 'A', 2, 1]); $set = new Set(['B', 'A', 2, 1]);
$this->assertEquals((new Set([1, 2, 2, 2, 'B']))->toArray(), $set->remove('A')->toArray()); self::assertEquals((new Set([1, 2, 2, 2, 'B']))->toArray(), $set->remove('A')->toArray());
} }
public function testAdd(): void public function testAdd(): void
{ {
$set = new Set(['B', 'A', 2, 1]); $set = new Set(['B', 'A', 2, 1]);
$set->addAll(['foo', 'bar']); $set->addAll(['foo', 'bar']);
$this->assertEquals(6, $set->cardinality()); self::assertEquals(6, $set->cardinality());
} }
public function testEmpty(): void public function testEmpty(): void
{ {
$set = new Set([1, 2]); $set = new Set([1, 2]);
$set->removeAll([2, 1]); $set->removeAll([2, 1]);
$this->assertEquals(new Set(), $set); self::assertEquals(new Set(), $set);
$this->assertTrue($set->isEmpty()); self::assertTrue($set->isEmpty());
} }
public function testToArray(): void public function testToArray(): void
{ {
$set = new Set([1, 2, 2, 3, 'A', false, '', 1.1, -1, -10, 'B']); $set = new Set([1, 2, 2, 3, 'A', false, '', 1.1, -1, -10, 'B']);
$this->assertEquals([-10, '', -1, 'A', 'B', 1, 1.1, 2, 3], $set->toArray()); self::assertEquals([-10, '', -1, 'A', 'B', 1, 1.1, 2, 3], $set->toArray());
} }
} }

View File

@ -16,18 +16,18 @@ class CorrelationTest extends TestCase
$delta = 0.001; $delta = 0.001;
$x = [9300, 10565, 15000, 15000, 17764, 57000, 65940, 73676, 77006, 93739, 146088, 153260]; $x = [9300, 10565, 15000, 15000, 17764, 57000, 65940, 73676, 77006, 93739, 146088, 153260];
$y = [7100, 15500, 4400, 4400, 5900, 4600, 8800, 2000, 2750, 2550, 960, 1025]; $y = [7100, 15500, 4400, 4400, 5900, 4600, 8800, 2000, 2750, 2550, 960, 1025];
$this->assertEquals(-0.641, Correlation::pearson($x, $y), '', $delta); self::assertEquals(-0.641, Correlation::pearson($x, $y), '', $delta);
//http://www.statisticshowto.com/how-to-compute-pearsons-correlation-coefficients/ //http://www.statisticshowto.com/how-to-compute-pearsons-correlation-coefficients/
$delta = 0.001; $delta = 0.001;
$x = [43, 21, 25, 42, 57, 59]; $x = [43, 21, 25, 42, 57, 59];
$y = [99, 65, 79, 75, 87, 82]; $y = [99, 65, 79, 75, 87, 82];
$this->assertEquals(0.549, Correlation::pearson($x, $y), '', $delta); self::assertEquals(0.549, Correlation::pearson($x, $y), '', $delta);
$delta = 0.001; $delta = 0.001;
$x = [60, 61, 62, 63, 65]; $x = [60, 61, 62, 63, 65];
$y = [3.1, 3.6, 3.8, 4, 4.1]; $y = [3.1, 3.6, 3.8, 4, 4.1];
$this->assertEquals(0.911, Correlation::pearson($x, $y), '', $delta); self::assertEquals(0.911, Correlation::pearson($x, $y), '', $delta);
} }
public function testThrowExceptionOnInvalidArgumentsForPearsonCorrelation(): void public function testThrowExceptionOnInvalidArgumentsForPearsonCorrelation(): void

Some files were not shown because too many files have changed in this diff Show More