├── .appveyor.yml ├── .travis.yml ├── CREDITS ├── LICENSE ├── README.md ├── config.m4 ├── config.w32 ├── libsvm ├── COPYRIGHT ├── svm.cpp └── svm.h ├── package.xml ├── php_svm.h ├── php_svm_internal.h ├── svm.c └── tests ├── 001_train.phpt ├── 002_predict.phpt ├── 003_getparms.phpt ├── 004_setoptions.phpt ├── 005_regression.phpt ├── 006_train_array.phpt ├── 007_kernels.phpt ├── 008_cross_validate.phpt ├── 009_badfile.phpt ├── 010_weights.phpt ├── 011_inttodouble.phpt ├── 012_baddata.phpt ├── 013_boolean_options.phpt ├── 014_predict_probability.phpt ├── 015_info_methods.phpt ├── 016_file_stream.phpt ├── abalone.scale ├── australian.scale └── baddata.scale /.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{branch}.build.{build}" 2 | skip_tags: true 3 | 4 | branches: 5 | only: 6 | - master 7 | 8 | clone_folder: c:\projects\svm 9 | 10 | install: 11 | ps: | 12 | if (-not (Test-Path c:\build-cache)) { 13 | mkdir c:\build-cache 14 | } 15 | $bname = 'php-sdk-' + $env:BIN_SDK_VER + '.zip' 16 | if (-not (Test-Path c:\build-cache\$bname)) { 17 | Invoke-WebRequest "https://github.com/Microsoft/php-sdk-binary-tools/archive/$bname" -OutFile "c:\build-cache\$bname" 18 | } 19 | $dname0 = 'php-sdk-binary-tools-php-sdk-' + $env:BIN_SDK_VER 20 | $dname1 = 'php-sdk-' + $env:BIN_SDK_VER 21 | if (-not (Test-Path c:\build-cache\$dname1)) { 22 | 7z x c:\build-cache\$bname -oc:\build-cache 23 | move c:\build-cache\$dname0 c:\build-cache\$dname1 24 | } 25 | 26 | cache: 27 | c:\build-cache -> .appveyor.yml 28 | 29 | environment: 30 | BIN_SDK_VER: 2.1.7 31 | matrix: 32 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 33 | ARCH: x64 34 | VC: vc14 35 | PHP_VER: 7.0.31 36 | TS: 0 37 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 38 | ARCH: x64 39 | VC: vc14 40 | PHP_VER: 7.0.31 41 | TS: 1 42 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 43 | ARCH: x86 44 | VC: vc14 45 | PHP_VER: 7.0.31 46 | TS: 0 47 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 48 | ARCH: x86 49 | VC: vc14 50 | PHP_VER: 7.0.31 51 | TS: 1 52 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 53 | ARCH: x64 54 | VC: vc14 55 | PHP_VER: 7.1.20 56 | TS: 0 57 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 58 | ARCH: x64 59 | VC: vc14 60 | PHP_VER: 7.1.20 61 | TS: 1 62 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 63 | ARCH: x86 64 | VC: vc14 65 | PHP_VER: 7.1.20 66 | TS: 0 67 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 68 | ARCH: x86 69 | VC: vc14 70 | PHP_VER: 7.1.20 71 | TS: 1 72 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 73 | ARCH: x64 74 | VC: vc15 75 | PHP_VER: 7.2.8 76 | TS: 0 77 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 78 | ARCH: x64 79 | VC: vc15 80 | PHP_VER: 7.2.8 81 | TS: 1 82 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 83 | ARCH: x86 84 | VC: vc15 85 | PHP_VER: 7.2.8 86 | TS: 0 87 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 88 | ARCH: x86 89 | VC: vc15 90 | PHP_VER: 7.2.8 91 | TS: 1 92 | 93 | build_script: 94 | ps: | 95 | $ts_part = '' 96 | if ('0' -eq $env:TS) { $ts_part = '-nts' } 97 | $bname = 'php-devel-pack-' + $env:PHP_VER + $ts_part + '-Win32-' + $env:VC.toUpper() + '-' + $env:ARCH + '.zip' 98 | if (-not (Test-Path c:\build-cache\$bname)) { 99 | Invoke-WebRequest "http://windows.php.net/downloads/releases/archives/$bname" -OutFile "c:\build-cache\$bname" 100 | if (-not (Test-Path c:\build-cache\$bname)) { 101 | Invoke-WebRequest "http://windows.php.net/downloads/releases/$bname" -OutFile "c:\build-cache\$bname" 102 | } 103 | } 104 | $dname0 = 'php-' + $env:PHP_VER + '-devel-' + $env:VC.toUpper() + '-' + $env:ARCH 105 | $dname1 = 'php-' + $env:PHP_VER + $ts_part + '-devel-' + $env:VC.toUpper() + '-' + $env:ARCH 106 | if (-not (Test-Path c:\build-cache\$dname1)) { 107 | 7z x c:\build-cache\$bname -oc:\build-cache 108 | move c:\build-cache\$dname0 c:\build-cache\$dname1 109 | } 110 | cd c:\projects\svm 111 | $env:PATH = 'c:\build-cache\' + $dname1 + ';' + $env:PATH 112 | #echo "@echo off" | Out-File -Encoding "ASCII" task.bat 113 | #echo "" | Out-File -Encoding "ASCII" -Append task.bat 114 | echo "" | Out-File -Encoding "ASCII" task.bat 115 | echo "call phpize 2>&1" | Out-File -Encoding "ASCII" -Append task.bat 116 | echo "call configure --with-svm --enable-debug-pack 2>&1" | Out-File -Encoding "ASCII" -Append task.bat 117 | echo "nmake /nologo 2>&1" | Out-File -Encoding "ASCII" -Append task.bat 118 | echo "exit %errorlevel%" | Out-File -Encoding "ASCII" -Append task.bat 119 | $here = (Get-Item -Path "." -Verbose).FullName 120 | $runner = 'c:\build-cache\php-sdk-' + $env:BIN_SDK_VER + '\phpsdk' + '-' + $env:VC + '-' + $env:ARCH + '.bat' 121 | $task = $here + '\task.bat' 122 | & $runner -t $task 123 | 124 | after_build: 125 | ps: | 126 | $ts_part = 'ts' 127 | if ('0' -eq $env:TS) { $ts_part = 'nts' } 128 | $zip_bname = 'php_svm-' + $env:APPVEYOR_REPO_COMMIT.substring(0, 8) + '-' + $env:PHP_VER.substring(0, 3) + '-' + $ts_part + '-' + $env:VC + '-' + $env:ARCH + '.zip' 129 | $dir = 'c:\projects\svm\'; 130 | if ('x64' -eq $env:ARCH) { $dir = $dir + 'x64\' } 131 | $dir = $dir + 'Release' 132 | if ('1' -eq $env:TS) { $dir = $dir + '_TS' } 133 | & 7z a c:\$zip_bname $dir\php_svm.dll $dir\php_svm.pdb c:\projects\svm\LICENSE c:\projects\svm\CREDITS 134 | Push-AppveyorArtifact c:\$zip_bname 135 | 136 | test_script: 137 | ps: | 138 | $ts_part = '' 139 | if ('0' -eq $env:TS) { $ts_part = '-nts' } 140 | $bname = 'php-' + $env:PHP_VER + $ts_part + '-Win32-' + $env:VC.toUpper() + '-' + $env:ARCH + '.zip' 141 | if (-not (Test-Path c:\build-cache\$bname)) { 142 | Invoke-WebRequest "http://windows.php.net/downloads/releases/archives/$bname" -OutFile "c:\build-cache\$bname" 143 | if (-not (Test-Path c:\build-cache\$bname)) { 144 | Invoke-WebRequest "http://windows.php.net/downloads/releases/$bname" -OutFile "c:\build-cache\$bname" 145 | } 146 | } 147 | $dname = 'php-' + $env:PHP_VER + $ts_part + '-' + $env:VC.toUpper() + '-' + $env:ARCH 148 | if (-not (Test-Path c:\build-cache\$dname)) { 149 | 7z x c:\build-cache\$bname -oc:\build-cache\$dname 150 | } 151 | cd c:\projects\svm 152 | echo "" | Out-File -Encoding "ASCII" task.bat 153 | echo "set REPORT_EXIT_STATUS=1" | Out-File -Encoding "ASCII" -Append task.bat 154 | $cmd = 'call configure --with-svm --with-prefix=c:\build-cache\' + $dname + " 2>&1" 155 | echo $cmd | Out-File -Encoding "ASCII" -Append task.bat 156 | echo "nmake /nologo test TESTS=-q 2>&1" | Out-File -Encoding "ASCII" -Append task.bat 157 | echo "exit %errorlevel%" | Out-File -Encoding "ASCII" -Append task.bat 158 | $here = (Get-Item -Path "." -Verbose).FullName 159 | $runner = 'c:\build-cache\php-sdk-' + $env:BIN_SDK_VER + '\phpsdk' + '-' + $env:VC + '-' + $env:ARCH + '.bat' 160 | $task = $here + '\task.bat' 161 | & $runner -t $task 162 | 163 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: php 3 | dist: trusty 4 | 5 | php: 6 | - 7.0 7 | - 7.1 8 | - 7.2 9 | - nightly 10 | 11 | matrix: 12 | allow_failures: 13 | - php: nightly 14 | 15 | addons: 16 | apt: 17 | packages: 18 | - libsvm-dev 19 | 20 | before_script: 21 | - phpize 22 | - ./configure 23 | - make 24 | - make install 25 | - echo "extension=svm.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini 26 | 27 | script: 28 | - REPORT_EXIT_STATUS=1 php ./run-tests.php -P -q --show-diff 29 | 30 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | svm 2 | Ian Barber, Mikko Koppanen, Anatol Belski 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | LibSVM extension for PHP 2 | Copyright (c) 2011, The php-svm authors (see CREDITS) 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of the copyright holder nor the 13 | names of its contributors may be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL IAN BARBER BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.com/ianbarber/php-svm.svg?branch=master)](https://travis-ci.com/ianbarber/php-svm) (Travis) 2 | 3 | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/2mbbc10n87dvw526?svg=true)](https://ci.appveyor.com/project/ianbarber/php-svm) (AppVeyor) 4 | 5 | # INTRODUCTION 6 | 7 | Available via PECL: http://pecl.php.net/package/svm 8 | Documentation at: http://php.net/manual/en/book.svm.php 9 | 10 | LibSVM is an efficient solver for SVM classification and regression problems. The svm extension wraps this in a PHP interface for easy use in PHP scripts. 11 | As of version 0.2.0 the extension requires PHP 7.0 or above. Older versions are compatible with PHP from 5.2 to 5.6. 12 | 13 | # INSTALLATION 14 | 15 | Libsvm itself is required. A bundled libsvm version is delivered as of version 0.2.0 and will be used when libsvm is not available on the target system. An external libsvm is usually available through some package management or can be compiled from source when fetched directly from the website. 16 | 17 | On ubuntu and other debian based systems, you may be able to installed the `libsvm-dev` package: 18 | 19 | apt-get install libsvm-dev 20 | 21 | If installing from the [website](http://www.csie.ntu.edu.tw/~cjlin/libsvm) then some steps will need to be taken as the package does not install automatically: 22 | 23 | wget http://www.csie.ntu.edu.tw/~cjlin/cgi-bin/libsvm.cgi?+http://www.csie.ntu.edu.tw/~cjlin/libsvm+tar.gz 24 | tar xvzf libsvm-3.1.tar.gz 25 | cd libsvm-3.1 26 | make lib 27 | cp libsvm.so.1 /usr/lib 28 | ln -s libsvm.so.1 libsvm.so 29 | ldconfig 30 | ldconfig --print | grep libsvm 31 | 32 | This last step should show libsvm is installed. 33 | 34 | Once libsvm is installed, the extension can be installed in the usual way. 35 | 36 | # INSTALLING ON WINDOWS 37 | 38 | A prebuilt win32 DLL is available from https://pecl.php.net/package/svm. The latest development snapshots are also fetcheable from AppVeyor artifacts. 39 | 40 | The extension builds properly on Windows, if following the Windows step by step build process: https://wiki.php.net/internals/windows/stepbystepbuild 41 | 42 | 0. Put SVM source in pecl/svm in phpdev directory 43 | 0. Download latest libsvm, rename Makefile to Makefile.old and Makefile.win to Makefile - nmake 44 | 0. Copy libsvm.lib from libsvm windows directory, and into deps/libs 45 | 0. Copy svm.h into `includes/libsvm/` 46 | ``` 47 | buildconf 48 | configure --disable-all --enable-cli --with-svm=shared 49 | nmake 50 | ``` 51 | Note, that if the bundled libsvm is used, the instructions about the libsvm setup can be ommited. Otherwise, make sure the libsvm.dll file from the libsvm windows directory in the path when running the resulting lib. 52 | 53 | # USAGE 54 | 55 | The basic process is to define parameters, supply training data to generate a model on, then make predictions based on the model. There are a default set of parameters that should get some results with most any input, so we'll start by looking at the data. 56 | 57 | Data is supplied in either a file, a stream, or as an an array. If supplied in a file or a stream, it must contain one line per training example, which must be formatted as an integer class (usually 1 and -1) followed by a series of feature/value pairs, in increasing feature order. The features are integers, the values floats, usually scaled 0-1. For example: 58 | 59 | -1 1:0.43 3:0.12 9284:0.2 60 | 61 | In a document classification problem, say a spam checker, each line would represent a document. There would be two classes, -1 for spam, 1 for ham. Each feature would represent some word, and the value would represent that importance of that word to the document (perhaps the frequency count, with the total scaled to unit length). Features that were 0 (e.g. the word did not appear in the document at all) would simply not be included. 62 | 63 | In array mode, the data must be passed as an array of arrays. Each sub-array must have the class as the first element, then key => value sets for the feature values pairs. E.g. 64 | 65 | array( 66 | array(-1, 1 => 0.43, 3 => 0.12, 9284 => 0.2), 67 | ); 68 | 69 | This data is passed to the SVM class's train function, which will return an SVM model is successful. 70 | 71 | $svm = new SVM(); 72 | $model = $svm->train($data); 73 | 74 | or, for example with a filename 75 | 76 | $svm = new SVM(); 77 | $model = $svm->train("traindata.txt"); 78 | 79 | Once a model has been generated, it can be used to make predictions about previously unseen data. This can be passed as an array to the model's predict function, in the same format as before, but without the label. The response will be the class. 80 | 81 | $data = array(1 => 0.43, 3 => 0.12, 9284 => 0.2); 82 | $result = $model->predict($data); 83 | 84 | In this case, $result would be -1. 85 | 86 | Models can be saved and restored as required, using the save and load functions, which both take a file location. 87 | 88 | $model->save('model.svm'); 89 | 90 | And to load: 91 | 92 | $model = new SVMModel(); 93 | $model->load('model.svm'); 94 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | PHP_ARG_WITH(svm, whether to enable svm support, 2 | [ --with-svm[=DIR] Enable svn support. DIR is the prefix to libsvm installation directory.], yes) 3 | 4 | if test "$PHP_SVM" != "no"; then 5 | 6 | 7 | dnl Get PHP version depending on shared/static build 8 | 9 | AC_MSG_CHECKING([PHP version is at least 7.0.0]) 10 | 11 | if test -z "${PHP_VERSION_ID}"; then 12 | if test -z "${PHP_CONFIG}"; then 13 | AC_MSG_ERROR([php-config not found]) 14 | fi 15 | PHP_SVM_FOUND_VERNUM=`${PHP_CONFIG} --vernum`; 16 | PHP_SVM_FOUND_VERSION=`${PHP_CONFIG} --version` 17 | else 18 | PHP_SVM_FOUND_VERNUM="${PHP_VERSION_ID}" 19 | PHP_SVM_FOUND_VERSION="${PHP_VERSION}" 20 | fi 21 | 22 | if test "$PHP_SVM_FOUND_VERNUM" -ge "70000"; then 23 | AC_MSG_RESULT(yes. found $PHP_SVM_FOUND_VERSION) 24 | else 25 | AC_MSG_ERROR(no. found $PHP_SVM_FOUND_VERSION) 26 | fi 27 | 28 | AC_MSG_CHECKING([for svm.h header]) 29 | for i in $PHP_SVM /usr/local /usr; 30 | do 31 | test -r $i/include/svm.h && SVM_PREFIX=$i && SVM_INC_DIR=$i/include/ && SVM_OK=1 32 | done 33 | 34 | if test "$SVM_OK" != "1"; then 35 | for i in $PHP_SVM /usr/local /usr; 36 | do 37 | test -r $i/include/libsvm/svm.h && SVM_PREFIX=$i && SVM_INC_DIR=$i/include/libsvm/ && SVM_OK=1 38 | done 39 | fi 40 | 41 | if test "$SVM_OK" != "1"; then 42 | for i in $PHP_SVM /usr/local /usr; 43 | do 44 | test -r $i/include/libsvm-2.0/libsvm/svm.h && SVM_PREFIX=$i && SVM_INC_DIR=$i/include/libsvm-2.0/libsvm/ && SVM_OK=1 45 | done 46 | fi 47 | 48 | if test "$SVM_OK" != "1"; then 49 | AC_MSG_RESULT([not found, using bundled libsvm]) 50 | 51 | PHP_REQUIRE_CXX() 52 | PHP_ADD_LIBRARY(stdc++,,SVM_SHARED_LIBADD) 53 | 54 | PHP_NEW_EXTENSION(svm, svm.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1, cxx) 55 | PHP_ADD_SOURCES_X(PHP_EXT_DIR(svm), $ext_builddir/libsvm/svm.cpp,, shared_objects_svm, yes) 56 | 57 | PHP_ADD_INCLUDE($ext_srcdir/libsvm) 58 | PHP_ADD_INCLUDE($ext_builddir/libsvm) 59 | else 60 | 61 | AC_MSG_RESULT([found in $SVM_INC_DIR]) 62 | 63 | AC_MSG_CHECKING([for libsvm shared libraries]) 64 | PHP_CHECK_LIBRARY(svm, svm_train, [ 65 | PHP_ADD_LIBRARY_WITH_PATH(svm, $SVM_PREFIX/$PHP_LIBDIR, SVM_SHARED_LIBADD) 66 | PHP_ADD_INCLUDE($SVM_INC_DIR) 67 | ],[ 68 | AC_MSG_ERROR([not found. Make sure that libsvm is installed]) 69 | ],[ 70 | SVM_SHARED_LIBADD -lsvm 71 | ]) 72 | 73 | PHP_NEW_EXTENSION(svm, svm.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) 74 | fi 75 | AC_DEFINE(HAVE_SVM,1,[ ]) 76 | 77 | PHP_SUBST(SVM_SHARED_LIBADD) 78 | fi 79 | 80 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | ARG_WITH("svm", "whether to enable svm support", "no"); 2 | 3 | if (PHP_SVM != "no") { 4 | if (CHECK_HEADER_ADD_INCLUDE("svm.h", "CFLAGS_SVM", PHP_PHP_BUILD + "\\include\\libsvm;" + PHP_SVM) 5 | && CHECK_LIB("libsvm.lib", "svm", PHP_PHP_BUILD + "\\lib;" + PHP_SVM)) 6 | { 7 | EXTENSION('svm', 'svm.c', PHP_SVM_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 /EHsc"); 8 | AC_DEFINE('HAVE_SVM', 1); 9 | } else if (CHECK_HEADER_ADD_INCLUDE("svm.h", "CFLAGS_SVM", configure_module_dirname + "\\libsvm")) { 10 | EXTENSION('svm', 'svm.c', PHP_SVM_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 /EHsc -std:c++14"); 11 | ADD_SOURCES(configure_module_dirname, "libsvm\\svm.cpp", "svm"); 12 | AC_DEFINE('HAVE_SVM', 1); 13 | } else { 14 | WARNING("SVM not enabled; libraries and headers not found"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /libsvm/COPYRIGHT: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2000-2018 Chih-Chung Chang and Chih-Jen Lin 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | 3. Neither name of copyright holders nor the names of its contributors 17 | may be used to endorse or promote products derived from this software 18 | without specific prior written permission. 19 | 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR 25 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /libsvm/svm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "svm.h" 11 | int libsvm_version = LIBSVM_VERSION; 12 | typedef float Qfloat; 13 | typedef signed char schar; 14 | #ifndef min 15 | template static inline T min(T x,T y) { return (x static inline T max(T x,T y) { return (x>y)?x:y; } 19 | #endif 20 | template static inline void swap(T& x, T& y) { T t=x; x=y; y=t; } 21 | template static inline void clone(T*& dst, S* src, int n) 22 | { 23 | dst = new T[n]; 24 | memcpy((void *)dst,(void *)src,sizeof(T)*n); 25 | } 26 | static inline double powi(double base, int times) 27 | { 28 | double tmp = base, ret = 1.0; 29 | 30 | for(int t=times; t>0; t/=2) 31 | { 32 | if(t%2==1) ret*=tmp; 33 | tmp = tmp * tmp; 34 | } 35 | return ret; 36 | } 37 | #define INF HUGE_VAL 38 | #define TAU 1e-12 39 | #define Malloc(type,n) (type *)malloc((n)*sizeof(type)) 40 | 41 | static void print_string_stdout(const char *s) 42 | { 43 | fputs(s,stdout); 44 | fflush(stdout); 45 | } 46 | static void (*svm_print_string) (const char *) = &print_string_stdout; 47 | #if 1 48 | static void info(const char *fmt,...) 49 | { 50 | char buf[BUFSIZ]; 51 | va_list ap; 52 | va_start(ap,fmt); 53 | vsprintf(buf,fmt,ap); 54 | va_end(ap); 55 | (*svm_print_string)(buf); 56 | } 57 | #else 58 | static void info(const char *fmt,...) {} 59 | #endif 60 | 61 | // 62 | // Kernel Cache 63 | // 64 | // l is the number of total data items 65 | // size is the cache size limit in bytes 66 | // 67 | class Cache 68 | { 69 | public: 70 | Cache(int l,long int size); 71 | ~Cache(); 72 | 73 | // request data [0,len) 74 | // return some position p where [p,len) need to be filled 75 | // (p >= len if nothing needs to be filled) 76 | int get_data(const int index, Qfloat **data, int len); 77 | void swap_index(int i, int j); 78 | private: 79 | int l; 80 | long int size; 81 | struct head_t 82 | { 83 | head_t *prev, *next; // a circular list 84 | Qfloat *data; 85 | int len; // data[0,len) is cached in this entry 86 | }; 87 | 88 | head_t *head; 89 | head_t lru_head; 90 | void lru_delete(head_t *h); 91 | void lru_insert(head_t *h); 92 | }; 93 | 94 | Cache::Cache(int l_,long int size_):l(l_),size(size_) 95 | { 96 | head = (head_t *)calloc(l,sizeof(head_t)); // initialized to 0 97 | size /= sizeof(Qfloat); 98 | size -= l * sizeof(head_t) / sizeof(Qfloat); 99 | size = max(size, 2 * (long int) l); // cache must be large enough for two columns 100 | lru_head.next = lru_head.prev = &lru_head; 101 | } 102 | 103 | Cache::~Cache() 104 | { 105 | for(head_t *h = lru_head.next; h != &lru_head; h=h->next) 106 | free(h->data); 107 | free(head); 108 | } 109 | 110 | void Cache::lru_delete(head_t *h) 111 | { 112 | // delete from current location 113 | h->prev->next = h->next; 114 | h->next->prev = h->prev; 115 | } 116 | 117 | void Cache::lru_insert(head_t *h) 118 | { 119 | // insert to last position 120 | h->next = &lru_head; 121 | h->prev = lru_head.prev; 122 | h->prev->next = h; 123 | h->next->prev = h; 124 | } 125 | 126 | int Cache::get_data(const int index, Qfloat **data, int len) 127 | { 128 | head_t *h = &head[index]; 129 | if(h->len) lru_delete(h); 130 | int more = len - h->len; 131 | 132 | if(more > 0) 133 | { 134 | // free old space 135 | while(size < more) 136 | { 137 | head_t *old = lru_head.next; 138 | lru_delete(old); 139 | free(old->data); 140 | size += old->len; 141 | old->data = 0; 142 | old->len = 0; 143 | } 144 | 145 | // allocate new space 146 | h->data = (Qfloat *)realloc(h->data,sizeof(Qfloat)*len); 147 | size -= more; 148 | swap(h->len,len); 149 | } 150 | 151 | lru_insert(h); 152 | *data = h->data; 153 | return len; 154 | } 155 | 156 | void Cache::swap_index(int i, int j) 157 | { 158 | if(i==j) return; 159 | 160 | if(head[i].len) lru_delete(&head[i]); 161 | if(head[j].len) lru_delete(&head[j]); 162 | swap(head[i].data,head[j].data); 163 | swap(head[i].len,head[j].len); 164 | if(head[i].len) lru_insert(&head[i]); 165 | if(head[j].len) lru_insert(&head[j]); 166 | 167 | if(i>j) swap(i,j); 168 | for(head_t *h = lru_head.next; h!=&lru_head; h=h->next) 169 | { 170 | if(h->len > i) 171 | { 172 | if(h->len > j) 173 | swap(h->data[i],h->data[j]); 174 | else 175 | { 176 | // give up 177 | lru_delete(h); 178 | free(h->data); 179 | size += h->len; 180 | h->data = 0; 181 | h->len = 0; 182 | } 183 | } 184 | } 185 | } 186 | 187 | // 188 | // Kernel evaluation 189 | // 190 | // the static method k_function is for doing single kernel evaluation 191 | // the constructor of Kernel prepares to calculate the l*l kernel matrix 192 | // the member function get_Q is for getting one column from the Q Matrix 193 | // 194 | class QMatrix { 195 | public: 196 | virtual Qfloat *get_Q(int column, int len) const = 0; 197 | virtual double *get_QD() const = 0; 198 | virtual void swap_index(int i, int j) const = 0; 199 | virtual ~QMatrix() {} 200 | }; 201 | 202 | class Kernel: public QMatrix { 203 | public: 204 | Kernel(int l, svm_node * const * x, const svm_parameter& param); 205 | virtual ~Kernel(); 206 | 207 | static double k_function(const svm_node *x, const svm_node *y, 208 | const svm_parameter& param); 209 | virtual Qfloat *get_Q(int column, int len) const = 0; 210 | virtual double *get_QD() const = 0; 211 | virtual void swap_index(int i, int j) const // no so const... 212 | { 213 | swap(x[i],x[j]); 214 | if(x_square) swap(x_square[i],x_square[j]); 215 | } 216 | protected: 217 | 218 | double (Kernel::*kernel_function)(int i, int j) const; 219 | 220 | private: 221 | const svm_node **x; 222 | double *x_square; 223 | 224 | // svm_parameter 225 | const int kernel_type; 226 | const int degree; 227 | const double gamma; 228 | const double coef0; 229 | 230 | static double dot(const svm_node *px, const svm_node *py); 231 | double kernel_linear(int i, int j) const 232 | { 233 | return dot(x[i],x[j]); 234 | } 235 | double kernel_poly(int i, int j) const 236 | { 237 | return powi(gamma*dot(x[i],x[j])+coef0,degree); 238 | } 239 | double kernel_rbf(int i, int j) const 240 | { 241 | return exp(-gamma*(x_square[i]+x_square[j]-2*dot(x[i],x[j]))); 242 | } 243 | double kernel_sigmoid(int i, int j) const 244 | { 245 | return tanh(gamma*dot(x[i],x[j])+coef0); 246 | } 247 | double kernel_precomputed(int i, int j) const 248 | { 249 | return x[i][(int)(x[j][0].value)].value; 250 | } 251 | }; 252 | 253 | Kernel::Kernel(int l, svm_node * const * x_, const svm_parameter& param) 254 | :kernel_type(param.kernel_type), degree(param.degree), 255 | gamma(param.gamma), coef0(param.coef0) 256 | { 257 | switch(kernel_type) 258 | { 259 | case LINEAR: 260 | kernel_function = &Kernel::kernel_linear; 261 | break; 262 | case POLY: 263 | kernel_function = &Kernel::kernel_poly; 264 | break; 265 | case RBF: 266 | kernel_function = &Kernel::kernel_rbf; 267 | break; 268 | case SIGMOID: 269 | kernel_function = &Kernel::kernel_sigmoid; 270 | break; 271 | case PRECOMPUTED: 272 | kernel_function = &Kernel::kernel_precomputed; 273 | break; 274 | } 275 | 276 | clone(x,x_,l); 277 | 278 | if(kernel_type == RBF) 279 | { 280 | x_square = new double[l]; 281 | for(int i=0;iindex != -1 && py->index != -1) 298 | { 299 | if(px->index == py->index) 300 | { 301 | sum += px->value * py->value; 302 | ++px; 303 | ++py; 304 | } 305 | else 306 | { 307 | if(px->index > py->index) 308 | ++py; 309 | else 310 | ++px; 311 | } 312 | } 313 | return sum; 314 | } 315 | 316 | double Kernel::k_function(const svm_node *x, const svm_node *y, 317 | const svm_parameter& param) 318 | { 319 | switch(param.kernel_type) 320 | { 321 | case LINEAR: 322 | return dot(x,y); 323 | case POLY: 324 | return powi(param.gamma*dot(x,y)+param.coef0,param.degree); 325 | case RBF: 326 | { 327 | double sum = 0; 328 | while(x->index != -1 && y->index !=-1) 329 | { 330 | if(x->index == y->index) 331 | { 332 | double d = x->value - y->value; 333 | sum += d*d; 334 | ++x; 335 | ++y; 336 | } 337 | else 338 | { 339 | if(x->index > y->index) 340 | { 341 | sum += y->value * y->value; 342 | ++y; 343 | } 344 | else 345 | { 346 | sum += x->value * x->value; 347 | ++x; 348 | } 349 | } 350 | } 351 | 352 | while(x->index != -1) 353 | { 354 | sum += x->value * x->value; 355 | ++x; 356 | } 357 | 358 | while(y->index != -1) 359 | { 360 | sum += y->value * y->value; 361 | ++y; 362 | } 363 | 364 | return exp(-param.gamma*sum); 365 | } 366 | case SIGMOID: 367 | return tanh(param.gamma*dot(x,y)+param.coef0); 368 | case PRECOMPUTED: //x: test (validation), y: SV 369 | return x[(int)(y->value)].value; 370 | default: 371 | return 0; // Unreachable 372 | } 373 | } 374 | 375 | // An SMO algorithm in Fan et al., JMLR 6(2005), p. 1889--1918 376 | // Solves: 377 | // 378 | // min 0.5(\alpha^T Q \alpha) + p^T \alpha 379 | // 380 | // y^T \alpha = \delta 381 | // y_i = +1 or -1 382 | // 0 <= alpha_i <= Cp for y_i = 1 383 | // 0 <= alpha_i <= Cn for y_i = -1 384 | // 385 | // Given: 386 | // 387 | // Q, p, y, Cp, Cn, and an initial feasible point \alpha 388 | // l is the size of vectors and matrices 389 | // eps is the stopping tolerance 390 | // 391 | // solution will be put in \alpha, objective value will be put in obj 392 | // 393 | class Solver { 394 | public: 395 | Solver() {}; 396 | virtual ~Solver() {}; 397 | 398 | struct SolutionInfo { 399 | double obj; 400 | double rho; 401 | double upper_bound_p; 402 | double upper_bound_n; 403 | double r; // for Solver_NU 404 | }; 405 | 406 | void Solve(int l, const QMatrix& Q, const double *p_, const schar *y_, 407 | double *alpha_, double Cp, double Cn, double eps, 408 | SolutionInfo* si, int shrinking); 409 | protected: 410 | int active_size; 411 | schar *y; 412 | double *G; // gradient of objective function 413 | enum { LOWER_BOUND, UPPER_BOUND, FREE }; 414 | char *alpha_status; // LOWER_BOUND, UPPER_BOUND, FREE 415 | double *alpha; 416 | const QMatrix *Q; 417 | const double *QD; 418 | double eps; 419 | double Cp,Cn; 420 | double *p; 421 | int *active_set; 422 | double *G_bar; // gradient, if we treat free variables as 0 423 | int l; 424 | bool unshrink; // XXX 425 | 426 | double get_C(int i) 427 | { 428 | return (y[i] > 0)? Cp : Cn; 429 | } 430 | void update_alpha_status(int i) 431 | { 432 | if(alpha[i] >= get_C(i)) 433 | alpha_status[i] = UPPER_BOUND; 434 | else if(alpha[i] <= 0) 435 | alpha_status[i] = LOWER_BOUND; 436 | else alpha_status[i] = FREE; 437 | } 438 | bool is_upper_bound(int i) { return alpha_status[i] == UPPER_BOUND; } 439 | bool is_lower_bound(int i) { return alpha_status[i] == LOWER_BOUND; } 440 | bool is_free(int i) { return alpha_status[i] == FREE; } 441 | void swap_index(int i, int j); 442 | void reconstruct_gradient(); 443 | virtual int select_working_set(int &i, int &j); 444 | virtual double calculate_rho(); 445 | virtual void do_shrinking(); 446 | private: 447 | bool be_shrunk(int i, double Gmax1, double Gmax2); 448 | }; 449 | 450 | void Solver::swap_index(int i, int j) 451 | { 452 | Q->swap_index(i,j); 453 | swap(y[i],y[j]); 454 | swap(G[i],G[j]); 455 | swap(alpha_status[i],alpha_status[j]); 456 | swap(alpha[i],alpha[j]); 457 | swap(p[i],p[j]); 458 | swap(active_set[i],active_set[j]); 459 | swap(G_bar[i],G_bar[j]); 460 | } 461 | 462 | void Solver::reconstruct_gradient() 463 | { 464 | // reconstruct inactive elements of G from G_bar and free variables 465 | 466 | if(active_size == l) return; 467 | 468 | int i,j; 469 | int nr_free = 0; 470 | 471 | for(j=active_size;j 2*active_size*(l-active_size)) 482 | { 483 | for(i=active_size;iget_Q(i,active_size); 486 | for(j=0;jget_Q(i,l); 497 | double alpha_i = alpha[i]; 498 | for(j=active_size;jl = l; 509 | this->Q = &Q; 510 | QD=Q.get_QD(); 511 | clone(p, p_,l); 512 | clone(y, y_,l); 513 | clone(alpha,alpha_,l); 514 | this->Cp = Cp; 515 | this->Cn = Cn; 516 | this->eps = eps; 517 | unshrink = false; 518 | 519 | // initialize alpha_status 520 | { 521 | alpha_status = new char[l]; 522 | for(int i=0;iINT_MAX/100 ? INT_MAX : 100*l); 562 | int counter = min(l,1000)+1; 563 | 564 | while(iter < max_iter) 565 | { 566 | // show progress and do shrinking 567 | 568 | if(--counter == 0) 569 | { 570 | counter = min(l,1000); 571 | if(shrinking) do_shrinking(); 572 | info("."); 573 | } 574 | 575 | int i,j; 576 | if(select_working_set(i,j)!=0) 577 | { 578 | // reconstruct the whole gradient 579 | reconstruct_gradient(); 580 | // reset active set size and check 581 | active_size = l; 582 | info("*"); 583 | if(select_working_set(i,j)!=0) 584 | break; 585 | else 586 | counter = 1; // do shrinking next iteration 587 | } 588 | 589 | ++iter; 590 | 591 | // update alpha[i] and alpha[j], handle bounds carefully 592 | 593 | const Qfloat *Q_i = Q.get_Q(i,active_size); 594 | const Qfloat *Q_j = Q.get_Q(j,active_size); 595 | 596 | double C_i = get_C(i); 597 | double C_j = get_C(j); 598 | 599 | double old_alpha_i = alpha[i]; 600 | double old_alpha_j = alpha[j]; 601 | 602 | if(y[i]!=y[j]) 603 | { 604 | double quad_coef = QD[i]+QD[j]+2*Q_i[j]; 605 | if (quad_coef <= 0) 606 | quad_coef = TAU; 607 | double delta = (-G[i]-G[j])/quad_coef; 608 | double diff = alpha[i] - alpha[j]; 609 | alpha[i] += delta; 610 | alpha[j] += delta; 611 | 612 | if(diff > 0) 613 | { 614 | if(alpha[j] < 0) 615 | { 616 | alpha[j] = 0; 617 | alpha[i] = diff; 618 | } 619 | } 620 | else 621 | { 622 | if(alpha[i] < 0) 623 | { 624 | alpha[i] = 0; 625 | alpha[j] = -diff; 626 | } 627 | } 628 | if(diff > C_i - C_j) 629 | { 630 | if(alpha[i] > C_i) 631 | { 632 | alpha[i] = C_i; 633 | alpha[j] = C_i - diff; 634 | } 635 | } 636 | else 637 | { 638 | if(alpha[j] > C_j) 639 | { 640 | alpha[j] = C_j; 641 | alpha[i] = C_j + diff; 642 | } 643 | } 644 | } 645 | else 646 | { 647 | double quad_coef = QD[i]+QD[j]-2*Q_i[j]; 648 | if (quad_coef <= 0) 649 | quad_coef = TAU; 650 | double delta = (G[i]-G[j])/quad_coef; 651 | double sum = alpha[i] + alpha[j]; 652 | alpha[i] -= delta; 653 | alpha[j] += delta; 654 | 655 | if(sum > C_i) 656 | { 657 | if(alpha[i] > C_i) 658 | { 659 | alpha[i] = C_i; 660 | alpha[j] = sum - C_i; 661 | } 662 | } 663 | else 664 | { 665 | if(alpha[j] < 0) 666 | { 667 | alpha[j] = 0; 668 | alpha[i] = sum; 669 | } 670 | } 671 | if(sum > C_j) 672 | { 673 | if(alpha[j] > C_j) 674 | { 675 | alpha[j] = C_j; 676 | alpha[i] = sum - C_j; 677 | } 678 | } 679 | else 680 | { 681 | if(alpha[i] < 0) 682 | { 683 | alpha[i] = 0; 684 | alpha[j] = sum; 685 | } 686 | } 687 | } 688 | 689 | // update G 690 | 691 | double delta_alpha_i = alpha[i] - old_alpha_i; 692 | double delta_alpha_j = alpha[j] - old_alpha_j; 693 | 694 | for(int k=0;k= max_iter) 732 | { 733 | if(active_size < l) 734 | { 735 | // reconstruct the whole gradient to calculate objective value 736 | reconstruct_gradient(); 737 | active_size = l; 738 | info("*"); 739 | } 740 | fprintf(stderr,"\nWARNING: reaching max number of iterations\n"); 741 | } 742 | 743 | // calculate rho 744 | 745 | si->rho = calculate_rho(); 746 | 747 | // calculate objective value 748 | { 749 | double v = 0; 750 | int i; 751 | for(i=0;iobj = v/2; 755 | } 756 | 757 | // put back the solution 758 | { 759 | for(int i=0;iupper_bound_p = Cp; 772 | si->upper_bound_n = Cn; 773 | 774 | info("\noptimization finished, #iter = %d\n",iter); 775 | 776 | delete[] p; 777 | delete[] y; 778 | delete[] alpha; 779 | delete[] alpha_status; 780 | delete[] active_set; 781 | delete[] G; 782 | delete[] G_bar; 783 | } 784 | 785 | // return 1 if already optimal, return 0 otherwise 786 | int Solver::select_working_set(int &out_i, int &out_j) 787 | { 788 | // return i,j such that 789 | // i: maximizes -y_i * grad(f)_i, i in I_up(\alpha) 790 | // j: minimizes the decrease of obj value 791 | // (if quadratic coefficeint <= 0, replace it with tau) 792 | // -y_j*grad(f)_j < -y_i*grad(f)_i, j in I_low(\alpha) 793 | 794 | double Gmax = -INF; 795 | double Gmax2 = -INF; 796 | int Gmax_idx = -1; 797 | int Gmin_idx = -1; 798 | double obj_diff_min = INF; 799 | 800 | for(int t=0;t= Gmax) 805 | { 806 | Gmax = -G[t]; 807 | Gmax_idx = t; 808 | } 809 | } 810 | else 811 | { 812 | if(!is_lower_bound(t)) 813 | if(G[t] >= Gmax) 814 | { 815 | Gmax = G[t]; 816 | Gmax_idx = t; 817 | } 818 | } 819 | 820 | int i = Gmax_idx; 821 | const Qfloat *Q_i = NULL; 822 | if(i != -1) // NULL Q_i not accessed: Gmax=-INF if i=-1 823 | Q_i = Q->get_Q(i,active_size); 824 | 825 | for(int j=0;j= Gmax2) 833 | Gmax2 = G[j]; 834 | if (grad_diff > 0) 835 | { 836 | double obj_diff; 837 | double quad_coef = QD[i]+QD[j]-2.0*y[i]*Q_i[j]; 838 | if (quad_coef > 0) 839 | obj_diff = -(grad_diff*grad_diff)/quad_coef; 840 | else 841 | obj_diff = -(grad_diff*grad_diff)/TAU; 842 | 843 | if (obj_diff <= obj_diff_min) 844 | { 845 | Gmin_idx=j; 846 | obj_diff_min = obj_diff; 847 | } 848 | } 849 | } 850 | } 851 | else 852 | { 853 | if (!is_upper_bound(j)) 854 | { 855 | double grad_diff= Gmax-G[j]; 856 | if (-G[j] >= Gmax2) 857 | Gmax2 = -G[j]; 858 | if (grad_diff > 0) 859 | { 860 | double obj_diff; 861 | double quad_coef = QD[i]+QD[j]+2.0*y[i]*Q_i[j]; 862 | if (quad_coef > 0) 863 | obj_diff = -(grad_diff*grad_diff)/quad_coef; 864 | else 865 | obj_diff = -(grad_diff*grad_diff)/TAU; 866 | 867 | if (obj_diff <= obj_diff_min) 868 | { 869 | Gmin_idx=j; 870 | obj_diff_min = obj_diff; 871 | } 872 | } 873 | } 874 | } 875 | } 876 | 877 | if(Gmax+Gmax2 < eps || Gmin_idx == -1) 878 | return 1; 879 | 880 | out_i = Gmax_idx; 881 | out_j = Gmin_idx; 882 | return 0; 883 | } 884 | 885 | bool Solver::be_shrunk(int i, double Gmax1, double Gmax2) 886 | { 887 | if(is_upper_bound(i)) 888 | { 889 | if(y[i]==+1) 890 | return(-G[i] > Gmax1); 891 | else 892 | return(-G[i] > Gmax2); 893 | } 894 | else if(is_lower_bound(i)) 895 | { 896 | if(y[i]==+1) 897 | return(G[i] > Gmax2); 898 | else 899 | return(G[i] > Gmax1); 900 | } 901 | else 902 | return(false); 903 | } 904 | 905 | void Solver::do_shrinking() 906 | { 907 | int i; 908 | double Gmax1 = -INF; // max { -y_i * grad(f)_i | i in I_up(\alpha) } 909 | double Gmax2 = -INF; // max { y_i * grad(f)_i | i in I_low(\alpha) } 910 | 911 | // find maximal violating pair first 912 | for(i=0;i= Gmax1) 919 | Gmax1 = -G[i]; 920 | } 921 | if(!is_lower_bound(i)) 922 | { 923 | if(G[i] >= Gmax2) 924 | Gmax2 = G[i]; 925 | } 926 | } 927 | else 928 | { 929 | if(!is_upper_bound(i)) 930 | { 931 | if(-G[i] >= Gmax2) 932 | Gmax2 = -G[i]; 933 | } 934 | if(!is_lower_bound(i)) 935 | { 936 | if(G[i] >= Gmax1) 937 | Gmax1 = G[i]; 938 | } 939 | } 940 | } 941 | 942 | if(unshrink == false && Gmax1 + Gmax2 <= eps*10) 943 | { 944 | unshrink = true; 945 | reconstruct_gradient(); 946 | active_size = l; 947 | info("*"); 948 | } 949 | 950 | for(i=0;i i) 955 | { 956 | if (!be_shrunk(active_size, Gmax1, Gmax2)) 957 | { 958 | swap_index(i,active_size); 959 | break; 960 | } 961 | active_size--; 962 | } 963 | } 964 | } 965 | 966 | double Solver::calculate_rho() 967 | { 968 | double r; 969 | int nr_free = 0; 970 | double ub = INF, lb = -INF, sum_free = 0; 971 | for(int i=0;i0) 997 | r = sum_free/nr_free; 998 | else 999 | r = (ub+lb)/2; 1000 | 1001 | return r; 1002 | } 1003 | 1004 | // 1005 | // Solver for nu-svm classification and regression 1006 | // 1007 | // additional constraint: e^T \alpha = constant 1008 | // 1009 | class Solver_NU: public Solver 1010 | { 1011 | public: 1012 | Solver_NU() {} 1013 | void Solve(int l, const QMatrix& Q, const double *p, const schar *y, 1014 | double *alpha, double Cp, double Cn, double eps, 1015 | SolutionInfo* si, int shrinking) 1016 | { 1017 | this->si = si; 1018 | Solver::Solve(l,Q,p,y,alpha,Cp,Cn,eps,si,shrinking); 1019 | } 1020 | private: 1021 | SolutionInfo *si; 1022 | int select_working_set(int &i, int &j); 1023 | double calculate_rho(); 1024 | bool be_shrunk(int i, double Gmax1, double Gmax2, double Gmax3, double Gmax4); 1025 | void do_shrinking(); 1026 | }; 1027 | 1028 | // return 1 if already optimal, return 0 otherwise 1029 | int Solver_NU::select_working_set(int &out_i, int &out_j) 1030 | { 1031 | // return i,j such that y_i = y_j and 1032 | // i: maximizes -y_i * grad(f)_i, i in I_up(\alpha) 1033 | // j: minimizes the decrease of obj value 1034 | // (if quadratic coefficeint <= 0, replace it with tau) 1035 | // -y_j*grad(f)_j < -y_i*grad(f)_i, j in I_low(\alpha) 1036 | 1037 | double Gmaxp = -INF; 1038 | double Gmaxp2 = -INF; 1039 | int Gmaxp_idx = -1; 1040 | 1041 | double Gmaxn = -INF; 1042 | double Gmaxn2 = -INF; 1043 | int Gmaxn_idx = -1; 1044 | 1045 | int Gmin_idx = -1; 1046 | double obj_diff_min = INF; 1047 | 1048 | for(int t=0;t= Gmaxp) 1053 | { 1054 | Gmaxp = -G[t]; 1055 | Gmaxp_idx = t; 1056 | } 1057 | } 1058 | else 1059 | { 1060 | if(!is_lower_bound(t)) 1061 | if(G[t] >= Gmaxn) 1062 | { 1063 | Gmaxn = G[t]; 1064 | Gmaxn_idx = t; 1065 | } 1066 | } 1067 | 1068 | int ip = Gmaxp_idx; 1069 | int in = Gmaxn_idx; 1070 | const Qfloat *Q_ip = NULL; 1071 | const Qfloat *Q_in = NULL; 1072 | if(ip != -1) // NULL Q_ip not accessed: Gmaxp=-INF if ip=-1 1073 | Q_ip = Q->get_Q(ip,active_size); 1074 | if(in != -1) 1075 | Q_in = Q->get_Q(in,active_size); 1076 | 1077 | for(int j=0;j= Gmaxp2) 1085 | Gmaxp2 = G[j]; 1086 | if (grad_diff > 0) 1087 | { 1088 | double obj_diff; 1089 | double quad_coef = QD[ip]+QD[j]-2*Q_ip[j]; 1090 | if (quad_coef > 0) 1091 | obj_diff = -(grad_diff*grad_diff)/quad_coef; 1092 | else 1093 | obj_diff = -(grad_diff*grad_diff)/TAU; 1094 | 1095 | if (obj_diff <= obj_diff_min) 1096 | { 1097 | Gmin_idx=j; 1098 | obj_diff_min = obj_diff; 1099 | } 1100 | } 1101 | } 1102 | } 1103 | else 1104 | { 1105 | if (!is_upper_bound(j)) 1106 | { 1107 | double grad_diff=Gmaxn-G[j]; 1108 | if (-G[j] >= Gmaxn2) 1109 | Gmaxn2 = -G[j]; 1110 | if (grad_diff > 0) 1111 | { 1112 | double obj_diff; 1113 | double quad_coef = QD[in]+QD[j]-2*Q_in[j]; 1114 | if (quad_coef > 0) 1115 | obj_diff = -(grad_diff*grad_diff)/quad_coef; 1116 | else 1117 | obj_diff = -(grad_diff*grad_diff)/TAU; 1118 | 1119 | if (obj_diff <= obj_diff_min) 1120 | { 1121 | Gmin_idx=j; 1122 | obj_diff_min = obj_diff; 1123 | } 1124 | } 1125 | } 1126 | } 1127 | } 1128 | 1129 | if(max(Gmaxp+Gmaxp2,Gmaxn+Gmaxn2) < eps || Gmin_idx == -1) 1130 | return 1; 1131 | 1132 | if (y[Gmin_idx] == +1) 1133 | out_i = Gmaxp_idx; 1134 | else 1135 | out_i = Gmaxn_idx; 1136 | out_j = Gmin_idx; 1137 | 1138 | return 0; 1139 | } 1140 | 1141 | bool Solver_NU::be_shrunk(int i, double Gmax1, double Gmax2, double Gmax3, double Gmax4) 1142 | { 1143 | if(is_upper_bound(i)) 1144 | { 1145 | if(y[i]==+1) 1146 | return(-G[i] > Gmax1); 1147 | else 1148 | return(-G[i] > Gmax4); 1149 | } 1150 | else if(is_lower_bound(i)) 1151 | { 1152 | if(y[i]==+1) 1153 | return(G[i] > Gmax2); 1154 | else 1155 | return(G[i] > Gmax3); 1156 | } 1157 | else 1158 | return(false); 1159 | } 1160 | 1161 | void Solver_NU::do_shrinking() 1162 | { 1163 | double Gmax1 = -INF; // max { -y_i * grad(f)_i | y_i = +1, i in I_up(\alpha) } 1164 | double Gmax2 = -INF; // max { y_i * grad(f)_i | y_i = +1, i in I_low(\alpha) } 1165 | double Gmax3 = -INF; // max { -y_i * grad(f)_i | y_i = -1, i in I_up(\alpha) } 1166 | double Gmax4 = -INF; // max { y_i * grad(f)_i | y_i = -1, i in I_low(\alpha) } 1167 | 1168 | // find maximal violating pair first 1169 | int i; 1170 | for(i=0;i Gmax1) Gmax1 = -G[i]; 1177 | } 1178 | else if(-G[i] > Gmax4) Gmax4 = -G[i]; 1179 | } 1180 | if(!is_lower_bound(i)) 1181 | { 1182 | if(y[i]==+1) 1183 | { 1184 | if(G[i] > Gmax2) Gmax2 = G[i]; 1185 | } 1186 | else if(G[i] > Gmax3) Gmax3 = G[i]; 1187 | } 1188 | } 1189 | 1190 | if(unshrink == false && max(Gmax1+Gmax2,Gmax3+Gmax4) <= eps*10) 1191 | { 1192 | unshrink = true; 1193 | reconstruct_gradient(); 1194 | active_size = l; 1195 | } 1196 | 1197 | for(i=0;i i) 1202 | { 1203 | if (!be_shrunk(active_size, Gmax1, Gmax2, Gmax3, Gmax4)) 1204 | { 1205 | swap_index(i,active_size); 1206 | break; 1207 | } 1208 | active_size--; 1209 | } 1210 | } 1211 | } 1212 | 1213 | double Solver_NU::calculate_rho() 1214 | { 1215 | int nr_free1 = 0,nr_free2 = 0; 1216 | double ub1 = INF, ub2 = INF; 1217 | double lb1 = -INF, lb2 = -INF; 1218 | double sum_free1 = 0, sum_free2 = 0; 1219 | 1220 | for(int i=0;i 0) 1250 | r1 = sum_free1/nr_free1; 1251 | else 1252 | r1 = (ub1+lb1)/2; 1253 | 1254 | if(nr_free2 > 0) 1255 | r2 = sum_free2/nr_free2; 1256 | else 1257 | r2 = (ub2+lb2)/2; 1258 | 1259 | si->r = (r1+r2)/2; 1260 | return (r1-r2)/2; 1261 | } 1262 | 1263 | // 1264 | // Q matrices for various formulations 1265 | // 1266 | class SVC_Q: public Kernel 1267 | { 1268 | public: 1269 | SVC_Q(const svm_problem& prob, const svm_parameter& param, const schar *y_) 1270 | :Kernel(prob.l, prob.x, param) 1271 | { 1272 | clone(y,y_,prob.l); 1273 | cache = new Cache(prob.l,(long int)(param.cache_size*(1<<20))); 1274 | QD = new double[prob.l]; 1275 | for(int i=0;i*kernel_function)(i,i); 1277 | } 1278 | 1279 | Qfloat *get_Q(int i, int len) const 1280 | { 1281 | Qfloat *data; 1282 | int start, j; 1283 | if((start = cache->get_data(i,&data,len)) < len) 1284 | { 1285 | for(j=start;j*kernel_function)(i,j)); 1287 | } 1288 | return data; 1289 | } 1290 | 1291 | double *get_QD() const 1292 | { 1293 | return QD; 1294 | } 1295 | 1296 | void swap_index(int i, int j) const 1297 | { 1298 | cache->swap_index(i,j); 1299 | Kernel::swap_index(i,j); 1300 | swap(y[i],y[j]); 1301 | swap(QD[i],QD[j]); 1302 | } 1303 | 1304 | ~SVC_Q() 1305 | { 1306 | delete[] y; 1307 | delete cache; 1308 | delete[] QD; 1309 | } 1310 | private: 1311 | schar *y; 1312 | Cache *cache; 1313 | double *QD; 1314 | }; 1315 | 1316 | class ONE_CLASS_Q: public Kernel 1317 | { 1318 | public: 1319 | ONE_CLASS_Q(const svm_problem& prob, const svm_parameter& param) 1320 | :Kernel(prob.l, prob.x, param) 1321 | { 1322 | cache = new Cache(prob.l,(long int)(param.cache_size*(1<<20))); 1323 | QD = new double[prob.l]; 1324 | for(int i=0;i*kernel_function)(i,i); 1326 | } 1327 | 1328 | Qfloat *get_Q(int i, int len) const 1329 | { 1330 | Qfloat *data; 1331 | int start, j; 1332 | if((start = cache->get_data(i,&data,len)) < len) 1333 | { 1334 | for(j=start;j*kernel_function)(i,j); 1336 | } 1337 | return data; 1338 | } 1339 | 1340 | double *get_QD() const 1341 | { 1342 | return QD; 1343 | } 1344 | 1345 | void swap_index(int i, int j) const 1346 | { 1347 | cache->swap_index(i,j); 1348 | Kernel::swap_index(i,j); 1349 | swap(QD[i],QD[j]); 1350 | } 1351 | 1352 | ~ONE_CLASS_Q() 1353 | { 1354 | delete cache; 1355 | delete[] QD; 1356 | } 1357 | private: 1358 | Cache *cache; 1359 | double *QD; 1360 | }; 1361 | 1362 | class SVR_Q: public Kernel 1363 | { 1364 | public: 1365 | SVR_Q(const svm_problem& prob, const svm_parameter& param) 1366 | :Kernel(prob.l, prob.x, param) 1367 | { 1368 | l = prob.l; 1369 | cache = new Cache(l,(long int)(param.cache_size*(1<<20))); 1370 | QD = new double[2*l]; 1371 | sign = new schar[2*l]; 1372 | index = new int[2*l]; 1373 | for(int k=0;k*kernel_function)(k,k); 1380 | QD[k+l] = QD[k]; 1381 | } 1382 | buffer[0] = new Qfloat[2*l]; 1383 | buffer[1] = new Qfloat[2*l]; 1384 | next_buffer = 0; 1385 | } 1386 | 1387 | void swap_index(int i, int j) const 1388 | { 1389 | swap(sign[i],sign[j]); 1390 | swap(index[i],index[j]); 1391 | swap(QD[i],QD[j]); 1392 | } 1393 | 1394 | Qfloat *get_Q(int i, int len) const 1395 | { 1396 | Qfloat *data; 1397 | int j, real_i = index[i]; 1398 | if(cache->get_data(real_i,&data,l) < l) 1399 | { 1400 | for(j=0;j*kernel_function)(real_i,j); 1402 | } 1403 | 1404 | // reorder and copy 1405 | Qfloat *buf = buffer[next_buffer]; 1406 | next_buffer = 1 - next_buffer; 1407 | schar si = sign[i]; 1408 | for(j=0;jl; 1445 | double *minus_ones = new double[l]; 1446 | schar *y = new schar[l]; 1447 | 1448 | int i; 1449 | 1450 | for(i=0;iy[i] > 0) y[i] = +1; else y[i] = -1; 1455 | } 1456 | 1457 | Solver s; 1458 | s.Solve(l, SVC_Q(*prob,*param,y), minus_ones, y, 1459 | alpha, Cp, Cn, param->eps, si, param->shrinking); 1460 | 1461 | double sum_alpha=0; 1462 | for(i=0;il)); 1467 | 1468 | for(i=0;il; 1481 | double nu = param->nu; 1482 | 1483 | schar *y = new schar[l]; 1484 | 1485 | for(i=0;iy[i]>0) 1487 | y[i] = +1; 1488 | else 1489 | y[i] = -1; 1490 | 1491 | double sum_pos = nu*l/2; 1492 | double sum_neg = nu*l/2; 1493 | 1494 | for(i=0;ieps, si, param->shrinking); 1514 | double r = si->r; 1515 | 1516 | info("C = %f\n",1/r); 1517 | 1518 | for(i=0;irho /= r; 1522 | si->obj /= (r*r); 1523 | si->upper_bound_p = 1/r; 1524 | si->upper_bound_n = 1/r; 1525 | 1526 | delete[] y; 1527 | delete[] zeros; 1528 | } 1529 | 1530 | static void solve_one_class( 1531 | const svm_problem *prob, const svm_parameter *param, 1532 | double *alpha, Solver::SolutionInfo* si) 1533 | { 1534 | int l = prob->l; 1535 | double *zeros = new double[l]; 1536 | schar *ones = new schar[l]; 1537 | int i; 1538 | 1539 | int n = (int)(param->nu*prob->l); // # of alpha's at upper bound 1540 | 1541 | for(i=0;il) 1544 | alpha[n] = param->nu * prob->l - n; 1545 | for(i=n+1;ieps, si, param->shrinking); 1557 | 1558 | delete[] zeros; 1559 | delete[] ones; 1560 | } 1561 | 1562 | static void solve_epsilon_svr( 1563 | const svm_problem *prob, const svm_parameter *param, 1564 | double *alpha, Solver::SolutionInfo* si) 1565 | { 1566 | int l = prob->l; 1567 | double *alpha2 = new double[2*l]; 1568 | double *linear_term = new double[2*l]; 1569 | schar *y = new schar[2*l]; 1570 | int i; 1571 | 1572 | for(i=0;ip - prob->y[i]; 1576 | y[i] = 1; 1577 | 1578 | alpha2[i+l] = 0; 1579 | linear_term[i+l] = param->p + prob->y[i]; 1580 | y[i+l] = -1; 1581 | } 1582 | 1583 | Solver s; 1584 | s.Solve(2*l, SVR_Q(*prob,*param), linear_term, y, 1585 | alpha2, param->C, param->C, param->eps, si, param->shrinking); 1586 | 1587 | double sum_alpha = 0; 1588 | for(i=0;iC*l)); 1594 | 1595 | delete[] alpha2; 1596 | delete[] linear_term; 1597 | delete[] y; 1598 | } 1599 | 1600 | static void solve_nu_svr( 1601 | const svm_problem *prob, const svm_parameter *param, 1602 | double *alpha, Solver::SolutionInfo* si) 1603 | { 1604 | int l = prob->l; 1605 | double C = param->C; 1606 | double *alpha2 = new double[2*l]; 1607 | double *linear_term = new double[2*l]; 1608 | schar *y = new schar[2*l]; 1609 | int i; 1610 | 1611 | double sum = C * param->nu * l / 2; 1612 | for(i=0;iy[i]; 1618 | y[i] = 1; 1619 | 1620 | linear_term[i+l] = prob->y[i]; 1621 | y[i+l] = -1; 1622 | } 1623 | 1624 | Solver_NU s; 1625 | s.Solve(2*l, SVR_Q(*prob,*param), linear_term, y, 1626 | alpha2, C, C, param->eps, si, param->shrinking); 1627 | 1628 | info("epsilon = %f\n",-si->r); 1629 | 1630 | for(i=0;il); 1652 | Solver::SolutionInfo si; 1653 | switch(param->svm_type) 1654 | { 1655 | case C_SVC: 1656 | solve_c_svc(prob,param,alpha,&si,Cp,Cn); 1657 | break; 1658 | case NU_SVC: 1659 | solve_nu_svc(prob,param,alpha,&si); 1660 | break; 1661 | case ONE_CLASS: 1662 | solve_one_class(prob,param,alpha,&si); 1663 | break; 1664 | case EPSILON_SVR: 1665 | solve_epsilon_svr(prob,param,alpha,&si); 1666 | break; 1667 | case NU_SVR: 1668 | solve_nu_svr(prob,param,alpha,&si); 1669 | break; 1670 | } 1671 | 1672 | info("obj = %f, rho = %f\n",si.obj,si.rho); 1673 | 1674 | // output SVs 1675 | 1676 | int nSV = 0; 1677 | int nBSV = 0; 1678 | for(int i=0;il;i++) 1679 | { 1680 | if(fabs(alpha[i]) > 0) 1681 | { 1682 | ++nSV; 1683 | if(prob->y[i] > 0) 1684 | { 1685 | if(fabs(alpha[i]) >= si.upper_bound_p) 1686 | ++nBSV; 1687 | } 1688 | else 1689 | { 1690 | if(fabs(alpha[i]) >= si.upper_bound_n) 1691 | ++nBSV; 1692 | } 1693 | } 1694 | } 1695 | 1696 | info("nSV = %d, nBSV = %d\n",nSV,nBSV); 1697 | 1698 | decision_function f; 1699 | f.alpha = alpha; 1700 | f.rho = si.rho; 1701 | return f; 1702 | } 1703 | 1704 | // Platt's binary SVM Probablistic Output: an improvement from Lin et al. 1705 | static void sigmoid_train( 1706 | int l, const double *dec_values, const double *labels, 1707 | double& A, double& B) 1708 | { 1709 | double prior1=0, prior0 = 0; 1710 | int i; 1711 | 1712 | for (i=0;i 0) prior1+=1; 1714 | else prior0+=1; 1715 | 1716 | int max_iter=100; // Maximal number of iterations 1717 | double min_step=1e-10; // Minimal step taken in line search 1718 | double sigma=1e-12; // For numerically strict PD of Hessian 1719 | double eps=1e-5; 1720 | double hiTarget=(prior1+1.0)/(prior1+2.0); 1721 | double loTarget=1/(prior0+2.0); 1722 | double *t=Malloc(double,l); 1723 | double fApB,p,q,h11,h22,h21,g1,g2,det,dA,dB,gd,stepsize; 1724 | double newA,newB,newf,d1,d2; 1725 | int iter; 1726 | 1727 | // Initial Point and Initial Fun Value 1728 | A=0.0; B=log((prior0+1.0)/(prior1+1.0)); 1729 | double fval = 0.0; 1730 | 1731 | for (i=0;i0) t[i]=hiTarget; 1734 | else t[i]=loTarget; 1735 | fApB = dec_values[i]*A+B; 1736 | if (fApB>=0) 1737 | fval += t[i]*fApB + log(1+exp(-fApB)); 1738 | else 1739 | fval += (t[i] - 1)*fApB +log(1+exp(fApB)); 1740 | } 1741 | for (iter=0;iter= 0) 1751 | { 1752 | p=exp(-fApB)/(1.0+exp(-fApB)); 1753 | q=1.0/(1.0+exp(-fApB)); 1754 | } 1755 | else 1756 | { 1757 | p=1.0/(1.0+exp(fApB)); 1758 | q=exp(fApB)/(1.0+exp(fApB)); 1759 | } 1760 | d2=p*q; 1761 | h11+=dec_values[i]*dec_values[i]*d2; 1762 | h22+=d2; 1763 | h21+=dec_values[i]*d2; 1764 | d1=t[i]-p; 1765 | g1+=dec_values[i]*d1; 1766 | g2+=d1; 1767 | } 1768 | 1769 | // Stopping Criteria 1770 | if (fabs(g1)= min_step) 1782 | { 1783 | newA = A + stepsize * dA; 1784 | newB = B + stepsize * dB; 1785 | 1786 | // New function value 1787 | newf = 0.0; 1788 | for (i=0;i= 0) 1792 | newf += t[i]*fApB + log(1+exp(-fApB)); 1793 | else 1794 | newf += (t[i] - 1)*fApB +log(1+exp(fApB)); 1795 | } 1796 | // Check sufficient decrease 1797 | if (newf=max_iter) 1814 | info("Reaching maximal iterations in two-class probability estimates\n"); 1815 | free(t); 1816 | } 1817 | 1818 | static double sigmoid_predict(double decision_value, double A, double B) 1819 | { 1820 | double fApB = decision_value*A+B; 1821 | // 1-p used later; avoid catastrophic cancellation 1822 | if (fApB >= 0) 1823 | return exp(-fApB)/(1.0+exp(-fApB)); 1824 | else 1825 | return 1.0/(1+exp(fApB)) ; 1826 | } 1827 | 1828 | // Method 2 from the multiclass_prob paper by Wu, Lin, and Weng 1829 | static void multiclass_probability(int k, double **r, double *p) 1830 | { 1831 | int t,j; 1832 | int iter = 0, max_iter=max(100,k); 1833 | double **Q=Malloc(double *,k); 1834 | double *Qp=Malloc(double,k); 1835 | double pQp, eps=0.005/k; 1836 | 1837 | for (t=0;tmax_error) 1869 | max_error=error; 1870 | } 1871 | if (max_error=max_iter) 1886 | info("Exceeds max_iter in multiclass_prob\n"); 1887 | for(t=0;tl); 1900 | double *dec_values = Malloc(double,prob->l); 1901 | 1902 | // random shuffle 1903 | for(i=0;il;i++) perm[i]=i; 1904 | for(i=0;il;i++) 1905 | { 1906 | int j = i+rand()%(prob->l-i); 1907 | swap(perm[i],perm[j]); 1908 | } 1909 | for(i=0;il/nr_fold; 1912 | int end = (i+1)*prob->l/nr_fold; 1913 | int j,k; 1914 | struct svm_problem subprob; 1915 | 1916 | subprob.l = prob->l-(end-begin); 1917 | subprob.x = Malloc(struct svm_node*,subprob.l); 1918 | subprob.y = Malloc(double,subprob.l); 1919 | 1920 | k=0; 1921 | for(j=0;jx[perm[j]]; 1924 | subprob.y[k] = prob->y[perm[j]]; 1925 | ++k; 1926 | } 1927 | for(j=end;jl;j++) 1928 | { 1929 | subprob.x[k] = prob->x[perm[j]]; 1930 | subprob.y[k] = prob->y[perm[j]]; 1931 | ++k; 1932 | } 1933 | int p_count=0,n_count=0; 1934 | for(j=0;j0) 1936 | p_count++; 1937 | else 1938 | n_count++; 1939 | 1940 | if(p_count==0 && n_count==0) 1941 | for(j=begin;j 0 && n_count == 0) 1944 | for(j=begin;j 0) 1947 | for(j=begin;jx[perm[j]],&(dec_values[perm[j]])); 1965 | // ensure +1 -1 order; reason not using CV subroutine 1966 | dec_values[perm[j]] *= submodel->label[0]; 1967 | } 1968 | svm_free_and_destroy_model(&submodel); 1969 | svm_destroy_param(&subparam); 1970 | } 1971 | free(subprob.x); 1972 | free(subprob.y); 1973 | } 1974 | sigmoid_train(prob->l,dec_values,prob->y,probA,probB); 1975 | free(dec_values); 1976 | free(perm); 1977 | } 1978 | 1979 | // Return parameter of a Laplace distribution 1980 | static double svm_svr_probability( 1981 | const svm_problem *prob, const svm_parameter *param) 1982 | { 1983 | int i; 1984 | int nr_fold = 5; 1985 | double *ymv = Malloc(double,prob->l); 1986 | double mae = 0; 1987 | 1988 | svm_parameter newparam = *param; 1989 | newparam.probability = 0; 1990 | svm_cross_validation(prob,&newparam,nr_fold,ymv); 1991 | for(i=0;il;i++) 1992 | { 1993 | ymv[i]=prob->y[i]-ymv[i]; 1994 | mae += fabs(ymv[i]); 1995 | } 1996 | mae /= prob->l; 1997 | double std=sqrt(2*mae*mae); 1998 | int count=0; 1999 | mae=0; 2000 | for(i=0;il;i++) 2001 | if (fabs(ymv[i]) > 5*std) 2002 | count=count+1; 2003 | else 2004 | mae+=fabs(ymv[i]); 2005 | mae /= (prob->l-count); 2006 | info("Prob. model for test data: target value = predicted value + z,\nz: Laplace distribution e^(-|z|/sigma)/(2sigma),sigma= %g\n",mae); 2007 | free(ymv); 2008 | return mae; 2009 | } 2010 | 2011 | 2012 | // label: label name, start: begin of each class, count: #data of classes, perm: indices to the original data 2013 | // perm, length l, must be allocated before calling this subroutine 2014 | static void svm_group_classes(const svm_problem *prob, int *nr_class_ret, int **label_ret, int **start_ret, int **count_ret, int *perm) 2015 | { 2016 | int l = prob->l; 2017 | int max_nr_class = 16; 2018 | int nr_class = 0; 2019 | int *label = Malloc(int,max_nr_class); 2020 | int *count = Malloc(int,max_nr_class); 2021 | int *data_label = Malloc(int,l); 2022 | int i; 2023 | 2024 | for(i=0;iy[i]; 2027 | int j; 2028 | for(j=0;jparam = *param; 2096 | model->free_sv = 0; // XXX 2097 | 2098 | if(param->svm_type == ONE_CLASS || 2099 | param->svm_type == EPSILON_SVR || 2100 | param->svm_type == NU_SVR) 2101 | { 2102 | // regression or one-class-svm 2103 | model->nr_class = 2; 2104 | model->label = NULL; 2105 | model->nSV = NULL; 2106 | model->probA = NULL; model->probB = NULL; 2107 | model->sv_coef = Malloc(double *,1); 2108 | 2109 | if(param->probability && 2110 | (param->svm_type == EPSILON_SVR || 2111 | param->svm_type == NU_SVR)) 2112 | { 2113 | model->probA = Malloc(double,1); 2114 | model->probA[0] = svm_svr_probability(prob,param); 2115 | } 2116 | 2117 | decision_function f = svm_train_one(prob,param,0,0); 2118 | model->rho = Malloc(double,1); 2119 | model->rho[0] = f.rho; 2120 | 2121 | int nSV = 0; 2122 | int i; 2123 | for(i=0;il;i++) 2124 | if(fabs(f.alpha[i]) > 0) ++nSV; 2125 | model->l = nSV; 2126 | model->SV = Malloc(svm_node *,nSV); 2127 | model->sv_coef[0] = Malloc(double,nSV); 2128 | model->sv_indices = Malloc(int,nSV); 2129 | int j = 0; 2130 | for(i=0;il;i++) 2131 | if(fabs(f.alpha[i]) > 0) 2132 | { 2133 | model->SV[j] = prob->x[i]; 2134 | model->sv_coef[0][j] = f.alpha[i]; 2135 | model->sv_indices[j] = i+1; 2136 | ++j; 2137 | } 2138 | 2139 | free(f.alpha); 2140 | } 2141 | else 2142 | { 2143 | // classification 2144 | int l = prob->l; 2145 | int nr_class; 2146 | int *label = NULL; 2147 | int *start = NULL; 2148 | int *count = NULL; 2149 | int *perm = Malloc(int,l); 2150 | 2151 | // group training data of the same class 2152 | svm_group_classes(prob,&nr_class,&label,&start,&count,perm); 2153 | if(nr_class == 1) 2154 | info("WARNING: training data in only one class. See README for details.\n"); 2155 | 2156 | svm_node **x = Malloc(svm_node *,l); 2157 | int i; 2158 | for(i=0;ix[perm[i]]; 2160 | 2161 | // calculate weighted C 2162 | 2163 | double *weighted_C = Malloc(double, nr_class); 2164 | for(i=0;iC; 2166 | for(i=0;inr_weight;i++) 2167 | { 2168 | int j; 2169 | for(j=0;jweight_label[i] == label[j]) 2171 | break; 2172 | if(j == nr_class) 2173 | fprintf(stderr,"WARNING: class label %d specified in weight is not found\n", param->weight_label[i]); 2174 | else 2175 | weighted_C[j] *= param->weight[i]; 2176 | } 2177 | 2178 | // train k*(k-1)/2 models 2179 | 2180 | bool *nonzero = Malloc(bool,l); 2181 | for(i=0;iprobability) 2187 | { 2188 | probA=Malloc(double,nr_class*(nr_class-1)/2); 2189 | probB=Malloc(double,nr_class*(nr_class-1)/2); 2190 | } 2191 | 2192 | int p = 0; 2193 | for(i=0;iprobability) 2215 | svm_binary_svc_probability(&sub_prob,param,weighted_C[i],weighted_C[j],probA[p],probB[p]); 2216 | 2217 | f[p] = svm_train_one(&sub_prob,param,weighted_C[i],weighted_C[j]); 2218 | for(k=0;k 0) 2220 | nonzero[si+k] = true; 2221 | for(k=0;k 0) 2223 | nonzero[sj+k] = true; 2224 | free(sub_prob.x); 2225 | free(sub_prob.y); 2226 | ++p; 2227 | } 2228 | 2229 | // build output 2230 | 2231 | model->nr_class = nr_class; 2232 | 2233 | model->label = Malloc(int,nr_class); 2234 | for(i=0;ilabel[i] = label[i]; 2236 | 2237 | model->rho = Malloc(double,nr_class*(nr_class-1)/2); 2238 | for(i=0;irho[i] = f[i].rho; 2240 | 2241 | if(param->probability) 2242 | { 2243 | model->probA = Malloc(double,nr_class*(nr_class-1)/2); 2244 | model->probB = Malloc(double,nr_class*(nr_class-1)/2); 2245 | for(i=0;iprobA[i] = probA[i]; 2248 | model->probB[i] = probB[i]; 2249 | } 2250 | } 2251 | else 2252 | { 2253 | model->probA=NULL; 2254 | model->probB=NULL; 2255 | } 2256 | 2257 | int total_sv = 0; 2258 | int *nz_count = Malloc(int,nr_class); 2259 | model->nSV = Malloc(int,nr_class); 2260 | for(i=0;inSV[i] = nSV; 2270 | nz_count[i] = nSV; 2271 | } 2272 | 2273 | info("Total nSV = %d\n",total_sv); 2274 | 2275 | model->l = total_sv; 2276 | model->SV = Malloc(svm_node *,total_sv); 2277 | model->sv_indices = Malloc(int,total_sv); 2278 | p = 0; 2279 | for(i=0;iSV[p] = x[i]; 2283 | model->sv_indices[p++] = perm[i] + 1; 2284 | } 2285 | 2286 | int *nz_start = Malloc(int,nr_class); 2287 | nz_start[0] = 0; 2288 | for(i=1;isv_coef = Malloc(double *,nr_class-1); 2292 | for(i=0;isv_coef[i] = Malloc(double,total_sv); 2294 | 2295 | p = 0; 2296 | for(i=0;isv_coef[j-1][q++] = f[p].alpha[k]; 2313 | q = nz_start[j]; 2314 | for(k=0;ksv_coef[i][q++] = f[p].alpha[ci+k]; 2317 | ++p; 2318 | } 2319 | 2320 | free(label); 2321 | free(probA); 2322 | free(probB); 2323 | free(count); 2324 | free(perm); 2325 | free(start); 2326 | free(x); 2327 | free(weighted_C); 2328 | free(nonzero); 2329 | for(i=0;il; 2344 | int *perm = Malloc(int,l); 2345 | int nr_class; 2346 | if (nr_fold > l) 2347 | { 2348 | nr_fold = l; 2349 | fprintf(stderr,"WARNING: # folds > # data. Will use # folds = # data instead (i.e., leave-one-out cross validation)\n"); 2350 | } 2351 | fold_start = Malloc(int,nr_fold+1); 2352 | // stratified cv may not give leave-one-out rate 2353 | // Each class to l folds -> some folds may have zero elements 2354 | if((param->svm_type == C_SVC || 2355 | param->svm_type == NU_SVC) && nr_fold < l) 2356 | { 2357 | int *start = NULL; 2358 | int *label = NULL; 2359 | int *count = NULL; 2360 | svm_group_classes(prob,&nr_class,&label,&start,&count,perm); 2361 | 2362 | // random shuffle and then data grouped by fold using the array perm 2363 | int *fold_count = Malloc(int,nr_fold); 2364 | int c; 2365 | int *index = Malloc(int,l); 2366 | for(i=0;ix[perm[j]]; 2430 | subprob.y[k] = prob->y[perm[j]]; 2431 | ++k; 2432 | } 2433 | for(j=end;jx[perm[j]]; 2436 | subprob.y[k] = prob->y[perm[j]]; 2437 | ++k; 2438 | } 2439 | struct svm_model *submodel = svm_train(&subprob,param); 2440 | if(param->probability && 2441 | (param->svm_type == C_SVC || param->svm_type == NU_SVC)) 2442 | { 2443 | double *prob_estimates=Malloc(double,svm_get_nr_class(submodel)); 2444 | for(j=begin;jx[perm[j]],prob_estimates); 2446 | free(prob_estimates); 2447 | } 2448 | else 2449 | for(j=begin;jx[perm[j]]); 2451 | svm_free_and_destroy_model(&submodel); 2452 | free(subprob.x); 2453 | free(subprob.y); 2454 | } 2455 | free(fold_start); 2456 | free(perm); 2457 | } 2458 | 2459 | 2460 | int svm_get_svm_type(const svm_model *model) 2461 | { 2462 | return model->param.svm_type; 2463 | } 2464 | 2465 | int svm_get_nr_class(const svm_model *model) 2466 | { 2467 | return model->nr_class; 2468 | } 2469 | 2470 | void svm_get_labels(const svm_model *model, int* label) 2471 | { 2472 | if (model->label != NULL) 2473 | for(int i=0;inr_class;i++) 2474 | label[i] = model->label[i]; 2475 | } 2476 | 2477 | void svm_get_sv_indices(const svm_model *model, int* indices) 2478 | { 2479 | if (model->sv_indices != NULL) 2480 | for(int i=0;il;i++) 2481 | indices[i] = model->sv_indices[i]; 2482 | } 2483 | 2484 | int svm_get_nr_sv(const svm_model *model) 2485 | { 2486 | return model->l; 2487 | } 2488 | 2489 | double svm_get_svr_probability(const svm_model *model) 2490 | { 2491 | if ((model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) && 2492 | model->probA!=NULL) 2493 | return model->probA[0]; 2494 | else 2495 | { 2496 | fprintf(stderr,"Model doesn't contain information for SVR probability inference\n"); 2497 | return 0; 2498 | } 2499 | } 2500 | 2501 | double svm_predict_values(const svm_model *model, const svm_node *x, double* dec_values) 2502 | { 2503 | int i; 2504 | if(model->param.svm_type == ONE_CLASS || 2505 | model->param.svm_type == EPSILON_SVR || 2506 | model->param.svm_type == NU_SVR) 2507 | { 2508 | double *sv_coef = model->sv_coef[0]; 2509 | double sum = 0; 2510 | for(i=0;il;i++) 2511 | sum += sv_coef[i] * Kernel::k_function(x,model->SV[i],model->param); 2512 | sum -= model->rho[0]; 2513 | *dec_values = sum; 2514 | 2515 | if(model->param.svm_type == ONE_CLASS) 2516 | return (sum>0)?1:-1; 2517 | else 2518 | return sum; 2519 | } 2520 | else 2521 | { 2522 | int nr_class = model->nr_class; 2523 | int l = model->l; 2524 | 2525 | double *kvalue = Malloc(double,l); 2526 | for(i=0;iSV[i],model->param); 2528 | 2529 | int *start = Malloc(int,nr_class); 2530 | start[0] = 0; 2531 | for(i=1;inSV[i-1]; 2533 | 2534 | int *vote = Malloc(int,nr_class); 2535 | for(i=0;inSV[i]; 2546 | int cj = model->nSV[j]; 2547 | 2548 | int k; 2549 | double *coef1 = model->sv_coef[j-1]; 2550 | double *coef2 = model->sv_coef[i]; 2551 | for(k=0;krho[p]; 2556 | dec_values[p] = sum; 2557 | 2558 | if(dec_values[p] > 0) 2559 | ++vote[i]; 2560 | else 2561 | ++vote[j]; 2562 | p++; 2563 | } 2564 | 2565 | int vote_max_idx = 0; 2566 | for(i=1;i vote[vote_max_idx]) 2568 | vote_max_idx = i; 2569 | 2570 | free(kvalue); 2571 | free(start); 2572 | free(vote); 2573 | return model->label[vote_max_idx]; 2574 | } 2575 | } 2576 | 2577 | double svm_predict(const svm_model *model, const svm_node *x) 2578 | { 2579 | int nr_class = model->nr_class; 2580 | double *dec_values; 2581 | if(model->param.svm_type == ONE_CLASS || 2582 | model->param.svm_type == EPSILON_SVR || 2583 | model->param.svm_type == NU_SVR) 2584 | dec_values = Malloc(double, 1); 2585 | else 2586 | dec_values = Malloc(double, nr_class*(nr_class-1)/2); 2587 | double pred_result = svm_predict_values(model, x, dec_values); 2588 | free(dec_values); 2589 | return pred_result; 2590 | } 2591 | 2592 | double svm_predict_probability( 2593 | const svm_model *model, const svm_node *x, double *prob_estimates) 2594 | { 2595 | if ((model->param.svm_type == C_SVC || model->param.svm_type == NU_SVC) && 2596 | model->probA!=NULL && model->probB!=NULL) 2597 | { 2598 | int i; 2599 | int nr_class = model->nr_class; 2600 | double *dec_values = Malloc(double, nr_class*(nr_class-1)/2); 2601 | svm_predict_values(model, x, dec_values); 2602 | 2603 | double min_prob=1e-7; 2604 | double **pairwise_prob=Malloc(double *,nr_class); 2605 | for(i=0;iprobA[k],model->probB[k]),min_prob),1-min_prob); 2612 | pairwise_prob[j][i]=1-pairwise_prob[i][j]; 2613 | k++; 2614 | } 2615 | if (nr_class == 2) 2616 | { 2617 | prob_estimates[0] = pairwise_prob[0][1]; 2618 | prob_estimates[1] = pairwise_prob[1][0]; 2619 | } 2620 | else 2621 | multiclass_probability(nr_class,pairwise_prob,prob_estimates); 2622 | 2623 | int prob_max_idx = 0; 2624 | for(i=1;i prob_estimates[prob_max_idx]) 2626 | prob_max_idx = i; 2627 | for(i=0;ilabel[prob_max_idx]; 2632 | } 2633 | else 2634 | return svm_predict(model, x); 2635 | } 2636 | 2637 | static const char *svm_type_table[] = 2638 | { 2639 | "c_svc","nu_svc","one_class","epsilon_svr","nu_svr",NULL 2640 | }; 2641 | 2642 | static const char *kernel_type_table[]= 2643 | { 2644 | "linear","polynomial","rbf","sigmoid","precomputed",NULL 2645 | }; 2646 | 2647 | int svm_save_model(const char *model_file_name, const svm_model *model) 2648 | { 2649 | FILE *fp = fopen(model_file_name,"w"); 2650 | if(fp==NULL) return -1; 2651 | 2652 | char *old_locale = setlocale(LC_ALL, NULL); 2653 | if (old_locale) { 2654 | old_locale = strdup(old_locale); 2655 | } 2656 | setlocale(LC_ALL, "C"); 2657 | 2658 | const svm_parameter& param = model->param; 2659 | 2660 | fprintf(fp,"svm_type %s\n", svm_type_table[param.svm_type]); 2661 | fprintf(fp,"kernel_type %s\n", kernel_type_table[param.kernel_type]); 2662 | 2663 | if(param.kernel_type == POLY) 2664 | fprintf(fp,"degree %d\n", param.degree); 2665 | 2666 | if(param.kernel_type == POLY || param.kernel_type == RBF || param.kernel_type == SIGMOID) 2667 | fprintf(fp,"gamma %.17g\n", param.gamma); 2668 | 2669 | if(param.kernel_type == POLY || param.kernel_type == SIGMOID) 2670 | fprintf(fp,"coef0 %.17g\n", param.coef0); 2671 | 2672 | int nr_class = model->nr_class; 2673 | int l = model->l; 2674 | fprintf(fp, "nr_class %d\n", nr_class); 2675 | fprintf(fp, "total_sv %d\n",l); 2676 | 2677 | { 2678 | fprintf(fp, "rho"); 2679 | for(int i=0;irho[i]); 2681 | fprintf(fp, "\n"); 2682 | } 2683 | 2684 | if(model->label) 2685 | { 2686 | fprintf(fp, "label"); 2687 | for(int i=0;ilabel[i]); 2689 | fprintf(fp, "\n"); 2690 | } 2691 | 2692 | if(model->probA) // regression has probA only 2693 | { 2694 | fprintf(fp, "probA"); 2695 | for(int i=0;iprobA[i]); 2697 | fprintf(fp, "\n"); 2698 | } 2699 | if(model->probB) 2700 | { 2701 | fprintf(fp, "probB"); 2702 | for(int i=0;iprobB[i]); 2704 | fprintf(fp, "\n"); 2705 | } 2706 | 2707 | if(model->nSV) 2708 | { 2709 | fprintf(fp, "nr_sv"); 2710 | for(int i=0;inSV[i]); 2712 | fprintf(fp, "\n"); 2713 | } 2714 | 2715 | fprintf(fp, "SV\n"); 2716 | const double * const *sv_coef = model->sv_coef; 2717 | const svm_node * const *SV = model->SV; 2718 | 2719 | for(int i=0;ivalue)); 2728 | else 2729 | while(p->index != -1) 2730 | { 2731 | fprintf(fp,"%d:%.8g ",p->index,p->value); 2732 | p++; 2733 | } 2734 | fprintf(fp, "\n"); 2735 | } 2736 | 2737 | setlocale(LC_ALL, old_locale); 2738 | free(old_locale); 2739 | 2740 | if (ferror(fp) != 0 || fclose(fp) != 0) return -1; 2741 | else return 0; 2742 | } 2743 | 2744 | static char *line = NULL; 2745 | static int max_line_len; 2746 | 2747 | static char* readline(FILE *input) 2748 | { 2749 | int len; 2750 | 2751 | if(fgets(line,max_line_len,input) == NULL) 2752 | return NULL; 2753 | 2754 | while(strrchr(line,'\n') == NULL) 2755 | { 2756 | max_line_len *= 2; 2757 | line = (char *) realloc(line,max_line_len); 2758 | len = (int) strlen(line); 2759 | if(fgets(line+len,max_line_len-len,input) == NULL) 2760 | break; 2761 | } 2762 | return line; 2763 | } 2764 | 2765 | // 2766 | // FSCANF helps to handle fscanf failures. 2767 | // Its do-while block avoids the ambiguity when 2768 | // if (...) 2769 | // FSCANF(); 2770 | // is used 2771 | // 2772 | #define FSCANF(_stream, _format, _var) do{ if (fscanf(_stream, _format, _var) != 1) return false; }while(0) 2773 | bool read_model_header(FILE *fp, svm_model* model) 2774 | { 2775 | svm_parameter& param = model->param; 2776 | // parameters for training only won't be assigned, but arrays are assigned as NULL for safety 2777 | param.nr_weight = 0; 2778 | param.weight_label = NULL; 2779 | param.weight = NULL; 2780 | 2781 | char cmd[81]; 2782 | while(1) 2783 | { 2784 | FSCANF(fp,"%80s",cmd); 2785 | 2786 | if(strcmp(cmd,"svm_type")==0) 2787 | { 2788 | FSCANF(fp,"%80s",cmd); 2789 | int i; 2790 | for(i=0;svm_type_table[i];i++) 2791 | { 2792 | if(strcmp(svm_type_table[i],cmd)==0) 2793 | { 2794 | param.svm_type=i; 2795 | break; 2796 | } 2797 | } 2798 | if(svm_type_table[i] == NULL) 2799 | { 2800 | fprintf(stderr,"unknown svm type.\n"); 2801 | return false; 2802 | } 2803 | } 2804 | else if(strcmp(cmd,"kernel_type")==0) 2805 | { 2806 | FSCANF(fp,"%80s",cmd); 2807 | int i; 2808 | for(i=0;kernel_type_table[i];i++) 2809 | { 2810 | if(strcmp(kernel_type_table[i],cmd)==0) 2811 | { 2812 | param.kernel_type=i; 2813 | break; 2814 | } 2815 | } 2816 | if(kernel_type_table[i] == NULL) 2817 | { 2818 | fprintf(stderr,"unknown kernel function.\n"); 2819 | return false; 2820 | } 2821 | } 2822 | else if(strcmp(cmd,"degree")==0) 2823 | FSCANF(fp,"%d",¶m.degree); 2824 | else if(strcmp(cmd,"gamma")==0) 2825 | FSCANF(fp,"%lf",¶m.gamma); 2826 | else if(strcmp(cmd,"coef0")==0) 2827 | FSCANF(fp,"%lf",¶m.coef0); 2828 | else if(strcmp(cmd,"nr_class")==0) 2829 | FSCANF(fp,"%d",&model->nr_class); 2830 | else if(strcmp(cmd,"total_sv")==0) 2831 | FSCANF(fp,"%d",&model->l); 2832 | else if(strcmp(cmd,"rho")==0) 2833 | { 2834 | int n = model->nr_class * (model->nr_class-1)/2; 2835 | model->rho = Malloc(double,n); 2836 | for(int i=0;irho[i]); 2838 | } 2839 | else if(strcmp(cmd,"label")==0) 2840 | { 2841 | int n = model->nr_class; 2842 | model->label = Malloc(int,n); 2843 | for(int i=0;ilabel[i]); 2845 | } 2846 | else if(strcmp(cmd,"probA")==0) 2847 | { 2848 | int n = model->nr_class * (model->nr_class-1)/2; 2849 | model->probA = Malloc(double,n); 2850 | for(int i=0;iprobA[i]); 2852 | } 2853 | else if(strcmp(cmd,"probB")==0) 2854 | { 2855 | int n = model->nr_class * (model->nr_class-1)/2; 2856 | model->probB = Malloc(double,n); 2857 | for(int i=0;iprobB[i]); 2859 | } 2860 | else if(strcmp(cmd,"nr_sv")==0) 2861 | { 2862 | int n = model->nr_class; 2863 | model->nSV = Malloc(int,n); 2864 | for(int i=0;inSV[i]); 2866 | } 2867 | else if(strcmp(cmd,"SV")==0) 2868 | { 2869 | while(1) 2870 | { 2871 | int c = getc(fp); 2872 | if(c==EOF || c=='\n') break; 2873 | } 2874 | break; 2875 | } 2876 | else 2877 | { 2878 | fprintf(stderr,"unknown text in model file: [%s]\n",cmd); 2879 | return false; 2880 | } 2881 | } 2882 | 2883 | return true; 2884 | 2885 | } 2886 | 2887 | svm_model *svm_load_model(const char *model_file_name) 2888 | { 2889 | FILE *fp = fopen(model_file_name,"rb"); 2890 | if(fp==NULL) return NULL; 2891 | 2892 | char *old_locale = setlocale(LC_ALL, NULL); 2893 | if (old_locale) { 2894 | old_locale = strdup(old_locale); 2895 | } 2896 | setlocale(LC_ALL, "C"); 2897 | 2898 | // read parameters 2899 | 2900 | svm_model *model = Malloc(svm_model,1); 2901 | model->rho = NULL; 2902 | model->probA = NULL; 2903 | model->probB = NULL; 2904 | model->sv_indices = NULL; 2905 | model->label = NULL; 2906 | model->nSV = NULL; 2907 | 2908 | // read header 2909 | if (!read_model_header(fp, model)) 2910 | { 2911 | fprintf(stderr, "ERROR: fscanf failed to read model\n"); 2912 | setlocale(LC_ALL, old_locale); 2913 | free(old_locale); 2914 | free(model->rho); 2915 | free(model->label); 2916 | free(model->nSV); 2917 | free(model); 2918 | return NULL; 2919 | } 2920 | 2921 | // read sv_coef and SV 2922 | 2923 | int elements = 0; 2924 | long pos = ftell(fp); 2925 | 2926 | max_line_len = 1024; 2927 | line = Malloc(char,max_line_len); 2928 | char *p,*endptr,*idx,*val; 2929 | 2930 | while(readline(fp)!=NULL) 2931 | { 2932 | p = strtok(line,":"); 2933 | while(1) 2934 | { 2935 | p = strtok(NULL,":"); 2936 | if(p == NULL) 2937 | break; 2938 | ++elements; 2939 | } 2940 | } 2941 | elements += model->l; 2942 | 2943 | fseek(fp,pos,SEEK_SET); 2944 | 2945 | int m = model->nr_class - 1; 2946 | int l = model->l; 2947 | model->sv_coef = Malloc(double *,m); 2948 | int i; 2949 | for(i=0;isv_coef[i] = Malloc(double,l); 2951 | model->SV = Malloc(svm_node*,l); 2952 | svm_node *x_space = NULL; 2953 | if(l>0) x_space = Malloc(svm_node,elements); 2954 | 2955 | int j=0; 2956 | for(i=0;iSV[i] = &x_space[j]; 2960 | 2961 | p = strtok(line, " \t"); 2962 | model->sv_coef[0][i] = strtod(p,&endptr); 2963 | for(int k=1;ksv_coef[k][i] = strtod(p,&endptr); 2967 | } 2968 | 2969 | while(1) 2970 | { 2971 | idx = strtok(NULL, ":"); 2972 | val = strtok(NULL, " \t"); 2973 | 2974 | if(val == NULL) 2975 | break; 2976 | x_space[j].index = (int) strtol(idx,&endptr,10); 2977 | x_space[j].value = strtod(val,&endptr); 2978 | 2979 | ++j; 2980 | } 2981 | x_space[j++].index = -1; 2982 | } 2983 | free(line); 2984 | 2985 | setlocale(LC_ALL, old_locale); 2986 | free(old_locale); 2987 | 2988 | if (ferror(fp) != 0 || fclose(fp) != 0) 2989 | return NULL; 2990 | 2991 | model->free_sv = 1; // XXX 2992 | return model; 2993 | } 2994 | 2995 | void svm_free_model_content(svm_model* model_ptr) 2996 | { 2997 | if(model_ptr->free_sv && model_ptr->l > 0 && model_ptr->SV != NULL) 2998 | free((void *)(model_ptr->SV[0])); 2999 | if(model_ptr->sv_coef) 3000 | { 3001 | for(int i=0;inr_class-1;i++) 3002 | free(model_ptr->sv_coef[i]); 3003 | } 3004 | 3005 | free(model_ptr->SV); 3006 | model_ptr->SV = NULL; 3007 | 3008 | free(model_ptr->sv_coef); 3009 | model_ptr->sv_coef = NULL; 3010 | 3011 | free(model_ptr->rho); 3012 | model_ptr->rho = NULL; 3013 | 3014 | free(model_ptr->label); 3015 | model_ptr->label= NULL; 3016 | 3017 | free(model_ptr->probA); 3018 | model_ptr->probA = NULL; 3019 | 3020 | free(model_ptr->probB); 3021 | model_ptr->probB= NULL; 3022 | 3023 | free(model_ptr->sv_indices); 3024 | model_ptr->sv_indices = NULL; 3025 | 3026 | free(model_ptr->nSV); 3027 | model_ptr->nSV = NULL; 3028 | } 3029 | 3030 | void svm_free_and_destroy_model(svm_model** model_ptr_ptr) 3031 | { 3032 | if(model_ptr_ptr != NULL && *model_ptr_ptr != NULL) 3033 | { 3034 | svm_free_model_content(*model_ptr_ptr); 3035 | free(*model_ptr_ptr); 3036 | *model_ptr_ptr = NULL; 3037 | } 3038 | } 3039 | 3040 | void svm_destroy_param(svm_parameter* param) 3041 | { 3042 | free(param->weight_label); 3043 | free(param->weight); 3044 | } 3045 | 3046 | const char *svm_check_parameter(const svm_problem *prob, const svm_parameter *param) 3047 | { 3048 | // svm_type 3049 | 3050 | int svm_type = param->svm_type; 3051 | if(svm_type != C_SVC && 3052 | svm_type != NU_SVC && 3053 | svm_type != ONE_CLASS && 3054 | svm_type != EPSILON_SVR && 3055 | svm_type != NU_SVR) 3056 | return "unknown svm type"; 3057 | 3058 | // kernel_type, degree 3059 | 3060 | int kernel_type = param->kernel_type; 3061 | if(kernel_type != LINEAR && 3062 | kernel_type != POLY && 3063 | kernel_type != RBF && 3064 | kernel_type != SIGMOID && 3065 | kernel_type != PRECOMPUTED) 3066 | return "unknown kernel type"; 3067 | 3068 | if(param->gamma < 0) 3069 | return "gamma < 0"; 3070 | 3071 | if(param->degree < 0) 3072 | return "degree of polynomial kernel < 0"; 3073 | 3074 | // cache_size,eps,C,nu,p,shrinking 3075 | 3076 | if(param->cache_size <= 0) 3077 | return "cache_size <= 0"; 3078 | 3079 | if(param->eps <= 0) 3080 | return "eps <= 0"; 3081 | 3082 | if(svm_type == C_SVC || 3083 | svm_type == EPSILON_SVR || 3084 | svm_type == NU_SVR) 3085 | if(param->C <= 0) 3086 | return "C <= 0"; 3087 | 3088 | if(svm_type == NU_SVC || 3089 | svm_type == ONE_CLASS || 3090 | svm_type == NU_SVR) 3091 | if(param->nu <= 0 || param->nu > 1) 3092 | return "nu <= 0 or nu > 1"; 3093 | 3094 | if(svm_type == EPSILON_SVR) 3095 | if(param->p < 0) 3096 | return "p < 0"; 3097 | 3098 | if(param->shrinking != 0 && 3099 | param->shrinking != 1) 3100 | return "shrinking != 0 and shrinking != 1"; 3101 | 3102 | if(param->probability != 0 && 3103 | param->probability != 1) 3104 | return "probability != 0 and probability != 1"; 3105 | 3106 | if(param->probability == 1 && 3107 | svm_type == ONE_CLASS) 3108 | return "one-class SVM probability output not supported yet"; 3109 | 3110 | 3111 | // check whether nu-svc is feasible 3112 | 3113 | if(svm_type == NU_SVC) 3114 | { 3115 | int l = prob->l; 3116 | int max_nr_class = 16; 3117 | int nr_class = 0; 3118 | int *label = Malloc(int,max_nr_class); 3119 | int *count = Malloc(int,max_nr_class); 3120 | 3121 | int i; 3122 | for(i=0;iy[i]; 3125 | int j; 3126 | for(j=0;jnu*(n1+n2)/2 > min(n1,n2)) 3153 | { 3154 | free(label); 3155 | free(count); 3156 | return "specified nu is infeasible"; 3157 | } 3158 | } 3159 | } 3160 | free(label); 3161 | free(count); 3162 | } 3163 | 3164 | return NULL; 3165 | } 3166 | 3167 | int svm_check_probability_model(const svm_model *model) 3168 | { 3169 | return ((model->param.svm_type == C_SVC || model->param.svm_type == NU_SVC) && 3170 | model->probA!=NULL && model->probB!=NULL) || 3171 | ((model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) && 3172 | model->probA!=NULL); 3173 | } 3174 | 3175 | void svm_set_print_string_function(void (*print_func)(const char *)) 3176 | { 3177 | if(print_func == NULL) 3178 | svm_print_string = &print_string_stdout; 3179 | else 3180 | svm_print_string = print_func; 3181 | } 3182 | -------------------------------------------------------------------------------- /libsvm/svm.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIBSVM_H 2 | #define _LIBSVM_H 3 | 4 | #define LIBSVM_VERSION 323 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | extern int libsvm_version; 11 | 12 | struct svm_node 13 | { 14 | int index; 15 | double value; 16 | }; 17 | 18 | struct svm_problem 19 | { 20 | int l; 21 | double *y; 22 | struct svm_node **x; 23 | }; 24 | 25 | enum { C_SVC, NU_SVC, ONE_CLASS, EPSILON_SVR, NU_SVR }; /* svm_type */ 26 | enum { LINEAR, POLY, RBF, SIGMOID, PRECOMPUTED }; /* kernel_type */ 27 | 28 | struct svm_parameter 29 | { 30 | int svm_type; 31 | int kernel_type; 32 | int degree; /* for poly */ 33 | double gamma; /* for poly/rbf/sigmoid */ 34 | double coef0; /* for poly/sigmoid */ 35 | 36 | /* these are for training only */ 37 | double cache_size; /* in MB */ 38 | double eps; /* stopping criteria */ 39 | double C; /* for C_SVC, EPSILON_SVR and NU_SVR */ 40 | int nr_weight; /* for C_SVC */ 41 | int *weight_label; /* for C_SVC */ 42 | double* weight; /* for C_SVC */ 43 | double nu; /* for NU_SVC, ONE_CLASS, and NU_SVR */ 44 | double p; /* for EPSILON_SVR */ 45 | int shrinking; /* use the shrinking heuristics */ 46 | int probability; /* do probability estimates */ 47 | }; 48 | 49 | // 50 | // svm_model 51 | // 52 | struct svm_model 53 | { 54 | struct svm_parameter param; /* parameter */ 55 | int nr_class; /* number of classes, = 2 in regression/one class svm */ 56 | int l; /* total #SV */ 57 | struct svm_node **SV; /* SVs (SV[l]) */ 58 | double **sv_coef; /* coefficients for SVs in decision functions (sv_coef[k-1][l]) */ 59 | double *rho; /* constants in decision functions (rho[k*(k-1)/2]) */ 60 | double *probA; /* pariwise probability information */ 61 | double *probB; 62 | int *sv_indices; /* sv_indices[0,...,nSV-1] are values in [1,...,num_traning_data] to indicate SVs in the training set */ 63 | 64 | /* for classification only */ 65 | 66 | int *label; /* label of each class (label[k]) */ 67 | int *nSV; /* number of SVs for each class (nSV[k]) */ 68 | /* nSV[0] + nSV[1] + ... + nSV[k-1] = l */ 69 | /* XXX */ 70 | int free_sv; /* 1 if svm_model is created by svm_load_model*/ 71 | /* 0 if svm_model is created by svm_train */ 72 | }; 73 | 74 | struct svm_model *svm_train(const struct svm_problem *prob, const struct svm_parameter *param); 75 | void svm_cross_validation(const struct svm_problem *prob, const struct svm_parameter *param, int nr_fold, double *target); 76 | 77 | int svm_save_model(const char *model_file_name, const struct svm_model *model); 78 | struct svm_model *svm_load_model(const char *model_file_name); 79 | 80 | int svm_get_svm_type(const struct svm_model *model); 81 | int svm_get_nr_class(const struct svm_model *model); 82 | void svm_get_labels(const struct svm_model *model, int *label); 83 | void svm_get_sv_indices(const struct svm_model *model, int *sv_indices); 84 | int svm_get_nr_sv(const struct svm_model *model); 85 | double svm_get_svr_probability(const struct svm_model *model); 86 | 87 | double svm_predict_values(const struct svm_model *model, const struct svm_node *x, double* dec_values); 88 | double svm_predict(const struct svm_model *model, const struct svm_node *x); 89 | double svm_predict_probability(const struct svm_model *model, const struct svm_node *x, double* prob_estimates); 90 | 91 | void svm_free_model_content(struct svm_model *model_ptr); 92 | void svm_free_and_destroy_model(struct svm_model **model_ptr_ptr); 93 | void svm_destroy_param(struct svm_parameter *param); 94 | 95 | const char *svm_check_parameter(const struct svm_problem *prob, const struct svm_parameter *param); 96 | int svm_check_probability_model(const struct svm_model *model); 97 | 98 | void svm_set_print_string_function(void (*print_func)(const char *)); 99 | 100 | #ifdef __cplusplus 101 | } 102 | #endif 103 | 104 | #endif /* _LIBSVM_H */ 105 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | svm 7 | pecl.php.net 8 | Support Vector Machine Library 9 | LibSVM is an efficient solver for SVM classification and regression problems. The svm extension wraps this in a PHP interface for easy use in PHP scripts. 10 | 11 | 12 | Ian Barber 13 | ianb 14 | ian.barber@gmail.co.uk 15 | yes 16 | 17 | 18 | Anatol Belski 19 | ab 20 | ab@php.net 21 | yes 22 | 23 | 2018-08-20 24 | 25 | 0.2.4 26 | 0.2.4 27 | 28 | 29 | beta 30 | beta 31 | 32 | BSD 3-clause 33 | 34 | - Fixed lib dir evaluation in config.m4 (Remi Collet) 35 | - Fixed phpinfo() crash (Remi Collet, Anatol) 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 7.0.0 92 | 93 | 94 | 1.4.0 95 | 96 | 97 | 98 | svm 99 | 100 | 101 | 102 | 2018-08-20 103 | 104 | 0.2.3 105 | 0.2.3 106 | 107 | 108 | beta 109 | beta 110 | 111 | BSD 3-clause 112 | 113 | - Fixed lib dir evaluation in config.m4 (Remi Collet) 114 | - Fixed phpinfo() crash (Remi Collet, Anatol) 115 | 116 | 117 | 118 | 2018-08-19 119 | 120 | 0.2.2 121 | 0.2.2 122 | 123 | 124 | beta 125 | beta 126 | 127 | BSD 3-clause 128 | 129 | - Fixed compilation with bundled libsvm through pecl 130 | 131 | 132 | 133 | 2018-08-19 134 | 135 | 0.2.1 136 | 0.2.1 137 | 138 | 139 | beta 140 | beta 141 | 142 | BSD 3-clause 143 | 144 | - Fix package.xml which was missing config.w32 145 | 146 | 147 | 148 | 2018-08-19 149 | 150 | 0.2.0 151 | 0.2.0 152 | 153 | 154 | beta 155 | beta 156 | 157 | BSD 3-clause 158 | 159 | - Fixed PHP 7 compatibility. (@echobot, Anatol) 160 | - Bundled libsvm. External libsvm is still preferred, if installed. (Anatol) 161 | 162 | 163 | 164 | 2013-04-02 165 | 166 | 167 | 0.1.10 168 | 0.1.5 169 | 170 | 171 | beta 172 | beta 173 | 174 | BSD 3-clause 175 | 176 | - Verified works with 3.14 177 | - Fixed option return format 178 | - Removed unused variable 179 | 180 | 181 | 182 | 183 | -------------------------------------------------------------------------------- /php_svm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LibSVM extension for PHP 3 | * Copyright (c) 2011, The php-svm authors 4 | * All rights reserved. 5 | 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the copyright holder nor the 14 | * names of its contributors may be used to endorse or promote products 15 | * derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL IAN BARBER BE LIABLE FOR ANY 21 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | #ifndef _PHP_SVM_H_ 29 | # define _PHP_SVM_H_ 30 | 31 | #define PHP_SVM_VERSION "0.2.4-dev" 32 | 33 | #ifdef HAVE_CONFIG_H 34 | # include "config.h" 35 | #endif 36 | 37 | #ifdef ZTS 38 | # include "TSRM.h" 39 | #endif 40 | 41 | #include "php.h" 42 | 43 | #ifdef ZTS 44 | # define SVM_G(v) TSRMG(svm_globals_id, zend_svm_globals *, v) 45 | #else 46 | # define SVM_G(v) (svm_globals.v) 47 | #endif 48 | 49 | extern zend_module_entry svm_module_entry; 50 | #define phpext_svm_ptr &svm_module_entry 51 | 52 | #endif /* _PHP_SVM_H_ */ 53 | 54 | 55 | -------------------------------------------------------------------------------- /php_svm_internal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LibSVM extension for PHP 3 | * Copyright (c) 2011, The php-svm authors 4 | * All rights reserved. 5 | 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the copyright holder nor the 14 | * names of its contributors may be used to endorse or promote products 15 | * derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL IAN BARBER BE LIABLE FOR ANY 21 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef _PHP_SVM_INTERNAL_H_ 30 | # define _PHP_SVM_INTERNAL_H_ 31 | 32 | #include 33 | 34 | typedef struct _php_svm_object { 35 | /* hold the SVM parameters */ 36 | struct svm_parameter param; 37 | 38 | /* Store the last error message here */ 39 | char last_error[512]; 40 | 41 | zend_object zo; 42 | } php_svm_object; 43 | 44 | typedef struct _php_svm_model_object { 45 | /* Hold the training data */ 46 | struct svm_node *x_space; 47 | 48 | /* hold the model generated by training, or to be used for classifying*/ 49 | struct svm_model *model; 50 | 51 | zend_object zo; 52 | } php_svm_model_object; 53 | 54 | #endif /* _PHP_SVM_INTERNAL_H_ */ 55 | 56 | 57 | -------------------------------------------------------------------------------- /svm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * LibSVM extension for PHP 3 | * Copyright (c) 2011, The php-svm authors 4 | * All rights reserved. 5 | 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * * Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of the copyright holder nor the 14 | * names of its contributors may be used to endorse or promote products 15 | * derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | * DISCLAIMED. IN NO EVENT SHALL IAN BARBER BE LIABLE FOR ANY 21 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #include "php_svm.h" 30 | #include "php_svm_internal.h" 31 | #include "php_ini.h" /* needed for 5.2 */ 32 | #include "Zend/zend_exceptions.h" 33 | #include "ext/standard/info.h" 34 | 35 | static zend_class_entry *php_svm_sc_entry; 36 | static zend_class_entry *php_svm_model_sc_entry; 37 | static zend_class_entry *php_svm_exception_sc_entry; 38 | 39 | static zend_object_handlers svm_object_handlers; 40 | static zend_object_handlers svm_model_object_handlers; 41 | 42 | #ifndef TRUE 43 | # define TRUE 1 44 | # define FALSE 0 45 | #endif 46 | 47 | #define SUCCESS 0 48 | 49 | #define SVM_MAX_LINE_SIZE 4096 50 | #define SVM_THROW(message, code) \ 51 | zend_throw_exception(php_svm_exception_sc_entry, message, code); \ 52 | return; 53 | 54 | #define SVM_ERROR_MSG_SIZE 512 55 | #define SVM_THROW_LAST_ERROR(fallback, code) \ 56 | zend_throw_exception(php_svm_exception_sc_entry, (strlen(intern->last_error) ? intern->last_error : fallback), code); \ 57 | memset(intern->last_error, 0, SVM_ERROR_MSG_SIZE); \ 58 | return; 59 | 60 | typedef enum SvmLongAttribute { 61 | SvmLongAttributeMin = 100, 62 | phpsvm_svm_type, 63 | phpsvm_kernel_type, 64 | phpsvm_degree, 65 | SvmLongAttributeMax /* Always add before this */ 66 | } SvmLongAttribute; 67 | 68 | typedef enum SvmDoubleAttribute { 69 | SvmDoubleAttributeMin = 200, 70 | phpsvm_gamma, 71 | phpsvm_nu, 72 | phpsvm_eps, 73 | phpsvm_p, 74 | phpsvm_coef0, 75 | phpsvm_C, 76 | phpsvm_cache_size, 77 | SvmDoubleAttributeMax /* Always add before this */ 78 | } SvmDoubleAttribute; 79 | 80 | typedef enum SvmBoolAttribute { 81 | SvmBoolAttributeMin = 300, 82 | phpsvm_shrinking, 83 | phpsvm_probability, 84 | SvmBoolAttributeMax /* Always add before this */ 85 | } SvmBoolAttribute; 86 | 87 | /* ---- START HELPER FUNCS ---- */ 88 | 89 | static zend_always_inline php_svm_object* php_svm_fetch_svm_object(zend_object* obj)/*{{{*/ 90 | { 91 | return (php_svm_object *)((char *)obj - XtOffsetOf(php_svm_object, zo)); 92 | }/*}}}*/ 93 | 94 | 95 | static zend_always_inline php_svm_model_object* php_svm_fetch_svm_model_object(zend_object* obj)/*{{{*/ 96 | { 97 | return (php_svm_model_object *)((char *)obj - XtOffsetOf(php_svm_model_object, zo)); 98 | }/*}}}*/ 99 | 100 | static void print_null(const char *s) {} 101 | 102 | static zend_bool php_svm_set_bool_attribute(php_svm_object *intern, SvmBoolAttribute name, zend_bool value) /*{{{*/ 103 | { 104 | if (name >= SvmBoolAttributeMax) { 105 | return FALSE; 106 | } 107 | 108 | switch (name) { 109 | case phpsvm_shrinking: 110 | intern->param.shrinking = value == TRUE ? 1 : 0; 111 | break; 112 | case phpsvm_probability: 113 | intern->param.probability = value == TRUE ? 1 : 0; 114 | break; 115 | default: 116 | return FALSE; 117 | } 118 | 119 | return TRUE; 120 | }/*}}}*/ 121 | 122 | static zend_bool php_svm_set_double_attribute(php_svm_object *intern, SvmDoubleAttribute name, double value) /*{{{*/ 123 | { 124 | if (name >= SvmDoubleAttributeMax) { 125 | return FALSE; 126 | } 127 | 128 | switch (name) { 129 | case phpsvm_gamma: 130 | intern->param.gamma = value; 131 | break; 132 | case phpsvm_nu: 133 | intern->param.nu = value; 134 | break; 135 | case phpsvm_eps: 136 | intern->param.eps = value; 137 | break; 138 | case phpsvm_cache_size: 139 | intern->param.cache_size = value; 140 | break; 141 | case phpsvm_p: 142 | intern->param.p = value; 143 | break; 144 | case phpsvm_coef0: 145 | intern->param.coef0 = value; 146 | break; 147 | case phpsvm_C: 148 | intern->param.C = value; 149 | break; 150 | default: 151 | return FALSE; 152 | } 153 | 154 | return TRUE; 155 | }/*}}}*/ 156 | 157 | static zend_bool php_svm_set_long_attribute(php_svm_object *intern, SvmLongAttribute name, zend_long value) /*{{{*/ 158 | { 159 | if (name >= SvmLongAttributeMax) { 160 | return FALSE; 161 | } 162 | 163 | switch (name) { 164 | case phpsvm_svm_type: 165 | if( value != C_SVC && 166 | value != NU_SVC && 167 | value != ONE_CLASS && 168 | value != EPSILON_SVR && 169 | value != NU_SVR ) { 170 | return FALSE; 171 | } 172 | intern->param.svm_type = (int)value; 173 | break; 174 | case phpsvm_kernel_type: 175 | if( value != LINEAR && 176 | value != POLY && 177 | value != RBF && 178 | value != SIGMOID && 179 | value != PRECOMPUTED ) { 180 | return FALSE; 181 | } 182 | intern->param.kernel_type = (int)value; 183 | break; 184 | case phpsvm_degree: 185 | intern->param.degree = (int)value; 186 | break; 187 | default: 188 | return FALSE; 189 | } 190 | 191 | return TRUE; 192 | }/*}}}*/ 193 | 194 | /** {{{ zend_bool php_svm_stream_to_array(php_svm_object *intern, php_stream *stream, zval *retval) 195 | Take a stream containing lines of SVMLight format data and convert them into a PHP array for use by the training 196 | function. 197 | */ 198 | static zend_bool php_svm_stream_to_array(php_svm_object *intern, php_stream *stream, zval *retval) 199 | { 200 | while (!php_stream_eof(stream)) { 201 | char buf[SVM_MAX_LINE_SIZE]; 202 | size_t retlen = 0; 203 | int line = 1; 204 | 205 | /* Read line by line */ 206 | if (php_stream_get_line(stream, buf, SVM_MAX_LINE_SIZE, &retlen)) { 207 | 208 | zval line_array, pz_label; 209 | char *label, *ptr, *l = NULL; 210 | 211 | ptr = buf; 212 | label = php_strtok_r(ptr, " \t", &l); 213 | 214 | if (!label) { 215 | snprintf(intern->last_error, SVM_ERROR_MSG_SIZE, "Incorrect data format on line %d", line); 216 | return FALSE; 217 | } 218 | 219 | /* The line array */ 220 | array_init(&line_array); 221 | 222 | /* The label */ 223 | ZVAL_STRING(&pz_label, label); 224 | convert_to_double(&pz_label); 225 | 226 | /* Label is the first item in the line array */ 227 | add_next_index_zval(&line_array, &pz_label); 228 | 229 | /* Read rest of the values on the line */ 230 | while (1) { 231 | char *idx, *value; 232 | zval pz_idx, pz_value; 233 | 234 | /* idx:value format */ 235 | idx = php_strtok_r(NULL, ":", &l); 236 | value = php_strtok_r(NULL, " \t", &l); 237 | 238 | if (!value) { 239 | break; 240 | } 241 | 242 | /* Make zvals and convert to correct types */ 243 | ZVAL_STRING(&pz_idx, idx); 244 | convert_to_long(&pz_idx); 245 | 246 | ZVAL_STRING(&pz_value, value); 247 | convert_to_double(&pz_value); 248 | 249 | add_index_zval(&line_array, Z_LVAL(pz_idx), &pz_value); 250 | zval_dtor(&pz_idx); 251 | } 252 | add_next_index_zval(retval, &line_array); 253 | line++; 254 | } 255 | } 256 | return TRUE; 257 | } 258 | /* }}} */ 259 | 260 | 261 | /* {{{ int _php_count_values(zval *array); 262 | For a an array of arrays, count the number of items in all subarrays. 263 | */ 264 | static int _php_count_values(zval *array) 265 | { 266 | int values = 0; 267 | zval *val; 268 | 269 | ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), val) { 270 | if (Z_TYPE_P(val) == IS_ARRAY) { 271 | values += zend_hash_num_elements(Z_ARRVAL_P(val)); 272 | } 273 | } ZEND_HASH_FOREACH_END(); 274 | 275 | 276 | return values; 277 | } 278 | /* }}} */ 279 | 280 | /* {{{ static void php_svm_free_problem(struct svm_problem *problem) { 281 | Free the generated problem. 282 | */ 283 | static void php_svm_free_problem(struct svm_problem *problem) { 284 | if (problem->x) { 285 | efree(problem->x); 286 | } 287 | 288 | if (problem->y) { 289 | efree(problem->y); 290 | } 291 | 292 | efree(problem); 293 | } 294 | /* }}} */ 295 | 296 | #define ALLOC_XSPACE(model, sz) model->x_space = (struct svm_node *)emalloc(sz * sizeof(struct svm_node)) 297 | 298 | /* {{{ static zend_bool php_svm_read_array(php_svm_object *intern, php_svm_model_object *intern_model, zval *array) 299 | Take a PHP array, and prepare libSVM problem data for training with. 300 | */ 301 | static struct svm_problem* php_svm_read_array(php_svm_object *intern, php_svm_model_object **intern_model_ptr, zval *array, zval * rzval) 302 | { 303 | zval *pzval; 304 | char *err_msg = NULL; 305 | zend_string *key; 306 | char *endptr; 307 | int i, num_labels, elements; 308 | int j = 0, max_index = 0, inst_max_index = 0; 309 | zend_ulong index; 310 | struct svm_problem *problem; 311 | //zval svm_mo; 312 | zend_object * zobj; 313 | php_svm_model_object * intern_model = NULL; 314 | 315 | /* total number of elements */ 316 | elements = _php_count_values(array); 317 | 318 | if (intern_model) 319 | { 320 | /* If reading multiple times make sure that we don't leak */ 321 | if (intern_model->x_space) { 322 | efree(intern_model->x_space); 323 | intern_model->x_space = NULL; 324 | } 325 | if (intern_model->model) { 326 | #if LIBSVM_VERSION >= 300 327 | svm_free_and_destroy_model(&intern_model->model); 328 | #else 329 | svm_destroy_model(intern_model->model); 330 | #endif 331 | 332 | intern_model->model = NULL; 333 | } 334 | } else { 335 | // create model object 336 | object_init_ex(rzval, php_svm_model_sc_entry); 337 | 338 | zobj = Z_OBJ_P(rzval); 339 | intern_model = (php_svm_model_object *)((char *)zobj - XtOffsetOf(php_svm_model_object, zo)); 340 | ALLOC_XSPACE(intern_model, elements); 341 | } 342 | 343 | 344 | /* Allocate the problem */ 345 | problem = emalloc(sizeof(struct svm_problem)); 346 | 347 | /* x and y */ 348 | num_labels = zend_hash_num_elements(HASH_OF(array)); 349 | 350 | /* Allocate space for the labels */ 351 | problem->y = emalloc(num_labels * sizeof(double)); 352 | 353 | /* allocate space for x */ 354 | problem->x = emalloc(num_labels * sizeof(struct svm_node *)); 355 | 356 | 357 | /* How many labels */ 358 | problem->l = num_labels; 359 | 360 | i = 0; 361 | /* Fill the problem */ 362 | ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), pzval) { 363 | 364 | zval *pz_label; 365 | 366 | if (Z_TYPE_P(pzval) != IS_ARRAY) { 367 | err_msg = "Data format error"; 368 | goto return_error; 369 | } 370 | 371 | if (zend_hash_num_elements(Z_ARRVAL_P(pzval)) < 2) { 372 | err_msg = "Wrong amount of nodes in the sub-array"; 373 | goto return_error; 374 | } 375 | 376 | problem->x[i] = &intern_model->x_space[j]; 377 | 378 | zend_hash_internal_pointer_reset(Z_ARRVAL_P(pzval)); 379 | 380 | if ((pz_label = zend_hash_get_current_data_ex(Z_ARRVAL_P(pzval), &(Z_ARRVAL_P(pzval))->nInternalPointer)) != NULL) { 381 | 382 | if (Z_TYPE_P(pz_label) != IS_DOUBLE) { 383 | convert_to_double(pz_label); 384 | } 385 | problem->y[i] = Z_DVAL_P(pz_label); 386 | } else { 387 | err_msg = "The sub-array contains only the label. Missing index-value pairs"; 388 | goto return_error; 389 | } 390 | 391 | while (1) { 392 | zval *pz_value; 393 | 394 | if ((zend_hash_move_forward(Z_ARRVAL_P(pzval)) == SUCCESS) && 395 | ((pz_value = zend_hash_get_current_data(Z_ARRVAL_P(pzval))) != NULL)) { 396 | 397 | if (zend_hash_get_current_key(Z_ARRVAL_P(pzval), &key, &index) == HASH_KEY_IS_STRING) { 398 | intern_model->x_space[j].index = (int) strtol(ZSTR_VAL(key), &endptr, 10); 399 | } else { 400 | intern_model->x_space[j].index = (int) index; 401 | } 402 | 403 | if (Z_TYPE_P(pz_value) != IS_DOUBLE) { 404 | convert_to_double(pz_value); 405 | } 406 | intern_model->x_space[j].value = Z_DVAL_P(pz_value); 407 | 408 | inst_max_index = intern_model->x_space[j].index; 409 | j++; 410 | } else { 411 | break; 412 | } 413 | } 414 | 415 | intern_model->x_space[j++].index = -1; 416 | 417 | if (inst_max_index > max_index) { 418 | max_index = inst_max_index; 419 | } 420 | i++; 421 | } ZEND_HASH_FOREACH_END(); 422 | 423 | if (intern->param.gamma == 0 && max_index > 0) { 424 | intern->param.gamma = 1.0/max_index; 425 | } 426 | 427 | *intern_model_ptr = intern_model; 428 | 429 | return problem; 430 | 431 | return_error: 432 | php_svm_free_problem(problem); 433 | if (err_msg) { 434 | snprintf(intern->last_error, SVM_ERROR_MSG_SIZE, "%s", err_msg); 435 | } 436 | 437 | return NULL; 438 | } 439 | /* }}} */ 440 | 441 | /* {{{ static zend_bool php_svm_train(php_svm_object *intern, php_svm_model_object *intern_model, struct svm_problem *problem) 442 | Train based on a libsvm problem structure 443 | */ 444 | static zend_bool php_svm_train(php_svm_object *intern, php_svm_model_object *intern_model, struct svm_problem *problem) 445 | { 446 | const char *err_msg = NULL; 447 | err_msg = svm_check_parameter(problem, &(intern->param)); 448 | if (err_msg) { 449 | snprintf(intern->last_error, SVM_ERROR_MSG_SIZE, "%s", err_msg); 450 | return FALSE; 451 | } 452 | 453 | intern_model->model = svm_train(problem, &(intern->param)); 454 | 455 | /* Failure ? */ 456 | if (!intern_model->model) { 457 | snprintf(intern->last_error, SVM_ERROR_MSG_SIZE, "Failed to train using the data"); 458 | return FALSE; 459 | } 460 | 461 | return TRUE; 462 | } 463 | /* }}} */ 464 | 465 | /* {{{ static zval* php_svm_get_data_from_param(php_svm_object *intern, zval *zparam) 466 | Take an incoming parameter and convert it into a PHP array of svmlight style data. 467 | */ 468 | static int php_svm_get_data_from_param(php_svm_object *intern, zval *zparam, zval ** data_ptr) 469 | { 470 | zend_bool our_stream = 0; 471 | zend_bool need_read = 1; 472 | php_stream *stream = NULL; 473 | 474 | switch (Z_TYPE_P(zparam)) { 475 | case IS_STRING: 476 | stream = php_stream_open_wrapper(Z_STRVAL_P(zparam), "r", REPORT_ERRORS, NULL); 477 | our_stream = 1; 478 | break; 479 | 480 | case IS_RESOURCE: 481 | php_stream_from_zval_no_verify(stream, zparam); 482 | our_stream = 0; 483 | break; 484 | 485 | case IS_ARRAY: 486 | our_stream = 0; 487 | need_read = 0; 488 | break; 489 | 490 | default: 491 | snprintf(intern->last_error, SVM_ERROR_MSG_SIZE, "Incorrect parameter type, expecting string, stream or an array"); 492 | return FALSE; 493 | break; 494 | } 495 | 496 | /* If we got stream then read it in */ 497 | if (need_read) { 498 | if (!stream) { 499 | snprintf(intern->last_error, SVM_ERROR_MSG_SIZE, "Failed to open the data file"); 500 | return FALSE; 501 | } 502 | 503 | if (!php_svm_stream_to_array(intern, stream, *data_ptr)) { 504 | zval_dtor(*data_ptr); 505 | efree(data_ptr); 506 | if (our_stream) { 507 | php_stream_close(stream); 508 | } 509 | snprintf(intern->last_error, SVM_ERROR_MSG_SIZE, "Failed to read the data"); 510 | return FALSE; 511 | } 512 | } else { 513 | *data_ptr = zparam; 514 | } 515 | 516 | if (our_stream) { 517 | php_stream_close(stream); 518 | } 519 | 520 | return TRUE; 521 | } 522 | /* }}} */ 523 | 524 | /* {{{ static svm_node* php_svm_get_data_from_array(zval *arr) 525 | Take an array of training data and turn it into an array of svm nodes. 526 | */ 527 | static struct svm_node* php_svm_get_data_from_array(zval* arr) 528 | { 529 | struct svm_node *x; 530 | HashTable *arr_hash; 531 | int array_count, i; 532 | char *endptr; 533 | zval temp; 534 | zend_string *key; 535 | zend_ulong num_key; 536 | zval *val; 537 | 538 | arr_hash = Z_ARRVAL_P(arr); 539 | array_count = zend_hash_num_elements(arr_hash); 540 | 541 | /* need 1 extra to indicate the end */ 542 | x = safe_emalloc((array_count + 1), sizeof(struct svm_node), 0); 543 | i = 0; 544 | 545 | /* Loop over the array in the argument and convert into svm_nodes for the prediction */ 546 | ZEND_HASH_FOREACH_KEY_VAL(arr_hash, num_key, key, val) 547 | { 548 | if (key) { 549 | x[i].index = (int) strtol(ZSTR_VAL(key), &endptr, 10); 550 | } else { 551 | x[i].index = (int) num_key; 552 | } 553 | temp = *val; 554 | zval_copy_ctor(&temp); 555 | convert_to_double(&temp); 556 | x[i].value = Z_DVAL(temp); 557 | zval_dtor(&temp); 558 | i++; 559 | } ZEND_HASH_FOREACH_END(); 560 | 561 | /* needed so the predictor knows when to end */ 562 | x[i].index = -1; 563 | 564 | return x; 565 | } 566 | /* }}} */ 567 | 568 | /* ---- END HELPER FUNCS ---- */ 569 | 570 | 571 | /* ---- START SVM ---- */ 572 | 573 | /* {{{ SVM SVM::__construct(); 574 | The constructor 575 | */ 576 | PHP_METHOD(svm, __construct) 577 | { 578 | php_svm_object *intern; 579 | 580 | if (zend_parse_parameters_none() == FAILURE) { 581 | /* Actually this should just return. Keep for BC. */ 582 | SVM_THROW("Invalid parameters passed to constructor", 154); 583 | } 584 | 585 | intern = php_svm_fetch_svm_object(Z_OBJ_P(getThis())); 586 | 587 | /* Setup the default parameters to match those in libsvm's svm_train */ 588 | php_svm_set_long_attribute(intern, phpsvm_svm_type, C_SVC); 589 | php_svm_set_long_attribute(intern, phpsvm_kernel_type, RBF); 590 | php_svm_set_long_attribute(intern, phpsvm_degree, 3); 591 | php_svm_set_double_attribute(intern, phpsvm_gamma, 0); 592 | php_svm_set_double_attribute(intern, phpsvm_coef0, 0); 593 | php_svm_set_double_attribute(intern, phpsvm_nu, 0.5); 594 | php_svm_set_double_attribute(intern, phpsvm_cache_size, 100.0); 595 | php_svm_set_double_attribute(intern, phpsvm_C, 1); 596 | php_svm_set_double_attribute(intern, phpsvm_eps, 1e-3); 597 | php_svm_set_double_attribute(intern, phpsvm_p, 0.1); 598 | php_svm_set_bool_attribute(intern, phpsvm_shrinking, TRUE); 599 | php_svm_set_bool_attribute(intern, phpsvm_probability, FALSE); 600 | return; 601 | } 602 | /* }}} */ 603 | 604 | /* {{{ array SVM::getOptions(); 605 | Get training parameters, in an array. 606 | */ 607 | PHP_METHOD(svm, getOptions) 608 | { 609 | php_svm_object *intern; 610 | 611 | if (zend_parse_parameters_none() == FAILURE) { 612 | return; 613 | } 614 | 615 | intern = php_svm_fetch_svm_object(Z_OBJ_P(getThis())); 616 | 617 | array_init(return_value); 618 | 619 | add_index_long(return_value, phpsvm_svm_type, intern->param.svm_type); 620 | add_index_long(return_value, phpsvm_kernel_type, intern->param.kernel_type); 621 | add_index_long(return_value, phpsvm_degree, intern->param.degree); 622 | add_index_long(return_value, phpsvm_coef0, intern->param.shrinking); 623 | add_index_long(return_value, phpsvm_probability, intern->param.probability == 1 ? TRUE : FALSE); 624 | add_index_long(return_value, phpsvm_shrinking, intern->param.shrinking == 1 ? TRUE : FALSE); 625 | 626 | add_index_double(return_value, phpsvm_gamma, intern->param.gamma); 627 | add_index_double(return_value, phpsvm_coef0, intern->param.coef0); 628 | add_index_double(return_value, phpsvm_nu, intern->param.nu); 629 | add_index_double(return_value, phpsvm_cache_size, intern->param.cache_size); 630 | add_index_double(return_value, phpsvm_C, intern->param.C); 631 | add_index_double(return_value, phpsvm_eps, intern->param.eps); 632 | add_index_double(return_value, phpsvm_p, intern->param.p); 633 | } 634 | /* }}} */ 635 | 636 | 637 | /* {{{ int SVM::setOptopms(array params); 638 | Takes an array of parameters and sets the training options to match them. 639 | Only used by the training functions, will not modify an existing model. 640 | */ 641 | PHP_METHOD(svm, setOptions) 642 | { 643 | HashTable *params_ht; 644 | php_svm_object *intern; 645 | zval *params, *pzval; 646 | zend_string *string_key = NULL; 647 | zend_ulong num_key; 648 | zend_bool boolTmp; 649 | 650 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", ¶ms) == FAILURE) { 651 | RETURN_FALSE; 652 | } 653 | 654 | params_ht = HASH_OF(params); 655 | 656 | if (zend_hash_num_elements(params_ht) == 0) { 657 | return; 658 | } 659 | 660 | intern = php_svm_fetch_svm_object(Z_OBJ_P(getThis())); 661 | 662 | for (zend_hash_internal_pointer_reset(params_ht); 663 | (pzval = zend_hash_get_current_data(params_ht)) != NULL; 664 | zend_hash_move_forward(params_ht)) { 665 | 666 | zval tmp_zval, *tmp_pzval; 667 | 668 | if (zend_hash_get_current_key(params_ht, &string_key, &num_key) != HASH_KEY_IS_LONG) { 669 | continue; /* Ignore the arg (TODO: throw exception?) */ 670 | } 671 | 672 | /* Make sure we don't modify the original array */ 673 | tmp_zval = *pzval; 674 | zval_copy_ctor(&tmp_zval); 675 | tmp_pzval = &tmp_zval; 676 | 677 | /* Long attribute */ 678 | if (num_key > SvmLongAttributeMin && num_key < SvmLongAttributeMax) { 679 | 680 | if (Z_TYPE_P(tmp_pzval) != IS_LONG) { 681 | convert_to_long(tmp_pzval); 682 | } 683 | 684 | if (!php_svm_set_long_attribute(intern, num_key, Z_LVAL_P(tmp_pzval))) { 685 | SVM_THROW("Failed to set the attribute", 999); 686 | } 687 | 688 | /* Double attribute */ 689 | } else if (num_key > SvmDoubleAttributeMin && num_key < SvmDoubleAttributeMax) { 690 | 691 | if (Z_TYPE_P(tmp_pzval) != IS_DOUBLE) { 692 | convert_to_double(tmp_pzval); 693 | } 694 | 695 | if (!php_svm_set_double_attribute(intern, num_key, Z_DVAL_P(tmp_pzval))) { 696 | SVM_THROW("Failed to set the attribute", 999); 697 | } 698 | /* Bool attribute */ 699 | } else if(num_key > SvmBoolAttributeMin && num_key < SvmBoolAttributeMax) { 700 | 701 | if ((Z_TYPE_P(tmp_pzval) != IS_TRUE) && (Z_TYPE_P(tmp_pzval) != IS_FALSE)) { 702 | convert_to_boolean(tmp_pzval); 703 | } 704 | 705 | boolTmp = FALSE; 706 | if(Z_TYPE_P(tmp_pzval) == IS_TRUE) { 707 | boolTmp = TRUE; 708 | } 709 | 710 | if (!php_svm_set_bool_attribute(intern, num_key, boolTmp)) { 711 | SVM_THROW("Failed to set the attribute", 999); 712 | } 713 | } else { 714 | continue; /* Ignore the arg (TODO: throw exception?) */ 715 | } 716 | 717 | tmp_pzval = NULL; 718 | } 719 | 720 | RETURN_TRUE; 721 | } 722 | /* }}} */ 723 | 724 | /* {{{ double SVM::crossvalidate(mixed string|resource|array, long folds); 725 | Cross validate a the SVM parameters on the training data for tuning parameters. Will attempt to train then classify 726 | on different segments of the training data (the total number of segments is the folds parameter). The training data 727 | can be supplied as with the train function. For SVM classification, this will we return the correct percentage, 728 | for regression the mean squared error. 729 | @throws SVMException if the data format is incorrect 730 | */ 731 | PHP_METHOD(svm, crossvalidate) 732 | { 733 | int i; 734 | int total_correct = 0; 735 | zend_long nrfolds; 736 | double total_error = 0; 737 | double sumv = 0, sumy = 0, sumvv = 0, sumyy = 0, sumvy = 0; 738 | struct svm_problem *problem; 739 | double returnval = 0.0; 740 | double *target; 741 | php_svm_object *intern; 742 | php_svm_model_object *intern_return = NULL; 743 | zval *zparam, data; 744 | zval * data_p = &data; 745 | 746 | 747 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &zparam, &nrfolds) == FAILURE) { 748 | return; 749 | } 750 | 751 | intern = php_svm_fetch_svm_object(Z_OBJ_P(getThis())); 752 | 753 | array_init(data_p); 754 | int ret = php_svm_get_data_from_param(intern, zparam, &data_p); 755 | if(ret != TRUE) { 756 | SVM_THROW_LAST_ERROR("Could not load data", 234); 757 | } 758 | 759 | intern->param.nr_weight = 0; 760 | 761 | problem = php_svm_read_array(intern, &intern_return, data_p, return_value); 762 | if(!problem) { 763 | SVM_THROW_LAST_ERROR("Cross validation failed", 1001); 764 | } 765 | 766 | target = emalloc(problem->l * sizeof(double)); 767 | svm_cross_validation(problem, &(intern->param), nrfolds, target); 768 | if(intern->param.svm_type == EPSILON_SVR || intern->param.svm_type == NU_SVR) { 769 | for(i=0;il;i++) { 770 | double y = problem->y[i]; 771 | double v = target[i]; 772 | total_error += (v-y)*(v-y); 773 | sumv += v; 774 | sumy += y; 775 | sumvv += v*v; 776 | sumyy += y*y; 777 | sumvy += v*y; 778 | } 779 | returnval = (total_error/problem->l); // return total_error divded by number of examples 780 | } else { 781 | for(i=0; il; i++) { 782 | if(target[i] == problem->y[i]) { 783 | ++total_correct; 784 | } 785 | } 786 | returnval = 1.0*total_correct/problem->l; 787 | } 788 | 789 | if (data_p != zparam) { 790 | zval_dtor(data_p); 791 | } 792 | efree(target); 793 | php_svm_free_problem(problem); 794 | 795 | RETURN_DOUBLE(returnval); 796 | } 797 | /* }}} */ 798 | 799 | /* {{{ SVMModel SVM::train(mixed string|resource|array, [array classWeights]); 800 | Train a SVM based on the SVMLight format data either in a file, an array, or in a previously opened stream. 801 | @throws SVMException if the data format is incorrect. Can optionally accept a set of weights that will 802 | be used to multiply C. Only useful for C_SVC kernels. These should be in the form array(class (int) => weight (float)) 803 | */ 804 | PHP_METHOD(svm, train) 805 | { 806 | php_svm_object *intern; 807 | php_svm_model_object *intern_return = NULL; 808 | struct svm_problem *problem; 809 | zval data; 810 | zval *zparam; 811 | zval *weights; 812 | zval *pzval; 813 | HashTable *weights_ht; 814 | int i; 815 | zend_string *key; 816 | zend_ulong index; 817 | zval * data_p = &data; 818 | 819 | zend_bool status = 0; 820 | weights = 0; 821 | 822 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|a!", &zparam, &weights) == FAILURE) { 823 | return; 824 | } 825 | 826 | intern = php_svm_fetch_svm_object(Z_OBJ_P(getThis())); 827 | 828 | if(weights && intern->param.svm_type != C_SVC) { 829 | SVM_THROW("Weights can only be supplied for C_SyVC training", 424); 830 | } 831 | array_init(data_p); 832 | int ret = php_svm_get_data_from_param(intern, zparam, &data_p); 833 | if(ret != TRUE) { 834 | zval_dtor(data_p); 835 | SVM_THROW_LAST_ERROR("Could not load data", 234); 836 | } 837 | 838 | if(weights) { 839 | weights_ht = Z_ARRVAL_P(weights); 840 | if(zend_hash_num_elements(weights_ht) > 0) { 841 | intern->param.nr_weight = zend_hash_num_elements(weights_ht); 842 | intern->param.weight_label = emalloc(intern->param.nr_weight * sizeof(int)); 843 | intern->param.weight = emalloc(intern->param.nr_weight * sizeof(double)); 844 | 845 | for (zend_hash_internal_pointer_reset(weights_ht), i = 0; 846 | (pzval = zend_hash_get_current_data(weights_ht)) != NULL; 847 | zend_hash_move_forward(weights_ht), i++) { 848 | 849 | zval tmp_zval, *tmp_pzval; 850 | 851 | if (zend_hash_get_current_key(weights_ht, &key, &index) == HASH_KEY_IS_LONG) { 852 | intern->param.weight_label[i] = (int)index; 853 | 854 | /* Make sure we don't modify the original array */ 855 | tmp_zval = *pzval; 856 | zval_copy_ctor(&tmp_zval); 857 | tmp_pzval = &tmp_zval; 858 | convert_to_double(tmp_pzval); 859 | intern->param.weight[i] = Z_DVAL_P(tmp_pzval); 860 | tmp_pzval = NULL; 861 | } 862 | } 863 | } 864 | } else { 865 | intern->param.nr_weight = 0; 866 | } 867 | 868 | problem = php_svm_read_array(intern, &intern_return, data_p, return_value); 869 | 870 | 871 | if(problem != NULL) { 872 | if (php_svm_train(intern, intern_return, problem)) { 873 | status = 1; 874 | } 875 | php_svm_free_problem(problem); 876 | } 877 | 878 | if(weights) { 879 | efree(intern->param.weight_label); 880 | efree(intern->param.weight); 881 | } 882 | 883 | zval_dtor(&data); 884 | 885 | 886 | if (!status) { 887 | SVM_THROW_LAST_ERROR("Training failed", 1000); 888 | } 889 | return; 890 | } 891 | /* }}} */ 892 | 893 | /* ---- END SVM ---- */ 894 | 895 | /* ---- START SVMMODEL ---- */ 896 | 897 | /** {{{ SvmModel::__construct([string filename]) 898 | Constructs an svm model 899 | */ 900 | PHP_METHOD(svmmodel, __construct) 901 | { 902 | php_svm_model_object *intern; 903 | char *filename = NULL; 904 | size_t filename_len; 905 | 906 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &filename, &filename_len) == FAILURE) { 907 | SVM_THROW("Invalid parameters passed to constructor", 154); 908 | } 909 | 910 | if (!filename) { 911 | return; 912 | } 913 | 914 | intern = php_svm_fetch_svm_model_object(Z_OBJ_P(getThis())); 915 | intern->model = svm_load_model(filename); 916 | 917 | if (!intern->model) { 918 | SVM_THROW("Failed to load the model", 1233); 919 | } 920 | 921 | return; 922 | } 923 | /* }}} */ 924 | 925 | /** {{{ SvmModel::load(string filename) 926 | Loads the svm model from a file 927 | */ 928 | PHP_METHOD(svmmodel, load) 929 | { 930 | php_svm_model_object *intern; 931 | char *filename = NULL; 932 | size_t filename_len; 933 | 934 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &filename, &filename_len) == FAILURE) { 935 | return; 936 | } 937 | 938 | intern = php_svm_fetch_svm_model_object(Z_OBJ_P(getThis())); 939 | intern->model = svm_load_model(filename); 940 | 941 | if (!intern->model) { 942 | SVM_THROW("Failed to load the model", 1233); 943 | } 944 | 945 | RETURN_TRUE; 946 | } 947 | /* }}} */ 948 | 949 | /** {{{ SvmModel::save(string filename) 950 | Saves the svm model to a file 951 | */ 952 | PHP_METHOD(svmmodel, save) 953 | { 954 | php_svm_model_object *intern; 955 | char *filename; 956 | size_t filename_len; 957 | 958 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &filename, &filename_len) == FAILURE) { 959 | return; 960 | } 961 | 962 | intern = php_svm_fetch_svm_model_object(Z_OBJ_P(getThis())); 963 | 964 | if (!intern->model) { 965 | SVM_THROW("The object does not contain a model", 2321); 966 | } 967 | 968 | if (svm_save_model(filename, intern->model) != 0) { 969 | SVM_THROW("Failed to save the model", 121); 970 | } 971 | 972 | RETURN_TRUE; 973 | } 974 | /* }}} */ 975 | 976 | 977 | /** {{{ SvmModel::getSvmType() 978 | Gets the type of SVM the model was trained with 979 | */ 980 | PHP_METHOD(svmmodel, getSvmType) 981 | { 982 | php_svm_model_object *intern; 983 | int svm_type; 984 | 985 | if (zend_parse_parameters_none() == FAILURE) { 986 | return; 987 | } 988 | 989 | intern = php_svm_fetch_svm_model_object(Z_OBJ_P(getThis())); 990 | if(!intern->model) { 991 | SVM_THROW("No model available", 106); 992 | } 993 | 994 | svm_type = svm_get_svm_type(intern->model); 995 | 996 | RETURN_LONG(svm_type); 997 | } 998 | /* }}} */ 999 | 1000 | 1001 | 1002 | /** {{{ SvmModel::getNrClass() 1003 | Gets the number of classes the model was trained with. Note that for a regression 1004 | or 1 class model 2 is returned. 1005 | */ 1006 | PHP_METHOD(svmmodel, getNrClass) 1007 | { 1008 | php_svm_model_object *intern; 1009 | int nr_classes; 1010 | 1011 | if (zend_parse_parameters_none() == FAILURE) { 1012 | return; 1013 | } 1014 | 1015 | intern = php_svm_fetch_svm_model_object(Z_OBJ_P(getThis())); 1016 | if(!intern->model) { 1017 | SVM_THROW("No model available", 106); 1018 | } 1019 | 1020 | nr_classes = svm_get_nr_class(intern->model); 1021 | 1022 | RETURN_LONG(nr_classes); 1023 | } 1024 | /* }}} */ 1025 | 1026 | 1027 | /** {{{ SvmModel::getLabels() 1028 | Gets an array of labels that the model was trained with. For regression 1029 | and one class models, an empty array is returned. 1030 | */ 1031 | PHP_METHOD(svmmodel, getLabels) 1032 | { 1033 | php_svm_model_object *intern; 1034 | int nr_classes; 1035 | int* labels; 1036 | int i; 1037 | 1038 | if (zend_parse_parameters_none() == FAILURE) { 1039 | return; 1040 | } 1041 | 1042 | intern = php_svm_fetch_svm_model_object(Z_OBJ_P(getThis())); 1043 | if(!intern->model) { 1044 | SVM_THROW("No model available", 106); 1045 | } 1046 | 1047 | nr_classes = svm_get_nr_class(intern->model); 1048 | labels = safe_emalloc(nr_classes, sizeof(int), 0); 1049 | svm_get_labels(intern->model, labels); 1050 | 1051 | array_init(return_value); 1052 | 1053 | for( i = 0; i < nr_classes; i++ ) { 1054 | add_next_index_long(return_value, labels[i]); 1055 | } 1056 | 1057 | efree(labels); 1058 | } 1059 | /* }}} */ 1060 | 1061 | 1062 | /** {{{ SvmModel::checkProbabilityModel() 1063 | Returns true if the model contains probability estimates 1064 | */ 1065 | PHP_METHOD(svmmodel, checkProbabilityModel) 1066 | { 1067 | php_svm_model_object *intern; 1068 | int prob; 1069 | 1070 | if (zend_parse_parameters_none() == FAILURE) { 1071 | return; 1072 | } 1073 | 1074 | intern = php_svm_fetch_svm_model_object(Z_OBJ_P(getThis())); 1075 | if(!intern->model) { 1076 | SVM_THROW("No model available", 106); 1077 | } 1078 | 1079 | prob = svm_check_probability_model(intern->model); 1080 | 1081 | RETURN_BOOL( prob ); 1082 | } 1083 | /* }}} */ 1084 | 1085 | 1086 | /** {{{ SvmModel::getSvrProbability() 1087 | For regression models, returns a sigma value. If there is no probability 1088 | information or the model is not SVR, 0 is returned. 1089 | */ 1090 | PHP_METHOD(svmmodel, getSvrProbability) 1091 | { 1092 | php_svm_model_object *intern; 1093 | double svr_prob; 1094 | 1095 | if (zend_parse_parameters_none() == FAILURE) { 1096 | return; 1097 | } 1098 | 1099 | intern = php_svm_fetch_svm_model_object(Z_OBJ_P(getThis())); 1100 | if(!intern->model) { 1101 | SVM_THROW("No model available", 106); 1102 | } 1103 | 1104 | svr_prob = svm_get_svr_probability(intern->model); 1105 | 1106 | RETURN_DOUBLE(svr_prob); 1107 | } 1108 | /* }}} */ 1109 | 1110 | /** {{{ SvmModel::predict(array data) 1111 | Predicts based on the model 1112 | */ 1113 | PHP_METHOD(svmmodel, predict) 1114 | { 1115 | php_svm_model_object *intern; 1116 | double predict_label; 1117 | struct svm_node *x; 1118 | zval *arr; 1119 | 1120 | /* we want an array of data to be passed in */ 1121 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &arr) == FAILURE) { 1122 | return; 1123 | } 1124 | 1125 | intern = php_svm_fetch_svm_model_object(Z_OBJ_P(getThis())); 1126 | if(!intern->model) { 1127 | SVM_THROW("No model available to classify with", 106); 1128 | } 1129 | 1130 | x = php_svm_get_data_from_array(arr); 1131 | predict_label = svm_predict(intern->model, x); 1132 | efree(x); 1133 | 1134 | RETURN_DOUBLE(predict_label); 1135 | } 1136 | 1137 | /* }}} */ 1138 | 1139 | /** {{{ SvmModel::predict_probability(array data, array probabilities) 1140 | Predicts based on the model 1141 | */ 1142 | PHP_METHOD(svmmodel, predict_probability) 1143 | { 1144 | php_svm_model_object *intern; 1145 | double predict_probability; 1146 | int nr_classes, i; 1147 | double *estimates; 1148 | struct svm_node *x; 1149 | int *labels; 1150 | zval *arr; 1151 | zval *retarr = NULL; 1152 | 1153 | /* we want an array of data to be passed in */ 1154 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "az/", &arr, &retarr) == FAILURE) { 1155 | return; 1156 | } 1157 | 1158 | intern = php_svm_fetch_svm_model_object(Z_OBJ_P(getThis())); 1159 | if(!intern->model) { 1160 | SVM_THROW("No model available to classify with", 106); 1161 | } 1162 | 1163 | x = php_svm_get_data_from_array(arr); 1164 | nr_classes = svm_get_nr_class(intern->model); 1165 | estimates = safe_emalloc(nr_classes, sizeof(double), 0); 1166 | labels = safe_emalloc(nr_classes, sizeof(int), 0); 1167 | predict_probability = svm_predict_probability(intern->model, x, estimates); 1168 | 1169 | if (retarr != NULL) { 1170 | zval_dtor(retarr); 1171 | array_init(retarr); 1172 | svm_get_labels(intern->model, labels); 1173 | for (i = 0; i < nr_classes; ++i) { 1174 | add_index_double(retarr, labels[i], estimates[i]); 1175 | } 1176 | } 1177 | 1178 | efree(estimates); 1179 | efree(labels); 1180 | efree(x); 1181 | 1182 | RETURN_DOUBLE(predict_probability); 1183 | } 1184 | /* }}} */ 1185 | 1186 | /* ---- END SVMMODEL ---- */ 1187 | 1188 | static void php_svm_object_free_storage(zend_object *object)/*{{{*/ 1189 | { 1190 | php_svm_object *intern; 1191 | 1192 | intern = (php_svm_object *)((char *)object - XtOffsetOf(php_svm_object, zo)); 1193 | 1194 | if (!intern) { 1195 | return; 1196 | } 1197 | 1198 | zend_object_std_dtor(&intern->zo); 1199 | }/*}}}*/ 1200 | 1201 | static zend_object* php_svm_object_new_ex(zend_class_entry *class_type, php_svm_object **ptr)/*{{{*/ 1202 | { 1203 | php_svm_object *intern; 1204 | 1205 | /* Allocate memory for the internal structure */ 1206 | intern = (php_svm_object *) ecalloc(1, sizeof(php_svm_object) + zend_object_properties_size(class_type)); 1207 | 1208 | if (ptr) { 1209 | *ptr = intern; 1210 | } 1211 | 1212 | /* Null model by default */ 1213 | memset(intern->last_error, 0, 512); 1214 | 1215 | zend_object_std_init(&intern->zo, class_type); 1216 | object_properties_init(&intern->zo, class_type); 1217 | intern->zo.handlers = &svm_object_handlers; 1218 | 1219 | return &intern->zo; 1220 | }/*}}}*/ 1221 | 1222 | static zend_object * php_svm_object_new(zend_class_entry *class_type)/*{{{*/ 1223 | { 1224 | return php_svm_object_new_ex(class_type, NULL); 1225 | }/*}}}*/ 1226 | 1227 | static void php_svm_model_object_free_storage(zend_object *object)/*{{{*/ 1228 | { 1229 | php_svm_model_object *intern; 1230 | 1231 | intern = (php_svm_model_object *)((char *)object - XtOffsetOf(php_svm_model_object, zo)); 1232 | 1233 | if (!intern) { 1234 | return; 1235 | } 1236 | 1237 | if (intern->model) { 1238 | #if LIBSVM_VERSION >= 300 1239 | svm_free_and_destroy_model(&intern->model); 1240 | #else 1241 | svm_destroy_model(intern->model); 1242 | #endif 1243 | 1244 | efree(intern->model); 1245 | intern->model = NULL; 1246 | } 1247 | 1248 | if (intern->x_space) { 1249 | efree(intern->x_space); 1250 | intern->x_space = NULL; 1251 | } 1252 | 1253 | zend_object_std_dtor(&intern->zo); 1254 | }/*}}}*/ 1255 | 1256 | 1257 | static zend_object * php_svm_model_object_new_ex(zend_class_entry *class_type, php_svm_model_object **ptr, size_t problemSize)/*{{{*/ 1258 | { 1259 | php_svm_model_object *intern; 1260 | 1261 | /* Allocate memory for the internal structure */ 1262 | intern = (php_svm_model_object *) ecalloc(1, sizeof(php_svm_model_object) + zend_object_properties_size(class_type)); 1263 | 1264 | if (ptr) { 1265 | *ptr = intern; 1266 | } 1267 | 1268 | if (problemSize) { 1269 | ALLOC_XSPACE(intern, problemSize); 1270 | } else { 1271 | intern->x_space = NULL; 1272 | } 1273 | intern->model = NULL; 1274 | 1275 | zend_object_std_init(&intern->zo, class_type); 1276 | 1277 | object_properties_init(&intern->zo, class_type); 1278 | 1279 | intern->zo.handlers = &svm_model_object_handlers; 1280 | 1281 | return &intern->zo; 1282 | }/*}}}*/ 1283 | 1284 | 1285 | static zend_object * php_svm_model_object_new(zend_class_entry *class_type)/*{{{*/ 1286 | { 1287 | return php_svm_model_object_new_ex(class_type, NULL, 0); 1288 | }/*}}}*/ 1289 | 1290 | /* {{{ SVM arginfo */ 1291 | ZEND_BEGIN_ARG_INFO_EX(svm_empty_args, 0, 0, 0) 1292 | ZEND_END_ARG_INFO() 1293 | 1294 | ZEND_BEGIN_ARG_INFO_EX(svm_train_args, 0, 0, 1) 1295 | ZEND_ARG_INFO(0, problem) 1296 | ZEND_ARG_INFO(0, weights) 1297 | ZEND_END_ARG_INFO() 1298 | 1299 | ZEND_BEGIN_ARG_INFO_EX(svm_crossvalidate_args, 0, 0, 2) 1300 | ZEND_ARG_INFO(0, problem) 1301 | ZEND_ARG_INFO(0, number_of_folds) 1302 | ZEND_END_ARG_INFO() 1303 | 1304 | ZEND_BEGIN_ARG_INFO_EX(svm_params_args, 0, 0, 1) 1305 | ZEND_ARG_INFO(0, params) 1306 | ZEND_END_ARG_INFO() 1307 | /* }}} */ 1308 | 1309 | static zend_function_entry php_svm_class_methods[] =/*{{{*/ 1310 | { 1311 | PHP_ME(svm, __construct, svm_empty_args, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) 1312 | PHP_ME(svm, getOptions, svm_empty_args, ZEND_ACC_PUBLIC) 1313 | PHP_ME(svm, setOptions, svm_params_args, ZEND_ACC_PUBLIC) 1314 | PHP_ME(svm, train, svm_train_args, ZEND_ACC_PUBLIC) 1315 | PHP_ME(svm, crossvalidate, svm_crossvalidate_args, ZEND_ACC_PUBLIC) 1316 | { NULL, NULL, NULL } 1317 | };/*}}}*/ 1318 | 1319 | /* {{{ Model arginfo */ 1320 | ZEND_BEGIN_ARG_INFO_EX(svm_model_construct_args, 0, 0, 0) 1321 | ZEND_ARG_INFO(0, filename) 1322 | ZEND_END_ARG_INFO() 1323 | 1324 | ZEND_BEGIN_ARG_INFO_EX(svm_model_predict_args, 0, 0, 1) 1325 | ZEND_ARG_INFO(0, data) 1326 | ZEND_END_ARG_INFO() 1327 | 1328 | ZEND_BEGIN_ARG_INFO_EX(svm_model_predict_probs_args, 0, 0, 1) 1329 | ZEND_ARG_INFO(0, data) 1330 | ZEND_ARG_INFO(1, probabilities) 1331 | ZEND_END_ARG_INFO() 1332 | 1333 | ZEND_BEGIN_ARG_INFO_EX(svm_model_file_args, 0, 0, 1) 1334 | ZEND_ARG_INFO(0, filename) 1335 | ZEND_END_ARG_INFO() 1336 | 1337 | ZEND_BEGIN_ARG_INFO_EX(svm_model_info_args, 0, 0, 0) 1338 | ZEND_END_ARG_INFO() 1339 | /* }}} */ 1340 | 1341 | static zend_function_entry php_svm_model_class_methods[] =/*{{{*/ 1342 | { 1343 | PHP_ME(svmmodel, __construct, svm_model_construct_args, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) 1344 | PHP_ME(svmmodel, save, svm_model_file_args, ZEND_ACC_PUBLIC) 1345 | PHP_ME(svmmodel, load, svm_model_file_args, ZEND_ACC_PUBLIC) 1346 | PHP_ME(svmmodel, getSvmType, svm_model_info_args, ZEND_ACC_PUBLIC) 1347 | PHP_ME(svmmodel, getLabels, svm_model_info_args, ZEND_ACC_PUBLIC) 1348 | PHP_ME(svmmodel, getNrClass, svm_model_info_args, ZEND_ACC_PUBLIC) 1349 | PHP_ME(svmmodel, getSvrProbability, svm_model_info_args, ZEND_ACC_PUBLIC) 1350 | PHP_ME(svmmodel, checkProbabilityModel, svm_model_info_args, ZEND_ACC_PUBLIC) 1351 | PHP_ME(svmmodel, predict, svm_model_predict_args, ZEND_ACC_PUBLIC) 1352 | PHP_ME(svmmodel, predict_probability, svm_model_predict_probs_args, ZEND_ACC_PUBLIC) 1353 | { NULL, NULL, NULL } 1354 | };/*}}}*/ 1355 | 1356 | PHP_MINIT_FUNCTION(svm)/*{{{*/ 1357 | { 1358 | zend_class_entry ce; 1359 | memcpy(&svm_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); 1360 | svm_object_handlers.free_obj = php_svm_object_free_storage; 1361 | svm_object_handlers.offset = XtOffsetOf(php_svm_object, zo); 1362 | 1363 | memcpy(&svm_model_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); 1364 | svm_model_object_handlers.free_obj = php_svm_model_object_free_storage; 1365 | svm_model_object_handlers.offset = XtOffsetOf(php_svm_model_object, zo); 1366 | 1367 | INIT_CLASS_ENTRY(ce, "svm", php_svm_class_methods); 1368 | ce.create_object = php_svm_object_new; 1369 | php_svm_sc_entry = zend_register_internal_class(&ce); 1370 | 1371 | INIT_CLASS_ENTRY(ce, "svmmodel", php_svm_model_class_methods); 1372 | ce.create_object = php_svm_model_object_new; 1373 | php_svm_model_sc_entry = zend_register_internal_class(&ce); 1374 | 1375 | INIT_CLASS_ENTRY(ce, "svmexception", NULL); 1376 | php_svm_exception_sc_entry = zend_register_internal_class_ex(&ce, zend_exception_get_default()); 1377 | php_svm_exception_sc_entry->ce_flags |= ZEND_ACC_FINAL; 1378 | 1379 | /* Redirect the lib svm output */ 1380 | #if LIBSVM_VERSION >= 291 1381 | svm_set_print_string_function(&print_null); 1382 | #else 1383 | svm_print_string = &print_null; 1384 | #endif 1385 | 1386 | #define SVM_REGISTER_CONST_LONG(const_name, value) \ 1387 | zend_declare_class_constant_long(php_svm_sc_entry, const_name, sizeof(const_name)-1, value); 1388 | 1389 | /* SVM types */ 1390 | SVM_REGISTER_CONST_LONG("C_SVC", C_SVC); 1391 | SVM_REGISTER_CONST_LONG("NU_SVC", NU_SVC); 1392 | SVM_REGISTER_CONST_LONG("ONE_CLASS", ONE_CLASS); 1393 | SVM_REGISTER_CONST_LONG("EPSILON_SVR", EPSILON_SVR); 1394 | SVM_REGISTER_CONST_LONG("NU_SVR", NU_SVR); 1395 | 1396 | /* Kernel types */ 1397 | SVM_REGISTER_CONST_LONG("KERNEL_LINEAR", LINEAR); 1398 | SVM_REGISTER_CONST_LONG("KERNEL_POLY", POLY); 1399 | SVM_REGISTER_CONST_LONG("KERNEL_RBF", RBF); 1400 | SVM_REGISTER_CONST_LONG("KERNEL_SIGMOID", SIGMOID); 1401 | SVM_REGISTER_CONST_LONG("KERNEL_PRECOMPUTED", PRECOMPUTED); 1402 | 1403 | /* Long options (for setOptions) */ 1404 | SVM_REGISTER_CONST_LONG("OPT_TYPE", phpsvm_svm_type); 1405 | SVM_REGISTER_CONST_LONG("OPT_KERNEL_TYPE", phpsvm_kernel_type); 1406 | SVM_REGISTER_CONST_LONG("OPT_DEGREE", phpsvm_degree); 1407 | SVM_REGISTER_CONST_LONG("OPT_SHRINKING", phpsvm_shrinking); 1408 | SVM_REGISTER_CONST_LONG("OPT_PROBABILITY", phpsvm_probability); 1409 | 1410 | /* Double options (for setOptions) */ 1411 | SVM_REGISTER_CONST_LONG("OPT_GAMMA", phpsvm_gamma); 1412 | SVM_REGISTER_CONST_LONG("OPT_NU", phpsvm_nu); 1413 | SVM_REGISTER_CONST_LONG("OPT_EPS", phpsvm_eps); 1414 | SVM_REGISTER_CONST_LONG("OPT_P", phpsvm_p); 1415 | SVM_REGISTER_CONST_LONG("OPT_COEF_ZERO", phpsvm_coef0); 1416 | SVM_REGISTER_CONST_LONG("OPT_C", phpsvm_C); 1417 | SVM_REGISTER_CONST_LONG("OPT_CACHE_SIZE", phpsvm_cache_size); 1418 | 1419 | #undef SVM_REGISTER_CONST_LONG 1420 | 1421 | return SUCCESS; 1422 | }/*}}}*/ 1423 | 1424 | PHP_MSHUTDOWN_FUNCTION(svm)/*{{{*/ 1425 | { 1426 | UNREGISTER_INI_ENTRIES(); 1427 | return SUCCESS; 1428 | }/*}}}*/ 1429 | 1430 | PHP_MINFO_FUNCTION(svm)/*{{{*/ 1431 | { 1432 | char _tmp[8]; 1433 | php_info_print_table_start(); 1434 | php_info_print_table_header(2, "svm extension", "enabled"); 1435 | php_info_print_table_row(2, "svm extension version", PHP_SVM_VERSION); 1436 | snprintf(_tmp, sizeof(_tmp), "%d.%d", (int)(LIBSVM_VERSION/100), (int)(LIBSVM_VERSION % 100)); 1437 | php_info_print_table_row(2, "libsvm version", _tmp); 1438 | php_info_print_table_end(); 1439 | 1440 | DISPLAY_INI_ENTRIES(); 1441 | }/*}}}*/ 1442 | 1443 | /* No global functions */ 1444 | zend_function_entry svm_functions[] = { 1445 | {NULL, NULL, NULL} 1446 | }; 1447 | 1448 | zend_module_entry svm_module_entry = 1449 | { 1450 | STANDARD_MODULE_HEADER, 1451 | "svm", 1452 | svm_functions, /* Functions */ 1453 | PHP_MINIT(svm), /* MINIT */ 1454 | PHP_MSHUTDOWN(svm), /* MSHUTDOWN */ 1455 | NULL, /* RINIT */ 1456 | NULL, /* RSHUTDOWN */ 1457 | PHP_MINFO(svm), /* MINFO */ 1458 | PHP_SVM_VERSION, /* version */ 1459 | STANDARD_MODULE_PROPERTIES 1460 | }; 1461 | 1462 | 1463 | #ifdef COMPILE_DL_SVM 1464 | ZEND_GET_MODULE(svm) 1465 | #endif /* COMPILE_DL_SVM */ 1466 | 1467 | /* 1468 | * Local variables: 1469 | * tab-width: 4 1470 | * c-basic-offset: 4 1471 | * End: 1472 | * vim600: noet sw=4 ts=4 fdm=marker 1473 | * vim<600: noet sw=4 ts=4 1474 | */ 1475 | -------------------------------------------------------------------------------- /tests/001_train.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Load training data from a file. 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | train(dirname(__FILE__) . '/australian.scale'); 11 | 12 | try { 13 | $result->save(dirname(__FILE__) . '/australian.model'); 14 | echo "ok"; 15 | } catch (SvmException $e) { 16 | echo $e->getMessage(); 17 | } 18 | 19 | ?> 20 | --EXPECT-- 21 | ok 22 | -------------------------------------------------------------------------------- /tests/002_predict.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Make a prediction based on the model 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | load(dirname(__FILE__) . '/australian.model'); 11 | 12 | if($result) { 13 | $data = array( 14 | "1" => 1, 15 | 2 => -0.731729, 16 | 3 => -0.886786, 17 | 4 => -1, 18 | 5 => 0.230769, 19 | "6" => -0.25, 20 | 7 => -0.783509, 21 | 8 => 1, 22 | 9 => 1, 23 | 10 => "-0.820896", 24 | 11 => -1, 25 | 13 => -0.92, 26 | "14" => "-1" 27 | ); 28 | $result = $svm->predict($data); 29 | if($result > 0) { 30 | echo "ok"; 31 | } else { 32 | echo "predict failed: $result"; 33 | } 34 | } else { 35 | echo "loading failed"; 36 | } 37 | ?> 38 | --EXPECT-- 39 | ok 40 | -------------------------------------------------------------------------------- /tests/003_getparms.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test retrieving the SVM training parameters. 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | getOptions(); 11 | if(count($params) > 2) { 12 | echo "ok 1\n"; 13 | } else { 14 | echo "retrieving params failed"; 15 | } 16 | 17 | if(isset($params[SVM::OPT_CACHE_SIZE])) { 18 | echo "ok 2\n"; 19 | } else { 20 | echo "missing cache size"; 21 | } 22 | 23 | if($params[SVM::OPT_CACHE_SIZE] == 100) { 24 | echo "ok 3\n"; 25 | } else { 26 | echo "invalid cache size"; 27 | } 28 | ?> 29 | --EXPECT-- 30 | ok 1 31 | ok 2 32 | ok 3 33 | 34 | -------------------------------------------------------------------------------- /tests/004_setoptions.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test setOptions 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | SVM::NU_SVR, Svm::OPT_COEF_ZERO => 1.2); 12 | 13 | $svm->setOptions($options); 14 | 15 | var_dump($options[Svm::OPT_TYPE] == SVM::NU_SVR); 16 | 17 | echo "ok\n"; 18 | 19 | $options = array(Svm::OPT_TYPE => 31337); 20 | try { 21 | $svm->setOptions($options); 22 | } catch (SVMException $e) { 23 | echo "got exception"; 24 | } 25 | ?> 26 | --EXPECT-- 27 | bool(true) 28 | ok 29 | got exception -------------------------------------------------------------------------------- /tests/005_regression.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Load a larger amount of training data from a file and test regressions 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | array(1 => -1 ,2 => 0.418919 ,3 => 0.411765 ,4 => -0.637168 ,5 => -0.168408 ,6 => -0.294553 ,7 => -0.24424 ,8 => -0.389138 ), 11 | "10" => array(2 => 0.486486 ,3 => 0.445378 ,4 => -0.734513 ,5 => -0.226138 ,6 => -0.287155 ,7 => -0.314022 ,8 => -0.413054 ), 12 | "12" => array(1 => -1 ,2 => 0.716216 ,3 => 0.680672 ,4 => -0.654867 ,5 => 0.378785 ,6 => 0.270343 ,7 => -0.00987492 ,8 => -0.0164425), 13 | ); 14 | 15 | sleep(1); 16 | $svm = new svm(); 17 | 18 | $svm->setOptions(array( 19 | SVM::OPT_TYPE => SVM::C_SVC, 20 | SVM::OPT_KERNEL_TYPE => SVM::KERNEL_LINEAR, 21 | SVM::OPT_P => 0.1, // epsilon 0.1 22 | )); 23 | $model = $svm->train(dirname(__FILE__) . '/abalone.scale'); 24 | if($model) { 25 | echo "ok train\n"; 26 | foreach($data as $class => $d) { 27 | $result = $model->predict($d); 28 | if($result > 0) { 29 | echo "ok\n"; 30 | } else { 31 | echo "regression failed: " . $result . "\n"; 32 | } 33 | } 34 | } else { 35 | echo "training failed"; 36 | } 37 | ?> 38 | --EXPECT-- 39 | ok train 40 | ok 41 | ok 42 | ok 43 | -------------------------------------------------------------------------------- /tests/006_train_array.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Train from an array 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | train(array(array(1, -13 => 1.33))); 11 | var_dump($result); 12 | 13 | try { 14 | $svm->train(array(array(1))); 15 | 16 | } catch (SvmException $e) { 17 | echo "got exception"; 18 | } 19 | 20 | ?> 21 | --EXPECTF-- 22 | object(svmmodel)#%d (%d) { 23 | } 24 | got exception 25 | -------------------------------------------------------------------------------- /tests/007_kernels.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test for various kernels 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | 1, 11 | 2 => -0.731729, 12 | 3 => -0.886786, 13 | 4 => -1, 14 | 5 => 0.230769, 15 | "6" => -0.25, 16 | 7 => -0.783509, 17 | 8 => 1, 18 | 9 => 1, 19 | 10 => "-0.820896", 20 | 11 => -1, 21 | 13 => -0.92, 22 | "14" => "-1" 23 | ); 24 | 25 | $kernels = array( 26 | array( 27 | SVM::OPT_TYPE => SVM::C_SVC, 28 | SVM::OPT_KERNEL_TYPE => SVM::KERNEL_POLY, 29 | ), 30 | array( 31 | SVM::OPT_TYPE => SVM::ONE_CLASS, 32 | SVM::OPT_KERNEL_TYPE => SVM::KERNEL_RBF, 33 | ), 34 | array( 35 | SVM::OPT_TYPE => SVM::EPSILON_SVR, 36 | SVM::OPT_KERNEL_TYPE => SVM::KERNEL_SIGMOID, 37 | ), 38 | array( 39 | SVM::OPT_TYPE => SVM::NU_SVR, 40 | SVM::OPT_KERNEL_TYPE => SVM::KERNEL_PRECOMPUTED, 41 | ), 42 | ); 43 | 44 | $svm = new svm(); 45 | 46 | foreach($kernels as $kernel) { 47 | $svm->setOptions($kernel); 48 | $model = $svm->train(dirname(__FILE__) . '/australian.scale'); 49 | 50 | if($model) { 51 | echo "ok train " . $kernel[SVM::OPT_TYPE] . "\n"; 52 | 53 | $result = $model->predict($data); 54 | if($result != false) { 55 | echo "ok " . $kernel[SVM::OPT_TYPE] . "\n"; 56 | } else { 57 | echo "failed: " . $result . "\n"; 58 | } 59 | } else { 60 | echo "training failed"; 61 | } 62 | } 63 | ?> 64 | --EXPECT-- 65 | ok train 0 66 | ok 0 67 | ok train 2 68 | ok 2 69 | ok train 3 70 | ok 3 71 | ok train 4 72 | ok 4 -------------------------------------------------------------------------------- /tests/008_cross_validate.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test 5 fold cross validation 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | crossvalidate(dirname(__FILE__) . '/australian.scale', 5); 11 | if($result > 0) { 12 | echo "ok"; 13 | } 14 | ?> 15 | --EXPECT-- 16 | ok 17 | -------------------------------------------------------------------------------- /tests/009_badfile.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test handling a bad filename 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | train('bad.file'); // shutup op to stop the warning which has current path in it 12 | } catch(SVMException $e) { 13 | echo "got exception"; 14 | } 15 | ?> 16 | --EXPECT-- 17 | got exception 18 | -------------------------------------------------------------------------------- /tests/010_weights.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test training with some unbalanced weighting 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | setOptions(array(SVM::OPT_TYPE => SVM::NU_SVC)); 11 | try { 12 | $model = $svm->train(dirname(__FILE__) . '/australian.scale', array(1 => 1, -1 => 0.5)); 13 | } catch(SVMException $e) { 14 | echo "got exception\n"; 15 | } 16 | 17 | $svm->setOptions(array(SVM::OPT_TYPE => SVM::C_SVC)); 18 | $model = $svm->train(dirname(__FILE__) . '/australian.scale', array(1 => 1, -1 => 0.5)); 19 | 20 | try { 21 | $data = array( 22 | "1" => 1, 23 | 2 => -0.731729, 24 | 3 => -0.886786, 25 | 4 => -1, 26 | 5 => 0.230769, 27 | "6" => -0.25, 28 | 7 => -0.783509, 29 | 8 => 1, 30 | 9 => 1, 31 | 10 => "-0.820896", 32 | 11 => -1, 33 | 13 => -0.92, 34 | "14" => "-1" 35 | ); 36 | $result = $model->predict($data); 37 | if($result > 0) { 38 | echo "ok"; 39 | } 40 | } catch (SvmException $e) { 41 | echo $e->getMessage(); 42 | } 43 | 44 | ?> 45 | --EXPECT-- 46 | got exception 47 | ok -------------------------------------------------------------------------------- /tests/011_inttodouble.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Train from an array, testing conversion of ints to doubles 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | train(array(array(1, 1 => 1), array(-1, 1 => -1))); 11 | echo $result->predict(array(0, 1 => -1)), PHP_EOL; 12 | echo $result->predict(array(0, 1 => 1)); 13 | 14 | 15 | ?> 16 | --EXPECTF-- 17 | -1 18 | 1 -------------------------------------------------------------------------------- /tests/012_baddata.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test handling a bad filename 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | train(dirname(__FILE__) . '/baddata.scale'); 12 | } catch(SVMException $e) { 13 | echo "got exception"; 14 | } 15 | ?> 16 | --EXPECT-- 17 | got exception 18 | -------------------------------------------------------------------------------- /tests/013_boolean_options.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test setting boolean options 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | true, Svm::OPT_PROBABILITY => false); 12 | 13 | $svm->setOptions($options); 14 | 15 | var_dump($options[Svm::OPT_SHRINKING]); 16 | var_dump($options[Svm::OPT_PROBABILITY]); 17 | 18 | echo "ok\n"; 19 | ?> 20 | --EXPECT-- 21 | bool(true) 22 | bool(false) 23 | ok 24 | -------------------------------------------------------------------------------- /tests/014_predict_probability.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Get the prediction probability based on the model 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | setOptions(array( 11 | SVM::OPT_TYPE => SVM::C_SVC, 12 | SVM::OPT_KERNEL_TYPE => SVM::KERNEL_LINEAR, 13 | SVM::OPT_P => 0.1, // epsilon 0.1 14 | SVM::OPT_PROBABILITY => 1 15 | )); 16 | $model = $svm->train(dirname(__FILE__) . '/abalone.scale'); 17 | 18 | if($model) { 19 | $data = array( 20 | 1 => -1, 21 | 2 => 0.027027, 22 | 3 => 0.0420168, 23 | 4 => -0.831858, 24 | 5 => -0.63733, 25 | 6 => -0.699395, 26 | 7 => -0.735352, 27 | 8 => -0.704036 28 | ); 29 | $class = $model->predict($data); 30 | $return = array(); 31 | $result = $model->predict_probability($data, $return); 32 | arsort($return); 33 | reset($return); 34 | $key = key($return); 35 | if($class == 9 && $key == 9) { 36 | echo "ok"; 37 | } else { 38 | echo "predict failed: $class $result"; 39 | } 40 | } else { 41 | echo "loading failed"; 42 | } 43 | ?> 44 | --EXPECT-- 45 | ok 46 | -------------------------------------------------------------------------------- /tests/015_info_methods.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test some basic info functions 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | setOptions(array( 11 | SVM::OPT_TYPE => SVM::C_SVC, 12 | SVM::OPT_KERNEL_TYPE => SVM::KERNEL_LINEAR, 13 | SVM::OPT_P => 0.1, // epsilon 0.1 14 | SVM::OPT_PROBABILITY => 1 15 | )); 16 | $model = $svm->train(dirname(__FILE__) . '/abalone.scale'); 17 | 18 | if($model) { 19 | if($model->getSvmType() == SVM::C_SVC) { 20 | echo "ok\n"; 21 | } 22 | echo $model->getNrClass(), "\n"; 23 | echo count($model->getLabels()), "\n"; 24 | } else { 25 | echo "loading failed"; 26 | } 27 | 28 | $data = array( 29 | "0.1" => array(1 => 1, 2 => 1, 3 => 1), 30 | "2" => array(1 => 20, 2 => 20, 3 => 20), 31 | "0.5" => array(1 => 5, 2 => 5, 3 => 5), 32 | ); 33 | $svm = new SVM(); 34 | $svm->setOptions(array( 35 | SVM::OPT_TYPE => SVM::EPSILON_SVR, 36 | SVM::OPT_PROBABILITY => 1 37 | )); 38 | $model = $svm->train($data); 39 | echo $model->getSvrProbability() > 11 ? "ok\n" : "fail\n"; 40 | var_dump($model->checkProbabilityModel()); 41 | 42 | ?> 43 | --EXPECTF-- 44 | ok 45 | 28 46 | 28 47 | %Aok 48 | bool(true) 49 | -------------------------------------------------------------------------------- /tests/016_file_stream.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Load training data from a stream. 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | train($fh); 12 | fclose($fh); 13 | 14 | try { 15 | $result->save(dirname(__FILE__) . '/australian.model'); 16 | echo "ok"; 17 | } catch (SvmException $e) { 18 | echo $e->getMessage(); 19 | } 20 | 21 | ?> 22 | --EXPECT-- 23 | ok 24 | -------------------------------------------------------------------------------- /tests/australian.scale: -------------------------------------------------------------------------------- 1 | -1 1:1 2:-0.749474 3:-0.181429 5:-0.538462 6:-0.25 7:-0.888772 8:-1 9:-1 10:-1 11:1 13:-0.9 14:-0.97576 2 | -1 1:-1 2:-0.731729 3:-0.5 5:0.0769231 6:-0.25 7:-0.988421 8:-1 9:-1 10:-1 11:-1 13:-0.84 14:-1 3 | -1 1:-1 2:-0.52391 3:-0.875 4:-1 5:-0.538462 6:-0.25 7:-0.912281 8:-1 9:-1 10:-1 11:1 13:-0.72 14:-1 4 | +1 1:-1 2:-0.761805 3:-0.178571 4:-1 5:-0.384615 6:-0.5 7:-1 8:1 9:1 10:-0.671642 11:1 13:-1 14:-1 5 | +1 1:1 2:-0.806917 3:-0.416429 5:-0.230769 6:-0.25 7:-0.862456 8:1 9:1 10:-0.58209 11:-1 13:-0.94 14:-0.99684 6 | +1 1:-1 2:-0.937444 3:-0.958214 5:0.0769231 6:0.75 7:-0.894737 8:1 9:1 10:-0.940299 11:-1 13:-0.9 14:-1 7 | -1 1:1 2:-0.889624 3:-0.535714 5:-0.692308 6:-0.25 7:-0.991228 8:-1 9:-1 10:-1 11:-1 13:-0.94 14:-0.998 8 | +1 1:-1 2:0.350977 3:-0.681429 5:0.538462 6:0.75 7:-0.786667 8:1 9:1 10:-0.820896 11:-1 13:-0.957 14:-0.9888 9 | -1 1:1 2:-0.576541 3:-0.928571 4:-1 5:-0.846154 6:0.75 7:-0.789474 8:-1 9:-1 10:-1 11:-1 13:-0.824 14:-0.98926 10 | -1 1:-1 2:0.263158 3:-0.494286 5:-0.538462 6:0.75 7:-0.526316 8:1 9:1 10:-0.910448 11:1 13:-0.9 14:-0.999 11 | +1 1:1 2:-0.406015 3:-0.875 5:1 6:0.75 7:-0.684211 8:1 9:1 10:-0.880597 11:1 13:-0.747 14:-0.98286 12 | +1 1:1 2:-0.16782 3:-0.642857 5:0.538462 6:0.75 7:-0.649123 8:1 9:1 10:-0.820896 11:1 13:-0.53 14:-1 13 | -1 1:1 2:-0.79188 3:-0.910714 4:-1 5:0.0769231 6:0.75 7:-0.903509 8:1 9:1 10:-0.910448 11:1 13:-0.86 14:-0.9958 14 | +1 1:1 2:-0.363308 3:-0.642857 5:1 6:0.75 7:-0.473684 8:1 9:1 10:-0.820896 11:1 13:-1 14:-0.98 15 | -1 1:1 2:0.348271 3:-0.806429 5:0.0769231 6:-0.25 7:-0.830526 8:-1 9:-1 10:-1 11:1 13:-0.68 14:-1 16 | +1 1:1 2:0.0324813 3:-0.568571 5:-0.538462 6:-0.25 7:-0.997193 8:-1 9:-1 10:-1 11:-1 13:-1 14:-0.9462 17 | +1 1:1 2:-0.52391 3:-0.678571 5:0.230769 6:-0.25 7:-0.473684 8:1 9:1 10:-0.940299 11:1 13:-0.67 14:-1 18 | +1 1:-1 2:-0.844511 3:-0.357143 5:-0.230769 6:-0.25 7:-0.947368 8:1 9:1 10:-0.940299 11:-1 13:-0.912 14:-0.98818 19 | -1 1:1 2:-0.81203 3:-0.910714 4:-1 5:-0.538462 6:-0.25 7:-0.991228 8:-1 9:-1 10:-1 11:-1 13:-0.86 14:-0.99992 20 | +1 1:-1 2:-0.739248 3:-0.595357 5:0.538462 6:-0.25 7:-0.818596 8:1 9:1 10:-0.791045 11:-1 13:-0.871 14:-0.93486 21 | -1 1:-1 2:-0.566316 3:-0.958214 5:-0.230769 6:-0.25 7:-0.997193 8:-1 9:-1 10:-1 11:-1 13:-0.74 14:-0.97992 22 | -1 1:-1 2:-0.836992 3:-0.958214 4:-1 5:-0.230769 6:-0.25 7:-0.958947 8:1 9:-1 10:-1 11:1 13:-0.84 14:-1 23 | -1 1:1 2:-0.175338 3:-0.904643 5:-0.846154 6:-0.25 7:-0.988421 8:-1 9:-1 10:-1 11:-1 13:-0.832 14:-1 24 | -1 1:1 2:-0.163007 3:-0.875 5:-0.538462 6:-0.25 7:-0.985263 8:1 9:-1 10:-1 11:-1 13:-0.84 14:-1 25 | -1 1:1 2:-0.827068 3:-0.315357 5:-0.230769 6:-0.25 7:-0.944561 8:-1 9:-1 10:-1 11:-1 13:-0.92 14:-0.993 26 | +1 1:1 2:-0.428571 3:-0.892857 5:0.846154 6:0.75 7:-0.614035 8:1 9:1 10:-0.910448 11:1 13:-1 14:-1 27 | -1 1:1 2:-0.736842 3:-0.991071 4:-1 5:-0.538462 6:-0.25 7:-0.991228 8:-1 9:-1 10:-1 11:-1 13:-0.8 14:-0.9986 28 | +1 1:1 2:-0.41594 3:-0.782857 4:-1 5:0.0769231 6:0.75 7:-0.856842 8:1 9:1 10:-0.970149 11:1 13:-0.82 14:-0.63946 29 | +1 1:-1 2:-0.491128 3:-0.142857 5:0.0769231 6:-0.25 7:-0.859649 8:1 9:1 10:-0.970149 11:-1 13:-0.78 14:-0.99962 30 | +1 1:1 2:-0.719398 3:-0.821429 5:0.0769231 6:-0.25 7:-0.92386 8:1 9:1 10:-0.671642 11:1 13:-0.94 14:-0.95632 31 | +1 1:1 2:-0.601504 3:-0.946429 5:0.0769231 6:0.75 7:-0.701754 8:1 9:1 10:-0.910448 11:1 13:-0.688 14:-0.997 32 | -1 1:-1 2:-0.799398 3:-0.25 4:-1 5:1 6:0.75 7:-1 8:-1 9:-1 10:-1 11:1 13:-0.846 14:-0.99936 33 | -1 1:1 2:0.160301 3:-0.901786 4:-1 5:0.0769231 6:0.75 7:-0.33614 8:1 9:-1 10:-1 11:1 13:-0.8 14:-0.998 34 | +1 1:1 2:-0.719398 3:-0.178571 5:0.230769 6:0.75 7:-0.850877 8:1 9:1 10:-0.671642 11:1 13:-0.71 14:-0.99432 35 | -1 1:1 2:-0.125413 3:-0.910714 5:-0.0769231 6:-0.25 7:-0.0263158 8:-1 9:1 10:-0.970149 11:1 13:-0.648 14:-0.99776 36 | -1 1:1 2:0.836993 3:0.357143 4:-1 5:-1 6:-1 7:-0.997193 8:-1 9:1 10:-0.940299 11:-1 13:-1 14:-0.99298 37 | +1 1:1 2:-0.661654 3:-0.107143 5:-0.230769 6:-0.25 7:-0.789474 8:1 9:-1 10:-1 11:1 12:-1 13:-0.98 14:-1 38 | +1 1:1 2:-0.223158 3:-0.00607143 5:0.230769 6:-0.25 7:-0.394737 8:1 9:1 10:-0.820896 11:1 13:-0.93 14:-1 39 | +1 1:-1 2:0.0225564 3:-0.428571 5:0.0769231 6:-0.25 7:-0.447368 8:1 9:1 10:-0.820896 11:1 13:-1 14:-0.9748 40 | +1 1:-1 2:0.0126315 3:-0.785714 5:1 6:-0.25 7:-0.0263158 8:1 9:1 10:-0.940299 11:1 13:-0.481 14:-0.96592 41 | +1 1:1 2:-0.716692 3:-1 5:0.846154 6:-0.25 7:-0.994035 8:1 9:-1 10:-1 11:-1 13:-1 14:-1 42 | -1 1:1 2:-0.734436 3:-0.892857 4:-1 5:-0.230769 6:-0.25 7:-0.962105 8:-1 9:-1 10:-1 11:1 13:-0.88 14:-0.99866 43 | +1 1:1 2:-0.609023 3:-0.919643 5:1 6:0.75 7:-0.912281 8:1 9:-1 10:-1 11:-1 13:-1 14:-0.89404 44 | -1 1:1 2:0.491128 3:-0.961429 5:0.0769231 6:-0.25 7:-0.958947 8:1 9:1 10:-0.910448 11:1 13:-0.82 14:-1 45 | -1 1:1 2:-0.699248 3:-0.970357 4:-1 5:0.0769231 6:-0.25 7:-0.997193 8:-1 9:1 10:-0.940299 11:-1 13:-0.872 14:-0.99988 46 | +1 1:-1 2:-0.789474 3:-0.267857 5:0.538462 6:-0.25 7:-0.950175 8:1 9:1 10:-0.940299 11:1 13:-0.951 14:-1 47 | -1 1:-1 2:-0.676692 3:-0.875 4:-1 5:0.0769231 6:-0.25 7:-0.988421 8:-1 9:-1 10:-1 11:-1 13:-0.868 14:-1 48 | +1 1:1 2:-0.927218 3:-0.997143 5:0.0769231 6:-0.25 7:-0.997193 8:-1 9:-1 10:-1 11:-1 13:-1 14:-1 49 | -1 1:-1 2:-0.526316 3:-0.857143 4:-1 5:0.384615 6:0.75 7:-0.859649 8:-1 9:-1 10:-1 11:-1 13:-0.744 14:-0.99966 50 | +1 1:-1 2:0.175338 3:0.0714286 5:0.0769231 6:-0.25 7:-0.614035 8:1 9:1 10:-0.58209 11:-1 13:-1 14:-0.956 51 | -1 1:1 2:-0.441203 3:-0.75 5:-0.538462 6:-0.25 7:-0.964912 8:-1 9:-1 10:-1 11:1 13:-0.768 14:-1 52 | -1 1:1 2:-0.779549 3:-0.705357 4:-1 5:-0.692308 6:0.75 7:-0.997193 8:-1 9:-1 10:-1 11:-1 13:-0.86 14:-0.998 53 | -1 1:1 2:-0.566316 3:-0.991071 4:-1 5:-0.538462 6:-0.25 7:-0.994035 8:-1 9:-1 10:-1 11:-1 13:-0.784 14:-0.958 54 | -1 1:1 2:-0.842105 3:-0.875 4:-1 5:0.0769231 6:-0.25 7:-0.83614 8:-1 9:-1 10:-1 11:1 13:-0.888 14:-0.99988 55 | -1 1:1 2:-0.58406 3:-0.767857 4:-1 5:0.538462 6:0.75 7:-0.643158 8:-1 9:1 10:-0.940299 11:1 13:-0.631 14:-0.99998 56 | +1 1:1 2:-0.576541 3:-0.892857 5:0.230769 6:-0.25 7:-0.859649 8:1 9:1 10:-0.671642 11:1 13:-0.566 14:-0.9993 57 | +1 1:1 2:-0.210526 3:-0.535714 5:-0.230769 7:-0.754386 8:1 9:1 10:-0.970149 11:-1 13:-1 14:-0.99 58 | -1 1:-1 2:-0.290827 3:-0.821429 5:-0.692308 6:0.75 7:-0.985263 8:-1 9:-1 10:-1 11:-1 13:-0.74 14:-0.99508 59 | +1 1:1 2:-0.135338 3:-0.648929 4:-1 5:0.230769 6:-0.25 7:-0.777895 8:1 9:-1 10:-1 11:1 13:-0.948 14:-0.97116 60 | +1 1:1 2:0.293233 3:-0.125 5:-0.0769231 6:-0.25 7:-0.912281 8:1 9:1 10:-0.880597 11:1 13:-0.8 14:-1 61 | -1 1:1 2:-0.115188 3:-0.642857 5:-0.692308 7:-0.842105 8:-1 9:-1 10:-1 11:1 13:-0.859 14:-1 62 | -1 1:-1 2:-0.699248 3:-0.949286 5:0.230769 6:-0.25 7:-0.982456 8:-1 9:1 10:-0.970149 11:1 13:-0.76 14:-0.99992 63 | +1 1:1 2:-0.857143 3:-0.857143 5:-0.692308 6:-0.25 7:-0.894737 8:1 9:1 10:-0.940299 11:-1 13:-0.88 14:-0.994 64 | -1 1:-1 2:-0.185564 3:-0.75 5:-0.692308 7:-0.964912 8:-1 9:-1 10:-1 11:-1 12:-1 13:0.16 14:-1 65 | +1 1:-1 2:-0.676692 3:-0.964286 5:0.538462 6:0.75 7:-0.894737 8:1 9:-1 10:-1 11:-1 13:-0.72 14:-0.98352 66 | +1 1:1 2:-0.150376 3:-0.300714 5:1 6:0.75 7:-0.441404 8:1 9:1 10:-0.761194 11:-1 13:-1 14:-1 67 | -1 1:-1 2:-0.827068 3:-0.988214 5:0.538462 6:-0.25 7:-0.997193 8:-1 9:-1 10:-1 11:1 13:-0.62 14:-1 68 | -1 1:1 2:-0.766917 3:-0.178571 5:-0.692308 6:-0.25 7:-0.964912 8:1 9:-1 10:-1 11:1 13:-0.9 14:-0.99864 69 | -1 1:1 2:-0.473684 3:-0.7975 5:-1 6:-1 7:-1 8:-1 9:1 10:-0.850746 11:-1 13:-0.824 14:-0.99708 70 | +1 1:1 2:-0.593985 3:-0.886786 5:0.846154 6:0.75 7:-0.871228 8:1 9:1 10:-0.641791 11:1 13:-0.417 14:-0.98574 71 | -1 1:1 2:0.0526316 3:0.881071 4:-1 5:-1 6:-1 7:-1 8:1 9:-1 10:-1 11:1 13:-1 14:-1 72 | -1 1:-1 2:-0.498647 3:-0.901786 5:0.230769 6:0.75 7:-0.997193 8:-1 9:1 10:-0.910448 11:-1 13:-1 14:-0.99934 73 | -1 1:1 2:-0.528722 3:-0.910714 5:0.230769 6:-0.25 7:-0.877193 8:-1 9:-1 10:-1 11:-1 13:-0.8 14:-1 74 | +1 1:1 2:-0.56391 3:-0.64 4:-1 5:0.0769231 7:-0.894737 8:1 9:1 10:-0.761194 11:1 13:-0.856 14:-0.99986 75 | +1 1:1 2:-0.203008 3:0.535714 5:0.384615 6:1 7:0.403509 8:1 9:1 10:-0.671642 11:-1 13:-1 14:-0.976 76 | -1 1:1 2:-0.315789 3:-0.696429 5:0.538462 6:-0.25 7:-0.754386 8:-1 9:-1 10:-1 11:-1 13:-0.546 14:-0.999 77 | -1 1:1 2:-0.644211 3:-0.976071 5:-0.538462 6:0.75 7:-0.754386 8:-1 9:-1 10:-1 11:1 13:-0.66 14:-1 78 | -1 1:1 2:-0.516391 3:-0.75 5:0.0769231 6:-0.25 7:-0.988421 8:-1 9:-1 10:-1 11:-1 13:-0.784 14:-1 79 | -1 1:1 2:-0.719398 3:-1 5:-0.538462 6:-0.25 7:-0.929825 8:-1 9:1 10:-0.671642 11:-1 12:-1 13:-1 14:-1 80 | +1 1:-1 2:-0.446015 3:-0.895714 5:0.230769 6:-0.25 7:-0.92386 8:1 9:1 10:-0.522388 11:-1 13:-0.88 14:-0.95842 81 | +1 1:1 2:-0.656541 3:-0.75 5:0.846154 6:-0.25 7:-0.95614 8:1 9:1 10:-0.791045 11:-1 13:-1 14:-0.85882 82 | -1 1:-1 2:-0.35579 3:-0.732143 5:-1 6:-1 7:-1 8:-1 9:1 10:-0.820896 11:-1 13:-1 14:-0.996 83 | -1 1:-1 2:-0.854737 3:-0.285714 5:-0.846154 6:-0.25 7:-0.970877 8:-1 9:-1 10:-1 11:-1 13:-0.92 14:-0.99916 84 | -1 1:1 2:-0.212932 3:-0.642857 5:-0.692308 7:-0.985263 8:-1 9:-1 10:-1 11:-1 13:-0.45 14:-1 85 | -1 1:1 2:-0.709173 3:-0.928571 5:0.0769231 6:-0.25 7:-0.964912 8:-1 9:-1 10:-1 11:1 12:-1 13:-0.72 14:-1 86 | +1 1:1 2:-0.283308 3:-1 5:0.0769231 6:-0.25 7:-1 8:-1 9:-1 10:-1 11:-1 12:1 13:-0.816 14:-1 87 | -1 1:-1 2:-0.669173 3:-0.0239286 5:0.538462 6:0.75 7:-0.894737 8:-1 9:-1 10:-1 11:-1 13:-0.72 14:-0.99998 88 | +1 1:-1 3:-0.0714286 5:-0.692308 7:-0.637544 8:1 9:1 10:-0.731343 11:1 13:-1 14:-1 89 | +1 1:1 2:-0.385865 3:-0.625 5:0.230769 6:-0.25 7:-0.994035 8:-1 9:-1 10:-1 11:1 13:-0.71 14:-0.99988 90 | -1 1:1 2:-0.746767 3:-0.958214 4:-1 5:-1 6:-1 7:-1 8:-1 9:-1 10:-1 11:-1 13:-0.9 14:-1 91 | -1 1:1 2:-0.578947 3:-0.907857 5:-0.538462 6:0.75 7:-0.982456 8:-1 9:-1 10:-1 11:1 12:-1 13:-0.86 14:-1 92 | -1 1:1 2:-0.12782 3:-0.708214 5:-0.230769 6:-0.25 7:-0.997193 8:-1 9:-1 10:-1 11:-1 13:-0.892 14:-0.998 93 | -1 1:1 2:-0.551278 3:0.0357143 5:-0.846154 6:-0.25 7:-0.991228 8:-1 9:-1 10:-1 11:-1 13:-1 14:-0.99428 94 | +1 1:1 2:-0.323308 3:-0.642857 5:0.0769231 7:-0.824561 8:1 9:1 10:-0.820896 11:-1 13:-1 14:-0.99266 95 | -1 1:-1 2:-0.867068 3:-0.285714 4:-1 5:0.538462 6:0.75 7:-0.988421 8:-1 9:-1 10:-1 11:-1 13:-0.66 14:-1 96 | +1 1:1 2:-0.774436 3:-0.892857 5:0.230769 6:-0.25 7:-0.894737 8:-1 9:-1 10:-1 11:-1 13:-0.85 14:-0.99984 97 | -1 1:-1 2:-0.243008 3:-0.881071 5:-0.230769 6:-0.25 7:-0.982456 8:-1 9:-1 10:-1 11:-1 13:-1 14:-0.9922 98 | -1 1:1 2:-0.456241 3:-0.997143 4:-1 5:-0.0769231 6:-0.25 7:-0.997193 8:-1 9:-1 10:-1 11:-1 13:-1 14:-1 99 | -1 1:-1 2:-0.892331 3:-0.321429 5:-0.230769 6:-0.25 7:-0.877193 8:-1 9:1 10:-0.701493 11:1 13:-1 14:-0.9998 100 | +1 1:-1 2:-0.799398 3:-0.940357 5:0.538462 6:-0.25 7:-0.888772 8:1 9:1 10:-0.970149 11:-1 13:-1 14:-1 101 | -1 1:-1 2:-0.238195 3:-0.714286 5:0.0769231 6:-0.25 7:-0.789474 8:-1 9:-1 10:-1 11:-1 13:-0.52 14:-1 102 | +1 1:1 2:-0.250526 3:-0.985 5:-0.538462 6:-0.25 7:-0.994035 8:1 9:-1 10:-1 11:1 13:-0.72 14:-1 103 | +1 1:1 2:-0.581353 3:-0.0178571 5:0.230769 6:-0.25 7:-0.596491 8:1 9:-1 10:-1 11:1 13:-0.513 14:-0.99 104 | +1 1:1 2:-0.578947 3:-0.958214 4:-1 5:0.846154 6:-0.25 7:-0.982456 8:1 9:1 10:-0.940299 11:-1 13:-0.74 14:-0.99 105 | -1 1:1 2:-0.842105 3:-1 4:-1 5:-1 6:-1 7:-1 8:-1 9:1 10:-0.880597 11:-1 13:-0.955 14:-0.99998 106 | +1 1:-1 2:-0.661654 3:-0.9375 5:1 6:0.75 7:-0.927018 8:1 9:-1 10:-1 11:1 13:-0.84 14:-0.8828 107 | +1 1:1 2:-0.581353 3:-0.857143 5:1 6:0.75 7:-0.929825 8:1 9:1 10:-0.880597 11:-1 13:-0.86 14:-0.84912 108 | -1 1:1 2:-0.744361 3:-0.357143 5:-0.230769 6:-0.25 7:-0.994035 8:-1 9:-1 10:-1 11:-1 13:-1 14:-1 109 | +1 1:-1 2:0.0851128 3:-0.0296429 5:-0.538462 6:0.75 7:-0.403509 8:1 9:-1 10:-1 11:1 13:-1 14:-1 110 | -1 1:1 2:-0.441203 3:-0.821429 5:0.0769231 6:-0.25 7:-0.912281 8:-1 9:-1 10:-1 11:1 13:-0.72 14:-1 111 | -1 1:1 2:-0.263158 3:-0.276786 4:-1 5:-0.538462 6:-0.25 7:-0.991228 8:-1 9:-1 10:-1 11:-1 13:-0.84 14:-1 112 | -1 1:1 2:0.00992487 3:-0.535714 5:0.0769231 6:-0.25 7:-0.929825 8:-1 9:-1 10:-1 11:1 13:-1 14:-0.99544 113 | -1 1:1 2:-0.576541 3:-0.714286 4:-1 5:-0.692308 6:0.75 7:-0.596491 8:1 9:1 10:-0.940299 11:1 13:-0.925 14:-1 114 | +1 1:-1 2:-0.338346 3:-0.934643 5:-0.230769 6:-0.25 7:-0.947368 8:1 9:1 10:-0.880597 11:-1 13:-1 14:-0.96834 115 | -1 1:1 2:-0.403609 3:-0.982143 5:-0.692308 7:-0.719298 8:-1 9:-1 10:-1 11:1 12:-1 13:-0.58 14:-1 116 | +1 1:1 2:-0.388571 3:-0.994286 4:-1 5:-0.0769231 7:-0.997193 8:1 9:1 10:-0.970149 11:1 13:-0.72 14:-0.96 117 | +1 1:-1 2:-0.789474 3:-0.261786 5:0.846154 6:0.75 7:-0.976491 8:1 9:1 10:-0.970149 11:1 13:-0.92 14:-0.999 118 | +1 1:1 2:-0.41594 3:-0.928571 5:1 6:-0.25 7:-0.947368 8:1 9:1 10:-0.791045 11:1 13:-0.66 14:-0.91858 119 | +1 1:1 2:-0.729323 3:-0.214286 5:0.538462 6:-0.25 7:-0.824561 8:1 9:1 10:-0.791045 11:1 13:-0.9 14:-0.98382 120 | +1 1:1 2:0.0526316 3:-0.392857 5:0.0769231 6:0.75 7:-0.122807 8:1 9:1 10:-0.731343 11:-1 13:-0.819 14:-0.9669 121 | +1 1:1 2:-0.193083 3:-0.642857 5:0.0769231 6:-0.25 7:-0.649123 8:1 9:1 10:-0.791045 11:-1 13:-1 14:-0.9387 122 | -1 1:1 2:-0.79188 3:-0.940357 4:-1 5:0.0769231 6:-0.25 7:-0.859649 8:-1 9:-1 10:-1 11:1 12:-1 13:-0.76 14:-1 123 | -1 1:-1 2:-0.24812 3:-0.892857 5:-1 6:-1 7:-1 8:-1 9:-1 10:-1 11:-1 13:-0.924 14:-1 124 | +1 1:-1 2:0.303158 3:0.392857 5:0.0769231 6:-0.25 7:-0.614035 8:1 9:1 10:-0.791045 11:-1 13:-1 14:-0.94 125 | +1 1:-1 2:-0.473684 3:-0.732143 5:0.846154 6:0.75 7:-0.95614 8:1 9:1 10:-0.731343 11:1 13:-0.819 14:-1 126 | -1 1:1 2:-0.75188 3:-0.943571 5:0.230769 6:-0.25 7:-0.979649 8:-1 9:1 10:-0.970149 11:-1 13:-0.58 14:-0.99434 127 | +1 1:-1 2:0.340752 3:-0.285714 5:0.538462 6:-0.25 7:-0.719298 8:1 9:1 10:-0.58209 11:-1 13:-1 14:-0.96796 128 | -1 1:1 2:-0.543759 3:-0.973214 5:0.0769231 6:-0.25 7:-0.979649 8:-1 9:-1 10:-1 11:-1 13:-0.78 14:-0.9972 129 | +1 1:-1 2:-0.0300752 3:-0.714286 5:-0.384615 6:-0.5 7:-1 8:1 9:-1 10:-1 11:-1 13:-0.9 14:-0.9808 130 | +1 1:1 2:-0.781955 3:-0.785714 4:-1 5:-0.846154 6:-0.25 7:-0.92386 8:1 9:1 10:-0.761194 11:1 13:-0.84 14:-0.99998 131 | +1 1:-1 2:-0.669173 3:-0.107143 5:-0.230769 6:-0.25 7:-0.894737 8:1 9:1 10:-0.641791 11:1 13:-0.88 14:-0.98866 132 | -1 1:-1 2:-0.787068 3:-0.964286 4:-1 5:0.384615 6:-0.75 7:-0.929825 8:-1 9:-1 10:-1 11:-1 13:-0.74 14:-1 133 | -1 1:1 2:-0.674286 3:-0.0357143 4:-1 5:-1 6:-1 7:-1 8:-1 9:-1 10:-1 11:-1 13:-0.816 14:-1 134 | -1 1:-1 2:-0.616541 3:-0.806429 4:-1 5:0.0769231 6:-0.25 7:-0.994035 8:-1 9:-1 10:-1 11:-1 12:-1 13:-0.92 14:-1 135 | -1 1:1 2:-0.182857 3:-0.940357 5:-1 6:-1 7:-1 8:1 9:-1 10:-1 11:-1 13:-0.87 14:-0.99998 136 | -1 1:-1 2:-0.260752 3:-0.684643 5:0.0769231 6:-0.25 7:-0.991228 8:-1 9:-1 10:-1 11:-1 13:-0.84 14:-1 137 | -1 1:1 2:-0.824662 3:-0.958214 5:-1 6:-1 7:-1 8:-1 9:1 10:-0.910448 11:-1 13:-0.65 14:-0.98462 138 | +1 1:1 2:-0.233083 3:-0.321429 5:-0.0769231 6:-0.25 7:-0.54386 8:1 9:1 10:-0.58209 11:-1 13:-0.76 14:-0.90786 139 | +1 1:1 2:-0.639098 3:-0.964286 5:0.0769231 6:-0.25 7:-0.897544 8:1 9:1 10:-0.850746 11:1 13:-0.688 14:-1 140 | +1 1:-1 2:-0.0276691 3:-0.785714 5:0.0769231 6:-0.25 7:-0.833333 8:1 9:1 10:-0.761194 11:1 13:-0.604 14:-0.91682 141 | -1 1:1 2:-0.821955 3:-0.285714 4:-1 5:-0.538462 6:0.75 7:-0.941404 8:1 9:-1 10:-1 11:1 13:-0.86 14:-1 142 | -1 1:-1 2:-0.744361 3:-0.910714 4:-1 5:-1 6:-1 7:-0.77193 8:-1 9:-1 10:-1 11:-1 13:-0.72 14:-1 143 | -1 1:1 2:-0.847218 3:-0.747143 4:-1 5:-1 6:-1 7:-1 8:-1 9:-1 10:-1 11:1 13:-0.82 14:-0.99998 144 | +1 1:1 2:0.513684 3:0.428571 5:1 6:0.75 7:0.22807 8:1 9:1 10:-0.731343 11:1 13:-1 14:-0.98 145 | -1 1:-1 2:-0.917293 3:-0.910714 5:0.538462 6:-0.25 7:-0.982456 8:-1 9:1 10:-0.970149 11:-1 13:-0.892 14:-0.99804 146 | +1 1:-1 2:0.651729 3:0.0714286 5:0.384615 6:1 7:-1 8:1 9:1 10:-0.58209 11:-1 13:-1 14:-0.93248 147 | +1 1:1 2:0.894737 3:0.592143 5:0.384615 6:1 7:-0.105263 8:1 9:1 10:-0.970149 11:1 13:-1 14:-0.99782 148 | -1 1:-1 2:-0.934737 3:-0.794643 5:0.538462 6:-0.25 7:-0.994035 8:-1 9:-1 10:-1 11:-1 13:-0.88 14:-1 149 | -1 1:1 2:-0.366015 3:-0.714286 5:-0.846154 7:-0.122807 8:1 9:-1 10:-1 11:1 13:-0.816 14:-1 150 | +1 1:-1 2:0.0126315 3:-0.428571 5:0.384615 7:-0.54386 8:1 9:1 10:-0.820896 11:-1 13:-0.625 14:0.022 151 | +1 1:1 2:-0.716692 3:-1 5:0.0769231 6:-0.25 7:-1 8:-1 9:-1 10:-1 11:-1 12:1 13:-0.816 14:-1 152 | -1 1:1 2:-0.0550377 3:-0.892857 5:0.0769231 6:-0.25 7:-0.824561 8:1 9:-1 10:-1 11:1 13:-0.86 14:-1 153 | -1 1:1 2:-0.957293 3:-0.5 5:0.384615 6:-0.25 7:-0.929825 8:-1 9:-1 10:-1 11:-1 13:-0.4 14:-1 154 | -1 1:1 2:-0.847218 3:-0.970357 4:-1 5:0.0769231 6:-0.25 7:-0.988421 8:-1 9:1 10:-0.970149 11:-1 13:-0.8 14:-0.99998 155 | +1 1:1 2:0.165414 3:-0.535714 5:-0.538462 6:-0.25 7:-0.558596 8:1 9:1 10:-0.552239 11:-1 13:-1 14:-0.77596 156 | +1 1:1 2:-0.836992 3:-1 4:-1 5:-0.0769231 7:-1 8:-1 9:-1 10:-1 11:1 12:-1 13:-0.5 14:-0.99998 157 | +1 1:1 2:-0.87218 3:-0.988214 5:0.538462 6:0.5 7:-0.985263 8:-1 9:-1 10:-1 11:-1 13:-0.8 14:-0.9992 158 | -1 1:1 2:-0.285714 3:-0.940357 5:0.384615 6:-0.25 7:-0.997193 8:-1 9:-1 10:-1 11:-1 13:-0.88 14:-0.9999 159 | +1 1:1 2:-0.731729 3:-0.886786 4:-1 5:0.230769 6:-0.25 7:-0.783509 8:1 9:1 10:-0.820896 11:-1 13:-0.92 14:-1 160 | -1 1:1 2:0.0249625 3:-0.7025 5:1 7:-0.994035 8:-1 9:-1 10:-1 11:1 13:-0.48 14:-1 161 | -1 1:1 2:-0.388571 3:-0.821429 5:0.0769231 6:-0.25 7:-0.929825 8:-1 9:-1 10:-1 11:-1 13:-0.54 14:-0.99968 162 | -1 1:-1 2:-0.418647 3:-0.883929 5:-0.846154 6:-0.25 7:-0.962105 8:-1 9:-1 10:-1 11:1 13:-1 14:-1 163 | +1 1:1 2:-0.117895 3:-0.973214 4:-1 5:0.0769231 6:-0.25 7:-0.973684 8:1 9:1 10:-0.761194 11:1 13:-0.7 14:-0.99676 164 | +1 1:1 2:-0.37594 3:-0.711429 4:-1 5:-0.692308 7:-0.403509 8:1 9:1 10:-0.791045 11:1 13:-0.805 14:-1 165 | -1 1:1 2:-0.12782 3:-0.785714 5:-0.692308 7:-0.929825 8:1 9:-1 10:-1 11:-1 13:-1 14:-0.996 166 | -1 1:1 2:-0.864662 3:-0.988214 5:-0.846154 6:-0.25 7:-0.982456 8:-1 9:-1 10:-1 11:1 12:-1 13:-0.72 14:-1 167 | -1 1:1 2:-0.719398 3:-0.821429 5:-1 6:-1 7:-0.994035 8:-1 9:-1 10:-1 11:1 13:-0.9 14:-0.91584 168 | +1 1:-1 2:-0.736842 3:-0.395714 4:-1 5:1 6:-0.25 7:-0.827368 8:-1 9:-1 10:-1 11:-1 13:-0.836 14:-1 169 | -1 1:1 2:-0.874586 3:-0.985357 5:-0.230769 6:-0.25 7:-0.997193 8:-1 9:-1 10:-1 11:-1 13:-0.72 14:-0.985 170 | -1 1:1 2:-0.859549 3:-0.256071 4:-1 5:-0.230769 6:-0.25 7:-0.991228 8:1 9:-1 10:-1 11:-1 13:-0.88 14:-0.9925 171 | -1 1:-1 2:-0.581353 3:-0.892857 5:-0.0769231 6:-0.25 7:-0.859649 8:1 9:-1 10:-1 11:-1 12:-1 13:-0.632 14:-1 172 | +1 1:-1 2:-0.844511 3:-0.339286 4:-1 5:0.0769231 6:-0.25 7:-0.929825 8:1 9:1 10:-0.880597 11:1 13:-0.92 14:-0.99 173 | -1 1:1 2:-0.731729 3:-0.946429 5:-0.692308 6:-0.25 7:-0.888772 8:-1 9:1 10:-0.970149 11:1 13:-0.6 14:-0.99982 174 | -1 1:1 2:0.466165 3:-0.0892857 4:-1 5:0.0769231 6:0.75 7:-0.649123 8:1 9:-1 10:-1 11:-1 13:-0.888 14:-1 175 | +1 1:-1 2:-0.706767 3:-0.357143 5:0.538462 6:-0.25 7:-0.403509 8:1 9:1 10:-0.850746 11:1 13:-0.88 14:-1 176 | -1 1:1 2:-0.353383 3:-0.773929 5:1 6:0.75 7:-0.736842 8:1 9:-1 10:-1 11:1 13:-0.32 14:-1 177 | -1 1:-1 2:0.295639 3:-0.696429 4:-1 5:-1 6:-1 7:-0.649123 8:-1 9:-1 10:-1 11:1 13:-1 14:-0.99992 178 | -1 1:-1 2:0.190376 3:-0.988214 5:-1 6:-1 7:-1 8:-1 9:-1 10:-1 11:1 12:-1 13:-0.938 14:-0.99946 179 | +1 1:1 2:-0.175338 3:-0.711429 5:0.846154 6:0.75 7:-0.508772 8:1 9:1 10:-0.761194 11:-1 13:-0.68 14:-1 180 | +1 1:1 2:-0.145263 3:-0.64 5:0.538462 6:0.75 7:-0.105263 8:1 9:-1 10:-1 11:1 13:-0.908 14:-1 181 | -1 1:1 2:-0.175338 3:-0.910714 4:-1 5:0.230769 6:-0.25 7:-0.982456 8:-1 9:-1 10:-1 11:-1 13:-1 14:-0.9961 182 | -1 1:-1 2:-0.398496 3:-0.946429 5:-0.538462 7:-0.929825 8:1 9:1 10:-0.910448 11:1 13:-0.788 14:-1 183 | +1 1:1 2:-0.641504 3:-0.107143 5:0.846154 6:-0.25 7:-0.915088 8:1 9:1 10:1 11:1 13:-0.86 14:-0.99484 184 | +1 1:1 2:-0.681805 3:-0.526786 4:-1 5:-0.846154 6:-0.25 7:-0.614035 8:1 9:-1 10:-1 11:1 12:-1 13:-0.9 14:-1 185 | +1 1:1 2:-0.71188 3:-0.169643 4:-1 5:0.230769 6:-0.25 7:-0.941404 8:1 9:-1 10:-1 11:1 13:-0.84 14:-0.994 186 | -1 1:1 2:-0.491128 3:-0.821429 5:0.846154 6:0.75 7:-0.842105 8:-1 9:-1 10:-1 11:1 12:-1 13:-0.66 14:-1 187 | -1 1:1 2:-0.295639 3:-0.714286 5:0.0769231 7:-0.649123 8:1 9:-1 10:-1 11:1 12:-1 13:-0.72 14:-1 -------------------------------------------------------------------------------- /tests/baddata.scale: -------------------------------------------------------------------------------- 1 | test data --------------------------------------------------------------------------------