liblinear-ruby 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/ext/blasp.h +8 -0
  3. data/ext/daxpy.c +8 -0
  4. data/ext/ddot.c +8 -0
  5. data/ext/dnrm2.c +8 -0
  6. data/ext/dscal.c +8 -0
  7. data/ext/liblinear_wrap.cxx +128 -3
  8. data/ext/linear.cpp +344 -175
  9. data/ext/linear.h +2 -0
  10. data/ext/tron.cpp +14 -8
  11. data/ext/tron.h +2 -1
  12. data/lib/liblinear/version.rb +1 -1
  13. data/{liblinear-1.95 → liblinear-2.1}/COPYRIGHT +1 -1
  14. data/{liblinear-1.95 → liblinear-2.1}/Makefile +1 -1
  15. data/{liblinear-1.95 → liblinear-2.1}/Makefile.win +3 -9
  16. data/{liblinear-1.95 → liblinear-2.1}/README +45 -7
  17. data/{liblinear-1.95 → liblinear-2.1}/blas/Makefile +0 -0
  18. data/{liblinear-1.95 → liblinear-2.1}/blas/blas.h +0 -0
  19. data/{liblinear-1.95 → liblinear-2.1}/blas/blasp.h +0 -0
  20. data/{liblinear-1.95 → liblinear-2.1}/blas/daxpy.c +0 -0
  21. data/{liblinear-1.95 → liblinear-2.1}/blas/ddot.c +0 -0
  22. data/{liblinear-1.95 → liblinear-2.1}/blas/dnrm2.c +0 -0
  23. data/{liblinear-1.95 → liblinear-2.1}/blas/dscal.c +0 -0
  24. data/{liblinear-1.95 → liblinear-2.1}/heart_scale +0 -0
  25. data/{liblinear-1.95 → liblinear-2.1}/linear.cpp +344 -175
  26. data/{liblinear-1.95 → liblinear-2.1}/linear.def +1 -0
  27. data/{liblinear-1.95 → liblinear-2.1}/linear.h +2 -0
  28. data/{liblinear-1.95 → liblinear-2.1}/matlab/Makefile +0 -0
  29. data/{liblinear-1.95 → liblinear-2.1}/matlab/README +12 -2
  30. data/{liblinear-1.95 → liblinear-2.1}/matlab/libsvmread.c +0 -0
  31. data/{liblinear-1.95 → liblinear-2.1}/matlab/libsvmwrite.c +1 -1
  32. data/{liblinear-1.95 → liblinear-2.1}/matlab/linear_model_matlab.c +1 -1
  33. data/{liblinear-1.95 → liblinear-2.1}/matlab/linear_model_matlab.h +0 -0
  34. data/liblinear-2.1/matlab/make.m +22 -0
  35. data/{liblinear-1.95 → liblinear-2.1}/matlab/predict.c +1 -1
  36. data/{liblinear-1.95 → liblinear-2.1}/matlab/train.c +65 -10
  37. data/{liblinear-1.95 → liblinear-2.1}/predict.c +0 -0
  38. data/{liblinear-1.95 → liblinear-2.1}/python/Makefile +0 -0
  39. data/{liblinear-1.95 → liblinear-2.1}/python/README +7 -0
  40. data/{liblinear-1.95 → liblinear-2.1}/python/liblinear.py +27 -8
  41. data/{liblinear-1.95 → liblinear-2.1}/python/liblinearutil.py +16 -2
  42. data/{liblinear-1.95 → liblinear-2.1}/train.c +51 -1
  43. data/{liblinear-1.95 → liblinear-2.1}/tron.cpp +14 -8
  44. data/{liblinear-1.95 → liblinear-2.1}/tron.h +2 -1
  45. data/liblinear-2.1/windows/liblinear.dll +0 -0
  46. data/{liblinear-1.95 → liblinear-2.1}/windows/libsvmread.mexw64 +0 -0
  47. data/{liblinear-1.95 → liblinear-2.1}/windows/libsvmwrite.mexw64 +0 -0
  48. data/liblinear-2.1/windows/predict.exe +0 -0
  49. data/{liblinear-1.95 → liblinear-2.1}/windows/predict.mexw64 +0 -0
  50. data/liblinear-2.1/windows/train.exe +0 -0
  51. data/liblinear-2.1/windows/train.mexw64 +0 -0
  52. data/liblinear-ruby.gemspec +9 -10
  53. metadata +49 -50
  54. data/liblinear-1.95/matlab/make.m +0 -21
  55. data/liblinear-1.95/windows/liblinear.dll +0 -0
  56. data/liblinear-1.95/windows/predict.exe +0 -0
  57. data/liblinear-1.95/windows/train.exe +0 -0
  58. data/liblinear-1.95/windows/train.mexw64 +0 -0
@@ -32,6 +32,7 @@ struct parameter
32
32
  int *weight_label;
33
33
  double* weight;
34
34
  double p;
35
+ double *init_sol;
35
36
  };
36
37
 
37
38
  struct model
@@ -46,6 +47,7 @@ struct model
46
47
 
47
48
  struct model* train(const struct problem *prob, const struct parameter *param);
48
49
  void cross_validation(const struct problem *prob, const struct parameter *param, int nr_fold, double *target);
50
+ void find_parameter_C(const struct problem *prob, const struct parameter *param, int nr_fold, double start_C, double max_C, double *best_C, double *best_rate);
49
51
 
50
52
  double predict_values(const struct model *model_, const struct feature_node *x, double* dec_values);
51
53
  double predict(const struct model *model_, const struct feature_node *x);
@@ -41,10 +41,11 @@ void TRON::info(const char *fmt,...)
41
41
  (*tron_print_string)(buf);
42
42
  }
43
43
 
44
- TRON::TRON(const function *fun_obj, double eps, int max_iter)
44
+ TRON::TRON(const function *fun_obj, double eps, double eps_cg, int max_iter)
45
45
  {
46
46
  this->fun_obj=const_cast<function *>(fun_obj);
47
47
  this->eps=eps;
48
+ this->eps_cg=eps_cg;
48
49
  this->max_iter=max_iter;
49
50
  tron_print_string = default_print;
50
51
  }
@@ -68,23 +69,28 @@ void TRON::tron(double *w)
68
69
  int search = 1, iter = 1, inc = 1;
69
70
  double *s = new double[n];
70
71
  double *r = new double[n];
71
- double *w_new = new double[n];
72
72
  double *g = new double[n];
