liblinear-ruby 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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