From 3e23ef45f45cbe029c9713b1fea8f1d13d657223 Mon Sep 17 00:00:00 2001
From: Andrew DalPino <me@andrewdalpino.com>
Date: Tue, 6 Dec 2022 16:59:52 -0600
Subject: [PATCH 1/6] Fix Grid Search best model selection

---
 CHANGELOG.md             | 3 +++
 composer.json            | 5 ++++-
 src/GridSearch.php       | 2 +-
 tests/GridSearchTest.php | 8 ++++++++
 4 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 383416759..f1d291251 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+- 1.3.5
+    - Fix Grid Search best model selection
+
 - 1.3.4
     - Fix Decision Tree max height terminating condition
 
diff --git a/composer.json b/composer.json
index 43b6e605f..7268c22bf 100644
--- a/composer.json
+++ b/composer.json
@@ -92,7 +92,10 @@
     "config": {
         "preferred-install": "dist",
         "sort-packages": true,
-        "process-timeout": 3000
+        "process-timeout": 3000,
+        "allow-plugins": {
+            "phpstan/extension-installer": true
+        }
     },
     "funding": [
         {
diff --git a/src/GridSearch.php b/src/GridSearch.php
index fd32414cb..eb3ad3de4 100644
--- a/src/GridSearch.php
+++ b/src/GridSearch.php
@@ -284,7 +284,7 @@ public function train(Dataset $dataset) : void
 
         $scores = $this->backend->process();
 
-        array_multisort($scores, $combinations, SORT_DESC);
+        array_multisort($scores, SORT_DESC, $combinations);
 
         $best = reset($combinations) ?: [];
 
diff --git a/tests/GridSearchTest.php b/tests/GridSearchTest.php
index b03e07282..c593f6714 100644
--- a/tests/GridSearchTest.php
+++ b/tests/GridSearchTest.php
@@ -140,5 +140,13 @@ public function trainPredictBest() : void
         $score = $this->metric->score($predictions, $testing->labels());
 
         $this->assertGreaterThanOrEqual(self::MIN_SCORE, $score);
+
+        $expectedBest = [
+            'k' => 10,
+            'weighted' => true,
+            'kernel' => new Manhattan(),
+        ];
+
+        $this->assertEquals($expectedBest, $this->estimator->base()->params());
     }
 }

From 48ec79b2ecae73247646d63e8b164565651c8b9a Mon Sep 17 00:00:00 2001
From: Andrew DalPino <me@andrewdalpino.com>
Date: Tue, 6 Dec 2022 17:31:30 -0600
Subject: [PATCH 2/6] Add deltas to floating point assertions

---
 tests/GridSearchTest.php                       |  4 ++--
 tests/Kernels/Distance/CosineTest.php          |  2 +-
 .../HyperbolicTangentTest.php                  |  4 ++--
 .../NeuralNet/ActivationFunctions/SiLUTest.php |  4 ++--
 .../ActivationFunctions/SoftmaxTest.php        |  4 ++--
 .../CostFunctions/CrossEntropyTest.php         |  4 ++--
 .../NeuralNet/CostFunctions/HuberLossTest.php  |  4 ++--
 .../CostFunctions/RelativeEntropyTest.php      |  4 ++--
 tests/NeuralNet/Layers/BatchNormTest.php       | 12 ++++++------
 tests/NeuralNet/Layers/BinaryTest.php          |  6 +++---
 tests/NeuralNet/Layers/ContinuousTest.php      |  6 +++---
 tests/NeuralNet/Layers/DenseTest.php           |  6 +++---
 tests/NeuralNet/Layers/MulticlassTest.php      |  6 +++---
 tests/NeuralNet/Layers/NoiseTest.php           | 18 +++++++++---------
 tests/NeuralNet/Optimizers/AdaMaxTest.php      |  2 +-
 .../GaussianRandomProjectorTest.php            |  2 +-
 tests/Transformers/MinMaxNormalizerTest.php    |  2 +-
 .../Transformers/SparseRandomProjectorTest.php |  2 +-
 tests/Transformers/ZScaleStandardizerTest.php  |  2 +-
 19 files changed, 47 insertions(+), 47 deletions(-)