73
73
 
74
+ // calculate gradient norm at w=0 for stopping condition.
75
+ double *w0 = new double[n];
74
76
  for (i=0; i<n; i++)
75
- w[i] = 0;
77
+ w0[i] = 0;
78
+ fun_obj->fun(w0);
79
+ fun_obj->grad(w0, g);
80
+ double gnorm0 = dnrm2_(&n, g, &inc);
81
+ delete [] w0;
76
82
 
77
83
  f = fun_obj->fun(w);
78
84
  fun_obj->grad(w, g);
79
85
  delta = dnrm2_(&n, g, &inc);
80
- double gnorm1 = delta;
81
- double gnorm = gnorm1;
86
+ double gnorm = delta;
82
87
 
83
- if (gnorm <= eps*gnorm1)
88
+ if (gnorm <= eps*gnorm0)
84
89
  search = 0;
85
90
 
86
91
  iter = 1;
87
92
 
93
+ double *w_new = new double[n];
88
94
  while (iter <= max_iter && search)
89
95
  {
90
96
  cg_iter = trcg(delta, g, s, r);
@@ -130,7 +136,7 @@ void TRON::tron(double *w)
130
136
  fun_obj->grad(w, g);
131
137
 
132
138
  gnorm = dnrm2_(&n, g, &inc);
133
- if (gnorm <= eps*gnorm1)
139
+ if (gnorm <= eps*gnorm0)
134
140
  break;
135
141
  }
136
142
  if (f < -1.0e+32)
@@ -172,7 +178,7 @@ int TRON::trcg(double delta, double *g, double *s, double *r)
172
178
  r[i] = -g[i];
173
179
  d[i] = r[i];
174
180
  }
175
- cgtol = 0.1*dnrm2_(&n, g, &inc);
181
+ cgtol = eps_cg*dnrm2_(&n, g, &inc);
176
182
 
177
183
  int cg_iter = 0;
178
184
  rTr = ddot_(&n, r, &inc, r, &inc);
data/ext/tron.h CHANGED
@@ -15,7 +15,7 @@ public:
15
15
  class TRON
