protected function getKernel(): Closure
    {
        switch ($this->kernel) {
            case self::KERNEL_LINEAR:
                // k(x,y) = xT.y
                return function ($x, $y) {
                    return Matrix::dot($x, $y)[0];
                };
            case self::KERNEL_RBF:
                // k(x,y)=exp(-γ.|x-y|) where |..| is Euclidean distance
                $dist = new Euclidean();

                return function ($x, $y) use ($dist) {
                    return exp(-$this->gamma * $dist->sqDistance($x, $y));
                };

            case self::KERNEL_SIGMOID:
                // k(x,y)=tanh(γ.xT.y+c0) where c0=1
                return function ($x, $y) {
                    $res = Matrix::dot($x, $y)[0] + 1.0;

                    return tanh((float) $this->gamma * $res);
                };

            case self::KERNEL_LAPLACIAN:
                // k(x,y)=exp(-γ.|x-y|) where |..| is Manhattan distance
                $dist = new Manhattan();

                return function ($x, $y) use ($dist) {
                    return exp(-$this->gamma * $dist->distance($x, $y));
                };

            default:
                // Not reached
                throw new InvalidArgumentException(sprintf('KernelPCA initialized with invalid kernel: %d', $this->kernel));
        }
    }