mirror of
https://github.com/php-win-ext/php-svm.git
synced 2026-03-24 00:52:06 +01:00
1479 lines
38 KiB
C
Executable File
1479 lines
38 KiB
C
Executable File
/*
|
|
* LibSVM extension for PHP
|
|
* Copyright (c) 2011, The php-svm authors
|
|
* All rights reserved.
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of the copyright holder nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL IAN BARBER BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "php_svm.h"
|
|
#include "php_svm_internal.h"
|
|
#include "php_ini.h" /* needed for 5.2 */
|
|
#include "Zend/zend_exceptions.h"
|
|
#include "ext/standard/info.h"
|
|
|
|
static zend_class_entry *php_svm_sc_entry;
|
|
static zend_class_entry *php_svm_model_sc_entry;
|
|
static zend_class_entry *php_svm_exception_sc_entry;
|
|
|
|
static zend_object_handlers svm_object_handlers;
|
|
static zend_object_handlers svm_model_object_handlers;
|
|
|
|
#ifndef TRUE
|
|
# define TRUE 1
|
|
# define FALSE 0
|
|
#endif
|
|
|
|
#define SUCCESS 0
|
|
|
|
#define SVM_MAX_LINE_SIZE 4096
|
|
#define SVM_THROW(message, code) \
|
|
zend_throw_exception(php_svm_exception_sc_entry, message, code); \
|
|
return;
|
|
|
|
#define SVM_ERROR_MSG_SIZE 512
|
|
#define SVM_THROW_LAST_ERROR(fallback, code) \
|
|
zend_throw_exception(php_svm_exception_sc_entry, (strlen(intern->last_error) ? intern->last_error : fallback), code); \
|
|
memset(intern->last_error, 0, SVM_ERROR_MSG_SIZE); \
|
|
return;
|
|
|
|
typedef enum SvmLongAttribute {
|
|
SvmLongAttributeMin = 100,
|
|
phpsvm_svm_type,
|
|
phpsvm_kernel_type,
|
|
phpsvm_degree,
|
|
SvmLongAttributeMax /* Always add before this */
|
|
} SvmLongAttribute;
|
|
|
|
typedef enum SvmDoubleAttribute {
|
|
SvmDoubleAttributeMin = 200,
|
|
phpsvm_gamma,
|
|
phpsvm_nu,
|
|
phpsvm_eps,
|
|
phpsvm_p,
|
|
phpsvm_coef0,
|
|
phpsvm_C,
|
|
phpsvm_cache_size,
|
|
SvmDoubleAttributeMax /* Always add before this */
|
|
} SvmDoubleAttribute;
|
|
|
|
typedef enum SvmBoolAttribute {
|
|
SvmBoolAttributeMin = 300,
|
|
phpsvm_shrinking,
|
|
phpsvm_probability,
|
|
SvmBoolAttributeMax /* Always add before this */
|
|
} SvmBoolAttribute;
|
|
|
|
/* ---- START HELPER FUNCS ---- */
|
|
|
|
static zend_always_inline php_svm_object* php_svm_fetch_svm_object(zend_object* obj)/*{{{*/
|
|
{
|
|
return (php_svm_object *)((char *)obj - XtOffsetOf(php_svm_object, zo));
|
|
}/*}}}*/
|
|
|
|
|
|
static zend_always_inline php_svm_model_object* php_svm_fetch_svm_model_object(zend_object* obj)/*{{{*/
|
|
{
|
|
return (php_svm_model_object *)((char *)obj - XtOffsetOf(php_svm_model_object, zo));
|
|
}/*}}}*/
|
|
|
|
static void print_null(const char *s) {}
|
|
|
|
static zend_bool php_svm_set_bool_attribute(php_svm_object *intern, SvmBoolAttribute name, zend_bool value) /*{{{*/
|
|
{
|
|
if (name >= SvmBoolAttributeMax) {
|
|
return FALSE;
|
|
}
|
|
|
|
switch (name) {
|
|
case phpsvm_shrinking:
|
|
intern->param.shrinking = value == TRUE ? 1 : 0;
|
|
break;
|
|
case phpsvm_probability:
|
|
intern->param.probability = value == TRUE ? 1 : 0;
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}/*}}}*/
|
|
|
|
static zend_bool php_svm_set_double_attribute(php_svm_object *intern, SvmDoubleAttribute name, double value) /*{{{*/
|
|
{
|
|
if (name >= SvmDoubleAttributeMax) {
|
|
return FALSE;
|
|
}
|
|
|
|
switch (name) {
|
|
case phpsvm_gamma:
|
|
intern->param.gamma = value;
|
|
break;
|
|
case phpsvm_nu:
|
|
intern->param.nu = value;
|
|
break;
|
|
case phpsvm_eps:
|
|
intern->param.eps = value;
|
|
break;
|
|
case phpsvm_cache_size:
|
|
intern->param.cache_size = value;
|
|
break;
|
|
case phpsvm_p:
|
|
intern->param.p = value;
|
|
break;
|
|
case phpsvm_coef0:
|
|
intern->param.coef0 = value;
|
|
break;
|
|
case phpsvm_C:
|
|
intern->param.C = value;
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}/*}}}*/
|
|
|
|
static zend_bool php_svm_set_long_attribute(php_svm_object *intern, SvmLongAttribute name, zend_long value) /*{{{*/
|
|
{
|
|
if (name >= SvmLongAttributeMax) {
|
|
return FALSE;
|
|
}
|
|
|
|
switch (name) {
|
|
case phpsvm_svm_type:
|
|
if( value != C_SVC &&
|
|
value != NU_SVC &&
|
|
value != ONE_CLASS &&
|
|
value != EPSILON_SVR &&
|
|
value != NU_SVR ) {
|
|
return FALSE;
|
|
}
|
|
intern->param.svm_type = (int)value;
|
|
break;
|
|
case phpsvm_kernel_type:
|
|
if( value != LINEAR &&
|
|
value != POLY &&
|
|
value != RBF &&
|
|
value != SIGMOID &&
|
|
value != PRECOMPUTED ) {
|
|
return FALSE;
|
|
}
|
|
intern->param.kernel_type = (int)value;
|
|
break;
|
|
case phpsvm_degree:
|
|
intern->param.degree = (int)value;
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}/*}}}*/
|
|
|
|
/** {{{ zend_bool php_svm_stream_to_array(php_svm_object *intern, php_stream *stream, zval *retval)
|
|
Take a stream containing lines of SVMLight format data and convert them into a PHP array for use by the training
|
|
function.
|
|
*/
|
|
static zend_bool php_svm_stream_to_array(php_svm_object *intern, php_stream *stream, zval *retval)
|
|
{
|
|
while (!php_stream_eof(stream)) {
|
|
char buf[SVM_MAX_LINE_SIZE];
|
|
size_t retlen = 0;
|
|
int line = 1;
|
|
|
|
/* Read line by line */
|
|
if (php_stream_get_line(stream, buf, SVM_MAX_LINE_SIZE, &retlen)) {
|
|
|
|
zval line_array, pz_label;
|
|
char *label, *ptr, *l = NULL;
|
|
|
|
ptr = buf;
|
|
label = php_strtok_r(ptr, " \t", &l);
|
|
|
|
if (!label) {
|
|
snprintf(intern->last_error, SVM_ERROR_MSG_SIZE, "Incorrect data format on line %d", line);
|
|
return FALSE;
|
|
}
|
|
|
|
/* The line array */
|
|
array_init(&line_array);
|
|
|
|
/* The label */
|
|
ZVAL_STRING(&pz_label, label);
|
|
convert_to_double(&pz_label);
|
|
|
|
/* Label is the first item in the line array */
|
|
add_next_index_zval(&line_array, &pz_label);
|
|
|
|
/* Read rest of the values on the line */
|
|
while (1) {
|
|
char *idx, *value;
|
|
zval pz_idx, pz_value;
|
|
|
|
/* idx:value format */
|
|
idx = php_strtok_r(NULL, ":", &l);
|
|
value = php_strtok_r(NULL, " \t", &l);
|
|
|
|
if (!value) {
|
|
break;
|
|
}
|
|
|
|
/* Make zvals and convert to correct types */
|
|
ZVAL_STRING(&pz_idx, idx);
|
|
convert_to_long(&pz_idx);
|
|
|
|
ZVAL_STRING(&pz_value, value);
|
|
convert_to_double(&pz_value);
|
|
|
|
add_index_zval(&line_array, Z_LVAL(pz_idx), &pz_value);
|
|
zval_dtor(&pz_idx);
|
|
}
|
|
add_next_index_zval(retval, &line_array);
|
|
line++;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ int _php_count_values(zval *array);
|
|
For a an array of arrays, count the number of items in all subarrays.
|
|
*/
|
|
static int _php_count_values(zval *array)
|
|
{
|
|
int values = 0;
|
|
zval *val;
|
|
|
|
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), val) {
|
|
if (Z_TYPE_P(val) == IS_ARRAY) {
|
|
values += zend_hash_num_elements(Z_ARRVAL_P(val));
|
|
}
|
|
} ZEND_HASH_FOREACH_END();
|
|
|
|
|
|
return values;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ static void php_svm_free_problem(struct svm_problem *problem) {
|
|
Free the generated problem.
|
|
*/
|
|
static void php_svm_free_problem(struct svm_problem *problem) {
|
|
if (problem->x) {
|
|
efree(problem->x);
|
|
}
|
|
|
|
if (problem->y) {
|
|
efree(problem->y);
|
|
}
|
|
|
|
efree(problem);
|
|
}
|
|
/* }}} */
|
|
|
|
#define ALLOC_XSPACE(model, sz) model->x_space = (struct svm_node *)emalloc(sz * sizeof(struct svm_node))
|
|
|
|
/* {{{ static zend_bool php_svm_read_array(php_svm_object *intern, php_svm_model_object *intern_model, zval *array)
|
|
Take a PHP array, and prepare libSVM problem data for training with.
|
|
*/
|
|
static struct svm_problem* php_svm_read_array(php_svm_object *intern, php_svm_model_object **intern_model_ptr, zval *array, zval * rzval)
|
|
{
|
|
zval *pzval;
|
|
char *err_msg = NULL;
|
|
zend_string *key;
|
|
char *endptr;
|
|
int i, num_labels, elements;
|
|
int j = 0, max_index = 0, inst_max_index = 0;
|
|
zend_ulong index;
|
|
struct svm_problem *problem;
|
|
//zval svm_mo;
|
|
zend_object * zobj;
|
|
php_svm_model_object * intern_model = NULL;
|
|
|
|
/* total number of elements */
|
|
elements = _php_count_values(array);
|
|
|
|
if (intern_model)
|
|
{
|
|
/* If reading multiple times make sure that we don't leak */
|
|
if (intern_model->x_space) {
|
|
efree(intern_model->x_space);
|
|
intern_model->x_space = NULL;
|
|
}
|
|
if (intern_model->model) {
|
|
#if LIBSVM_VERSION >= 300
|
|
svm_free_and_destroy_model(&intern_model->model);
|
|
#else
|
|
svm_destroy_model(intern_model->model);
|
|
#endif
|
|
|
|
intern_model->model = NULL;
|
|
}
|
|
} else {
|
|
// create model object
|
|
object_init_ex(rzval, php_svm_model_sc_entry);
|
|
|
|
zobj = Z_OBJ_P(rzval);
|
|
intern_model = (php_svm_model_object *)((char *)zobj - XtOffsetOf(php_svm_model_object, zo));
|
|
ALLOC_XSPACE(intern_model, elements);
|
|
}
|
|
|
|
|
|
/* Allocate the problem */
|
|
problem = emalloc(sizeof(struct svm_problem));
|
|
|
|
/* x and y */
|
|
num_labels = zend_hash_num_elements(HASH_OF(array));
|
|
|
|
/* Allocate space for the labels */
|
|
problem->y = emalloc(num_labels * sizeof(double));
|
|
|
|
/* allocate space for x */
|
|
problem->x = emalloc(num_labels * sizeof(struct svm_node *));
|
|
|
|
|
|
/* How many labels */
|
|
problem->l = num_labels;
|
|
|
|
i = 0;
|
|
/* Fill the problem */
|
|
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), pzval) {
|
|
|
|
zval *pz_label;
|
|
|
|
if (Z_TYPE_P(pzval) != IS_ARRAY) {
|
|
err_msg = "Data format error";
|
|
goto return_error;
|
|
}
|
|
|
|
if (zend_hash_num_elements(Z_ARRVAL_P(pzval)) < 2) {
|
|
err_msg = "Wrong amount of nodes in the sub-array";
|
|
goto return_error;
|
|
}
|
|
|
|
problem->x[i] = &intern_model->x_space[j];
|
|
|
|
zend_hash_internal_pointer_reset(Z_ARRVAL_P(pzval));
|
|
|
|
if ((pz_label = zend_hash_get_current_data_ex(Z_ARRVAL_P(pzval), &(Z_ARRVAL_P(pzval))->nInternalPointer)) != NULL) {
|
|
|
|
if (Z_TYPE_P(pz_label) != IS_DOUBLE) {
|
|
convert_to_double(pz_label);
|
|
}
|
|
problem->y[i] = Z_DVAL_P(pz_label);
|
|
} else {
|
|
err_msg = "The sub-array contains only the label. Missing index-value pairs";
|
|
goto return_error;
|
|
}
|
|
|
|
while (1) {
|
|
zval *pz_value;
|
|
|
|
if ((zend_hash_move_forward(Z_ARRVAL_P(pzval)) == SUCCESS) &&
|
|
((pz_value = zend_hash_get_current_data(Z_ARRVAL_P(pzval))) != NULL)) {
|
|
|
|
if (zend_hash_get_current_key(Z_ARRVAL_P(pzval), &key, &index) == HASH_KEY_IS_STRING) {
|
|
intern_model->x_space[j].index = (int) strtol(ZSTR_VAL(key), &endptr, 10);
|
|
} else {
|
|
intern_model->x_space[j].index = (int) index;
|
|
}
|
|
|
|
if (Z_TYPE_P(pz_value) != IS_DOUBLE) {
|
|
convert_to_double(pz_value);
|
|
}
|
|
intern_model->x_space[j].value = Z_DVAL_P(pz_value);
|
|
|
|
inst_max_index = intern_model->x_space[j].index;
|
|
j++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
intern_model->x_space[j++].index = -1;
|
|
|
|
if (inst_max_index > max_index) {
|
|
max_index = inst_max_index;
|
|
}
|
|
i++;
|
|
} ZEND_HASH_FOREACH_END();
|
|
|
|
if (intern->param.gamma == 0 && max_index > 0) {
|
|
intern->param.gamma = 1.0/max_index;
|
|
}
|
|
|
|
*intern_model_ptr = intern_model;
|
|
|
|
return problem;
|
|
|
|
return_error:
|
|
php_svm_free_problem(problem);
|
|
if (err_msg) {
|
|
snprintf(intern->last_error, SVM_ERROR_MSG_SIZE, "%s", err_msg);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ static zend_bool php_svm_train(php_svm_object *intern, php_svm_model_object *intern_model, struct svm_problem *problem)
|
|
Train based on a libsvm problem structure
|
|
*/
|
|
static zend_bool php_svm_train(php_svm_object *intern, php_svm_model_object *intern_model, struct svm_problem *problem)
|
|
{
|
|
const char *err_msg = NULL;
|
|
err_msg = svm_check_parameter(problem, &(intern->param));
|
|
if (err_msg) {
|
|
snprintf(intern->last_error, SVM_ERROR_MSG_SIZE, "%s", err_msg);
|
|
return FALSE;
|
|
}
|
|
|
|
intern_model->model = svm_train(problem, &(intern->param));
|
|
|
|
/* Failure ? */
|
|
if (!intern_model->model) {
|
|
snprintf(intern->last_error, SVM_ERROR_MSG_SIZE, "Failed to train using the data");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ static zval* php_svm_get_data_from_param(php_svm_object *intern, zval *zparam)
|
|
Take an incoming parameter and convert it into a PHP array of svmlight style data.
|
|
*/
|
|
static int php_svm_get_data_from_param(php_svm_object *intern, zval *zparam, zval ** data_ptr)
|
|
{
|
|
zend_bool our_stream = 0;
|
|
zend_bool need_read = 1;
|
|
php_stream *stream = NULL;
|
|
|
|
switch (Z_TYPE_P(zparam)) {
|
|
case IS_STRING:
|
|
stream = php_stream_open_wrapper(Z_STRVAL_P(zparam), "r", REPORT_ERRORS, NULL);
|
|
our_stream = 1;
|
|
break;
|
|
|
|
case IS_RESOURCE:
|
|
php_stream_from_zval_no_verify(stream, zparam);
|
|
our_stream = 0;
|
|
break;
|
|
|
|
case IS_ARRAY:
|
|
our_stream = 0;
|
|
need_read = 0;
|
|
break;
|
|
|
|
default:
|
|
snprintf(intern->last_error, SVM_ERROR_MSG_SIZE, "Incorrect parameter type, expecting string, stream or an array");
|
|
return FALSE;
|
|
break;
|
|
}
|
|
|
|
/* If we got stream then read it in */
|
|
if (need_read) {
|
|
if (!stream) {
|
|
snprintf(intern->last_error, SVM_ERROR_MSG_SIZE, "Failed to open the data file");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!php_svm_stream_to_array(intern, stream, *data_ptr)) {
|
|
zval_dtor(*data_ptr);
|
|
efree(data_ptr);
|
|
if (our_stream) {
|
|
php_stream_close(stream);
|
|
}
|
|
snprintf(intern->last_error, SVM_ERROR_MSG_SIZE, "Failed to read the data");
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
*data_ptr = zparam;
|
|
}
|
|
|
|
if (our_stream) {
|
|
php_stream_close(stream);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ static svm_node* php_svm_get_data_from_array(zval *arr)
|
|
Take an array of training data and turn it into an array of svm nodes.
|
|
*/
|
|
static struct svm_node* php_svm_get_data_from_array(zval* arr)
|
|
{
|
|
struct svm_node *x;
|
|
HashTable *arr_hash;
|
|
int array_count, i;
|
|
char *endptr;
|
|
zval temp;
|
|
zend_string *key;
|
|
zend_ulong num_key;
|
|
zval *val;
|
|
|
|
arr_hash = Z_ARRVAL_P(arr);
|
|
array_count = zend_hash_num_elements(arr_hash);
|
|
|
|
/* need 1 extra to indicate the end */
|
|
x = safe_emalloc((array_count + 1), sizeof(struct svm_node), 0);
|
|
i = 0;
|
|
|
|
/* Loop over the array in the argument and convert into svm_nodes for the prediction */
|
|
ZEND_HASH_FOREACH_KEY_VAL(arr_hash, num_key, key, val)
|
|
{
|
|
if (key) {
|
|
x[i].index = (int) strtol(ZSTR_VAL(key), &endptr, 10);
|
|
} else {
|
|
x[i].index = (int) num_key;
|
|
}
|
|
temp = *val;
|
|
zval_copy_ctor(&temp);
|
|
convert_to_double(&temp);
|
|
x[i].value = Z_DVAL(temp);
|
|
zval_dtor(&temp);
|
|
i++;
|
|
} ZEND_HASH_FOREACH_END();
|
|
|
|
/* needed so the predictor knows when to end */
|
|
x[i].index = -1;
|
|
|
|
return x;
|
|
}
|
|
/* }}} */
|
|
|
|
/* ---- END HELPER FUNCS ---- */
|
|
|
|
|
|
/* ---- START SVM ---- */
|
|
|
|
/* {{{ SVM SVM::__construct();
|
|
The constructor
|
|
*/
|
|
PHP_METHOD(svm, __construct)
|
|
{
|
|
php_svm_object *intern;
|
|
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
/* Actually this should just return. Keep for BC. */
|
|
SVM_THROW("Invalid parameters passed to constructor", 154);
|
|
}
|
|
|
|
intern = php_svm_fetch_svm_object(Z_OBJ_P(getThis()));
|
|
|
|
/* Setup the default parameters to match those in libsvm's svm_train */
|
|
php_svm_set_long_attribute(intern, phpsvm_svm_type, C_SVC);
|
|
php_svm_set_long_attribute(intern, phpsvm_kernel_type, RBF);
|
|
php_svm_set_long_attribute(intern, phpsvm_degree, 3);
|
|
php_svm_set_double_attribute(intern, phpsvm_gamma, 0);
|
|
php_svm_set_double_attribute(intern, phpsvm_coef0, 0);
|
|
php_svm_set_double_attribute(intern, phpsvm_nu, 0.5);
|
|
php_svm_set_double_attribute(intern, phpsvm_cache_size, 100.0);
|
|
php_svm_set_double_attribute(intern, phpsvm_C, 1);
|
|
php_svm_set_double_attribute(intern, phpsvm_eps, 1e-3);
|
|
php_svm_set_double_attribute(intern, phpsvm_p, 0.1);
|
|
php_svm_set_bool_attribute(intern, phpsvm_shrinking, TRUE);
|
|
php_svm_set_bool_attribute(intern, phpsvm_probability, FALSE);
|
|
return;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ array SVM::getOptions();
|
|
Get training parameters, in an array.
|
|
*/
|
|
PHP_METHOD(svm, getOptions)
|
|
{
|
|
php_svm_object *intern;
|
|
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
intern = php_svm_fetch_svm_object(Z_OBJ_P(getThis()));
|
|
|
|
array_init(return_value);
|
|
|
|
add_index_long(return_value, phpsvm_svm_type, intern->param.svm_type);
|
|
add_index_long(return_value, phpsvm_kernel_type, intern->param.kernel_type);
|
|
add_index_long(return_value, phpsvm_degree, intern->param.degree);
|
|
add_index_long(return_value, phpsvm_coef0, intern->param.shrinking);
|
|
add_index_long(return_value, phpsvm_probability, intern->param.probability == 1 ? TRUE : FALSE);
|
|
add_index_long(return_value, phpsvm_shrinking, intern->param.shrinking == 1 ? TRUE : FALSE);
|
|
|
|
add_index_double(return_value, phpsvm_gamma, intern->param.gamma);
|
|
add_index_double(return_value, phpsvm_coef0, intern->param.coef0);
|
|
add_index_double(return_value, phpsvm_nu, intern->param.nu);
|
|
add_index_double(return_value, phpsvm_cache_size, intern->param.cache_size);
|
|
add_index_double(return_value, phpsvm_C, intern->param.C);
|
|
add_index_double(return_value, phpsvm_eps, intern->param.eps);
|
|
add_index_double(return_value, phpsvm_p, intern->param.p);
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ int SVM::setOptopms(array params);
|
|
Takes an array of parameters and sets the training options to match them.
|
|
Only used by the training functions, will not modify an existing model.
|
|
*/
|
|
PHP_METHOD(svm, setOptions)
|
|
{
|
|
HashTable *params_ht;
|
|
php_svm_object *intern;
|
|
zval *params, *pzval;
|
|
zend_string *string_key = NULL;
|
|
zend_ulong num_key;
|
|
zend_bool boolTmp;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", ¶ms) == FAILURE) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
params_ht = HASH_OF(params);
|
|
|
|
if (zend_hash_num_elements(params_ht) == 0) {
|
|
return;
|
|
}
|
|
|
|
intern = php_svm_fetch_svm_object(Z_OBJ_P(getThis()));
|
|
|
|
for (zend_hash_internal_pointer_reset(params_ht);
|
|
(pzval = zend_hash_get_current_data(params_ht)) != NULL;
|
|
zend_hash_move_forward(params_ht)) {
|
|
|
|
zval tmp_zval, *tmp_pzval;
|
|
|
|
if (zend_hash_get_current_key(params_ht, &string_key, &num_key) != HASH_KEY_IS_LONG) {
|
|
continue; /* Ignore the arg (TODO: throw exception?) */
|
|
}
|
|
|
|
/* Make sure we don't modify the original array */
|
|
tmp_zval = *pzval;
|
|
zval_copy_ctor(&tmp_zval);
|
|
tmp_pzval = &tmp_zval;
|
|
|
|
/* Long attribute */
|
|
if (num_key > SvmLongAttributeMin && num_key < SvmLongAttributeMax) {
|
|
|
|
if (Z_TYPE_P(tmp_pzval) != IS_LONG) {
|
|
convert_to_long(tmp_pzval);
|
|
}
|
|
|
|
if (!php_svm_set_long_attribute(intern, num_key, Z_LVAL_P(tmp_pzval))) {
|
|
SVM_THROW("Failed to set the attribute", 999);
|
|
}
|
|
|
|
/* Double attribute */
|
|
} else if (num_key > SvmDoubleAttributeMin && num_key < SvmDoubleAttributeMax) {
|
|
|
|
if (Z_TYPE_P(tmp_pzval) != IS_DOUBLE) {
|
|
convert_to_double(tmp_pzval);
|
|
}
|
|
|
|
if (!php_svm_set_double_attribute(intern, num_key, Z_DVAL_P(tmp_pzval))) {
|
|
SVM_THROW("Failed to set the attribute", 999);
|
|
}
|
|
/* Bool attribute */
|
|
} else if(num_key > SvmBoolAttributeMin && num_key < SvmBoolAttributeMax) {
|
|
|
|
if ((Z_TYPE_P(tmp_pzval) != IS_TRUE) && (Z_TYPE_P(tmp_pzval) != IS_FALSE)) {
|
|
convert_to_boolean(tmp_pzval);
|
|
}
|
|
|
|
boolTmp = FALSE;
|
|
if(Z_TYPE_P(tmp_pzval) == IS_TRUE) {
|
|
boolTmp = TRUE;
|
|
}
|
|
|
|
if (!php_svm_set_bool_attribute(intern, num_key, boolTmp)) {
|
|
SVM_THROW("Failed to set the attribute", 999);
|
|
}
|
|
} else {
|
|
continue; /* Ignore the arg (TODO: throw exception?) */
|
|
}
|
|
|
|
tmp_pzval = NULL;
|
|
}
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ double SVM::crossvalidate(mixed string|resource|array, long folds);
|
|
Cross validate a the SVM parameters on the training data for tuning parameters. Will attempt to train then classify
|
|
on different segments of the training data (the total number of segments is the folds parameter). The training data
|
|
can be supplied as with the train function. For SVM classification, this will we return the correct percentage,
|
|
for regression the mean squared error.
|
|
@throws SVMException if the data format is incorrect
|
|
*/
|
|
PHP_METHOD(svm, crossvalidate)
|
|
{
|
|
int i;
|
|
int total_correct = 0;
|
|
zend_long nrfolds;
|
|
double total_error = 0;
|
|
double sumv = 0, sumy = 0, sumvv = 0, sumyy = 0, sumvy = 0;
|
|
struct svm_problem *problem;
|
|
double returnval = 0.0;
|
|
double *target;
|
|
php_svm_object *intern;
|
|
php_svm_model_object *intern_return = NULL;
|
|
zval *zparam, data;
|
|
zval * data_p = &data;
|
|
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &zparam, &nrfolds) == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
intern = php_svm_fetch_svm_object(Z_OBJ_P(getThis()));
|
|
|
|
array_init(data_p);
|
|
int ret = php_svm_get_data_from_param(intern, zparam, &data_p);
|
|
if(ret != TRUE) {
|
|
SVM_THROW_LAST_ERROR("Could not load data", 234);
|
|
}
|
|
|
|
intern->param.nr_weight = 0;
|
|
|
|
problem = php_svm_read_array(intern, &intern_return, data_p, return_value);
|
|
if(!problem) {
|
|
SVM_THROW_LAST_ERROR("Cross validation failed", 1001);
|
|
}
|
|
|
|
target = emalloc(problem->l * sizeof(double));
|
|
svm_cross_validation(problem, &(intern->param), nrfolds, target);
|
|
if(intern->param.svm_type == EPSILON_SVR || intern->param.svm_type == NU_SVR) {
|
|
for(i=0;i<problem->l;i++) {
|
|
double y = problem->y[i];
|
|
double v = target[i];
|
|
total_error += (v-y)*(v-y);
|
|
sumv += v;
|
|
sumy += y;
|
|
sumvv += v*v;
|
|
sumyy += y*y;
|
|
sumvy += v*y;
|
|
}
|
|
returnval = (total_error/problem->l); // return total_error divded by number of examples
|
|
} else {
|
|
for(i=0; i<problem->l; i++) {
|
|
if(target[i] == problem->y[i]) {
|
|
++total_correct;
|
|
}
|
|
}
|
|
returnval = 1.0*total_correct/problem->l;
|
|
}
|
|
|
|
if (data_p != zparam) {
|
|
zval_dtor(data_p);
|
|
}
|
|
efree(target);
|
|
php_svm_free_problem(problem);
|
|
|
|
RETURN_DOUBLE(returnval);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ SVMModel SVM::train(mixed string|resource|array, [array classWeights]);
|
|
Train a SVM based on the SVMLight format data either in a file, an array, or in a previously opened stream.
|
|
@throws SVMException if the data format is incorrect. Can optionally accept a set of weights that will
|
|
be used to multiply C. Only useful for C_SVC kernels. These should be in the form array(class (int) => weight (float))
|
|
*/
|
|
PHP_METHOD(svm, train)
|
|
{
|
|
php_svm_object *intern;
|
|
php_svm_model_object *intern_return = NULL;
|
|
struct svm_problem *problem;
|
|
zval data;
|
|
zval *zparam;
|
|
zval *weights;
|
|
zval *pzval;
|
|
HashTable *weights_ht;
|
|
int i;
|
|
zend_string *key;
|
|
zend_ulong index;
|
|
zval * data_p = &data;
|
|
|
|
zend_bool status = 0;
|
|
weights = 0;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|a!", &zparam, &weights) == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
intern = php_svm_fetch_svm_object(Z_OBJ_P(getThis()));
|
|
|
|
if(weights && intern->param.svm_type != C_SVC) {
|
|
SVM_THROW("Weights can only be supplied for C_SyVC training", 424);
|
|
}
|
|
array_init(data_p);
|
|
int ret = php_svm_get_data_from_param(intern, zparam, &data_p);
|
|
if(ret != TRUE) {
|
|
zval_dtor(data_p);
|
|
SVM_THROW_LAST_ERROR("Could not load data", 234);
|
|
}
|
|
|
|
if(weights) {
|
|
weights_ht = Z_ARRVAL_P(weights);
|
|
if(zend_hash_num_elements(weights_ht) > 0) {
|
|
intern->param.nr_weight = zend_hash_num_elements(weights_ht);
|
|
intern->param.weight_label = emalloc(intern->param.nr_weight * sizeof(int));
|
|
intern->param.weight = emalloc(intern->param.nr_weight * sizeof(double));
|
|
|
|
for (zend_hash_internal_pointer_reset(weights_ht), i = 0;
|
|
(pzval = zend_hash_get_current_data(weights_ht)) != NULL;
|
|
zend_hash_move_forward(weights_ht), i++) {
|
|
|
|
zval tmp_zval, *tmp_pzval;
|
|
|
|
if (zend_hash_get_current_key(weights_ht, &key, &index) == HASH_KEY_IS_LONG) {
|
|
intern->param.weight_label[i] = (int)index;
|
|
|
|
/* Make sure we don't modify the original array */
|
|
tmp_zval = *pzval;
|
|
zval_copy_ctor(&tmp_zval);
|
|
tmp_pzval = &tmp_zval;
|
|
convert_to_double(tmp_pzval);
|
|
intern->param.weight[i] = Z_DVAL_P(tmp_pzval);
|
|
tmp_pzval = NULL;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
intern->param.nr_weight = 0;
|
|
}
|
|
|
|
problem = php_svm_read_array(intern, &intern_return, data_p, return_value);
|
|
|
|
|
|
if(problem != NULL) {
|
|
if (php_svm_train(intern, intern_return, problem)) {
|
|
status = 1;
|
|
}
|
|
php_svm_free_problem(problem);
|
|
}
|
|
|
|
if(weights) {
|
|
efree(intern->param.weight_label);
|
|
efree(intern->param.weight);
|
|
}
|
|
|
|
zval_dtor(&data);
|
|
|
|
|
|
if (!status) {
|
|
SVM_THROW_LAST_ERROR("Training failed", 1000);
|
|
}
|
|
return;
|
|
}
|
|
/* }}} */
|
|
|
|
/* ---- END SVM ---- */
|
|
|
|
/* ---- START SVMMODEL ---- */
|
|
|
|
/** {{{ SvmModel::__construct([string filename])
|
|
Constructs an svm model
|
|
*/
|
|
PHP_METHOD(svmmodel, __construct)
|
|
{
|
|
php_svm_model_object *intern;
|
|
char *filename = NULL;
|
|
size_t filename_len;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &filename, &filename_len) == FAILURE) {
|
|
SVM_THROW("Invalid parameters passed to constructor", 154);
|
|
}
|
|
|
|
if (!filename) {
|
|
return;
|
|
}
|
|
|
|
intern = php_svm_fetch_svm_model_object(Z_OBJ_P(getThis()));
|
|
intern->model = svm_load_model(filename);
|
|
|
|
if (!intern->model) {
|
|
SVM_THROW("Failed to load the model", 1233);
|
|
}
|
|
|
|
return;
|
|
}
|
|
/* }}} */
|
|
|
|
/** {{{ SvmModel::load(string filename)
|
|
Loads the svm model from a file
|
|
*/
|
|
PHP_METHOD(svmmodel, load)
|
|
{
|
|
php_svm_model_object *intern;
|
|
char *filename = NULL;
|
|
size_t filename_len;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &filename, &filename_len) == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
intern = php_svm_fetch_svm_model_object(Z_OBJ_P(getThis()));
|
|
intern->model = svm_load_model(filename);
|
|
|
|
if (!intern->model) {
|
|
SVM_THROW("Failed to load the model", 1233);
|
|
}
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
/** {{{ SvmModel::save(string filename)
|
|
Saves the svm model to a file
|
|
*/
|
|
PHP_METHOD(svmmodel, save)
|
|
{
|
|
php_svm_model_object *intern;
|
|
char *filename;
|
|
size_t filename_len;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &filename, &filename_len) == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
intern = php_svm_fetch_svm_model_object(Z_OBJ_P(getThis()));
|
|
|
|
if (!intern->model) {
|
|
SVM_THROW("The object does not contain a model", 2321);
|
|
}
|
|
|
|
if (svm_save_model(filename, intern->model) != 0) {
|
|
SVM_THROW("Failed to save the model", 121);
|
|
}
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/** {{{ SvmModel::getSvmType()
|
|
Gets the type of SVM the model was trained with
|
|
*/
|
|
PHP_METHOD(svmmodel, getSvmType)
|
|
{
|
|
php_svm_model_object *intern;
|
|
int svm_type;
|
|
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
intern = php_svm_fetch_svm_model_object(Z_OBJ_P(getThis()));
|
|
if(!intern->model) {
|
|
SVM_THROW("No model available", 106);
|
|
}
|
|
|
|
svm_type = svm_get_svm_type(intern->model);
|
|
|
|
RETURN_LONG(svm_type);
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
|
|
/** {{{ SvmModel::getNrClass()
|
|
Gets the number of classes the model was trained with. Note that for a regression
|
|
or 1 class model 2 is returned.
|
|
*/
|
|
PHP_METHOD(svmmodel, getNrClass)
|
|
{
|
|
php_svm_model_object *intern;
|
|
int nr_classes;
|
|
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
intern = php_svm_fetch_svm_model_object(Z_OBJ_P(getThis()));
|
|
if(!intern->model) {
|
|
SVM_THROW("No model available", 106);
|
|
}
|
|
|
|
nr_classes = svm_get_nr_class(intern->model);
|
|
|
|
RETURN_LONG(nr_classes);
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/** {{{ SvmModel::getLabels()
|
|
Gets an array of labels that the model was trained with. For regression
|
|
and one class models, an empty array is returned.
|
|
*/
|
|
PHP_METHOD(svmmodel, getLabels)
|
|
{
|
|
php_svm_model_object *intern;
|
|
int nr_classes;
|
|
int* labels;
|
|
int i;
|
|
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
intern = php_svm_fetch_svm_model_object(Z_OBJ_P(getThis()));
|
|
if(!intern->model) {
|
|
SVM_THROW("No model available", 106);
|
|
}
|
|
|
|
nr_classes = svm_get_nr_class(intern->model);
|
|
labels = safe_emalloc(nr_classes, sizeof(int), 0);
|
|
svm_get_labels(intern->model, labels);
|
|
|
|
array_init(return_value);
|
|
|
|
for( i = 0; i < nr_classes; i++ ) {
|
|
add_next_index_long(return_value, labels[i]);
|
|
}
|
|
|
|
efree(labels);
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/** {{{ SvmModel::checkProbabilityModel()
|
|
Returns true if the model contains probability estimates
|
|
*/
|
|
PHP_METHOD(svmmodel, checkProbabilityModel)
|
|
{
|
|
php_svm_model_object *intern;
|
|
int prob;
|
|
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
intern = php_svm_fetch_svm_model_object(Z_OBJ_P(getThis()));
|
|
if(!intern->model) {
|
|
SVM_THROW("No model available", 106);
|
|
}
|
|
|
|
prob = svm_check_probability_model(intern->model);
|
|
|
|
RETURN_BOOL( prob );
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/** {{{ SvmModel::getSvrProbability()
|
|
For regression models, returns a sigma value. If there is no probability
|
|
information or the model is not SVR, 0 is returned.
|
|
*/
|
|
PHP_METHOD(svmmodel, getSvrProbability)
|
|
{
|
|
php_svm_model_object *intern;
|
|
double svr_prob;
|
|
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
intern = php_svm_fetch_svm_model_object(Z_OBJ_P(getThis()));
|
|
if(!intern->model) {
|
|
SVM_THROW("No model available", 106);
|
|
}
|
|
|
|
svr_prob = svm_get_svr_probability(intern->model);
|
|
|
|
RETURN_DOUBLE(svr_prob);
|
|
}
|
|
/* }}} */
|
|
|
|
/** {{{ SvmModel::predict(array data)
|
|
Predicts based on the model
|
|
*/
|
|
PHP_METHOD(svmmodel, predict)
|
|
{
|
|
php_svm_model_object *intern;
|
|
double predict_label;
|
|
struct svm_node *x;
|
|
zval *arr;
|
|
|
|
/* we want an array of data to be passed in */
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &arr) == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
intern = php_svm_fetch_svm_model_object(Z_OBJ_P(getThis()));
|
|
if(!intern->model) {
|
|
SVM_THROW("No model available to classify with", 106);
|
|
}
|
|
|
|
x = php_svm_get_data_from_array(arr);
|
|
predict_label = svm_predict(intern->model, x);
|
|
efree(x);
|
|
|
|
RETURN_DOUBLE(predict_label);
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
/** {{{ SvmModel::predict_probability(array data, array probabilities)
|
|
Predicts based on the model
|
|
*/
|
|
PHP_METHOD(svmmodel, predict_probability)
|
|
{
|
|
php_svm_model_object *intern;
|
|
double predict_probability;
|
|
int nr_classes, i;
|
|
double *estimates;
|
|
struct svm_node *x;
|
|
int *labels;
|
|
zval *arr;
|
|
zval *retarr = NULL;
|
|
|
|
/* we want an array of data to be passed in */
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "az/", &arr, &retarr) == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
intern = php_svm_fetch_svm_model_object(Z_OBJ_P(getThis()));
|
|
if(!intern->model) {
|
|
SVM_THROW("No model available to classify with", 106);
|
|
}
|
|
|
|
x = php_svm_get_data_from_array(arr);
|
|
nr_classes = svm_get_nr_class(intern->model);
|
|
estimates = safe_emalloc(nr_classes, sizeof(double), 0);
|
|
labels = safe_emalloc(nr_classes, sizeof(int), 0);
|
|
predict_probability = svm_predict_probability(intern->model, x, estimates);
|
|
|
|
if (retarr != NULL) {
|
|
zval_dtor(retarr);
|
|
array_init(retarr);
|
|
svm_get_labels(intern->model, labels);
|
|
for (i = 0; i < nr_classes; ++i) {
|
|
add_index_double(retarr, labels[i], estimates[i]);
|
|
}
|
|
}
|
|
|
|
efree(estimates);
|
|
efree(labels);
|
|
efree(x);
|
|
|
|
RETURN_DOUBLE(predict_probability);
|
|
}
|
|
/* }}} */
|
|
|
|
/* ---- END SVMMODEL ---- */
|
|
|
|
static void php_svm_object_free_storage(zend_object *object)/*{{{*/
|
|
{
|
|
php_svm_object *intern;
|
|
|
|
intern = (php_svm_object *)((char *)object - XtOffsetOf(php_svm_object, zo));
|
|
|
|
if (!intern) {
|
|
return;
|
|
}
|
|
|
|
zend_object_std_dtor(&intern->zo);
|
|
}/*}}}*/
|
|
|
|
static zend_object* php_svm_object_new_ex(zend_class_entry *class_type, php_svm_object **ptr)/*{{{*/
|
|
{
|
|
php_svm_object *intern;
|
|
|
|
/* Allocate memory for the internal structure */
|
|
intern = (php_svm_object *) ecalloc(1, sizeof(php_svm_object) + zend_object_properties_size(class_type));
|
|
|
|
if (ptr) {
|
|
*ptr = intern;
|
|
}
|
|
|
|
/* Null model by default */
|
|
memset(intern->last_error, 0, 512);
|
|
|
|
zend_object_std_init(&intern->zo, class_type);
|
|
object_properties_init(&intern->zo, class_type);
|
|
intern->zo.handlers = &svm_object_handlers;
|
|
|
|
return &intern->zo;
|
|
}/*}}}*/
|
|
|
|
static zend_object * php_svm_object_new(zend_class_entry *class_type)/*{{{*/
|
|
{
|
|
return php_svm_object_new_ex(class_type, NULL);
|
|
}/*}}}*/
|
|
|
|
static void php_svm_model_object_free_storage(zend_object *object)/*{{{*/
|
|
{
|
|
php_svm_model_object *intern;
|
|
|
|
intern = (php_svm_model_object *)((char *)object - XtOffsetOf(php_svm_model_object, zo));
|
|
|
|
if (!intern) {
|
|
return;
|
|
}
|
|
|
|
if (intern->model) {
|
|
#if LIBSVM_VERSION >= 300
|
|
svm_free_and_destroy_model(&intern->model);
|
|
#else
|
|
svm_destroy_model(intern->model);
|
|
#endif
|
|
|
|
efree(intern->model);
|
|
intern->model = NULL;
|
|
}
|
|
|
|
if (intern->x_space) {
|
|
efree(intern->x_space);
|
|
intern->x_space = NULL;
|
|
}
|
|
|
|
zend_object_std_dtor(&intern->zo);
|
|
}/*}}}*/
|
|
|
|
|
|
static zend_object * php_svm_model_object_new_ex(zend_class_entry *class_type, php_svm_model_object **ptr, size_t problemSize)/*{{{*/
|
|
{
|
|
php_svm_model_object *intern;
|
|
|
|
/* Allocate memory for the internal structure */
|
|
intern = (php_svm_model_object *) ecalloc(1, sizeof(php_svm_model_object) + zend_object_properties_size(class_type));
|
|
|
|
if (ptr) {
|
|
*ptr = intern;
|
|
}
|
|
|
|
if (problemSize) {
|
|
ALLOC_XSPACE(intern, problemSize);
|
|
} else {
|
|
intern->x_space = NULL;
|
|
}
|
|
intern->model = NULL;
|
|
|
|
zend_object_std_init(&intern->zo, class_type);
|
|
|
|
object_properties_init(&intern->zo, class_type);
|
|
|
|
intern->zo.handlers = &svm_model_object_handlers;
|
|
|
|
return &intern->zo;
|
|
}/*}}}*/
|
|
|
|
|
|
static zend_object * php_svm_model_object_new(zend_class_entry *class_type)/*{{{*/
|
|
{
|
|
return php_svm_model_object_new_ex(class_type, NULL, 0);
|
|
}/*}}}*/
|
|
|
|
/* {{{ SVM arginfo */
|
|
ZEND_BEGIN_ARG_INFO_EX(svm_empty_args, 0, 0, 0)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(svm_train_args, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, problem)
|
|
ZEND_ARG_INFO(0, weights)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(svm_crossvalidate_args, 0, 0, 2)
|
|
ZEND_ARG_INFO(0, problem)
|
|
ZEND_ARG_INFO(0, number_of_folds)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(svm_params_args, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, params)
|
|
ZEND_END_ARG_INFO()
|
|
/* }}} */
|
|
|
|
static zend_function_entry php_svm_class_methods[] =/*{{{*/
|
|
{
|
|
PHP_ME(svm, __construct, svm_empty_args, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
|
|
PHP_ME(svm, getOptions, svm_empty_args, ZEND_ACC_PUBLIC)
|
|
PHP_ME(svm, setOptions, svm_params_args, ZEND_ACC_PUBLIC)
|
|
PHP_ME(svm, train, svm_train_args, ZEND_ACC_PUBLIC)
|
|
PHP_ME(svm, crossvalidate, svm_crossvalidate_args, ZEND_ACC_PUBLIC)
|
|
{ NULL, NULL, NULL }
|
|
};/*}}}*/
|
|
|
|
/* {{{ Model arginfo */
|
|
ZEND_BEGIN_ARG_INFO_EX(svm_model_construct_args, 0, 0, 0)
|
|
ZEND_ARG_INFO(0, filename)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(svm_model_predict_args, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, data)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(svm_model_predict_probs_args, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, data)
|
|
ZEND_ARG_INFO(1, probabilities)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(svm_model_file_args, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, filename)
|
|
ZEND_END_ARG_INFO()
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(svm_model_info_args, 0, 0, 0)
|
|
ZEND_END_ARG_INFO()
|
|
/* }}} */
|
|
|
|
static zend_function_entry php_svm_model_class_methods[] =/*{{{*/
|
|
{
|
|
PHP_ME(svmmodel, __construct, svm_model_construct_args, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
|
|
PHP_ME(svmmodel, save, svm_model_file_args, ZEND_ACC_PUBLIC)
|
|
PHP_ME(svmmodel, load, svm_model_file_args, ZEND_ACC_PUBLIC)
|
|
PHP_ME(svmmodel, getSvmType, svm_model_info_args, ZEND_ACC_PUBLIC)
|
|
PHP_ME(svmmodel, getLabels, svm_model_info_args, ZEND_ACC_PUBLIC)
|
|
PHP_ME(svmmodel, getNrClass, svm_model_info_args, ZEND_ACC_PUBLIC)
|
|
PHP_ME(svmmodel, getSvrProbability, svm_model_info_args, ZEND_ACC_PUBLIC)
|
|
PHP_ME(svmmodel, checkProbabilityModel, svm_model_info_args, ZEND_ACC_PUBLIC)
|
|
PHP_ME(svmmodel, predict, svm_model_predict_args, ZEND_ACC_PUBLIC)
|
|
PHP_ME(svmmodel, predict_probability, svm_model_predict_probs_args, ZEND_ACC_PUBLIC)
|
|
{ NULL, NULL, NULL }
|
|
};/*}}}*/
|
|
|
|
PHP_MINIT_FUNCTION(svm)/*{{{*/
|
|
{
|
|
zend_class_entry ce;
|
|
memcpy(&svm_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
|
|
svm_object_handlers.free_obj = php_svm_object_free_storage;
|
|
svm_object_handlers.offset = XtOffsetOf(php_svm_object, zo);
|
|
|
|
memcpy(&svm_model_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
|
|
svm_model_object_handlers.free_obj = php_svm_model_object_free_storage;
|
|
svm_model_object_handlers.offset = XtOffsetOf(php_svm_model_object, zo);
|
|
|
|
INIT_CLASS_ENTRY(ce, "svm", php_svm_class_methods);
|
|
ce.create_object = php_svm_object_new;
|
|
php_svm_sc_entry = zend_register_internal_class(&ce);
|
|
|
|
INIT_CLASS_ENTRY(ce, "svmmodel", php_svm_model_class_methods);
|
|
ce.create_object = php_svm_model_object_new;
|
|
php_svm_model_sc_entry = zend_register_internal_class(&ce);
|
|
|
|
INIT_CLASS_ENTRY(ce, "svmexception", NULL);
|
|
#if PHP_VERSION_ID < 80500
|
|
php_svm_exception_sc_entry = zend_register_internal_class_ex(&ce, zend_exception_get_default());
|
|
#else
|
|
php_svm_exception_sc_entry = zend_register_internal_class_ex(&ce, zend_ce_exception);
|
|
#endif
|
|
php_svm_exception_sc_entry->ce_flags |= ZEND_ACC_FINAL;
|
|
|
|
/* Redirect the lib svm output */
|
|
#if LIBSVM_VERSION >= 291
|
|
svm_set_print_string_function(&print_null);
|
|
#else
|
|
svm_print_string = &print_null;
|
|
#endif
|
|
|
|
#define SVM_REGISTER_CONST_LONG(const_name, value) \
|
|
zend_declare_class_constant_long(php_svm_sc_entry, const_name, sizeof(const_name)-1, value);
|
|
|
|
/* SVM types */
|
|
SVM_REGISTER_CONST_LONG("C_SVC", C_SVC);
|
|
SVM_REGISTER_CONST_LONG("NU_SVC", NU_SVC);
|
|
SVM_REGISTER_CONST_LONG("ONE_CLASS", ONE_CLASS);
|
|
SVM_REGISTER_CONST_LONG("EPSILON_SVR", EPSILON_SVR);
|
|
SVM_REGISTER_CONST_LONG("NU_SVR", NU_SVR);
|
|
|
|
/* Kernel types */
|
|
SVM_REGISTER_CONST_LONG("KERNEL_LINEAR", LINEAR);
|
|
SVM_REGISTER_CONST_LONG("KERNEL_POLY", POLY);
|
|
SVM_REGISTER_CONST_LONG("KERNEL_RBF", RBF);
|
|
SVM_REGISTER_CONST_LONG("KERNEL_SIGMOID", SIGMOID);
|
|
SVM_REGISTER_CONST_LONG("KERNEL_PRECOMPUTED", PRECOMPUTED);
|
|
|
|
/* Long options (for setOptions) */
|
|
SVM_REGISTER_CONST_LONG("OPT_TYPE", phpsvm_svm_type);
|
|
SVM_REGISTER_CONST_LONG("OPT_KERNEL_TYPE", phpsvm_kernel_type);
|
|
SVM_REGISTER_CONST_LONG("OPT_DEGREE", phpsvm_degree);
|
|
SVM_REGISTER_CONST_LONG("OPT_SHRINKING", phpsvm_shrinking);
|
|
SVM_REGISTER_CONST_LONG("OPT_PROBABILITY", phpsvm_probability);
|
|
|
|
/* Double options (for setOptions) */
|
|
SVM_REGISTER_CONST_LONG("OPT_GAMMA", phpsvm_gamma);
|
|
SVM_REGISTER_CONST_LONG("OPT_NU", phpsvm_nu);
|
|
SVM_REGISTER_CONST_LONG("OPT_EPS", phpsvm_eps);
|
|
SVM_REGISTER_CONST_LONG("OPT_P", phpsvm_p);
|
|
SVM_REGISTER_CONST_LONG("OPT_COEF_ZERO", phpsvm_coef0);
|
|
SVM_REGISTER_CONST_LONG("OPT_C", phpsvm_C);
|
|
SVM_REGISTER_CONST_LONG("OPT_CACHE_SIZE", phpsvm_cache_size);
|
|
|
|
#undef SVM_REGISTER_CONST_LONG
|
|
|
|
return SUCCESS;
|
|
}/*}}}*/
|
|
|
|
PHP_MSHUTDOWN_FUNCTION(svm)/*{{{*/
|
|
{
|
|
UNREGISTER_INI_ENTRIES();
|
|
return SUCCESS;
|
|
}/*}}}*/
|
|
|
|
PHP_MINFO_FUNCTION(svm)/*{{{*/
|
|
{
|
|
char _tmp[8];
|
|
php_info_print_table_start();
|
|
php_info_print_table_header(2, "svm extension", "enabled");
|
|
php_info_print_table_row(2, "svm extension version", PHP_SVM_VERSION);
|
|
snprintf(_tmp, sizeof(_tmp), "%d.%d", (int)(LIBSVM_VERSION/100), (int)(LIBSVM_VERSION % 100));
|
|
php_info_print_table_row(2, "libsvm version", _tmp);
|
|
php_info_print_table_end();
|
|
|
|
DISPLAY_INI_ENTRIES();
|
|
}/*}}}*/
|
|
|
|
/* No global functions */
|
|
zend_function_entry svm_functions[] = {
|
|
{NULL, NULL, NULL}
|
|
};
|
|
|
|
zend_module_entry svm_module_entry =
|
|
{
|
|
STANDARD_MODULE_HEADER,
|
|
"svm",
|
|
svm_functions, /* Functions */
|
|
PHP_MINIT(svm), /* MINIT */
|
|
PHP_MSHUTDOWN(svm), /* MSHUTDOWN */
|
|
NULL, /* RINIT */
|
|
NULL, /* RSHUTDOWN */
|
|
PHP_MINFO(svm), /* MINFO */
|
|
PHP_SVM_VERSION, /* version */
|
|
STANDARD_MODULE_PROPERTIES
|
|
};
|
|
|
|
|
|
#ifdef COMPILE_DL_SVM
|
|
ZEND_GET_MODULE(svm)
|
|
#endif /* COMPILE_DL_SVM */
|
|
|
|
/*
|
|
* Local variables:
|
|
* tab-width: 4
|
|
* c-basic-offset: 4
|
|
* End:
|
|
* vim600: noet sw=4 ts=4 fdm=marker
|
|
* vim<600: noet sw=4 ts=4
|
|
*/
|