16
16
  {
17
17
  public:
18
- TRON(const function *fun_obj, double eps = 0.1, int max_iter = 1000);
18
+ TRON(const function *fun_obj, double eps = 0.1, double eps_cg = 0.1, int max_iter = 1000);
19
19
  ~TRON();
20
20
 
21
21
  void tron(double *w);
@@ -26,6 +26,7 @@ private:
26
26
  double norm_inf(int n, double *x);
27
27
 
28
28
  double eps;
29
+ double eps_cg;
29
30
  int max_iter;
30
31
  function *fun_obj;
31
32
  void info(const char *fmt,...);
@@ -1,3 +1,3 @@
1
1
  module Liblinear
2
- VERSION = '0.0.6'
2
+ VERSION = '0.0.7'
3
3
  end
@@ -1,5 +1,5 @@
1
1
 
2
- Copyright (c) 2007-2014 The LIBLINEAR Project.
2
+ Copyright (c) 2007-2015 The LIBLINEAR Project.
3
3
  All rights reserved.
4
4
 
5
5
  Redistribution and use in source and binary forms, with or without
@@ -2,7 +2,7 @@ CXX ?= g++
2
2
  CC ?= gcc
3
3
  CFLAGS = -Wall -Wconversion -O3 -fPIC
4
4
  LIBS = blas/blas.a
5
- SHVER = 2
5
+ SHVER = 3
6
6
  OS = $(shell uname)
7
7
  #LIBS = -lblas
8
8
 
@@ -1,14 +1,8 @@
1
- #You must ensure nmake.exe, cl.exe, link.exe are in system path.
2
- #VCVARS32.bat
3
- #Under dosbox prompt
4
- #nmake -f Makefile.win
5
-
6
- ##########################################
7
1
  CXX = cl.exe
8
- CFLAGS = /nologo /O2 /EHsc /I. /D _WIN32 /D _CRT_SECURE_NO_DEPRECATE
2
+ CFLAGS = /nologo /O2 /EHsc /I. /D _WIN64 /D _CRT_SECURE_NO_DEPRECATE
9
3
  TARGET = windows
10
4
 
11
- all: $(TARGET)\train.exe $(TARGET)\predict.exe
5
+ all: $(TARGET)\train.exe $(TARGET)\predict.exe lib
12
6
 
13
7
  $(TARGET)\train.exe: tron.obj linear.obj train.c blas\*.c
14
8
  $(CXX) $(CFLAGS) -Fe$(TARGET)\train.exe tron.obj linear.obj train.c blas\*.c
@@ -26,5 +20,5 @@ lib: linear.cpp linear.h linear.def tron.obj
26
20
  $(CXX) $(CFLAGS) -LD linear.cpp tron.obj blas\*.c -Fe$(TARGET)\liblinear -link -DEF:linear.def
27
21
 
28
22
  clean:
29
- -erase /Q *.obj $(TARGET)\.
23
+ -erase /Q *.obj $(TARGET)\*.exe $(TARGET)\*.dll $(TARGET)\*.exp $(TARGET)\*.lib
30
24
 
@@ -131,11 +131,16 @@ options:
131
131
  -B bias : if bias >= 0, instance x becomes [x; bias]; if < 0, no bias term added (default -1)
132
132
  -wi weight: weights adjust the parameter C of different classes (see README for details)
133
133
  -v n: n-fold cross validation mode
134
+ -C : find parameter C (only for -s 0 and 2)
134
135
  -q : quiet mode (no outputs)
135
136
 
136
137
  Option -v randomly splits the data into n parts and calculates cross
137
138
  validation accuracy on them.
138
139
 
140
+ Option -C conducts cross validation under different C values and finds
141
+ the best one. This options is supported only by -s 0 and -s 2. If
142
+ the solver is not specified, -s 2 is used.
143
+
139
144
  Formulations:
140
145
 
141
146
  For L2-regularized logistic regression (-s 0), we solve
@@ -241,10 +246,27 @@ Train a logistic regression model.
241
246
 
242
247
  > train -v 5 -e 0.001 data_file
243
248
 
244
- Do five-fold cross-validation using L2-loss svm.
249
+ Do five-fold cross-validation using L2-loss SVM.
245
250
  Use a smaller stopping tolerance 0.001 than the default
246
251
  0.1 if you want more accurate solutions.
247
252
 
253
+ > train -C data_file
254
+
255
+ Conduct cross validation many times by L2-loss SVM
256
+ and find the parameter C which achieves the best cross
257
+ validation accuracy.
258
+
259
+ > train -C -s 0 -v 3 -c 0.5 -e 0.0001 data_file
260
+
261
+ For parameter selection by -C, users can specify other
262
+ solvers (currently -s 0 and -s 2 are supported) and
263
+ different number of CV folds. Further, users can use
264
+ the -c option to specify the smallest C value of the
265
+ search range. This setting is useful when users want
266
+ to rerun the parameter selection procedure from a
267
+ specified C under a different setting, such as a stricter
268
+ stopping tolerance -e 0.0001 in the above example.
269
+
248
270
  > train -c 10 -w1 2 -w2 5 -w3 2 four_class_data_file
249
271
 
250
272
  Train four classifiers:
@@ -407,6 +429,22 @@ Library Usage
407
429
 
408
430
  The format of prob is same as that for train().
409
431
 
432
+ - Function: void find_parameter_C(const struct problem *prob,
433
+ const struct parameter *param, int nr_fold, double start_C,
434
+ double max_C, double *best_C, double *best_rate);
435
+
436
+ This function is similar to cross_validation. However, instead of
437
+ conducting cross validation under a specified parameter C, it
438
+ conducts cross validation many times under parameters C = start_C,
439
+ 2*start_C, 4*start_C, 8*start_C, ..., and finds the best one with
440
+ the highest cross validation accuracy.
441
+
442
+ If start_C <= 0, then this procedure calculates a small enough C
443
+ for prob as the start_C. The procedure stops when the models of
444
+ all folds become stable or C reaches max_C. The best C and the
445
+ corresponding accuracy are assigned to *best_C and *best_rate,
446
+ respectively.
447
+
410
448
  - Function: double predict(const model *model_, const feature_node *x);
411
449
 
412
450
  For a classification model, the predicted class for x is returned.
@@ -418,11 +456,11 @@ Library Usage
418
456
 
419
457
  This function gives nr_w decision values in the array dec_values.
420
458
  nr_w=1 if regression is applied or the number of classes is two. An exception is
421
- multi-class svm by Crammer and Singer (-s 4), where nr_w = 2 if there are two classes. For all other situations, nr_w is the
459
+ multi-class SVM by Crammer and Singer (-s 4), where nr_w = 2 if there are two classes. For all other situations, nr_w is the
422
460
  number of classes.
423
461
 
424
462
  We implement one-vs-the rest multi-class strategy (-s 0,1,2,3,5,6,7)
425
- and multi-class svm by Crammer and Singer (-s 4) for multi-class SVM.
463
+ and multi-class SVM by Crammer and Singer (-s 4) for multi-class SVM.
426
464
  The class with the highest decision value is returned.
427
465
 
428
466
  - Function: double predict_probability(const struct model *model_,
@@ -523,7 +561,7 @@ Visual C++, use the following steps:
523
561
  1. Open a dos command box and change to liblinear directory. If
524
562
  environment variables of VC++ have not been set, type
525
563
 
526
- "C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\vcvars32.bat"
564
+ ""C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\amd64\vcvars64.bat""
527
565
 
528
566
  You may have to modify the above command according which version of
529
567
  VC++ or where it is installed.
@@ -532,9 +570,9 @@ VC++ or where it is installed.
532
570
 
533
571
  nmake -f Makefile.win clean all
534
572
 
535
- 2. (Optional) To build 64-bit windows binaries, you must
536
- (1) Setup vcvars64.bat instead of vcvars32.bat
537
- (2) Change CFLAGS in Makefile.win: /D _WIN32 to /D _WIN64
573
+ 2. (Optional) To build 32-bit windows binaries, you must
574
+ (1) Setup "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\vcvars32.bat" instead of vcvars64.bat
575
+ (2) Change CFLAGS in Makefile.win: /D _WIN64 to /D _WIN32
538
576
 
539
577
  MATLAB/OCTAVE Interface
540
578
  =======================
@@ -27,6 +27,7 @@ static void print_string_stdout(const char *s)
27
27
  fputs(s,stdout);
28
28
  fflush(stdout);
29
29
  }
30
+ static void print_null(const char *s) {}
30
31
 
31
32
  static void (*liblinear_print_string) (const char *) = &print_string_stdout;
32
33
 
@@ -43,6 +44,40 @@ static void info(const char *fmt,...)
43
44
  #else
44
45
  static void info(const char *fmt,...) {}
45
46
  #endif
47
+ class sparse_operator
48
+ {
49
+ public:
50
+ static double nrm2_sq(const feature_node *x)
51
+ {
52
+ double ret = 0;
53
+ while(x->index != -1)
54
+ {
55
+ ret += x->value*x->value;
56
+ x++;
57
+ }
58
+ return (ret);
59
+ }
60
+
61
+ static double dot(const double *s, const feature_node *x)
62
+ {
63
+ double ret = 0;
64
+ while(x->index != -1)
65
+ {
66
+ ret += s[x->index-1]*x->value;
67
+ x++;
68
+ }
69
+ return (ret);
70
+ }
71
+
72
+ static void axpy(const double a, const feature_node *x, double *y)
73
+ {
74
+ while(x->index != -1)
75
+ {
76
+ y[x->index-1] += a*x->value;
77
+ x++;
78
+ }
79
+ }
80
+ };
46
81
 
47
82
  class l2r_lr_fun: public function
48
83
  {
@@ -139,12 +174,19 @@ void l2r_lr_fun::Hv(double *s, double *Hs)
139
174
  int l=prob->l;
140
175
  int w_size=get_nr_variable();
141
176
  double *wa = new double[l];
177
+ feature_node **x=prob->x;
142
178
 
143
- Xv(s, wa);
179
+ for(i=0;i<w_size;i++)
180
+ Hs[i] = 0;
144
181
  for(i=0;i<l;i++)
182
+ {
183
+ feature_node * const xi=x[i];
184
+ wa[i] = sparse_operator::dot(s, xi);
185
+
145
186
  wa[i] = C[i]*D[i]*wa[i];
146
187
 
147
- XTv(wa, Hs);
188
+ sparse_operator::axpy(wa[i], xi, Hs);
189
+ }
148
190
  for(i=0;i<w_size;i++)
149
191
  Hs[i] = s[i] + Hs[i];
150
192
  delete[] wa;
@@ -157,15 +199,7 @@ void l2r_lr_fun::Xv(double *v, double *Xv)
157
199
  feature_node **x=prob->x;
158
200
 
159
201
  for(i=0;i<l;i++)
160
- {
161
- feature_node *s=x[i];
162
- Xv[i]=0;
163
- while(s->index!=-1)
164
- {
165
- Xv[i]+=v[s->index-1]*s->value;
166
- s++;
167
- }
168
- }
202
+ Xv[i]=sparse_operator::dot(v, x[i]);
169
203
  }
170
204
 
171
205
  void l2r_lr_fun::XTv(double *v, double *XTv)
@@ -178,14 +212,7 @@ void l2r_lr_fun::XTv(double *v, double *XTv)
178
212
  for(i=0;i<w_size;i++)
179
213
  XTv[i]=0;
180
214
  for(i=0;i<l;i++)
181
- {
182
- feature_node *s=x[i];
183
- while(s->index!=-1)
184
- {
185
- XTv[s->index-1]+=v[i]*s->value;
186
- s++;
187
- }
188
- }
215
+ sparse_operator::axpy(v[i], x[i], XTv);
189
216
  }
