File Coverage

blib/lib/AI/XGBoost/CAPI.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package AI::XGBoost::CAPI;
2 1     1   6 use strict;
  1         1  
  1         28  
3 1     1   4 use warnings;
  1         2  
  1         52  
4              
5             use Exporter::Easy (
6 1         8 TAGS => [
7             all => [
8             qw(
9             XGDMatrixCreateFromFile
10             XGDMatrixCreateFromMat
11             XGDMatrixNumRow
12             XGDMatrixNumCol
13             XGDMatrixSetFloatInfo
14             XGDMatrixGetFloatInfo
15             XGDMatrixFree
16             XGBoosterCreate
17             XGBoosterSetParam
18             XGBoosterSetAttr
19             XGBoosterGetAttr
20             XGBoosterGetAttrNames
21             XGBoosterUpdateOneIter
22             XGBoosterBoostOneIter
23             XGBoosterPredict
24             XGBoosterFree
25             XGBoosterDumpModel
26             XGBoosterDumpModelEx
27             XGBoosterDumpModelWithFeatures
28             XGBoosterDumpModelExWithFeatures
29             )
30             ]
31             ]
32 1     1   273 );
  1         1155  
33 1     1   522 use AI::XGBoost::CAPI::RAW;
  0            
  0            
34             use FFI::Platypus;
35             use Exception::Class ( 'XGBoostException' );
36              
37             our $VERSION = '0.008'; # VERSION
38              
39             # ABSTRACT: Perl wrapper for XGBoost C API https://github.com/dmlc/xgboost
40              
41             sub XGDMatrixCreateFromFile {
42             my ( $filename, $silent ) = @_;
43             $silent //= 1;
44             my $matrix = 0;
45             my $error = AI::XGBoost::CAPI::RAW::XGDMatrixCreateFromFile( $filename, $silent, \$matrix );
46             _CheckCall($error);
47             return $matrix;
48             }
49              
50             sub XGDMatrixCreateFromMat {
51             my ( $data, $missing ) = @_;
52             $missing //= "NaN";
53              
54             # TODO Support simple arrays
55             # TODO Support PDL
56             # TODO ¿Adapters?
57             my $data_adapter = [ map { @$_ } @$data ];
58             my $nrows = scalar @$data;
59             my $ncols = scalar @{ $data->[0] };
60             my $matrix = 0;
61             my $error = AI::XGBoost::CAPI::RAW::XGDMatrixCreateFromMat( $data_adapter, $nrows, $ncols, $missing, \$matrix );
62             _CheckCall($error);
63             return $matrix;
64             }
65              
66             sub XGDMatrixNumRow {
67             my ($matrix) = @_;
68             my $rows = 0;
69             _CheckCall( AI::XGBoost::CAPI::RAW::XGDMatrixNumRow( $matrix, \$rows ) );
70             return $rows;
71             }
72              
73             sub XGDMatrixNumCol {
74             my ($matrix) = @_;
75             my $cols = 0;
76             _CheckCall( AI::XGBoost::CAPI::RAW::XGDMatrixNumCol( $matrix, \$cols ) );
77             return $cols;
78             }
79              
80             sub XGDMatrixSetFloatInfo {
81             my ( $matrix, $info, $data ) = @_;
82             _CheckCall( AI::XGBoost::CAPI::RAW::XGDMatrixSetFloatInfo( $matrix, $info, $data, scalar @$data ) );
83             }
84              
85             sub XGDMatrixGetFloatInfo {
86             my ( $matrix, $info ) = @_;
87             my $out_len = 0;
88             my $out_result = 0;
89             _CheckCall( AI::XGBoost::CAPI::RAW::XGDMatrixGetFloatInfo( $matrix, $info, \$out_len, \$out_result ) );
90             my $ffi = FFI::Platypus->new();
91             return $ffi->cast( opaque => "float[$out_len]", $out_result );
92             }
93              
94             sub XGDMatrixFree {
95             my ($matrix) = @_;
96             _CheckCall( AI::XGBoost::CAPI::RAW::XGDMatrixFree($matrix) );
97             return ();
98             }
99              
100             sub XGBoosterCreate {
101             my ($matrices) = @_;
102             my $booster = 0;
103             _CheckCall( AI::XGBoost::CAPI::RAW::XGBoosterCreate( $matrices, scalar @$matrices, \$booster ) );
104             return $booster;
105             }
106              
107             sub XGBoosterSetParam {
108             my ( $booster, $name, $value ) = @_;
109             _CheckCall( AI::XGBoost::CAPI::RAW::XGBoosterSetParam( $booster, $name, $value ) );
110             }
111              
112             sub XGBoosterSetAttr {
113             my ( $booster, $name, $value ) = @_;
114             _CheckCall( AI::XGBoost::CAPI::RAW::XGBoosterSetAttr( $booster, $name, $value ) );
115             }
116              
117             sub XGBoosterGetAttr {
118             my ( $booster, $name ) = @_;
119             my $value = 0;
120             my $success = -1;
121             _CheckCall( AI::XGBoost::CAPI::RAW::XGBoosterGetAttr( $booster, $name, \$value, \$success ) );
122             if ($success) {
123             my $ffi = FFI::Platypus->new();
124             return $ffi->cast( opaque => "string", $value );
125             }
126             return ();
127             }
128              
129             sub XGBoosterGetAttrNames {
130             my ($booster) = @_;
131             my $out_len = 0;
132             my $out_result = 0;
133             _CheckCall( AI::XGBoost::CAPI::RAW::XGBoosterGetAttrNames( $booster, \$out_len, \$out_result ) );
134             my $ffi = FFI::Platypus->new();
135             $out_result = $ffi->cast( opaque => "opaque[$out_len]", $out_result );
136             return [ map { $ffi->cast( opaque => "string", $_ ) } @$out_result ];
137             }
138              
139             sub XGBoosterUpdateOneIter {
140             my ( $booster, $iter, $train_matrix ) = @_;
141             _CheckCall( AI::XGBoost::CAPI::RAW::XGBoosterUpdateOneIter( $booster, $iter, $train_matrix ) );
142             return ();
143             }
144              
145             sub XGBoosterBoostOneIter {
146             my ( $booster, $train_matrix, $gradient, $hessian ) = @_;
147             my $out_result = 0;
148             _CheckCall(
149             AI::XGBoost::CAPI::RAW::XGBoosterBoostOneIter(
150             $booster, $train_matrix, $gradient, $hessian, scalar(@$gradient)
151             )
152             );
153             return ();
154             }
155              
156             sub XGBoosterEvalOneIter {
157             my ( $booster, $iter, $matrices, $matrices_names ) = @_;
158             my $out_result = 0;
159             my $number_of_matrices = scalar @$matrices;
160             my $ffi = FFI::Platypus->new();
161             my $array_of_opaque_matrices_names = [ map { $ffi->cast( string => "opaque", $_ ) } @$matrices_names ];
162             _CheckCall(
163             AI::XGBoost::CAPI::RAW::XGBoosterEvalOneIter(
164             $booster, $iter, $matrices, $array_of_opaque_matrices_names,
165             $number_of_matrices, \$out_result
166             )
167             );
168             $out_result = $ffi->cast( opaque => "opaque[$number_of_matrices]", $out_result );
169             return [ map { $ffi->cast( opaque => "string", $_ ) } @$out_result ];
170             }
171              
172             sub XGBoosterPredict {
173             my ( $booster, $data_matrix, $option_mask, $ntree_limit ) = @_;
174             $option_mask //= 0;
175             $ntree_limit //= 0;
176             my $out_len = 0;
177             my $out_result = 0;
178             _CheckCall(
179             AI::XGBoost::CAPI::RAW::XGBoosterPredict( $booster, $data_matrix, $option_mask,
180             $ntree_limit, \$out_len, \$out_result
181             )
182             );
183             my $ffi = FFI::Platypus->new();
184             return $ffi->cast( opaque => "float[$out_len]", $out_result );
185             }
186              
187             sub XGBoosterDumpModel {
188             my ( $booster, $feature_map, $with_stats ) = @_;
189             $feature_map //= "";
190             $with_stats //= 1;
191             my $out_len = 0;
192             my $out_result = 0;
193             _CheckCall(
194             AI::XGBoost::CAPI::RAW::XGBoosterDumpModel( $booster, $feature_map, $with_stats, \$out_len, \$out_result ) );
195             my $ffi = FFI::Platypus->new();
196             $out_result = $ffi->cast( opaque => "opaque[$out_len]", $out_result );
197             return [ map { $ffi->cast( opaque => "string", $_ ) } @$out_result ];
198             }
199              
200             sub XGBoosterDumpModelEx {
201             my ( $booster, $feature_map, $with_stats, $format ) = @_;
202             $feature_map //= "";
203             $with_stats //= 1;
204             my $out_len = 0;
205             my $out_result = 0;
206             _CheckCall(
207             AI::XGBoost::CAPI::RAW::XGBoosterDumpModelEx(
208             $booster, $feature_map, $with_stats, $format, \$out_len, \$out_result
209             )
210             );
211             my $ffi = FFI::Platypus->new();
212             $out_result = $ffi->cast( opaque => "opaque[$out_len]", $out_result );
213             return [ map { $ffi->cast( opaque => "string", $_ ) } @$out_result ];
214             }
215              
216             sub XGBoosterDumpModelWithFeatures {
217             my ( $booster, $feature_names, $feature_types, $with_stats ) = @_;
218             $with_stats //= 1;
219             my $out_len = 0;
220             my $out_result = 0;
221             my $ffi = FFI::Platypus->new();
222             my $number_of_features = scalar @$feature_names;
223             my $array_of_opaque_feature_names = [ map { $ffi->cast( string => "opaque", $_ ) } @$feature_names ];
224             my $array_of_opaque_feature_types = [ map { $ffi->cast( string => "opaque", $_ ) } @$feature_types ];
225             _CheckCall(
226             AI::XGBoost::CAPI::RAW::XGBoosterDumpModelWithFeatures( $booster, $number_of_features,
227             $array_of_opaque_feature_names,
228             $array_of_opaque_feature_types,
229             $with_stats, \$out_len, \$out_result
230             )
231             );
232             $out_result = $ffi->cast( opaque => "opaque[$out_len]", $out_result );
233             return [ map { $ffi->cast( opaque => "string", $_ ) } @$out_result ];
234             }
235              
236             sub XGBoosterDumpModelExWithFeatures {
237             my ( $booster, $feature_names, $feature_types, $with_stats, $format ) = @_;
238             my $out_len = 0;
239             my $out_result = 0;
240             my $ffi = FFI::Platypus->new();
241             my $number_of_features = scalar @$feature_names;
242             my $array_of_opaque_feature_names = [ map { $ffi->cast( string => "opaque", $_ ) } @$feature_names ];
243             my $array_of_opaque_feature_types = [ map { $ffi->cast( string => "opaque", $_ ) } @$feature_types ];
244             _CheckCall(
245             AI::XGBoost::CAPI::RAW::XGBoosterDumpModelExWithFeatures( $booster, $number_of_features,
246             $array_of_opaque_feature_names,
247             $array_of_opaque_feature_types,
248             $with_stats, $format, \$out_len, \$out_result
249             )
250             );
251             $out_result = $ffi->cast( opaque => "opaque[$out_len]", $out_result );
252             return [ map { $ffi->cast( opaque => "string", $_ ) } @$out_result ];
253             }
254              
255             sub XGBoosterFree {
256             my ($booster) = @_;
257             _CheckCall( AI::XGBoost::CAPI::RAW::XGBoosterFree($booster) );
258             return ();
259             }
260              
261             # _CheckCall
262             #
263             # Check return code and if necesary, launch an exception
264             #
265             sub _CheckCall {
266             my ($return_code) = @_;
267             if ($return_code) {
268             my $error_message = AI::XGBoost::CAPI::RAW::XGBGetLastError();
269             XGBoostException->throw( error => $error_message );
270             }
271             }
272              
273             1;
274              
275             __END__
276              
277             =pod
278              
279             =encoding utf-8
280              
281             =head1 NAME
282              
283             AI::XGBoost::CAPI - Perl wrapper for XGBoost C API https://github.com/dmlc/xgboost
284              
285             =head1 VERSION
286              
287             version 0.008
288              
289             =head1 SYNOPSIS
290              
291             use 5.010;
292             use AI::XGBoost::CAPI qw(:all);
293            
294             my $dtrain = XGDMatrixCreateFromFile('agaricus.txt.train');
295             my $dtest = XGDMatrixCreateFromFile('agaricus.txt.test');
296            
297             my ($rows, $cols) = (XGDMatrixNumRow($dtrain), XGDMatrixNumCol($dtrain));
298             say "Train dimensions: $rows, $cols";
299            
300             my $booster = XGBoosterCreate([$dtrain]);
301            
302             for my $iter (0 .. 10) {
303             XGBoosterUpdateOneIter($booster, $iter, $dtrain);
304             }
305            
306             my $predictions = XGBoosterPredict($booster, $dtest);
307             # say join "\n", @$predictions;
308            
309             XGBoosterFree($booster);
310             XGDMatrixFree($dtrain);
311             XGDMatrixFree($dtest);
312              
313             =head1 DESCRIPTION
314              
315             Perlified wrapper for the C API
316              
317             =head2 Error handling
318              
319             XGBoost c api functions returns some int to signal the presence/absence of error.
320             In this module that is achieved using Exceptions from L<Exception::Class>
321              
322             =head1 FUNCTIONS
323              
324             =head2 XGDMatrixCreateFromFile
325              
326             Load a data matrix
327              
328             Parameters:
329              
330             =over 4
331              
332             =item filename
333              
334             the name of the file
335              
336             =item silent
337              
338             whether print messages during loading
339              
340             =back
341              
342             Returns a loaded data matrix
343              
344             =head2 XGDMatrixCreateFromMat
345              
346             Create from dense matrix
347              
348             Parameters:
349              
350             =over 4
351              
352             =item matrix
353              
354             matrix data
355              
356             =item missing
357              
358             value indicating missing data (optional)
359              
360             =back
361              
362             Returns a loaded data matrix
363              
364             =head2 XGDMatrixNumRow
365              
366             Get number of rows
367              
368             Parameters:
369              
370             =over 4
371              
372             =item matrix
373              
374             DMatrix
375              
376             =back
377              
378             =head2 XGDMatrixNumCol
379              
380             Get number of cols
381              
382             Parameters:
383              
384             =over 4
385              
386             =item matrix
387              
388             DMatrix
389              
390             =back
391              
392             =head2 XGDMatrixSetFloatInfo
393              
394             =head2 XGDMatrixGetFloatInfo
395              
396             =head2 XGDMatrixFree
397              
398             Free space in data matrix
399              
400             Parameters:
401              
402             =over 4
403              
404             =item matrix
405              
406             DMatrix to be freed
407              
408             =back
409              
410             =head2 XGBoosterCreate
411              
412             Create XGBoost learner
413              
414             Parameters:
415              
416             =over 4
417              
418             =item matrices
419              
420             matrices that are set to be cached
421              
422             =back
423              
424             =head2 XGBoosterSetParam
425              
426             =head2 XGBoosterSetAttr
427              
428             =head2 XGBoosterGetAttr
429              
430             =head2 XGBoosterGetAttrNames
431              
432             =head2 XGBoosterUpdateOneIter
433              
434             Update the model in one round using train matrix
435              
436             Parameters:
437              
438             =over 4
439              
440             =item booster
441              
442             XGBoost learner to train
443              
444             =item iter
445              
446             current iteration rounds
447              
448             =item train_matrix
449              
450             training data
451              
452             =back
453              
454             =head2 XGBoosterBoostOneIter
455              
456             =head2 XGBoosterEvalOneIter
457              
458             =head2 XGBoosterPredict
459              
460             Make prediction based on train matrix
461              
462             Parameters:
463              
464             =over 4
465              
466             =item booster
467              
468             XGBoost learner
469              
470             =item data_matrix
471              
472             Data matrix with the elements to predict
473              
474             =item option_mask
475              
476             bit-mask of options taken in prediction, possible values
477              
478             =over 4
479              
480             =item
481              
482             0: normal prediction
483              
484             =item
485              
486             1: output margin instead of transformed value
487              
488             =item
489              
490             2: output leaf index of trees instead of leaf value, note leaf index is unique per tree
491              
492             =item
493              
494             4: output feature contributions to individual predictions
495              
496             =back
497              
498             =item ntree_limit
499              
500             limit number of trees used for prediction, this is only valid for boosted trees
501             when the parameter is set to 0, we will use all the trees
502              
503             =back
504              
505             Returns an arrayref with the predictions corresponding to the rows of data matrix
506              
507             =head2 XGBoosterDumpModel
508              
509             =head2 XGBoosterDumpModelEx
510              
511             =head2 XGBoosterDumpModelWithFeatures
512              
513             =head2 XGBoosterDumpModelExWithFeatures
514              
515             =head2 XGBoosterFree
516              
517             Free booster object
518              
519             Parameters:
520              
521             =over 4
522              
523             =item booster
524              
525             booster to be freed
526              
527             =back
528              
529             =head1 AUTHOR
530              
531             Pablo Rodríguez González <pablo.rodriguez.gonzalez@gmail.com>
532              
533             =head1 COPYRIGHT AND LICENSE
534              
535             This software is Copyright (c) 2017 by Pablo Rodríguez González.
536              
537             This is free software, licensed under:
538              
539             The Apache License, Version 2.0, January 2004
540              
541             =cut