Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
83.33% covered (warning)
83.33%
5 / 6
CRAP
98.41% covered (success)
98.41%
62 / 63
SupportVectorMachine
0.00% covered (danger)
0.00%
0 / 1
83.33% covered (warning)
83.33%
5 / 6
10
98.41% covered (success)
98.41%
62 / 63
 __construct
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
16 / 16
 train
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
11 / 11
 getModel
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 predict
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
17 / 17
 getOSExtension
0.00% covered (danger)
0.00%
0 / 1
2.15
66.67% covered (warning)
66.67%
2 / 3
 buildTrainCommand
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
15 / 15
<?php
declare (strict_types = 1);
namespace Phpml\SupportVectorMachine;
class SupportVectorMachine
{
    /**
     * @var int
     */
    private $type;
    /**
     * @var int
     */
    private $kernel;
    /**
     * @var float
     */
    private $cost;
    /**
     * @var float
     */
    private $nu;
    /**
     * @var int
     */
    private $degree;
    /**
     * @var float
     */
    private $gamma;
    /**
     * @var float
     */
    private $coef0;
    /**
     * @var float
     */
    private $epsilon;
    /**
     * @var float
     */
    private $tolerance;
    /**
     * @var int
     */
    private $cacheSize;
    /**
     * @var bool
     */
    private $shrinking;
    /**
     * @var bool
     */
    private $probabilityEstimates;
    /**
     * @var string
     */
    private $binPath;
    /**
     * @var string
     */
    private $varPath;
    /**
     * @var string
     */
    private $model;
    /**
     * @var array
     */
    private $labels;
    /**
     * @param int        $type
     * @param int        $kernel
     * @param float      $cost
     * @param float      $nu
     * @param int        $degree
     * @param float|null $gamma
     * @param float      $coef0
     * @param float      $epsilon
     * @param float      $tolerance
     * @param int        $cacheSize
     * @param bool       $shrinking
     * @param bool       $probabilityEstimates
     */
    public function __construct(
        int $type, int $kernel, float $cost = 1.0, float $nu = 0.5, int $degree = 3,
        float $gamma = null, float $coef0 = 0.0, float $epsilon = 0.1, float $tolerance = 0.001,
        int $cacheSize = 100, bool $shrinking = true, bool $probabilityEstimates = false
    ) {
        $this->type = $type;
        $this->kernel = $kernel;
        $this->cost = $cost;
        $this->nu = $nu;
        $this->degree = $degree;
        $this->gamma = $gamma;
        $this->coef0 = $coef0;
        $this->epsilon = $epsilon;
        $this->tolerance = $tolerance;
        $this->cacheSize = $cacheSize;
        $this->shrinking = $shrinking;
        $this->probabilityEstimates = $probabilityEstimates;
        $rootPath = realpath(implode(DIRECTORY_SEPARATOR, [dirname(__FILE__), '..', '..', '..'])).DIRECTORY_SEPARATOR;
        $this->binPath = $rootPath.'bin'.DIRECTORY_SEPARATOR.'libsvm'.DIRECTORY_SEPARATOR;
        $this->varPath = $rootPath.'var'.DIRECTORY_SEPARATOR;
    }
    /**
     * @param array $samples
     * @param array $labels
     */
    public function train(array $samples, array $labels)
    {
        $this->labels = $labels;
        $trainingSet = DataTransformer::trainingSet($samples, $labels, in_array($this->type, [Type::EPSILON_SVR, Type::NU_SVR]));
        file_put_contents($trainingSetFileName = $this->varPath.uniqid(), $trainingSet);
        $modelFileName = $trainingSetFileName.'-model';
        $command = $this->buildTrainCommand($trainingSetFileName, $modelFileName);
        $output = '';
        exec(escapeshellcmd($command), $output);
        $this->model = file_get_contents($modelFileName);
        unlink($trainingSetFileName);
        unlink($modelFileName);
    }
    /**
     * @return string
     */
    public function getModel()
    {
        return $this->model;
    }
    /**
     * @param array $samples
     *
     * @return array
     */
    public function predict(array $samples)
    {
        $testSet = DataTransformer::testSet($samples);
        file_put_contents($testSetFileName = $this->varPath.uniqid(), $testSet);
        file_put_contents($modelFileName = $testSetFileName.'-model', $this->model);
        $outputFileName = $testSetFileName.'-output';
        $command = sprintf('%ssvm-predict%s %s %s %s', $this->binPath, $this->getOSExtension(), $testSetFileName, $modelFileName, $outputFileName);
        $output = '';
        exec(escapeshellcmd($command), $output);
        $predictions = file_get_contents($outputFileName);
        unlink($testSetFileName);
        unlink($modelFileName);
        unlink($outputFileName);
        if (in_array($this->type, [Type::C_SVC, Type::NU_SVC])) {
            $predictions = DataTransformer::predictions($predictions, $this->labels);
        } else {
            $predictions = explode(PHP_EOL, trim($predictions));
        }
        if (!is_array($samples[0])) {
            return $predictions[0];
        }
        return $predictions;
    }
    /**
     * @return string
     */
    private function getOSExtension()
    {
        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
            return '.exe';
        }
        return '';
    }
    /**
     * @param $trainingSetFileName
     * @param $modelFileName
     *
     * @return string
     */
    private function buildTrainCommand(string $trainingSetFileName, string $modelFileName): string
    {
        return sprintf('%ssvm-train%s -s %s -t %s -c %s -n %s -d %s%s -r %s -p %s -m %s -e %s -h %d -b %d \'%s\' \'%s\'',
            $this->binPath,
            $this->getOSExtension(),
            $this->type,
            $this->kernel,
            $this->cost,
            $this->nu,
            $this->degree,
            $this->gamma !== null ? ' -g '.$this->gamma : '',
            $this->coef0,
            $this->epsilon,
            $this->cacheSize,
            $this->tolerance,
            $this->shrinking,
            $this->probabilityEstimates,
            $trainingSetFileName,
            $modelFileName
        );
    }
}