190
217
 
191
218
  class l2r_l2_svc_fun: public function
@@ -202,7 +229,6 @@ public:
202
229
 
203
230
  protected:
204
231
  void Xv(double *v, double *Xv);
205
- void subXv(double *v, double *Xv);
206
232
  void subXTv(double *v, double *XTv);
207
233
 
208
234
  double *C;
@@ -287,12 +313,19 @@ void l2r_l2_svc_fun::Hv(double *s, double *Hs)
287
313
  int i;
288
314
  int w_size=get_nr_variable();
289
315
  double *wa = new double[sizeI];
316
+ feature_node **x=prob->x;
290
317
 
291
- subXv(s, wa);
318
+ for(i=0;i<w_size;i++)
319
+ Hs[i]=0;
292
320
  for(i=0;i<sizeI;i++)
321
+ {
322
+ feature_node * const xi=x[I[i]];
323
+ wa[i] = sparse_operator::dot(s, xi);
324
+
293
325
  wa[i] = C[I[i]]*wa[i];
294
326
 
295
- subXTv(wa, Hs);
327
+ sparse_operator::axpy(wa[i], xi, Hs);
328
+ }
296
329
  for(i=0;i<w_size;i++)
297
330
  Hs[i] = s[i] + 2*Hs[i];
298
331
  delete[] wa;
@@ -305,32 +338,7 @@ void l2r_l2_svc_fun::Xv(double *v, double *Xv)
305
338
  feature_node **x=prob->x;
306
339
 
307
340
  for(i=0;i<l;i++)
308
- {
309
- feature_node *s=x[i];
310
- Xv[i]=0;
311
- while(s->index!=-1)
312
- {
313
- Xv[i]+=v[s->index-1]*s->value;
314
- s++;
315
- }
316
- }
317
- }
318
-
319
- void l2r_l2_svc_fun::subXv(double *v, double *Xv)
320
- {
321
- int i;
322
- feature_node **x=prob->x;
323
-
324
- for(i=0;i<sizeI;i++)
325
- {
326
- feature_node *s=x[I[i]];
327
- Xv[i]=0;
328
- while(s->index!=-1)
329
- {
330
- Xv[i]+=v[s->index-1]*s->value;
331
- s++;
332
- }
333
- }
341
+ Xv[i]=sparse_operator::dot(v, x[i]);
334
342
  }
335
343
 
336
344
  void l2r_l2_svc_fun::subXTv(double *v, double *XTv)
@@ -342,14 +350,7 @@ void l2r_l2_svc_fun::subXTv(double *v, double *XTv)
342
350
  for(i=0;i<w_size;i++)
343
351
  XTv[i]=0;
344
352
  for(i=0;i<sizeI;i++)
345
- {
346
- feature_node *s=x[I[i]];
347
- while(s->index!=-1)
348
- {
349
- XTv[s->index-1]+=v[i]*s->value;
350
- s++;
351
- }
352
- }
353
+ sparse_operator::axpy(v[i], x[I[i]], XTv);
353
354
  }
354
355
 
355
356
  class l2r_l2_svr_fun: public l2r_l2_svc_fun
