diff --git a/src/Classification/Ensemble/AdaBoost.php b/src/Classification/Ensemble/AdaBoost.php index b314c81..c7b5e75 100644 --- a/src/Classification/Ensemble/AdaBoost.php +++ b/src/Classification/Ensemble/AdaBoost.php @@ -4,10 +4,10 @@ declare(strict_types=1); namespace Phpml\Classification\Ensemble; -use Exception; use Phpml\Classification\Classifier; use Phpml\Classification\Linear\DecisionStump; use Phpml\Classification\WeightedClassifier; +use Phpml\Exception\InvalidArgumentException; use Phpml\Helper\Predictable; use Phpml\Helper\Trainable; use Phpml\Math\Statistic\Mean; @@ -93,14 +93,14 @@ class AdaBoost implements Classifier } /** - * @throws \Exception + * @throws InvalidArgumentException */ public function train(array $samples, array $targets): void { // Initialize usual variables $this->labels = array_keys(array_count_values($targets)); if (count($this->labels) != 2) { - throw new Exception('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'); } // Set all target values to either -1 or 1 diff --git a/src/Classification/Ensemble/Bagging.php b/src/Classification/Ensemble/Bagging.php index a1d51c2..02e958b 100644 --- a/src/Classification/Ensemble/Bagging.php +++ b/src/Classification/Ensemble/Bagging.php @@ -4,9 +4,9 @@ declare(strict_types=1); namespace Phpml\Classification\Ensemble; -use Exception; use Phpml\Classification\Classifier; use Phpml\Classification\DecisionTree; +use Phpml\Exception\InvalidArgumentException; use Phpml\Helper\Predictable; use Phpml\Helper\Trainable; use ReflectionClass; @@ -77,12 +77,12 @@ class Bagging implements Classifier * * @return $this * - * @throws \Exception + * @throws InvalidArgumentException */ public function setSubsetRatio(float $ratio) { if ($ratio < 0.1 || $ratio > 1.0) { - throw new Exception('Subset ratio should be between 0.1 and 1.0'); + throw new InvalidArgumentException('Subset ratio should be between 0.1 and 1.0'); } $this->subsetRatio = $ratio; diff --git a/src/Classification/Linear/Adaline.php b/src/Classification/Linear/Adaline.php index 3b6309d..a133732 100644 --- a/src/Classification/Linear/Adaline.php +++ b/src/Classification/Linear/Adaline.php @@ -4,7 +4,7 @@ declare(strict_types=1); namespace Phpml\Classification\Linear; -use Exception; +use Phpml\Exception\InvalidArgumentException; class Adaline extends Perceptron { @@ -34,7 +34,7 @@ class Adaline extends Perceptron * If normalizeInputs is set to true, then every input given to the algorithm will be standardized * by use of standard deviation and mean calculation * - * @throws \Exception + * @throws InvalidArgumentException */ public function __construct( float $learningRate = 0.001, @@ -43,7 +43,7 @@ class Adaline extends Perceptron int $trainingType = self::BATCH_TRAINING ) { if (!in_array($trainingType, [self::BATCH_TRAINING, self::ONLINE_TRAINING], true)) { - throw new Exception('Adaline can only be trained with batch and online/stochastic gradient descent algorithm'); + throw new InvalidArgumentException('Adaline can only be trained with batch and online/stochastic gradient descent algorithm'); } $this->trainingType = $trainingType; diff --git a/src/Classification/Linear/DecisionStump.php b/src/Classification/Linear/DecisionStump.php index 1903712..ef6a458 100644 --- a/src/Classification/Linear/DecisionStump.php +++ b/src/Classification/Linear/DecisionStump.php @@ -4,9 +4,9 @@ declare(strict_types=1); namespace Phpml\Classification\Linear; -use Exception; use Phpml\Classification\DecisionTree; use Phpml\Classification\WeightedClassifier; +use Phpml\Exception\InvalidArgumentException; use Phpml\Helper\OneVsRest; use Phpml\Helper\Predictable; use Phpml\Math\Comparison; @@ -104,7 +104,7 @@ class DecisionStump extends WeightedClassifier } /** - * @throws \Exception + * @throws InvalidArgumentException */ protected function trainBinary(array $samples, array $targets, array $labels): void { @@ -121,7 +121,7 @@ class DecisionStump extends WeightedClassifier if (!empty($this->weights)) { $numWeights = count($this->weights); if ($numWeights != count($samples)) { - throw new Exception('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); diff --git a/src/Classification/Linear/LogisticRegression.php b/src/Classification/Linear/LogisticRegression.php index 13f4b8a..0414d59 100644 --- a/src/Classification/Linear/LogisticRegression.php +++ b/src/Classification/Linear/LogisticRegression.php @@ -6,6 +6,7 @@ namespace Phpml\Classification\Linear; use Closure; use Exception; +use Phpml\Exception\InvalidArgumentException; use Phpml\Helper\Optimizer\ConjugateGradient; class LogisticRegression extends Adaline @@ -61,7 +62,7 @@ class LogisticRegression extends Adaline * * Penalty (Regularization term) can be 'L2' or empty string to cancel penalty term * - * @throws \Exception + * @throws InvalidArgumentException */ public function __construct( int $maxIterations = 500, @@ -72,18 +73,24 @@ class LogisticRegression extends Adaline ) { $trainingTypes = range(self::BATCH_TRAINING, self::CONJUGATE_GRAD_TRAINING); if (!in_array($trainingType, $trainingTypes, true)) { - throw new Exception('Logistic regression can only be trained with '. + throw new InvalidArgumentException( + 'Logistic regression can only be trained with '. 'batch (gradient descent), online (stochastic gradient descent) '. - 'or conjugate batch (conjugate gradients) algorithms'); + 'or conjugate batch (conjugate gradients) algorithms' + ); } if (!in_array($cost, ['log', 'sse'], true)) { - throw new Exception("Logistic regression cost function can be one of the following: \n". - "'log' for log-likelihood and 'sse' for sum of squared errors"); + throw new InvalidArgumentException( + "Logistic regression cost function can be one of the following: \n". + "'log' for log-likelihood and 'sse' for sum of squared errors" + ); } if ($penalty != '' && strtoupper($penalty) !== 'L2') { - throw new Exception("Logistic regression supports only 'L2' regularization"); + throw new InvalidArgumentException( + "Logistic regression supports only 'L2' regularization" + ); } $this->learningRate = 0.001; @@ -140,7 +147,8 @@ class LogisticRegression extends Adaline return; default: - throw new Exception('Logistic regression has invalid training type: %s.', $this->trainingType); + // Not reached + throw new Exception(sprintf('Logistic regression has invalid training type: %d.', $this->trainingType)); } } @@ -232,6 +240,7 @@ class LogisticRegression extends Adaline return $callback; default: + // Not reached throw new Exception(sprintf('Logistic regression has invalid cost function: %s.', $this->costFunction)); } } diff --git a/src/Classification/Linear/Perceptron.php b/src/Classification/Linear/Perceptron.php index 5fffc01..038b4c8 100644 --- a/src/Classification/Linear/Perceptron.php +++ b/src/Classification/Linear/Perceptron.php @@ -5,8 +5,8 @@ declare(strict_types=1); namespace Phpml\Classification\Linear; use Closure; -use Exception; use Phpml\Classification\Classifier; +use Phpml\Exception\InvalidArgumentException; use Phpml\Helper\OneVsRest; use Phpml\Helper\Optimizer\GD; use Phpml\Helper\Optimizer\StochasticGD; @@ -70,16 +70,16 @@ class Perceptron implements Classifier, IncrementalEstimator * @param float $learningRate Value between 0.0(exclusive) and 1.0(inclusive) * @param int $maxIterations Must be at least 1 * - * @throws \Exception + * @throws InvalidArgumentException */ public function __construct(float $learningRate = 0.001, int $maxIterations = 1000, bool $normalizeInputs = true) { if ($learningRate <= 0.0 || $learningRate > 1.0) { - throw new Exception('Learning rate should be a float value between 0.0(exclusive) and 1.0(inclusive)'); + throw new InvalidArgumentException('Learning rate should be a float value between 0.0(exclusive) and 1.0(inclusive)'); } if ($maxIterations <= 0) { - throw new Exception('Maximum number of iterations must be an integer greater than 0'); + throw new InvalidArgumentException('Maximum number of iterations must be an integer greater than 0'); } if ($normalizeInputs) { diff --git a/src/DimensionReduction/KernelPCA.php b/src/DimensionReduction/KernelPCA.php index b962b3d..29deb4c 100644 --- a/src/DimensionReduction/KernelPCA.php +++ b/src/DimensionReduction/KernelPCA.php @@ -6,6 +6,8 @@ namespace Phpml\DimensionReduction; use Closure; use Exception; +use Phpml\Exception\InvalidArgumentException; +use Phpml\Exception\InvalidOperationException; use Phpml\Math\Distance\Euclidean; use Phpml\Math\Distance\Manhattan; use Phpml\Math\Matrix; @@ -53,13 +55,13 @@ class KernelPCA extends PCA * @param int $numFeatures Number of columns to be returned * @param float $gamma Gamma parameter is used with RBF and Sigmoid kernels * - * @throws \Exception + * @throws InvalidArgumentException */ 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, $availableKernels, true)) { - throw new Exception('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'); } parent::__construct($totalVariance, $numFeatures); @@ -97,16 +99,17 @@ class KernelPCA extends PCA * Transforms the given sample to a lower dimensional vector by using * the variables obtained during the last run of fit. * - * @throws \Exception + * @throws InvalidArgumentException + * @throws InvalidOperationException */ public function transform(array $sample): array { if (!$this->fit) { - throw new Exception('KernelPCA has not been fitted with respect to original dataset, please run KernelPCA::fit() first'); + throw new InvalidOperationException('KernelPCA has not been fitted with respect to original dataset, please run KernelPCA::fit() first'); } if (is_array($sample[0])) { - throw new Exception('KernelPCA::transform() accepts only one-dimensional arrays'); + throw new InvalidArgumentException('KernelPCA::transform() accepts only one-dimensional arrays'); } $pairs = $this->getDistancePairs($sample); @@ -199,6 +202,7 @@ class KernelPCA extends PCA }; default: + // Not reached throw new Exception(sprintf('KernelPCA initialized with invalid kernel: %d', $this->kernel)); } } diff --git a/src/DimensionReduction/LDA.php b/src/DimensionReduction/LDA.php index d188205..68ab0cd 100644 --- a/src/DimensionReduction/LDA.php +++ b/src/DimensionReduction/LDA.php @@ -4,7 +4,8 @@ declare(strict_types=1); namespace Phpml\DimensionReduction; -use Exception; +use Phpml\Exception\InvalidArgumentException; +use Phpml\Exception\InvalidOperationException; use Phpml\Math\Matrix; class LDA extends EigenTransformerBase @@ -46,20 +47,20 @@ class LDA extends EigenTransformerBase * @param float|null $totalVariance Total explained variance to be preserved * @param int|null $numFeatures Number of features to be preserved * - * @throws \Exception + * @throws InvalidArgumentException */ public function __construct(?float $totalVariance = null, ?int $numFeatures = null) { if ($totalVariance !== null && ($totalVariance < 0.1 || $totalVariance > 0.99)) { - throw new Exception('Total variance can be a value between 0.1 and 0.99'); + throw new InvalidArgumentException('Total variance can be a value between 0.1 and 0.99'); } if ($numFeatures !== null && $numFeatures <= 0) { - throw new Exception('Number of features to be preserved should be greater than 0'); + throw new InvalidArgumentException('Number of features to be preserved should be greater than 0'); } - if ($totalVariance !== null && $numFeatures !== null) { - throw new Exception('Either totalVariance or numFeatures should be specified in order to run the algorithm'); + if (($totalVariance !== null) === ($numFeatures !== null)) { + throw new InvalidArgumentException('Either totalVariance or numFeatures should be specified in order to run the algorithm'); } if ($numFeatures !== null) { @@ -94,12 +95,12 @@ class LDA extends EigenTransformerBase * Transforms the given sample to a lower dimensional vector by using * the eigenVectors obtained in the last run of fit. * - * @throws \Exception + * @throws InvalidOperationException */ public function transform(array $sample): array { if (!$this->fit) { - throw new Exception('LDA has not been fitted with respect to original dataset, please run LDA::fit() first'); + throw new InvalidOperationException('LDA has not been fitted with respect to original dataset, please run LDA::fit() first'); } if (!is_array($sample[0])) { diff --git a/src/DimensionReduction/PCA.php b/src/DimensionReduction/PCA.php index 18879bb..a3d8a4d 100644 --- a/src/DimensionReduction/PCA.php +++ b/src/DimensionReduction/PCA.php @@ -4,7 +4,8 @@ declare(strict_types=1); namespace Phpml\DimensionReduction; -use Exception; +use Phpml\Exception\InvalidArgumentException; +use Phpml\Exception\InvalidOperationException; use Phpml\Math\Statistic\Covariance; use Phpml\Math\Statistic\Mean; @@ -31,20 +32,20 @@ class PCA extends EigenTransformerBase * @param float $totalVariance Total explained variance to be preserved * @param int $numFeatures Number of features to be preserved * - * @throws \Exception + * @throws InvalidArgumentException */ public function __construct(?float $totalVariance = null, ?int $numFeatures = null) { if ($totalVariance !== null && ($totalVariance < 0.1 || $totalVariance > 0.99)) { - throw new Exception('Total variance can be a value between 0.1 and 0.99'); + throw new InvalidArgumentException('Total variance can be a value between 0.1 and 0.99'); } if ($numFeatures !== null && $numFeatures <= 0) { - throw new Exception('Number of features to be preserved should be greater than 0'); + throw new InvalidArgumentException('Number of features to be preserved should be greater than 0'); } - if ($totalVariance !== null && $numFeatures !== null) { - throw new Exception('Either totalVariance or numFeatures should be specified in order to run the algorithm'); + if (($totalVariance !== null) === ($numFeatures !== null)) { + throw new InvalidArgumentException('Either totalVariance or numFeatures should be specified in order to run the algorithm'); } if ($numFeatures !== null) { @@ -81,12 +82,12 @@ class PCA extends EigenTransformerBase * Transforms the given sample to a lower dimensional vector by using * the eigenVectors obtained in the last run of fit. * - * @throws \Exception + * @throws InvalidOperationException */ public function transform(array $sample): array { if (!$this->fit) { - throw new Exception('PCA has not been fitted with respect to original dataset, please run PCA::fit() first'); + throw new InvalidOperationException('PCA has not been fitted with respect to original dataset, please run PCA::fit() first'); } if (!is_array($sample[0])) { diff --git a/tests/Classification/Ensemble/AdaBoostTest.php b/tests/Classification/Ensemble/AdaBoostTest.php index 7677c31..095cde0 100644 --- a/tests/Classification/Ensemble/AdaBoostTest.php +++ b/tests/Classification/Ensemble/AdaBoostTest.php @@ -5,12 +5,32 @@ declare(strict_types=1); namespace Phpml\Tests\Classification\Ensemble; use Phpml\Classification\Ensemble\AdaBoost; +use Phpml\Exception\InvalidArgumentException; use Phpml\ModelManager; use PHPUnit\Framework\TestCase; class AdaBoostTest extends TestCase { - public function testPredictSingleSample() + public function testTrainThrowWhenMultiClassTargetGiven(): void + { + $samples = [ + [0, 0], + [0.5, 0.5], + [1, 1], + ]; + $targets = [ + 0, + 1, + 2, + ]; + + $classifier = new AdaBoost(); + + $this->expectException(InvalidArgumentException::class); + $classifier->train($samples, $targets); + } + + public function testPredictSingleSample(): void { // AND problem $samples = [[0.1, 0.3], [1, 0], [0, 1], [1, 1], [0.9, 0.8], [1.1, 1.1]]; @@ -38,8 +58,6 @@ class AdaBoostTest extends TestCase $this->assertEquals(0, $classifier->predict([0.1, 0.1])); $this->assertEquals(1, $classifier->predict([0, 0.999])); $this->assertEquals(0, $classifier->predict([1.1, 0.8])); - - return $classifier; } public function testSaveAndRestore(): void diff --git a/tests/Classification/Ensemble/BaggingTest.php b/tests/Classification/Ensemble/BaggingTest.php index 69c4d01..5b2e47b 100644 --- a/tests/Classification/Ensemble/BaggingTest.php +++ b/tests/Classification/Ensemble/BaggingTest.php @@ -7,6 +7,7 @@ namespace Phpml\Tests\Classification\Ensemble; use Phpml\Classification\DecisionTree; use Phpml\Classification\Ensemble\Bagging; use Phpml\Classification\NaiveBayes; +use Phpml\Exception\InvalidArgumentException; use Phpml\ModelManager; use PHPUnit\Framework\TestCase; @@ -34,7 +35,15 @@ class BaggingTest extends TestCase ['scorching', 0, 0, 'false', 'Dont_play'], ]; - public function testPredictSingleSample() + public function testSetSubsetRatioThrowWhenRatioOutOfBounds(): void + { + $classifier = $this->getClassifier(); + + $this->expectException(InvalidArgumentException::class); + $classifier->setSubsetRatio(0); + } + + public function testPredictSingleSample(): void { [$data, $targets] = $this->getData($this->data); $classifier = $this->getClassifier(); @@ -48,8 +57,6 @@ class BaggingTest extends TestCase $classifier->train($data, $targets); $this->assertEquals('Dont_play', $classifier->predict(['scorching', 95, 90, 'true'])); $this->assertEquals('Play', $classifier->predict(['overcast', 60, 60, 'false'])); - - return $classifier; } public function testSaveAndRestore(): void diff --git a/tests/Classification/Linear/AdalineTest.php b/tests/Classification/Linear/AdalineTest.php index c8ca752..62224e8 100644 --- a/tests/Classification/Linear/AdalineTest.php +++ b/tests/Classification/Linear/AdalineTest.php @@ -5,11 +5,23 @@ declare(strict_types=1); namespace Phpml\Tests\Classification\Linear; use Phpml\Classification\Linear\Adaline; +use Phpml\Exception\InvalidArgumentException; use Phpml\ModelManager; use PHPUnit\Framework\TestCase; class AdalineTest extends TestCase { + public function testAdalineThrowWhenInvalidTrainingType(): void + { + $this->expectException(InvalidArgumentException::class); + $classifier = new Adaline( + 0.001, + 1000, + true, + 0 + ); + } + public function testPredictSingleSample(): void { // AND problem diff --git a/tests/Classification/Linear/DecisionStumpTest.php b/tests/Classification/Linear/DecisionStumpTest.php index 93c8595..1295ac5 100644 --- a/tests/Classification/Linear/DecisionStumpTest.php +++ b/tests/Classification/Linear/DecisionStumpTest.php @@ -5,11 +5,24 @@ declare(strict_types=1); namespace Phpml\Tests\Classification\Linear; use Phpml\Classification\Linear\DecisionStump; +use Phpml\Exception\InvalidArgumentException; use Phpml\ModelManager; use PHPUnit\Framework\TestCase; class DecisionStumpTest extends TestCase { + public function testTrainThrowWhenSample(): void + { + $samples = [[0, 0], [1, 0], [0, 1], [1, 1]]; + $targets = [0, 0, 1, 1]; + + $classifier = new DecisionStump(); + $classifier->setSampleWeights([0.1, 0.1, 0.1]); + + $this->expectException(InvalidArgumentException::class); + $classifier->train($samples, $targets); + } + public function testPredictSingleSample() { // Samples should be separable with a line perpendicular diff --git a/tests/Classification/Linear/LogisticRegressionTest.php b/tests/Classification/Linear/LogisticRegressionTest.php index ed9b878..37b5182 100644 --- a/tests/Classification/Linear/LogisticRegressionTest.php +++ b/tests/Classification/Linear/LogisticRegressionTest.php @@ -5,16 +5,16 @@ declare(strict_types=1); namespace Phpml\Tests\Classification\Linear; use Phpml\Classification\Linear\LogisticRegression; +use Phpml\Exception\InvalidArgumentException; use PHPUnit\Framework\TestCase; use ReflectionMethod; use ReflectionProperty; -use Throwable; class LogisticRegressionTest extends TestCase { public function testConstructorThrowWhenInvalidTrainingType(): void { - $this->expectException(Throwable::class); + $this->expectException(InvalidArgumentException::class); $classifier = new LogisticRegression( 500, @@ -27,7 +27,7 @@ class LogisticRegressionTest extends TestCase public function testConstructorThrowWhenInvalidCost(): void { - $this->expectException(Throwable::class); + $this->expectException(InvalidArgumentException::class); $classifier = new LogisticRegression( 500, @@ -40,7 +40,7 @@ class LogisticRegressionTest extends TestCase public function testConstructorThrowWhenInvalidPenalty(): void { - $this->expectException(Throwable::class); + $this->expectException(InvalidArgumentException::class); $classifier = new LogisticRegression( 500, diff --git a/tests/Classification/Linear/PerceptronTest.php b/tests/Classification/Linear/PerceptronTest.php index 35af855..731eac1 100644 --- a/tests/Classification/Linear/PerceptronTest.php +++ b/tests/Classification/Linear/PerceptronTest.php @@ -5,11 +5,24 @@ declare(strict_types=1); namespace Phpml\Tests\Classification\Linear; use Phpml\Classification\Linear\Perceptron; +use Phpml\Exception\InvalidArgumentException; use Phpml\ModelManager; use PHPUnit\Framework\TestCase; class PerceptronTest extends TestCase { + public function testPerceptronThrowWhenLearningRateOutOfRange(): void + { + $this->expectException(InvalidArgumentException::class); + $classifier = new Perceptron(0, 5000); + } + + public function testPerceptronThrowWhenMaxIterationsOutOfRange(): void + { + $this->expectException(InvalidArgumentException::class); + $classifier = new Perceptron(0.001, 0); + } + public function testPredictSingleSample(): void { // AND problem diff --git a/tests/DimensionReduction/KernelPCATest.php b/tests/DimensionReduction/KernelPCATest.php index 05a4138..dfd8bb9 100644 --- a/tests/DimensionReduction/KernelPCATest.php +++ b/tests/DimensionReduction/KernelPCATest.php @@ -5,6 +5,8 @@ declare(strict_types=1); namespace Phpml\Tests\DimensionReduction; use Phpml\DimensionReduction\KernelPCA; +use Phpml\Exception\InvalidArgumentException; +use Phpml\Exception\InvalidOperationException; use PHPUnit\Framework\TestCase; class KernelPCATest extends TestCase @@ -48,4 +50,34 @@ class KernelPCATest extends TestCase $newTransformed2 = $kpca->transform($newData); $this->assertEquals(abs($newTransformed[0]), abs($newTransformed2[0]), '', $epsilon); } + + public function testKernelPCAThrowWhenKernelInvalid(): void + { + $this->expectException(InvalidArgumentException::class); + $kpca = new KernelPCA(0, null, 1, 15); + } + + public function testTransformThrowWhenNotFitted(): void + { + $samples = [1, 0]; + + $kpca = new KernelPCA(KernelPCA::KERNEL_RBF, null, 1, 15); + + $this->expectException(InvalidOperationException::class); + $kpca->transform($samples); + } + + public function testTransformThrowWhenMultiDimensionalArrayGiven(): void + { + $samples = [ + [1, 0], + [1, 1], + ]; + + $kpca = new KernelPCA(KernelPCA::KERNEL_RBF, null, 1, 15); + $kpca->fit($samples); + + $this->expectException(InvalidArgumentException::class); + $kpca->transform($samples); + } } diff --git a/tests/DimensionReduction/LDATest.php b/tests/DimensionReduction/LDATest.php index 2803a4b..641c226 100644 --- a/tests/DimensionReduction/LDATest.php +++ b/tests/DimensionReduction/LDATest.php @@ -6,6 +6,8 @@ namespace Phpml\Tests\DimensionReduction; use Phpml\Dataset\Demo\IrisDataset; use Phpml\DimensionReduction\LDA; +use Phpml\Exception\InvalidArgumentException; +use Phpml\Exception\InvalidOperationException; use PHPUnit\Framework\TestCase; class LDATest extends TestCase @@ -62,4 +64,41 @@ class LDATest extends TestCase array_map($check, $newRow, $newRow2); } } + + public function testLDAThrowWhenTotalVarianceOutOfRange(): void + { + $this->expectException(InvalidArgumentException::class); + $pca = new LDA(0, null); + } + + public function testLDAThrowWhenNumFeaturesOutOfRange(): void + { + $this->expectException(InvalidArgumentException::class); + $pca = new LDA(null, 0); + } + + public function testLDAThrowWhenParameterNotSpecified(): void + { + $this->expectException(InvalidArgumentException::class); + $pca = new LDA(); + } + + public function testLDAThrowWhenBothParameterSpecified(): void + { + $this->expectException(InvalidArgumentException::class); + $pca = new LDA(0.9, 1); + } + + public function testTransformThrowWhenNotFitted(): void + { + $samples = [ + [1, 0], + [1, 1], + ]; + + $pca = new LDA(0.9); + + $this->expectException(InvalidOperationException::class); + $pca->transform($samples); + } } diff --git a/tests/DimensionReduction/PCATest.php b/tests/DimensionReduction/PCATest.php index 337b253..5fca211 100644 --- a/tests/DimensionReduction/PCATest.php +++ b/tests/DimensionReduction/PCATest.php @@ -5,6 +5,8 @@ declare(strict_types=1); namespace Phpml\Tests\DimensionReduction; use Phpml\DimensionReduction\PCA; +use Phpml\Exception\InvalidArgumentException; +use Phpml\Exception\InvalidOperationException; use PHPUnit\Framework\TestCase; class PCATest extends TestCase @@ -54,4 +56,41 @@ class PCATest extends TestCase }, $newRow, $newRow2); } } + + public function testPCAThrowWhenTotalVarianceOutOfRange(): void + { + $this->expectException(InvalidArgumentException::class); + $pca = new PCA(0, null); + } + + public function testPCAThrowWhenNumFeaturesOutOfRange(): void + { + $this->expectException(InvalidArgumentException::class); + $pca = new PCA(null, 0); + } + + public function testPCAThrowWhenParameterNotSpecified(): void + { + $this->expectException(InvalidArgumentException::class); + $pca = new PCA(); + } + + public function testPCAThrowWhenBothParameterSpecified(): void + { + $this->expectException(InvalidArgumentException::class); + $pca = new PCA(0.9, 1); + } + + public function testTransformThrowWhenNotFitted(): void + { + $samples = [ + [1, 0], + [1, 1], + ]; + + $pca = new PCA(0.9); + + $this->expectException(InvalidOperationException::class); + $pca->transform($samples); + } }