diff --git a/tests/GridSearchTest.php b/tests/GridSearchTest.php
index c593f6714..a2a2c736a 100644
--- a/tests/GridSearchTest.php
+++ b/tests/GridSearchTest.php
@@ -27,9 +27,9 @@
  */
 class GridSearchTest extends TestCase
 {
-    protected const TRAIN_SIZE = 300;
+    protected const TRAIN_SIZE = 512;
 
-    protected const TEST_SIZE = 10;
+    protected const TEST_SIZE = 256;
 
     protected const MIN_SCORE = 0.9;
 
diff --git a/tests/Kernels/Distance/CosineTest.php b/tests/Kernels/Distance/CosineTest.php
index 035722dad..a576b69d8 100644
--- a/tests/Kernels/Distance/CosineTest.php
+++ b/tests/Kernels/Distance/CosineTest.php
@@ -48,7 +48,7 @@ public function compute(array $a, array $b, float $expected) : void
         $distance = $this->kernel->compute($a, $b);
 
         $this->assertGreaterThanOrEqual(0.0, $distance);
-        $this->assertEquals($expected, $distance);
+        $this->assertEqualsWithDelta($expected, $distance, 1e-8);
     }
 
     /**
diff --git a/tests/NeuralNet/ActivationFunctions/HyperbolicTangentTest.php b/tests/NeuralNet/ActivationFunctions/HyperbolicTangentTest.php
index 322305175..063f892ce 100644
--- a/tests/NeuralNet/ActivationFunctions/HyperbolicTangentTest.php
+++ b/tests/NeuralNet/ActivationFunctions/HyperbolicTangentTest.php
@@ -47,7 +47,7 @@ public function activate(Matrix $input, array $expected) : void
     {
         $activations = $this->activationFn->activate($input)->asArray();
 
-        $this->assertEquals($expected, $activations);
+        $this->assertEqualsWithDelta($expected, $activations, 1e-8);
     }
 
     /**
@@ -90,7 +90,7 @@ public function differentiate(Matrix $input, Matrix $activations, array $expecte
     {
         $derivatives = $this->activationFn->differentiate($input, $activations)->asArray();
 
-        $this->assertEquals($expected, $derivatives);
+        $this->assertEqualsWithDelta($expected, $derivatives, 1e-8);
     }
 
     /**
diff --git a/tests/NeuralNet/ActivationFunctions/SiLUTest.php b/tests/NeuralNet/ActivationFunctions/SiLUTest.php
index 4728f69ab..b44aa94d2 100644
--- a/tests/NeuralNet/ActivationFunctions/SiLUTest.php
+++ b/tests/NeuralNet/ActivationFunctions/SiLUTest.php
@@ -47,7 +47,7 @@ public function compute(Matrix $input, array $expected) : void
     {
         $activations = $this->activationFn->activate($input)->asArray();
 
-        $this->assertEquals($expected, $activations);
+        $this->assertEqualsWithDelta($expected, $activations, 1e-8);
     }
 
     /**
@@ -90,7 +90,7 @@ public function differentiate(Matrix $input, Matrix $activations, array $expecte
     {
         $derivatives = $this->activationFn->differentiate($input, $activations)->asArray();
 
-        $this->assertEquals($expected, $derivatives);
+        $this->assertEqualsWithDelta($expected, $derivatives, 1e-8);
     }
 
     /**
diff --git a/tests/NeuralNet/ActivationFunctions/SoftmaxTest.php b/tests/NeuralNet/ActivationFunctions/SoftmaxTest.php
index 0f0419b5e..b0165742b 100644
--- a/tests/NeuralNet/ActivationFunctions/SoftmaxTest.php
+++ b/tests/NeuralNet/ActivationFunctions/SoftmaxTest.php
@@ -47,7 +47,7 @@ public function activate(Matrix $input, array $expected) : void
     {
         $activations = $this->activationFn->activate($input)->asArray();
 
-        $this->assertEquals($expected, $activations);
+        $this->assertEqualsWithDelta($expected, $activations, 1e-8);
     }
 
     /**
@@ -94,7 +94,7 @@ public function differentiate(Matrix $input, Matrix $activations, array $expecte
     {
         $derivatives = $this->activationFn->differentiate($input, $activations)->asArray();
 
-        $this->assertEquals($expected, $derivatives);
+        $this->assertEqualsWithDelta($expected, $derivatives, 1e-8);
     }
 
     /**
diff --git a/tests/NeuralNet/CostFunctions/CrossEntropyTest.php b/tests/NeuralNet/CostFunctions/CrossEntropyTest.php
index bdfff5069..f03341a65 100644
--- a/tests/NeuralNet/CostFunctions/CrossEntropyTest.php
+++ b/tests/NeuralNet/CostFunctions/CrossEntropyTest.php
@@ -48,7 +48,7 @@ public function compute(Matrix $output, Matrix $target, float $expected) : void
     {
         $loss = $this->costFn->compute($output, $target);
 
-        $this->assertEquals($expected, $loss);
+        $this->assertEqualsWithDelta($expected, $loss, 1e-8);
     }
 
     /**
@@ -113,7 +113,7 @@ public function differentiate(Matrix $output, Matrix $target, array $expected) :
     {
         $gradient = $this->costFn->differentiate($output, $target)->asArray();
 
-        $this->assertEquals($expected, $gradient);
+        $this->assertEqualsWithDelta($expected, $gradient, 1e-8);
     }
 
     /**
diff --git a/tests/NeuralNet/CostFunctions/HuberLossTest.php b/tests/NeuralNet/CostFunctions/HuberLossTest.php
index 6cd929aad..eeba46db5 100644
--- a/tests/NeuralNet/CostFunctions/HuberLossTest.php
+++ b/tests/NeuralNet/CostFunctions/HuberLossTest.php
@@ -48,7 +48,7 @@ public function compute(Matrix $output, Matrix $target, float $expected) : void
     {
         $loss = $this->costFn->compute($output, $target);
 
-        $this->assertEquals($expected, $loss);
+        $this->assertEqualsWithDelta($expected, $loss, 1e-8);
     }
 
     /**
@@ -107,7 +107,7 @@ public function differentiate(Matrix $output, Matrix $target, array $expected) :
     {
         $gradient = $this->costFn->differentiate($output, $target)->asArray();
 
-        $this->assertEquals($expected, $gradient);
+        $this->assertEqualsWithDelta($expected, $gradient, 1e-8);
     }
 
     /**
diff --git a/tests/NeuralNet/CostFunctions/RelativeEntropyTest.php b/tests/NeuralNet/CostFunctions/RelativeEntropyTest.php
index 459d9e453..6318ae97d 100644
--- a/tests/NeuralNet/CostFunctions/RelativeEntropyTest.php
+++ b/tests/NeuralNet/CostFunctions/RelativeEntropyTest.php
@@ -48,7 +48,7 @@ public function compute(Matrix $output, Matrix $target, float $expected) : void
     {
         $loss = $this->costFn->compute($output, $target);
 
-        $this->assertEquals($expected, $loss);
+        $this->assertEqualsWithDelta($expected, $loss, 1e-8);
     }
 
     /**
@@ -113,7 +113,7 @@ public function differentiate(Matrix $output, Matrix $target, array $expected) :
     {
         $gradient = $this->costFn->differentiate($output, $target)->asArray();
 
-        $this->assertEquals($expected, $gradient);
+        $this->assertEqualsWithDelta($expected, $gradient, 1e-8);
     }
 
     /**
diff --git a/tests/NeuralNet/Layers/BatchNormTest.php b/tests/NeuralNet/Layers/BatchNormTest.php
index 05beff7d0..53513c3d6 100644
--- a/tests/NeuralNet/Layers/BatchNormTest.php
+++ b/tests/NeuralNet/Layers/BatchNormTest.php
@@ -51,9 +51,9 @@ protected function setUp() : void
         $this->fanIn = 3;
 
         $this->input = Matrix::quick([
-            [1., 2.5, -0.1],
-            [0.1, 0., 3.],
-            [0.002, -6., -0.5],
+            [1.0, 2.5, -0.1],
+            [0.1, 00., 3.0],
+            [0.002, -6.0, -0.5],
         ]);
 
         $this->prevGrad = new Deferred(function () {
@@ -98,7 +98,7 @@ public function initializeForwardBackInfer() : void
         $forward = $this->layer->forward($this->input);
 
         $this->assertInstanceOf(Matrix::class, $forward);
-        $this->assertEquals($expected, $forward->asArray());
+        $this->assertEqualsWithDelta($expected, $forward->asArray(), 1e-8);
 
         $gradient = $this->layer->back($this->prevGrad, $this->optimizer)->compute();
 
@@ -109,7 +109,7 @@ public function initializeForwardBackInfer() : void
         ];
 
         $this->assertInstanceOf(Matrix::class, $gradient);
-        $this->assertEquals($expected, $gradient->asArray());
+        $this->assertEqualsWithDelta($expected, $gradient->asArray(), 1e-8);
 
         $expected = [
             [-0.12607831595417437, 1.2804902385302876, -1.1575619225761131],
@@ -120,6 +120,6 @@ public function initializeForwardBackInfer() : void
         $infer = $this->layer->infer($this->input);
 
         $this->assertInstanceOf(Matrix::class, $infer);
-        $this->assertEquals($expected, $infer->asArray());
+        $this->assertEqualsWithDelta($expected, $infer->asArray(), 1e-8);
     }
 }
diff --git a/tests/NeuralNet/Layers/BinaryTest.php b/tests/NeuralNet/Layers/BinaryTest.php
index 9ad32a13a..ec3b89ba4 100644
--- a/tests/NeuralNet/Layers/BinaryTest.php
+++ b/tests/NeuralNet/Layers/BinaryTest.php
@@ -83,7 +83,7 @@ public function initializeForwardBackInfer() : void
         $forward = $this->layer->forward($this->input);
 
         $this->assertInstanceOf(Matrix::class, $forward);
-        $this->assertEquals($expected, $forward->asArray());
+        $this->assertEqualsWithDelta($expected, $forward->asArray(), 1e-8);
 
         [$computation, $loss] = $this->layer->back($this->labels, $this->optimizer);
 
@@ -97,7 +97,7 @@ public function initializeForwardBackInfer() : void
         ];
 
         $this->assertInstanceOf(Matrix::class, $gradient);
-        $this->assertEquals($expected, $gradient->asArray());
+        $this->assertEqualsWithDelta($expected, $gradient->asArray(), 1e-8);
 
         $expected = [
             [0.7310585786300049, 0.9241418199787566, 0.47502081252106],
@@ -106,6 +106,6 @@ public function initializeForwardBackInfer() : void
         $infer = $this->layer->infer($this->input);
 
         $this->assertInstanceOf(Matrix::class, $infer);
-        $this->assertEquals($expected, $infer->asArray());
+        $this->assertEqualsWithDelta($expected, $infer->asArray(), 1e-8);
     }
 }
diff --git a/tests/NeuralNet/Layers/ContinuousTest.php b/tests/NeuralNet/Layers/ContinuousTest.php
index 2dba125b4..f992e0137 100644
--- a/tests/NeuralNet/Layers/ContinuousTest.php
+++ b/tests/NeuralNet/Layers/ContinuousTest.php
@@ -83,7 +83,7 @@ public function initializeForwardBackInfer() : void
         $forward = $this->layer->forward($this->input);
 
         $this->assertInstanceOf(Matrix::class, $forward);
-        $this->assertEquals($expected, $forward->asArray());
+        $this->assertEqualsWithDelta($expected, $forward->asArray(), 1e-8);
 
         [$computation, $loss] = $this->layer->back($this->labels, $this->optimizer);
 
@@ -97,7 +97,7 @@ public function initializeForwardBackInfer() : void
         ];
 
         $this->assertInstanceOf(Matrix::class, $gradient);
-        $this->assertEquals($expected, $gradient->asArray());
+        $this->assertEqualsWithDelta($expected, $gradient->asArray(), 1e-8);
 
         $expected = [
             [2.5, 0.0, -6.0],
@@ -106,6 +106,6 @@ public function initializeForwardBackInfer() : void
         $infer = $this->layer->infer($this->input);
 
         $this->assertInstanceOf(Matrix::class, $infer);
-        $this->assertEquals($expected, $infer->asArray());
+        $this->assertEqualsWithDelta($expected, $infer->asArray(), 1e-8);
     }
 }
diff --git a/tests/NeuralNet/Layers/DenseTest.php b/tests/NeuralNet/Layers/DenseTest.php
index 4720295c9..0f67277c3 100644
--- a/tests/NeuralNet/Layers/DenseTest.php
+++ b/tests/NeuralNet/Layers/DenseTest.php
@@ -101,7 +101,7 @@ public function initializeForwardBackInfer() : void
         $forward = $this->layer->forward($this->input);
 
         $this->assertInstanceOf(Matrix::class, $forward);
-        $this->assertEquals($expected, $forward->asArray());
+        $this->assertEqualsWithDelta($expected, $forward->asArray(), 1e-8);
 
         $gradient = $this->layer->back($this->prevGrad, $this->optimizer)->compute();
 
@@ -112,7 +112,7 @@ public function initializeForwardBackInfer() : void
         ];
 
         $this->assertInstanceOf(Matrix::class, $gradient);
-        $this->assertEquals($expected, $gradient->asArray());
+        $this->assertEqualsWithDelta($expected, $gradient->asArray(), 1e-8);
 
         $expected = [
             [0.1314490977703166, -2.670373438483866, 0.376362656428892],
@@ -122,6 +122,6 @@ public function initializeForwardBackInfer() : void
         $infer = $this->layer->infer($this->input);
 
         $this->assertInstanceOf(Matrix::class, $infer);
-        $this->assertEquals($expected, $infer->asArray());
+        $this->assertEqualsWithDelta($expected, $infer->asArray(), 1e-8);
     }
 }
diff --git a/tests/NeuralNet/Layers/MulticlassTest.php b/tests/NeuralNet/Layers/MulticlassTest.php
index 96a33ea11..44c624c0d 100644
--- a/tests/NeuralNet/Layers/MulticlassTest.php
+++ b/tests/NeuralNet/Layers/MulticlassTest.php
@@ -87,7 +87,7 @@ public function initializeForwardBackInfer() : void
         ];
 
         $this->assertInstanceOf(Matrix::class, $forward);
-        $this->assertEquals($expected, $forward->asArray());
+        $this->assertEqualsWithDelta($expected, $forward->asArray(), 1e-8);
 
         [$computation, $loss] = $this->layer->back($this->labels, $this->optimizer);
 
@@ -103,7 +103,7 @@ public function initializeForwardBackInfer() : void
         ];
 
         $this->assertInstanceOf(Matrix::class, $gradient);
-        $this->assertEquals($expected, $gradient->asArray());
+        $this->assertEqualsWithDelta($expected, $gradient->asArray(), 1e-8);
 
         $infer = $this->layer->infer($this->input);
 
@@ -114,6 +114,6 @@ public function initializeForwardBackInfer() : void
         ];
 
         $this->assertInstanceOf(Matrix::class, $infer);
-        $this->assertEquals($expected, $infer->asArray());
+        $this->assertEqualsWithDelta($expected, $infer->asArray(), 1e-8);
     }
 }
diff --git a/tests/NeuralNet/Layers/NoiseTest.php b/tests/NeuralNet/Layers/NoiseTest.php
index 11a2c80a3..ba8b95901 100644
--- a/tests/NeuralNet/Layers/NoiseTest.php
+++ b/tests/NeuralNet/Layers/NoiseTest.php
@@ -51,9 +51,9 @@ protected function setUp() : void
         $this->fanIn = 3;
 
         $this->input = Matrix::quick([
-            [1., 2.5, -0.1],
-            [0.1, 0., 3.],
-            [0.002, -6., -0.5],
+            [1.0, 2.5, -0.1],
+            [0.1, 0.0, 3.0],
+            [0.002, -6.0, -0.5],
         ]);
 
         $this->prevGrad = new Deferred(function () {
@@ -99,7 +99,7 @@ public function initializeForwardBackInfer() : void
         $forward = $this->layer->forward($this->input);
 
         $this->assertInstanceOf(Matrix::class, $forward);
-        $this->assertEquals($expected, $forward->asArray());
+        $this->assertEqualsWithDelta($expected, $forward->asArray(), 1e-8);
 
         $gradient = $this->layer->back($this->prevGrad, $this->optimizer)->compute();
 
@@ -110,17 +110,17 @@ public function initializeForwardBackInfer() : void
         ];
 
         $this->assertInstanceOf(Matrix::class, $gradient);
-        $this->assertEquals($expected, $gradient->asArray());
+        $this->assertEqualsWithDelta($expected, $gradient->asArray(), 1e-8);
 
         $expected = [
-            [1., 2.5, -0.1],
-            [0.1, 0., 3.],
-            [0.002, -6., -0.5],
+            [1.0, 2.5, -0.1],
+            [0.1, 0.0, 3.],
+            [0.002, -6.0, -0.5],
         ];
 
         $infer = $this->layer->infer($this->input);
 
         $this->assertInstanceOf(Matrix::class, $infer);
-        $this->assertEquals($expected, $infer->asArray());
+        $this->assertEqualsWithDelta($expected, $infer->asArray(), 1e-8);
     }
 }
diff --git a/tests/NeuralNet/Optimizers/AdaMaxTest.php b/tests/NeuralNet/Optimizers/AdaMaxTest.php
index c74f5a90d..f1fe569cd 100644
--- a/tests/NeuralNet/Optimizers/AdaMaxTest.php
+++ b/tests/NeuralNet/Optimizers/AdaMaxTest.php
@@ -54,7 +54,7 @@ public function step(Parameter $param, Tensor $gradient, array $expected) : void
 
         $step = $this->optimizer->step($param, $gradient);
 
-        $this->assertEquals($expected, $step->asArray());
+        $this->assertEqualsWithDelta($expected, $step->asArray(), 1e-8);
     }
 
     /**
diff --git a/tests/Transformers/GaussianRandomProjectorTest.php b/tests/Transformers/GaussianRandomProjectorTest.php
index 4452f3329..761456b07 100644
--- a/tests/Transformers/GaussianRandomProjectorTest.php
+++ b/tests/Transformers/GaussianRandomProjectorTest.php
@@ -118,7 +118,7 @@ public function fitTransform() : void
             ->sample(0);
 
         $this->assertCount(5, $sample);
-        $this->assertEquals($expected, $sample);
+        $this->assertEqualsWithDelta($expected, $sample, 1e-8);
     }
 
     /**
diff --git a/tests/Transformers/MinMaxNormalizerTest.php b/tests/Transformers/MinMaxNormalizerTest.php
index 3c811e975..b2855f472 100644
--- a/tests/Transformers/MinMaxNormalizerTest.php
+++ b/tests/Transformers/MinMaxNormalizerTest.php
@@ -88,7 +88,7 @@ public function fitUpdateTransformReverse() : void
 
         $dataset->reverseApply($this->transformer);
 
-        $this->assertEquals($original, $dataset->sample(0));
+        $this->assertEqualsWithDelta($original, $dataset->sample(0), 1e-8);
     }
 
     /**
diff --git a/tests/Transformers/SparseRandomProjectorTest.php b/tests/Transformers/SparseRandomProjectorTest.php
index b15767356..db23b9e5e 100644
--- a/tests/Transformers/SparseRandomProjectorTest.php
+++ b/tests/Transformers/SparseRandomProjectorTest.php
@@ -77,7 +77,7 @@ public function fitTransform() : void
             ->sample(0);
 
         $this->assertCount(4, $sample);
-        $this->assertEquals($expected, $sample);
+        $this->assertEqualsWithDelta($expected, $sample, 1e-8);
     }
 
     /**
diff --git a/tests/Transformers/ZScaleStandardizerTest.php b/tests/Transformers/ZScaleStandardizerTest.php
index 9d55c15a2..6d05b001b 100644
--- a/tests/Transformers/ZScaleStandardizerTest.php
+++ b/tests/Transformers/ZScaleStandardizerTest.php
@@ -90,7 +90,7 @@ public function fitUpdateTransformReverse() : void
 
         $dataset->reverseApply($this->transformer);
 
-        $this->assertEquals($original, $dataset->sample(0));
+        $this->assertEqualsWithDelta($original, $dataset->sample(0), 1e-8);
     }
 
     /**

From dfabe19234c19acf753bd4499a2a2b72b192e5c9 Mon Sep 17 00:00:00 2001
From: Andrew DalPino <me@andrewdalpino.com>
Date: Tue, 6 Dec 2022 17:36:53 -0600
Subject: [PATCH 3/6] Appease Stan

---
 src/Helpers/Params.php | 2 +-
 src/Report.php         | 1 +
 src/Tuple.php          | 1 +
 3 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/Helpers/Params.php b/src/Helpers/Params.php
index 7fbac333f..58fc655b5 100644
--- a/src/Helpers/Params.php
+++ b/src/Helpers/Params.php
@@ -164,7 +164,7 @@ public static function toString($value) : string
 
                 $class = get_class($value);
 
-                if ($class === false) {
+                if ($class == false) {
                     throw new RuntimeException('Could not locate object class.');
                 }
 
diff --git a/src/Report.php b/src/Report.php
index afd215881..736d5db3b 100644
--- a/src/Report.php
+++ b/src/Report.php
@@ -90,6 +90,7 @@ public function offsetExists($key) : bool
      * @throws \Rubix\ML\Exceptions\InvalidArgumentException
      * @return mixed
      */
