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