@@ -830,14 +831,10 @@ static void solve_l2r_l1l2_svc(
830
831
  {
831
832
  QD[i] = diag[GETI(i)];
832
833
 
833
- feature_node *xi = prob->x[i];
834
- while (xi->index != -1)
835
- {
836
- double val = xi->value;
837
- QD[i] += val*val;
838
- w[xi->index-1] += y[i]*alpha[i]*val;
839
- xi++;
840
- }
834
+ feature_node * const xi = prob->x[i];
835
+ QD[i] += sparse_operator::nrm2_sq(xi);
836
+ sparse_operator::axpy(y[i]*alpha[i], xi, w);
837
+
841
838
  index[i] = i;
842
839
  }
843
840
 
@@ -855,16 +852,10 @@ static void solve_l2r_l1l2_svc(
855
852
  for (s=0; s<active_size; s++)
856
853
  {
857
854
  i = index[s];
858
- G = 0;
859
- schar yi = y[i];
855
+ const schar yi = y[i];
856
+ feature_node * const xi = prob->x[i];
860
857
 
861
- feature_node *xi = prob->x[i];
862
- while(xi->index!= -1)
863
- {
864
- G += w[xi->index-1]*(xi->value);
865
- xi++;
866
- }
867
- G = G*yi-1;
858
+ G = yi*sparse_operator::dot(w, xi)-1;
868
859
 
869
860
  C = upper_bound[GETI(i)];
870
861
  G += alpha[i]*diag[GETI(i)];
@@ -905,12 +896,7 @@ static void solve_l2r_l1l2_svc(
905
896
  double alpha_old = alpha[i];
906
897
  alpha[i] = min(max(alpha[i] - G/QD[i], 0.0), C);
907
898
  d = (alpha[i] - alpha_old)*yi;
908
- xi = prob->x[i];
909
- while (xi->index != -1)
910
- {
911
- w[xi->index-1] += d*xi->value;
912
- xi++;
913
- }
899
+ sparse_operator::axpy(d, xi, w);
914
900
  }
915
901
  }
916
902
 
@@ -1035,15 +1021,9 @@ static void solve_l2r_l1l2_svr(
1035
1021
  w[i] = 0;
1036
1022
  for(i=0; i<l; i++)
1037
1023
  {
1038
- QD[i] = 0;
1039
- feature_node *xi = prob->x[i];
1040
- while(xi->index != -1)
1041
- {
1042
- double val = xi->value;
1043
- QD[i] += val*val;
1044
- w[xi->index-1] += beta[i]*val;
1045
- xi++;
1046
- }
1024
+ feature_node * const xi = prob->x[i];
1025
+ QD[i] = sparse_operator::nrm2_sq(xi);
1026
+ sparse_operator::axpy(beta[i], xi, w);
1047
1027
 
1048
1028
  index[i] = i;
1049
1029
  }
@@ -1066,14 +1046,8 @@ static void solve_l2r_l1l2_svr(
1066
1046
  G = -y[i] + lambda[GETI(i)]*beta[i];
1067
1047
  H = QD[i] + lambda[GETI(i)];
1068
1048
 
1069
- feature_node *xi = prob->x[i];
1070
- while(xi->index != -1)
1071
- {
1072
- int ind = xi->index-1;
1073
- double val = xi->value;
1074
- G += val*w[ind];
1075
- xi++;
1076
- }
1049
+ feature_node * const xi = prob->x[i];
1050
+ G += sparse_operator::dot(w, xi);
1077
1051
 
1078
1052
  double Gp = G+p;
1079
1053
  double Gn = G-p;
@@ -1140,14 +1114,7 @@ static void solve_l2r_l1l2_svr(
1140
1114
  d = beta[i]-beta_old;
1141
1115
 
1142
1116
  if(d != 0)
1143
- {
1144
- xi = prob->x[i];
1145
- while(xi->index != -1)
1146
- {
1147
- w[xi->index-1] += d*xi->value;
1148
- xi++;
1149
- }
1150
- }
1117
+ sparse_operator::axpy(d, xi, w);
1151
1118
  }
1152
1119
 
1153
1120
  if(iter == 0)
@@ -1260,15 +1227,9 @@ void solve_l2r_lr_dual(const problem *prob, double *w, double eps, double Cp, do
1260
1227
  w[i] = 0;
1261
1228
  for(i=0; i<l; i++)
1262
1229
  {
1263
- xTx[i] = 0;
1264
- feature_node *xi = prob->x[i];
1265
- while (xi->index != -1)
1266
- {
1267
- double val = xi->value;
1268
- xTx[i] += val*val;
1269
- w[xi->index-1] += y[i]*alpha[2*i]*val;
1270
- xi++;
1271
- }
1230
+ feature_node * const xi = prob->x[i];
1231
+ xTx[i] = sparse_operator::nrm2_sq(xi);
1232
+ sparse_operator::axpy(y[i]*alpha[2*i], xi, w);
1272
1233
  index[i] = i;
1273
1234
  }
1274
1235
 
@@ -1284,16 +1245,11 @@ void solve_l2r_lr_dual(const problem *prob, double *w, double eps, double Cp, do
1284
1245
  for (s=0; s<l; s++)
1285
1246
  {
1286
1247
  i = index[s];
1287
- schar yi = y[i];
1248
+ const schar yi = y[i];
1288
1249
  double C = upper_bound[GETI(i)];
1289
1250
  double ywTx = 0, xisq = xTx[i];
1290
- feature_node *xi = prob->x[i];
1291
- while (xi->index != -1)
1292
- {
1293
- ywTx += w[xi->index-1]*xi->value;
1294
- xi++;
1295
- }
1296
- ywTx *= y[i];
1251
+ feature_node * const xi = prob->x[i];
1252
+ ywTx = yi*sparse_operator::dot(w, xi);
1297
1253
  double a = xisq, b = ywTx;
1298
1254
 
1299
1255
  // Decide to minimize g_1(z) or g_2(z)
@@ -1335,12 +1291,7 @@ void solve_l2r_lr_dual(const problem *prob, double *w, double eps, double Cp, do
1335
1291
  {
1336
1292
  alpha[ind1] = z;
1337
1293
  alpha[ind2] = C-z;
1338
- xi = prob->x[i];
1339
- while (xi->index != -1)
1340
- {
1341
- w[xi->index-1] += sign*(z-alpha_old)*yi*xi->value;
1342
- xi++;
1343
- }
1294
+ sparse_operator::axpy(sign*(z-alpha_old)*yi, xi, w);
1344
1295
  }
1345
1296
  }
1346
1297
 
@@ -1534,11 +1485,7 @@ static void solve_l1r_l2_svc(
1534
1485
  if(appxcond <= 0)
1535
1486
  {
1536
1487
  x = prob_col->x[j];
1537
- while(x->index != -1)
1538
- {
1539
- b[x->index-1] += d_diff*x->value;
1540
- x++;
1541
- }
1488
+ sparse_operator::axpy(d_diff, x, b);
1542
1489
  break;
1543
1490
  }
1544
1491
 
@@ -1598,11 +1545,7 @@ static void solve_l1r_l2_svc(
1598
1545
  {
1599
1546
  if(w[i]==0) continue;
1600
1547
  x = prob_col->x[i];
1601
- while(x->index != -1)
1602
- {
1603
- b[x->index-1] -= w[i]*x->value;
1604
- x++;
1605
- }
1548
+ sparse_operator::axpy(-w[i], x, b);
1606
1549
  }
1607
1550
  }
1608
1551
  }
@@ -1891,12 +1834,7 @@ static void solve_l1r_lr(
1891
1834
  wpd[j] += z;
1892
1835
 
1893
1836
  x = prob_col->x[j];
1894
- while(x->index != -1)
1895
- {
1896
- int ind = x->index-1;
1897
- xTd[ind] += x->value*z;
1898
- x++;
1899
- }
1837
+ sparse_operator::axpy(z, x, xTd);
1900
1838
  }
1901
1839
 
1902
1840
  iter++;
@@ -1988,11 +1926,7 @@ static void solve_l1r_lr(
1988
1926
  {
1989
1927
  if(w[i]==0) continue;
1990
1928
  x = prob_col->x[i];
1991
- while(x->index != -1)
1992
- {
1993
- exp_wTx[x->index-1] += w[i]*x->value;
1994
- x++;
1995
- }
1929
+ sparse_operator::axpy(w[i], x, exp_wTx);
1996
1930
  }
1997
1931
 
1998
1932
  for(int i=0; i<l; i++)
@@ -2180,14 +2114,18 @@ static void group_classes(const problem *prob, int *nr_class_ret, int **label_re
2180
2114
 
2181
2115
  static void train_one(const problem *prob, const parameter *param, double *w, double Cp, double Cn)
2182
2116
  {
2183
- double eps=param->eps;
2117
+ //inner and outer tolerances for TRON
2118
+ double eps = param->eps;
2119
+ double eps_cg = 0.1;
2120
+ if(param->init_sol != NULL)
2121
+ eps_cg = 0.5;
2122
+
2184
2123
  int pos = 0;
2185
2124
  int neg = 0;
2186
2125
  for(int i=0;i<prob->l;i++)
2187
2126
  if(prob->y[i] > 0)
2188
2127
  pos++;
2189
2128
  neg = prob->l - pos;
2190
-
2191
2129
  double primal_solver_tol = eps*max(min(pos,neg), 1)/prob->l;
2192
2130
 
2193
2131
  function *fun_obj=NULL;
@@ -2204,7 +2142,7 @@ static void train_one(const problem *prob, const parameter *param, double *w, do
2204
2142
  C[i] = Cn;
2205
2143
  }
2206
2144
  fun_obj=new l2r_lr_fun(prob, C);
2207
- TRON tron_obj(fun_obj, primal_solver_tol);
2145
+ TRON tron_obj(fun_obj, primal_solver_tol, eps_cg);
2208
2146
  tron_obj.set_print_string(liblinear_print_string);
2209
2147
  tron_obj.tron(w);
2210
2148
  delete fun_obj;
@@ -2222,7 +2160,7 @@ static void train_one(const problem *prob, const parameter *param, double *w, do
2222
2160
  C[i] = Cn;
2223
2161
  }
2224
2162
  fun_obj=new l2r_l2_svc_fun(prob, C);
2225
- TRON tron_obj(fun_obj, primal_solver_tol);
2163
+ TRON tron_obj(fun_obj, primal_solver_tol, eps_cg);
2226
2164
  tron_obj.set_print_string(liblinear_print_string);
2227
2165
  tron_obj.tron(w);
2228
2166
  delete fun_obj;
@@ -2287,6 +2225,36 @@ static void train_one(const problem *prob, const parameter *param, double *w, do
2287
2225
  }
2288
2226
  }
2289
2227
 
2228
+ // Calculate the initial C for parameter selection
2229
+ static double calc_start_C(const problem *prob, const parameter *param)
2230
+ {
2231
+ int i;
2232
+ double xTx,max_xTx;
2233
+ max_xTx = 0;
2234
+ for(i=0; i<prob->l; i++)
2235
+ {
2236
+ xTx = 0;
2237
+ feature_node *xi=prob->x[i];
2238
+ while(xi->index != -1)
2239
+ {
2240
+ double val = xi->value;
2241
+ xTx += val*val;
2242
+ xi++;
2243
+ }
2244
+ if(xTx > max_xTx)
2245
+ max_xTx = xTx;
2246
+ }
2247
+
2248
+ double min_C = 1.0;
2249
+ if(param->solver_type == L2R_LR)
2250
+ min_C = 1.0 / (prob->l * max_xTx);
2251
+ else if(param->solver_type == L2R_L2LOSS_SVC)
2252
+ min_C = 1.0 / (2 * prob->l * max_xTx);
2253
+
2254
+ return pow( 2, floor(log(min_C) / log(2.0)) );
2255
+ }
2256
+
2257
+
2290
2258
  //
2291
2259
  // Interface functions
2292
2260
  //
@@ -2308,9 +2276,11 @@ model* train(const problem *prob, const parameter *param)
2308
2276
  if(check_regression_model(model_))
2309
2277
  {
2310
2278
  model_->w = Malloc(double, w_size);
2279
+ for(i=0; i<w_size; i++)
2280
+ model_->w[i] = 0;
2311
2281
  model_->nr_class = 2;
2312
2282
  model_->label = NULL;
2313
- train_one(prob, param, &model_->w[0], 0, 0);
2283
+ train_one(prob, param, model_->w, 0, 0);
2314
2284
  }
2315
2285
  else
2316
2286
  {
@@ -2380,8 +2350,15 @@ model* train(const problem *prob, const parameter *param)
2380
2350
  sub_prob.y[k] = +1;
2381
2351
  for(; k<sub_prob.l; k++)
2382
2352
  sub_prob.y[k] = -1;
2353
+
2354
+ if(param->init_sol != NULL)
2355
+ for(i=0;i<w_size;i++)
2356
+ model_->w[i] = param->init_sol[i];
2357
+ else
2358
+ for(i=0;i<w_size;i++)
2359
+ model_->w[i] = 0;
2383
2360
 
2384
- train_one(&sub_prob, param, &model_->w[0], weighted_C[0], weighted_C[1]);
2361
+ train_one(&sub_prob, param, model_->w, weighted_C[0], weighted_C[1]);
2385
2362
  }
2386
2363
  else
2387
2364
  {
@@ -2400,6 +2377,13 @@ model* train(const problem *prob, const parameter *param)
2400
2377
  for(; k<sub_prob.l; k++)
2401
2378
  sub_prob.y[k] = -1;
2402
2379
 
2380
+ if(param->init_sol != NULL)
2381
+ for(j=0;j<w_size;j++)
2382
+ w[j] = param->init_sol[j*nr_class+i];
2383
+ else
2384
+ for(j=0;j<w_size;j++)
2385
+ w[j] = 0;
2386
+
2403
2387
  train_one(&sub_prob, param, w, weighted_C[i], param->C);
2404
2388
 
2405
2389
  for(int j=0;j<w_size;j++)
@@ -2480,6 +2464,158 @@ void cross_validation(const problem *prob, const parameter *param, int nr_fold,
2480
2464
  free(perm);
2481
2465
  }
2482
2466
 
2467
+ void find_parameter_C(const problem *prob, const parameter *param, int nr_fold, double start_C, double max_C, double *best_C, double *best_rate)
2468
+ {
2469
+ // variables for CV
2470
+ int i;
2471
+ int *fold_start;
2472
+ int l = prob->l;
2473
+ int *perm = Malloc(int, l);
2474
+ double *target = Malloc(double, prob->l);
2475
+ struct problem *subprob = Malloc(problem,nr_fold);
2476
+
2477
+ // variables for warm start
2478
+ double ratio = 2;
2479
+ double **prev_w = Malloc(double*, nr_fold);
2480
+ for(i = 0; i < nr_fold; i++)
2481
+ prev_w[i] = NULL;
2482
+ int num_unchanged_w = 0;
2483
+ struct parameter param1 = *param;
2484
+ void (*default_print_string) (const char *) = liblinear_print_string;
2485
+
2486
+ if (nr_fold > l)
2487
+ {
2488
+ nr_fold = l;
2489
+ fprintf(stderr,"WARNING: # folds > # data. Will use # folds = # data instead (i.e., leave-one-out cross validation)\n");
2490
+ }
2491
+ fold_start = Malloc(int,nr_fold+1);
2492
+ for(i=0;i<l;i++) perm[i]=i;
2493
+ for(i=0;i<l;i++)
2494
+ {
2495
+ int j = i+rand()%(l-i);
2496
+ swap(perm[i],perm[j]);
2497
+ }
2498
+ for(i=0;i<=nr_fold;i++)
2499
+ fold_start[i]=i*l/nr_fold;
2500
+
2501
+ for(i=0;i<nr_fold;i++)
2502
+ {
2503
+ int begin = fold_start[i];
2504
+ int end = fold_start[i+1];
2505
+ int j,k;
2506
+
2507
+ subprob[i].bias = prob->bias;
2508
+ subprob[i].n = prob->n;
2509
+ subprob[i].l = l-(end-begin);
2510
+ subprob[i].x = Malloc(struct feature_node*,subprob[i].l);
2511
+ subprob[i].y = Malloc(double,subprob[i].l);
2512
+
2513
+ k=0;
2514
+ for(j=0;j<begin;j++)
2515
+ {
2516
+ subprob[i].x[k] = prob->x[perm[j]];
2517
+ subprob[i].y[k] = prob->y[perm[j]];
2518
+ ++k;
2519
+ }
2520
+ for(j=end;j<l;j++)
2521
+ {
2522
+ subprob[i].x[k] = prob->x[perm[j]];
2523
+ subprob[i].y[k] = prob->y[perm[j]];
2524
+ ++k;
2525
+ }
2526
+
2527
+ }
2528
+
2529
+ *best_rate = 0;
2530
+ if(start_C <= 0)
2531
+ start_C = calc_start_C(prob,param);
2532
+ param1.C = start_C;
2533
+
2534
+ while(param1.C <= max_C)
2535
+ {
2536
+ //Output disabled for running CV at a particular C
2537
+ set_print_string_function(&print_null);
2538
+
2539
+ for(i=0; i<nr_fold; i++)
2540
+ {
2541
+ int j;
2542
+ int begin = fold_start[i];
2543
+ int end = fold_start[i+1];
2544
+
2545
+ param1.init_sol = prev_w[i];
2546
+ struct model *submodel = train(&subprob[i],&param1);
2547
+
2548
+ int total_w_size;
2549
+ if(submodel->nr_class == 2)
2550
+ total_w_size = subprob[i].n;
2551
+ else
2552
+ total_w_size = subprob[i].n * submodel->nr_class;
2553
+
2554
+ if(prev_w[i] == NULL)
2555
+ {
2556
+ prev_w[i] = Malloc(double, total_w_size);
2557
+ for(j=0; j<total_w_size; j++)
2558
+ prev_w[i][j] = submodel->w[j];
2559
+ }
2560
+ else if(num_unchanged_w >= 0)
2561
+ {
2562
+ double norm_w_diff = 0;
2563
+ for(j=0; j<total_w_size; j++)
2564
+ {
2565
+ norm_w_diff += (submodel->w[j] - prev_w[i][j])*(submodel->w[j] - prev_w[i][j]);
2566
+ prev_w[i][j] = submodel->w[j];
2567
+ }
2568
+ norm_w_diff = sqrt(norm_w_diff);
2569
+
2570
+ if(norm_w_diff > 1e-15)
2571
+ num_unchanged_w = -1;
2572
+ }
2573
+ else
2574
+ {
2575
+ for(j=0; j<total_w_size; j++)
2576
+ prev_w[i][j] = submodel->w[j];
2577
+ }
2578
+
2579
+ for(j=begin; j<end; j++)
2580
+ target[perm[j]] = predict(submodel,prob->x[perm[j]]);
2581
+
2582
+ free_and_destroy_model(&submodel);
2583
+ }
2584
+ set_print_string_function(default_print_string);
2585
+
2586
+ int total_correct = 0;
2587
+ for(i=0; i<prob->l; i++)
2588
+ if(target[i] == prob->y[i])
2589
+ ++total_correct;
2590
+ double current_rate = (double)total_correct/prob->l;
2591
+ if(current_rate > *best_rate)
2592
+ {
2593
+ *best_C = param1.C;
2594
+ *best_rate = current_rate;
2595
+ }
2596
+
2597
+ info("log2c=%7.2f\trate=%g\n",log(param1.C)/log(2.0),100.0*current_rate);
2598
+ num_unchanged_w++;
2599
+ if(num_unchanged_w == 3)
2600
+ break;
2601
+ param1.C = param1.C*ratio;
2602
+ }
2603
+
2604
+ if(param1.C > max_C && max_C > start_C)
2605
+ info("warning: maximum C reached.\n");
2606
+ free(fold_start);
2607
+ free(perm);
2608
+ free(target);
2609
+ for(i=0; i<nr_fold; i++)
2610
+ {
2611
+ free(subprob[i].x);
2612
+ free(subprob[i].y);
2613
+ free(prev_w[i]);
2614
+ }
2615
+ free(prev_w);
2616
+ free(subprob);
2617
+ }
2618
+
2483
2619
  double predict_values(const struct model *model_, const struct feature_node *x, double *dec_values)
2484
2620
  {
2485
2621
  int idx;
@@ -2592,7 +2728,11 @@ int save_model(const char *model_file_name, const struct model *model_)
2592
2728
  FILE *fp = fopen(model_file_name,"w");
2593
2729
  if(fp==NULL) return -1;
2594
2730
 
2595
- char *old_locale = strdup(setlocale(LC_ALL, NULL));
2731
+ char *old_locale = setlocale(LC_ALL, NULL);
2732
+ if (old_locale)
2733
+ {
2734
+ old_locale = strdup(old_locale);
2735
+ }
2596
2736
  setlocale(LC_ALL, "C");
2597
2737
 
2598
2738
  int nr_w;
@@ -2632,6 +2772,30 @@ int save_model(const char *model_file_name, const struct model *model_)
2632
2772
  else return 0;
2633
2773
  }
2634
2774
 
2775
+ //
2776
+ // FSCANF helps to handle fscanf failures.
2777
+ // Its do-while block avoids the ambiguity when
2778
+ // if (...)
2779
+ // FSCANF();
2780
+ // is used
2781
+ //
2782
+ #define FSCANF(_stream, _format, _var)do\
2783
+ {\
2784
+ if (fscanf(_stream, _format, _var) != 1)\
2785
+ {\
2786
+ fprintf(stderr, "ERROR: fscanf failed to read the model\n");\
2787
+ EXIT_LOAD_MODEL()\
2788
+ }\
2789
+ }while(0)
2790
+ // EXIT_LOAD_MODEL should NOT end with a semicolon.
2791
+ #define EXIT_LOAD_MODEL()\
2792
+ {\
2793
+ setlocale(LC_ALL, old_locale);\
2794
+ free(model_->label);\
2795
+ free(model_);\
2796
+ free(old_locale);\
2797
+ return NULL;\
2798
+ }
2635
2799
  struct model *load_model(const char *model_file_name)
2636
2800
  {
2637
2801
  FILE *fp = fopen(model_file_name,"r");
@@ -2647,16 +2811,20 @@ struct model *load_model(const char *model_file_name)
2647
2811
 
2648
2812
  model_->label = NULL;
2649
2813
 
2650
- char *old_locale = strdup(setlocale(LC_ALL, NULL));
2814
+ char *old_locale = setlocale(LC_ALL, NULL);
2815
+ if (old_locale)
2816
+ {
2817
+ old_locale = strdup(old_locale);
2818
+ }
2651
2819
  setlocale(LC_ALL, "C");
2652
2820
 
2653
2821
  char cmd[81];
2654
2822
  while(1)
2655
2823
  {
2656
- fscanf(fp,"%80s",cmd);
2824
+ FSCANF(fp,"%80s",cmd);
2657
2825
  if(strcmp(cmd,"solver_type")==0)
2658
2826
  {
2659
- fscanf(fp,"%80s",cmd);
2827
+ FSCANF(fp,"%80s",cmd);
2660
2828
  int i;
2661
2829
  for(i=0;solver_type_table[i];i++)
2662
2830
  {
@@ -2669,27 +2837,22 @@ struct model *load_model(const char *model_file_name)
2669
2837
  if(solver_type_table[i] == NULL)
2670
2838
  {
2671
2839
  fprintf(stderr,"unknown solver type.\n");
2672
-
2673
- setlocale(LC_ALL, old_locale);
2674
- free(model_->label);
2675
- free(model_);
2676
- free(old_locale);
2677
- return NULL;
2840
+ EXIT_LOAD_MODEL()
2678
2841
  }
2679
2842
  }
2680
2843
  else if(strcmp(cmd,"nr_class")==0)
2681
2844
  {
2682
- fscanf(fp,"%d",&nr_class);
2845
+ FSCANF(fp,"%d",&nr_class);
2683
2846
  model_->nr_class=nr_class;
2684
2847
  }
2685
2848
  else if(strcmp(cmd,"nr_feature")==0)
2686
2849
  {
2687
- fscanf(fp,"%d",&nr_feature);
2850
+ FSCANF(fp,"%d",&nr_feature);
2688
2851
  model_->nr_feature=nr_feature;
2689
2852
  }
2690
2853
  else if(strcmp(cmd,"bias")==0)
2691
2854
  {
2692
- fscanf(fp,"%lf",&bias);
2855
+ FSCANF(fp,"%lf",&bias);
2693
2856
  model_->bias=bias;
2694
2857
  }
2695
2858
  else if(strcmp(cmd,"w")==0)
@@ -2701,16 +2864,12 @@ struct model *load_model(const char *model_file_name)
2701
2864
  int nr_class = model_->nr_class;
2702
2865
  model_->label = Malloc(int,nr_class);
2703
2866
  for(int i=0;i<nr_class;i++)
2704
- fscanf(fp,"%d",&model_->label[i]);
2867
+ FSCANF(fp,"%d",&model_->label[i]);
2705
2868
  }
2706
2869
  else
2707
2870
  {
2708
2871
  fprintf(stderr,"unknown text in model file: [%s]\n",cmd);
2709
- setlocale(LC_ALL, old_locale);
2710
- free(model_->label);
2711
- free(model_);
2712
- free(old_locale);
2713
- return NULL;
2872
+ EXIT_LOAD_MODEL()
2714
2873
  }
2715
2874
  }
2716
2875
 
@@ -2731,8 +2890,12 @@ struct model *load_model(const char *model_file_name)
2731
2890
  {
2732
2891
  int j;
2733
2892
  for(j=0; j<nr_w; j++)
2734
- fscanf(fp, "%lf ", &model_->w[i*nr_w+j]);
2735
- fscanf(fp, "\n");
2893
+ FSCANF(fp, "%lf ", &model_->w[i*nr_w+j]);
2894
+ if (fscanf(fp, "\n") !=0)
2895
+ {
2896
+ fprintf(stderr, "ERROR: fscanf failed to read the model\n");
2897
+ EXIT_LOAD_MODEL()
2898
+ }
2736
2899
  }
2737
2900
 
2738
2901
  setlocale(LC_ALL, old_locale);
@@ -2831,6 +2994,8 @@ void destroy_param(parameter* param)
2831
2994
  free(param->weight_label);
2832
2995
  if(param->weight != NULL)
2833
2996
  free(param->weight);
2997
+ if(param->init_sol != NULL)
2998
+ free(param->init_sol);
2834
2999
  }
2835
3000
 
2836
3001
  const char *check_parameter(const problem *prob, const parameter *param)
@@ -2857,6 +3022,10 @@ const char *check_parameter(const problem *prob, const parameter *param)
2857
3022
  && param->solver_type != L2R_L1LOSS_SVR_DUAL)
2858
3023
  return "unknown solver type";
2859
3024
 
3025
+ if(param->init_sol != NULL
3026
+ && param->solver_type != L2R_LR && param->solver_type != L2R_L2LOSS_SVC)
3027
+ return "Initial-solution specification supported only for solver L2R_LR and L2R_L2LOSS_SVC";
3028
+
2860
3029
  return NULL;
2861
3030
  }
2862
3031