+    #[\ReturnTypeWillChange]
     public function offsetGet($key)
     {
         if (isset($this->attributes[$key])) {
diff --git a/src/Tuple.php b/src/Tuple.php
index b17b1990b..0a7e8df5e 100644
--- a/src/Tuple.php
+++ b/src/Tuple.php
@@ -65,6 +65,7 @@ public function count() : int
      * @throws \Rubix\ML\Exceptions\InvalidArgumentException
      * @return mixed
      */
+    #[\ReturnTypeWillChange]
     public function offsetGet($offset)
     {
         if (isset($this->elements[$offset])) {

From f369730132e64be11f1115d53dee56a22c5ee1d6 Mon Sep 17 00:00:00 2001
From: Pablo Duboue <pablo.duboue@gmail.com>
Date: Tue, 6 Dec 2022 23:18:47 -0800
Subject: [PATCH 4/6] Backported Decision tree off-by-one fix

Pull Request #210 included a bug fix for a off-by-one bug found thanks to the visualzation

(see https://github.com/RubixML/ML/pull/210#issuecomment-1097611719 for details).
---
 src/Graph/Trees/DecisionTree.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Graph/Trees/DecisionTree.php b/src/Graph/Trees/DecisionTree.php
index 0edbf6e61..eec1c06e2 100644
--- a/src/Graph/Trees/DecisionTree.php
+++ b/src/Graph/Trees/DecisionTree.php
@@ -226,7 +226,7 @@ public function search(array $sample) : ?Outcome
                         $current = $current->right();
                     }
                 } else {
-                    if ($sample[$current->column()] < $value) {
+                    if ($sample[$current->column()] <= $value) {
                         $current = $current->left();
                     } else {
                         $current = $current->right();

From 8a5317c41c022dc5cb49d4e9564bedb19393a79a Mon Sep 17 00:00:00 2001
From: Andrew DalPino <me@andrewdalpino.com>
Date: Thu, 8 Dec 2022 17:23:14 -0600
Subject: [PATCH 5/6] Update CHANGELOG

---
 CHANGELOG.md                                 | 3 +++
 src/constants.php                            | 2 +-
 tests/Transformers/MaxAbsoluteScalerTest.php | 2 +-
 3 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f1d291251..aac641cdd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+- 1.3.6
+    - Fix decision tree off-by-one when searching for leaf node
+
 - 1.3.5
     - Fix Grid Search best model selection
 
diff --git a/src/constants.php b/src/constants.php
index 0ddfad2e4..5f54ff904 100644
--- a/src/constants.php
+++ b/src/constants.php
@@ -9,7 +9,7 @@
      *
      * @var string
      */
-    const VERSION = '1.3.3';
+    const VERSION = '1.3.6';
 
     /**
      * A small number used in substitution of 0.
diff --git a/tests/Transformers/MaxAbsoluteScalerTest.php b/tests/Transformers/MaxAbsoluteScalerTest.php
index b54fd7ff8..803d5b703 100644
--- a/tests/Transformers/MaxAbsoluteScalerTest.php
+++ b/tests/Transformers/MaxAbsoluteScalerTest.php
@@ -83,7 +83,7 @@ public function fitUpdateTransformReverse() : void
 
         $dataset->reverseApply($this->transformer);
 
-        $this->assertEquals($original, $dataset->sample(0));
+        $this->assertEqualsWithDelta($original, $dataset->sample(0), 1e-8);
     }
 
     /**

From 62f4a8a0f8f97fe662e785a3a9402a46085d5510 Mon Sep 17 00:00:00 2001
From: Andrew DalPino <me@andrewdalpino.com>
Date: Thu, 8 Dec 2022 17:47:11 -0600
Subject: [PATCH 6/6] Appease Stan

---
 src/Transformers/ImageResizer.php    | 4 ++--
 src/Transformers/ImageVectorizer.php | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/Transformers/ImageResizer.php b/src/Transformers/ImageResizer.php
index c6dc559f3..12415235c 100644
--- a/src/Transformers/ImageResizer.php
+++ b/src/Transformers/ImageResizer.php
@@ -92,8 +92,8 @@ public function resize(array &$sample) : void
     {
         foreach ($sample as &$value) {
             if (DataType::detect($value)->isImage()) {
-                $width = imagesx($value);
-                $height = imagesy($value);
+                $width = imagesx($value) ?: 0;
+                $height = imagesy($value) ?: 0;
 
                 if ($width === $this->width and $height === $this->height) {
                     continue;
diff --git a/src/Transformers/ImageVectorizer.php b/src/Transformers/ImageVectorizer.php
index 700c7d3bd..ce1a622f4 100644
--- a/src/Transformers/ImageVectorizer.php
+++ b/src/Transformers/ImageVectorizer.php
@@ -91,8 +91,8 @@ public function fit(Dataset $dataset) : void
                 $value = $sample[$column];
 
                 $this->sizes[$column] = [
-                    imagesx($value),
-                    imagesy($value),
+                    imagesx($value) ?: 0,
+                    imagesy($value) ?: 0,
                 ];
             }
         }