jrb-libsvm 0.1.2-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2814 @@
1
+
2
+ package libsvm;
3
+ import java.io.*;
4
+ import java.util.*;
5
+
6
+ //
7
+ // Kernel Cache
8
+ //
9
+ // l is the number of total data items
10
+ // size is the cache size limit in bytes
11
+ //
12
+ class Cache {
13
+ private final int l;
14
+ private long size;
15
+ private final class head_t
16
+ {
17
+ head_t prev, next; // a cicular list
18
+ float[] data;
19
+ int len; // data[0,len) is cached in this entry
20
+ }
21
+ private final head_t[] head;
22
+ private head_t lru_head;
23
+
24
+ Cache(int l_, long size_)
25
+ {
26
+ l = l_;
27
+ size = size_;
28
+ head = new head_t[l];
29
+ for(int i=0;i<l;i++) head[i] = new head_t();
30
+ size /= 4;
31
+ size -= l * (16/4); // sizeof(head_t) == 16
32
+ size = Math.max(size, 2* (long) l); // cache must be large enough for two columns
33
+ lru_head = new head_t();
34
+ lru_head.next = lru_head.prev = lru_head;
35
+ }
36
+
37
+ private void lru_delete(head_t h)
38
+ {
39
+ // delete from current location
40
+ h.prev.next = h.next;
41
+ h.next.prev = h.prev;
42
+ }
43
+
44
+ private void lru_insert(head_t h)
45
+ {
46
+ // insert to last position
47
+ h.next = lru_head;
48
+ h.prev = lru_head.prev;
49
+ h.prev.next = h;
50
+ h.next.prev = h;
51
+ }
52
+
53
+ // request data [0,len)
54
+ // return some position p where [p,len) need to be filled
55
+ // (p >= len if nothing needs to be filled)
56
+ // java: simulate pointer using single-element array
57
+ int get_data(int index, float[][] data, int len)
58
+ {
59
+ head_t h = head[index];
60
+ if(h.len > 0) lru_delete(h);
61
+ int more = len - h.len;
62
+
63
+ if(more > 0)
64
+ {
65
+ // free old space
66
+ while(size < more)
67
+ {
68
+ head_t old = lru_head.next;
69
+ lru_delete(old);
70
+ size += old.len;
71
+ old.data = null;
72
+ old.len = 0;
73
+ }
74
+
75
+ // allocate new space
76
+ float[] new_data = new float[len];
77
+ if(h.data != null) System.arraycopy(h.data,0,new_data,0,h.len);
78
+ h.data = new_data;
79
+ size -= more;
80
+ do {int _=h.len; h.len=len; len=_;} while(false);
81
+ }
82
+
83
+ lru_insert(h);
84
+ data[0] = h.data;
85
+ return len;
86
+ }
87
+
88
+ void swap_index(int i, int j)
89
+ {
90
+ if(i==j) return;
91
+
92
+ if(head[i].len > 0) lru_delete(head[i]);
93
+ if(head[j].len > 0) lru_delete(head[j]);
94
+ do {float[] _=head[i].data; head[i].data=head[j].data; head[j].data=_;} while(false);
95
+ do {int _=head[i].len; head[i].len=head[j].len; head[j].len=_;} while(false);
96
+ if(head[i].len > 0) lru_insert(head[i]);
97
+ if(head[j].len > 0) lru_insert(head[j]);
98
+
99
+ if(i>j) do {int _=i; i=j; j=_;} while(false);
100
+ for(head_t h = lru_head.next; h!=lru_head; h=h.next)
101
+ {
102
+ if(h.len > i)
103
+ {
104
+ if(h.len > j)
105
+ do {float _=h.data[i]; h.data[i]=h.data[j]; h.data[j]=_;} while(false);
106
+ else
107
+ {
108
+ // give up
109
+ lru_delete(h);
110
+ size += h.len;
111
+ h.data = null;
112
+ h.len = 0;
113
+ }
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ //
120
+ // Kernel evaluation
121
+ //
122
+ // the static method k_function is for doing single kernel evaluation
123
+ // the constructor of Kernel prepares to calculate the l*l kernel matrix
124
+ // the member function get_Q is for getting one column from the Q Matrix
125
+ //
126
+ abstract class QMatrix {
127
+ abstract float[] get_Q(int column, int len);
128
+ abstract double[] get_QD();
129
+ abstract void swap_index(int i, int j);
130
+ };
131
+
132
+ abstract class Kernel extends QMatrix {
133
+ private Node[][] x;
134
+ private final double[] x_square;
135
+
136
+ // Parameter
137
+ private final int kernel_type;
138
+ private final int degree;
139
+ private final double gamma;
140
+ private final double coef0;
141
+
142
+ abstract float[] get_Q(int column, int len);
143
+ abstract double[] get_QD();
144
+
145
+ void swap_index(int i, int j)
146
+ {
147
+ do {Node[] _=x[i]; x[i]=x[j]; x[j]=_;} while(false);
148
+ if(x_square != null) do {double _=x_square[i]; x_square[i]=x_square[j]; x_square[j]=_;} while(false);
149
+ }
150
+
151
+ private static double powi(double base, int times)
152
+ {
153
+ double tmp = base, ret = 1.0;
154
+
155
+ for(int t=times; t>0; t/=2)
156
+ {
157
+ if(t%2==1) ret*=tmp;
158
+ tmp = tmp * tmp;
159
+ }
160
+ return ret;
161
+ }
162
+
163
+ double kernel_function(int i, int j)
164
+ {
165
+ switch(kernel_type)
166
+ {
167
+ case Parameter.LINEAR:
168
+ return dot(x[i],x[j]);
169
+ case Parameter.POLY:
170
+ return powi(gamma*dot(x[i],x[j])+coef0,degree);
171
+ case Parameter.RBF:
172
+ return Math.exp(-gamma*(x_square[i]+x_square[j]-2*dot(x[i],x[j])));
173
+ case Parameter.SIGMOID:
174
+ return Math.tanh(gamma*dot(x[i],x[j])+coef0);
175
+ case Parameter.PRECOMPUTED:
176
+ return x[i][(int)(x[j][0].value)].value;
177
+ default:
178
+ return 0; // java
179
+ }
180
+ }
181
+
182
+ Kernel(int l, Node[][] x_, Parameter param)
183
+ {
184
+ this.kernel_type = param.kernel_type;
185
+ this.degree = param.degree;
186
+ this.gamma = param.gamma;
187
+ this.coef0 = param.coef0;
188
+
189
+ x = (Node[][])x_.clone();
190
+
191
+ if(kernel_type == Parameter.RBF)
192
+ {
193
+ x_square = new double[l];
194
+ for(int i=0;i<l;i++)
195
+ x_square[i] = dot(x[i],x[i]);
196
+ }
197
+ else x_square = null;
198
+ }
199
+
200
+ static double dot(Node[] x, Node[] y)
201
+ {
202
+ double sum = 0;
203
+ int xlen = x.length;
204
+ int ylen = y.length;
205
+ int i = 0;
206
+ int j = 0;
207
+ while(i < xlen && j < ylen)
208
+ {
209
+ if(x[i].index == y[j].index)
210
+ sum += x[i++].value * y[j++].value;
211
+ else
212
+ {
213
+ if(x[i].index > y[j].index)
214
+ ++j;
215
+ else
216
+ ++i;
217
+ }
218
+ }
219
+ return sum;
220
+ }
221
+
222
+ static double k_function(Node[] x, Node[] y,
223
+ Parameter param)
224
+ {
225
+ switch(param.kernel_type)
226
+ {
227
+ case Parameter.LINEAR:
228
+ return dot(x,y);
229
+ case Parameter.POLY:
230
+ return powi(param.gamma*dot(x,y)+param.coef0,param.degree);
231
+ case Parameter.RBF:
232
+ {
233
+ double sum = 0;
234
+ int xlen = x.length;
235
+ int ylen = y.length;
236
+ int i = 0;
237
+ int j = 0;
238
+ while(i < xlen && j < ylen)
239
+ {
240
+ if(x[i].index == y[j].index)
241
+ {
242
+ double d = x[i++].value - y[j++].value;
243
+ sum += d*d;
244
+ }
245
+ else if(x[i].index > y[j].index)
246
+ {
247
+ sum += y[j].value * y[j].value;
248
+ ++j;
249
+ }
250
+ else
251
+ {
252
+ sum += x[i].value * x[i].value;
253
+ ++i;
254
+ }
255
+ }
256
+
257
+ while(i < xlen)
258
+ {
259
+ sum += x[i].value * x[i].value;
260
+ ++i;
261
+ }
262
+
263
+ while(j < ylen)
264
+ {
265
+ sum += y[j].value * y[j].value;
266
+ ++j;
267
+ }
268
+
269
+ return Math.exp(-param.gamma*sum);
270
+ }
271
+ case Parameter.SIGMOID:
272
+ return Math.tanh(param.gamma*dot(x,y)+param.coef0);
273
+ case Parameter.PRECOMPUTED:
274
+ return x[(int)(y[0].value)].value;
275
+ default:
276
+ return 0; // java
277
+ }
278
+ }
279
+ }
280
+
281
+ // An SMO algorithm in Fan et al., JMLR 6(2005), p. 1889--1918
282
+ // Solves:
283
+ //
284
+ // min 0.5(\alpha^T Q \alpha) + p^T \alpha
285
+ //
286
+ // y^T \alpha = \delta
287
+ // y_i = +1 or -1
288
+ // 0 <= alpha_i <= Cp for y_i = 1
289
+ // 0 <= alpha_i <= Cn for y_i = -1
290
+ //
291
+ // Given:
292
+ //
293
+ // Q, p, y, Cp, Cn, and an initial feasible point \alpha
294
+ // l is the size of vectors and matrices
295
+ // eps is the stopping tolerance
296
+ //
297
+ // solution will be put in \alpha, objective value will be put in obj
298
+ //
299
+ class Solver {
300
+ int active_size;
301
+ byte[] y;
302
+ double[] G; // gradient of objective function
303
+ static final byte LOWER_BOUND = 0;
304
+ static final byte UPPER_BOUND = 1;
305
+ static final byte FREE = 2;
306
+ byte[] alpha_status; // LOWER_BOUND, UPPER_BOUND, FREE
307
+ double[] alpha;
308
+ QMatrix Q;
309
+ double[] QD;
310
+ double eps;
311
+ double Cp,Cn;
312
+ double[] p;
313
+ int[] active_set;
314
+ double[] G_bar; // gradient, if we treat free variables as 0
315
+ int l;
316
+ boolean unshrink; // XXX
317
+
318
+ static final double INF = java.lang.Double.POSITIVE_INFINITY;
319
+
320
+ double get_C(int i)
321
+ {
322
+ return (y[i] > 0)? Cp : Cn;
323
+ }
324
+ void update_alpha_status(int i)
325
+ {
326
+ if(alpha[i] >= get_C(i))
327
+ alpha_status[i] = UPPER_BOUND;
328
+ else if(alpha[i] <= 0)
329
+ alpha_status[i] = LOWER_BOUND;
330
+ else alpha_status[i] = FREE;
331
+ }
332
+ boolean is_upper_bound(int i) { return alpha_status[i] == UPPER_BOUND; }
333
+ boolean is_lower_bound(int i) { return alpha_status[i] == LOWER_BOUND; }
334
+ boolean is_free(int i) { return alpha_status[i] == FREE; }
335
+
336
+ // java: information about solution except alpha,
337
+ // because we cannot return multiple values otherwise...
338
+ static class SolutionInfo {
339
+ double obj;
340
+ double rho;
341
+ double upper_bound_p;
342
+ double upper_bound_n;
343
+ double r; // for Solver_NU
344
+ }
345
+
346
+ void swap_index(int i, int j)
347
+ {
348
+ Q.swap_index(i,j);
349
+ do {byte _=y[i]; y[i]=y[j]; y[j]=_;} while(false);
350
+ do {double _=G[i]; G[i]=G[j]; G[j]=_;} while(false);
351
+ do {byte _=alpha_status[i]; alpha_status[i]=alpha_status[j]; alpha_status[j]=_;} while(false);
352
+ do {double _=alpha[i]; alpha[i]=alpha[j]; alpha[j]=_;} while(false);
353
+ do {double _=p[i]; p[i]=p[j]; p[j]=_;} while(false);
354
+ do {int _=active_set[i]; active_set[i]=active_set[j]; active_set[j]=_;} while(false);
355
+ do {double _=G_bar[i]; G_bar[i]=G_bar[j]; G_bar[j]=_;} while(false);
356
+ }
357
+
358
+ void reconstruct_gradient()
359
+ {
360
+ // reconstruct inactive elements of G from G_bar and free variables
361
+
362
+ if(active_size == l) return;
363
+
364
+ int i,j;
365
+ int nr_free = 0;
366
+
367
+ for(j=active_size;j<l;j++)
368
+ G[j] = G_bar[j] + p[j];
369
+
370
+ for(j=0;j<active_size;j++)
371
+ if(is_free(j))
372
+ nr_free++;
373
+
374
+ if(2*nr_free < active_size)
375
+ Svm.info("\nWARNING: using -h 0 may be faster\n");
376
+
377
+ if (nr_free*l > 2*active_size*(l-active_size))
378
+ {
379
+ for(i=active_size;i<l;i++)
380
+ {
381
+ float[] Q_i = Q.get_Q(i,active_size);
382
+ for(j=0;j<active_size;j++)
383
+ if(is_free(j))
384
+ G[i] += alpha[j] * Q_i[j];
385
+ }
386
+ }
387
+ else
388
+ {
389
+ for(i=0;i<active_size;i++)
390
+ if(is_free(i))
391
+ {
392
+ float[] Q_i = Q.get_Q(i,l);
393
+ double alpha_i = alpha[i];
394
+ for(j=active_size;j<l;j++)
395
+ G[j] += alpha_i * Q_i[j];
396
+ }
397
+ }
398
+ }
399
+
400
+ void Solve(int l, QMatrix Q, double[] p_, byte[] y_,
401
+ double[] alpha_, double Cp, double Cn, double eps, SolutionInfo si, int shrinking)
402
+ {
403
+ this.l = l;
404
+ this.Q = Q;
405
+ QD = Q.get_QD();
406
+ p = (double[])p_.clone();
407
+ y = (byte[])y_.clone();
408
+ alpha = (double[])alpha_.clone();
409
+ this.Cp = Cp;
410
+ this.Cn = Cn;
411
+ this.eps = eps;
412
+ this.unshrink = false;
413
+
414
+ // initialize alpha_status
415
+ {
416
+ alpha_status = new byte[l];
417
+ for(int i=0;i<l;i++)
418
+ update_alpha_status(i);
419
+ }
420
+
421
+ // initialize active set (for shrinking)
422
+ {
423
+ active_set = new int[l];
424
+ for(int i=0;i<l;i++)
425
+ active_set[i] = i;
426
+ active_size = l;
427
+ }
428
+
429
+ // initialize gradient
430
+ {
431
+ G = new double[l];
432
+ G_bar = new double[l];
433
+ int i;
434
+ for(i=0;i<l;i++)
435
+ {
436
+ G[i] = p[i];
437
+ G_bar[i] = 0;
438
+ }
439
+ for(i=0;i<l;i++)
440
+ if(!is_lower_bound(i))
441
+ {
442
+ float[] Q_i = Q.get_Q(i,l);
443
+ double alpha_i = alpha[i];
444
+ int j;
445
+ for(j=0;j<l;j++)
446
+ G[j] += alpha_i*Q_i[j];
447
+ if(is_upper_bound(i))
448
+ for(j=0;j<l;j++)
449
+ G_bar[j] += get_C(i) * Q_i[j];
450
+ }
451
+ }
452
+
453
+ // optimization step
454
+
455
+ int iter = 0;
456
+ int max_iter = Math.max(10000000, l>Integer.MAX_VALUE/100 ? Integer.MAX_VALUE : 100*l);
457
+ int counter = Math.min(l,1000)+1;
458
+ int[] working_set = new int[2];
459
+
460
+ while(iter < max_iter)
461
+ {
462
+ // show progress and do shrinking
463
+
464
+ if(--counter == 0)
465
+ {
466
+ counter = Math.min(l,1000);
467
+ if(shrinking!=0) do_shrinking();
468
+ Svm.info(".");
469
+ }
470
+
471
+ if(select_working_set(working_set)!=0)
472
+ {
473
+ // reconstruct the whole gradient
474
+ reconstruct_gradient();
475
+ // reset active set size and check
476
+ active_size = l;
477
+ Svm.info("*");
478
+ if(select_working_set(working_set)!=0)
479
+ break;
480
+ else
481
+ counter = 1; // do shrinking next iteration
482
+ }
483
+
484
+ int i = working_set[0];
485
+ int j = working_set[1];
486
+
487
+ ++iter;
488
+
489
+ // update alpha[i] and alpha[j], handle bounds carefully
490
+
491
+ float[] Q_i = Q.get_Q(i,active_size);
492
+ float[] Q_j = Q.get_Q(j,active_size);
493
+
494
+ double C_i = get_C(i);
495
+ double C_j = get_C(j);
496
+
497
+ double old_alpha_i = alpha[i];
498
+ double old_alpha_j = alpha[j];
499
+
500
+ if(y[i]!=y[j])
501
+ {
502
+ double quad_coef = QD[i]+QD[j]+2*Q_i[j];
503
+ if (quad_coef <= 0)
504
+ quad_coef = 1e-12;
505
+ double delta = (-G[i]-G[j])/quad_coef;
506
+ double diff = alpha[i] - alpha[j];
507
+ alpha[i] += delta;
508
+ alpha[j] += delta;
509
+
510
+ if(diff > 0)
511
+ {
512
+ if(alpha[j] < 0)
513
+ {
514
+ alpha[j] = 0;
515
+ alpha[i] = diff;
516
+ }
517
+ }
518
+ else
519
+ {
520
+ if(alpha[i] < 0)
521
+ {
522
+ alpha[i] = 0;
523
+ alpha[j] = -diff;
524
+ }
525
+ }
526
+ if(diff > C_i - C_j)
527
+ {
528
+ if(alpha[i] > C_i)
529
+ {
530
+ alpha[i] = C_i;
531
+ alpha[j] = C_i - diff;
532
+ }
533
+ }
534
+ else
535
+ {
536
+ if(alpha[j] > C_j)
537
+ {
538
+ alpha[j] = C_j;
539
+ alpha[i] = C_j + diff;
540
+ }
541
+ }
542
+ }
543
+ else
544
+ {
545
+ double quad_coef = QD[i]+QD[j]-2*Q_i[j];
546
+ if (quad_coef <= 0)
547
+ quad_coef = 1e-12;
548
+ double delta = (G[i]-G[j])/quad_coef;
549
+ double sum = alpha[i] + alpha[j];
550
+ alpha[i] -= delta;
551
+ alpha[j] += delta;
552
+
553
+ if(sum > C_i)
554
+ {
555
+ if(alpha[i] > C_i)
556
+ {
557
+ alpha[i] = C_i;
558
+ alpha[j] = sum - C_i;
559
+ }
560
+ }
561
+ else
562
+ {
563
+ if(alpha[j] < 0)
564
+ {
565
+ alpha[j] = 0;
566
+ alpha[i] = sum;
567
+ }
568
+ }
569
+ if(sum > C_j)
570
+ {
571
+ if(alpha[j] > C_j)
572
+ {
573
+ alpha[j] = C_j;
574
+ alpha[i] = sum - C_j;
575
+ }
576
+ }
577
+ else
578
+ {
579
+ if(alpha[i] < 0)
580
+ {
581
+ alpha[i] = 0;
582
+ alpha[j] = sum;
583
+ }
584
+ }
585
+ }
586
+
587
+ // update G
588
+
589
+ double delta_alpha_i = alpha[i] - old_alpha_i;
590
+ double delta_alpha_j = alpha[j] - old_alpha_j;
591
+
592
+ for(int k=0;k<active_size;k++)
593
+ {
594
+ G[k] += Q_i[k]*delta_alpha_i + Q_j[k]*delta_alpha_j;
595
+ }
596
+
597
+ // update alpha_status and G_bar
598
+
599
+ {
600
+ boolean ui = is_upper_bound(i);
601
+ boolean uj = is_upper_bound(j);
602
+ update_alpha_status(i);
603
+ update_alpha_status(j);
604
+ int k;
605
+ if(ui != is_upper_bound(i))
606
+ {
607
+ Q_i = Q.get_Q(i,l);
608
+ if(ui)
609
+ for(k=0;k<l;k++)
610
+ G_bar[k] -= C_i * Q_i[k];
611
+ else
612
+ for(k=0;k<l;k++)
613
+ G_bar[k] += C_i * Q_i[k];
614
+ }
615
+
616
+ if(uj != is_upper_bound(j))
617
+ {
618
+ Q_j = Q.get_Q(j,l);
619
+ if(uj)
620
+ for(k=0;k<l;k++)
621
+ G_bar[k] -= C_j * Q_j[k];
622
+ else
623
+ for(k=0;k<l;k++)
624
+ G_bar[k] += C_j * Q_j[k];
625
+ }
626
+ }
627
+
628
+ }
629
+
630
+ if(iter >= max_iter)
631
+ {
632
+ if(active_size < l)
633
+ {
634
+ // reconstruct the whole gradient to calculate objective value
635
+ reconstruct_gradient();
636
+ active_size = l;
637
+ Svm.info("*");
638
+ }
639
+ System.err.print("\nWARNING: reaching max number of iterations\n");
640
+ }
641
+
642
+ // calculate rho
643
+
644
+ si.rho = calculate_rho();
645
+
646
+ // calculate objective value
647
+ {
648
+ double v = 0;
649
+ int i;
650
+ for(i=0;i<l;i++)
651
+ v += alpha[i] * (G[i] + p[i]);
652
+
653
+ si.obj = v/2;
654
+ }
655
+
656
+ // put back the solution
657
+ {
658
+ for(int i=0;i<l;i++)
659
+ alpha_[active_set[i]] = alpha[i];
660
+ }
661
+
662
+ si.upper_bound_p = Cp;
663
+ si.upper_bound_n = Cn;
664
+
665
+ Svm.info("\noptimization finished, #iter = "+iter+"\n");
666
+ }
667
+
668
+ // return 1 if already optimal, return 0 otherwise
669
+ int select_working_set(int[] working_set)
670
+ {
671
+ // return i,j such that
672
+ // i: maximizes -y_i * grad(f)_i, i in I_up(\alpha)
673
+ // j: mimimizes the decrease of obj value
674
+ // (if quadratic coefficeint <= 0, replace it with tau)
675
+ // -y_j*grad(f)_j < -y_i*grad(f)_i, j in I_low(\alpha)
676
+
677
+ double Gmax = -INF;
678
+ double Gmax2 = -INF;
679
+ int Gmax_idx = -1;
680
+ int Gmin_idx = -1;
681
+ double obj_diff_min = INF;
682
+
683
+ for(int t=0;t<active_size;t++)
684
+ if(y[t]==+1)
685
+ {
686
+ if(!is_upper_bound(t))
687
+ if(-G[t] >= Gmax)
688
+ {
689
+ Gmax = -G[t];
690
+ Gmax_idx = t;
691
+ }
692
+ }
693
+ else
694
+ {
695
+ if(!is_lower_bound(t))
696
+ if(G[t] >= Gmax)
697
+ {
698
+ Gmax = G[t];
699
+ Gmax_idx = t;
700
+ }
701
+ }
702
+
703
+ int i = Gmax_idx;
704
+ float[] Q_i = null;
705
+ if(i != -1) // null Q_i not accessed: Gmax=-INF if i=-1
706
+ Q_i = Q.get_Q(i,active_size);
707
+
708
+ for(int j=0;j<active_size;j++)
709
+ {
710
+ if(y[j]==+1)
711
+ {
712
+ if (!is_lower_bound(j))
713
+ {
714
+ double grad_diff=Gmax+G[j];
715
+ if (G[j] >= Gmax2)
716
+ Gmax2 = G[j];
717
+ if (grad_diff > 0)
718
+ {
719
+ double obj_diff;
720
+ double quad_coef = QD[i]+QD[j]-2.0*y[i]*Q_i[j];
721
+ if (quad_coef > 0)
722
+ obj_diff = -(grad_diff*grad_diff)/quad_coef;
723
+ else
724
+ obj_diff = -(grad_diff*grad_diff)/1e-12;
725
+
726
+ if (obj_diff <= obj_diff_min)
727
+ {
728
+ Gmin_idx=j;
729
+ obj_diff_min = obj_diff;
730
+ }
731
+ }
732
+ }
733
+ }
734
+ else
735
+ {
736
+ if (!is_upper_bound(j))
737
+ {
738
+ double grad_diff= Gmax-G[j];
739
+ if (-G[j] >= Gmax2)
740
+ Gmax2 = -G[j];
741
+ if (grad_diff > 0)
742
+ {
743
+ double obj_diff;
744
+ double quad_coef = QD[i]+QD[j]+2.0*y[i]*Q_i[j];
745
+ if (quad_coef > 0)
746
+ obj_diff = -(grad_diff*grad_diff)/quad_coef;
747
+ else
748
+ obj_diff = -(grad_diff*grad_diff)/1e-12;
749
+
750
+ if (obj_diff <= obj_diff_min)
751
+ {
752
+ Gmin_idx=j;
753
+ obj_diff_min = obj_diff;
754
+ }
755
+ }
756
+ }
757
+ }
758
+ }
759
+
760
+ if(Gmax+Gmax2 < eps)
761
+ return 1;
762
+
763
+ working_set[0] = Gmax_idx;
764
+ working_set[1] = Gmin_idx;
765
+ return 0;
766
+ }
767
+
768
+ private boolean be_shrunk(int i, double Gmax1, double Gmax2)
769
+ {
770
+ if(is_upper_bound(i))
771
+ {
772
+ if(y[i]==+1)
773
+ return(-G[i] > Gmax1);
774
+ else
775
+ return(-G[i] > Gmax2);
776
+ }
777
+ else if(is_lower_bound(i))
778
+ {
779
+ if(y[i]==+1)
780
+ return(G[i] > Gmax2);
781
+ else
782
+ return(G[i] > Gmax1);
783
+ }
784
+ else
785
+ return(false);
786
+ }
787
+
788
+ void do_shrinking()
789
+ {
790
+ int i;
791
+ double Gmax1 = -INF; // max { -y_i * grad(f)_i | i in I_up(\alpha) }
792
+ double Gmax2 = -INF; // max { y_i * grad(f)_i | i in I_low(\alpha) }
793
+
794
+ // find maximal violating pair first
795
+ for(i=0;i<active_size;i++)
796
+ {
797
+ if(y[i]==+1)
798
+ {
799
+ if(!is_upper_bound(i))
800
+ {
801
+ if(-G[i] >= Gmax1)
802
+ Gmax1 = -G[i];
803
+ }
804
+ if(!is_lower_bound(i))
805
+ {
806
+ if(G[i] >= Gmax2)
807
+ Gmax2 = G[i];
808
+ }
809
+ }
810
+ else
811
+ {
812
+ if(!is_upper_bound(i))
813
+ {
814
+ if(-G[i] >= Gmax2)
815
+ Gmax2 = -G[i];
816
+ }
817
+ if(!is_lower_bound(i))
818
+ {
819
+ if(G[i] >= Gmax1)
820
+ Gmax1 = G[i];
821
+ }
822
+ }
823
+ }
824
+
825
+ if(unshrink == false && Gmax1 + Gmax2 <= eps*10)
826
+ {
827
+ unshrink = true;
828
+ reconstruct_gradient();
829
+ active_size = l;
830
+ }
831
+
832
+ for(i=0;i<active_size;i++)
833
+ if (be_shrunk(i, Gmax1, Gmax2))
834
+ {
835
+ active_size--;
836
+ while (active_size > i)
837
+ {
838
+ if (!be_shrunk(active_size, Gmax1, Gmax2))
839
+ {
840
+ swap_index(i,active_size);
841
+ break;
842
+ }
843
+ active_size--;
844
+ }
845
+ }
846
+ }
847
+
848
+ double calculate_rho()
849
+ {
850
+ double r;
851
+ int nr_free = 0;
852
+ double ub = INF, lb = -INF, sum_free = 0;
853
+ for(int i=0;i<active_size;i++)
854
+ {
855
+ double yG = y[i]*G[i];
856
+
857
+ if(is_lower_bound(i))
858
+ {
859
+ if(y[i] > 0)
860
+ ub = Math.min(ub,yG);
861
+ else
862
+ lb = Math.max(lb,yG);
863
+ }
864
+ else if(is_upper_bound(i))
865
+ {
866
+ if(y[i] < 0)
867
+ ub = Math.min(ub,yG);
868
+ else
869
+ lb = Math.max(lb,yG);
870
+ }
871
+ else
872
+ {
873
+ ++nr_free;
874
+ sum_free += yG;
875
+ }
876
+ }
877
+
878
+ if(nr_free>0)
879
+ r = sum_free/nr_free;
880
+ else
881
+ r = (ub+lb)/2;
882
+
883
+ return r;
884
+ }
885
+
886
+ }
887
+
888
+ //
889
+ // Solver for nu-svm classification and regression
890
+ //
891
+ // additional constraint: e^T \alpha = constant
892
+ //
893
+ final class Solver_NU extends Solver
894
+ {
895
+ private SolutionInfo si;
896
+
897
+ void Solve(int l, QMatrix Q, double[] p, byte[] y,
898
+ double[] alpha, double Cp, double Cn, double eps,
899
+ SolutionInfo si, int shrinking)
900
+ {
901
+ this.si = si;
902
+ super.Solve(l,Q,p,y,alpha,Cp,Cn,eps,si,shrinking);
903
+ }
904
+
905
+ // return 1 if already optimal, return 0 otherwise
906
+ int select_working_set(int[] working_set)
907
+ {
908
+ // return i,j such that y_i = y_j and
909
+ // i: maximizes -y_i * grad(f)_i, i in I_up(\alpha)
910
+ // j: minimizes the decrease of obj value
911
+ // (if quadratic coefficeint <= 0, replace it with tau)
912
+ // -y_j*grad(f)_j < -y_i*grad(f)_i, j in I_low(\alpha)
913
+
914
+ double Gmaxp = -INF;
915
+ double Gmaxp2 = -INF;
916
+ int Gmaxp_idx = -1;
917
+
918
+ double Gmaxn = -INF;
919
+ double Gmaxn2 = -INF;
920
+ int Gmaxn_idx = -1;
921
+
922
+ int Gmin_idx = -1;
923
+ double obj_diff_min = INF;
924
+
925
+ for(int t=0;t<active_size;t++)
926
+ if(y[t]==+1)
927
+ {
928
+ if(!is_upper_bound(t))
929
+ if(-G[t] >= Gmaxp)
930
+ {
931
+ Gmaxp = -G[t];
932
+ Gmaxp_idx = t;
933
+ }
934
+ }
935
+ else
936
+ {
937
+ if(!is_lower_bound(t))
938
+ if(G[t] >= Gmaxn)
939
+ {
940
+ Gmaxn = G[t];
941
+ Gmaxn_idx = t;
942
+ }
943
+ }
944
+
945
+ int ip = Gmaxp_idx;
946
+ int in = Gmaxn_idx;
947
+ float[] Q_ip = null;
948
+ float[] Q_in = null;
949
+ if(ip != -1) // null Q_ip not accessed: Gmaxp=-INF if ip=-1
950
+ Q_ip = Q.get_Q(ip,active_size);
951
+ if(in != -1)
952
+ Q_in = Q.get_Q(in,active_size);
953
+
954
+ for(int j=0;j<active_size;j++)
955
+ {
956
+ if(y[j]==+1)
957
+ {
958
+ if (!is_lower_bound(j))
959
+ {
960
+ double grad_diff=Gmaxp+G[j];
961
+ if (G[j] >= Gmaxp2)
962
+ Gmaxp2 = G[j];
963
+ if (grad_diff > 0)
964
+ {
965
+ double obj_diff;
966
+ double quad_coef = QD[ip]+QD[j]-2*Q_ip[j];
967
+ if (quad_coef > 0)
968
+ obj_diff = -(grad_diff*grad_diff)/quad_coef;
969
+ else
970
+ obj_diff = -(grad_diff*grad_diff)/1e-12;
971
+
972
+ if (obj_diff <= obj_diff_min)
973
+ {
974
+ Gmin_idx=j;
975
+ obj_diff_min = obj_diff;
976
+ }
977
+ }
978
+ }
979
+ }
980
+ else
981
+ {
982
+ if (!is_upper_bound(j))
983
+ {
984
+ double grad_diff=Gmaxn-G[j];
985
+ if (-G[j] >= Gmaxn2)
986
+ Gmaxn2 = -G[j];
987
+ if (grad_diff > 0)
988
+ {
989
+ double obj_diff;
990
+ double quad_coef = QD[in]+QD[j]-2*Q_in[j];
991
+ if (quad_coef > 0)
992
+ obj_diff = -(grad_diff*grad_diff)/quad_coef;
993
+ else
994
+ obj_diff = -(grad_diff*grad_diff)/1e-12;
995
+
996
+ if (obj_diff <= obj_diff_min)
997
+ {
998
+ Gmin_idx=j;
999
+ obj_diff_min = obj_diff;
1000
+ }
1001
+ }
1002
+ }
1003
+ }
1004
+ }
1005
+
1006
+ if(Math.max(Gmaxp+Gmaxp2,Gmaxn+Gmaxn2) < eps)
1007
+ return 1;
1008
+
1009
+ if(y[Gmin_idx] == +1)
1010
+ working_set[0] = Gmaxp_idx;
1011
+ else
1012
+ working_set[0] = Gmaxn_idx;
1013
+ working_set[1] = Gmin_idx;
1014
+
1015
+ return 0;
1016
+ }
1017
+
1018
+ private boolean be_shrunk(int i, double Gmax1, double Gmax2, double Gmax3, double Gmax4)
1019
+ {
1020
+ if(is_upper_bound(i))
1021
+ {
1022
+ if(y[i]==+1)
1023
+ return(-G[i] > Gmax1);
1024
+ else
1025
+ return(-G[i] > Gmax4);
1026
+ }
1027
+ else if(is_lower_bound(i))
1028
+ {
1029
+ if(y[i]==+1)
1030
+ return(G[i] > Gmax2);
1031
+ else
1032
+ return(G[i] > Gmax3);
1033
+ }
1034
+ else
1035
+ return(false);
1036
+ }
1037
+
1038
+ void do_shrinking()
1039
+ {
1040
+ double Gmax1 = -INF; // max { -y_i * grad(f)_i | y_i = +1, i in I_up(\alpha) }
1041
+ double Gmax2 = -INF; // max { y_i * grad(f)_i | y_i = +1, i in I_low(\alpha) }
1042
+ double Gmax3 = -INF; // max { -y_i * grad(f)_i | y_i = -1, i in I_up(\alpha) }
1043
+ double Gmax4 = -INF; // max { y_i * grad(f)_i | y_i = -1, i in I_low(\alpha) }
1044
+
1045
+ // find maximal violating pair first
1046
+ int i;
1047
+ for(i=0;i<active_size;i++)
1048
+ {
1049
+ if(!is_upper_bound(i))
1050
+ {
1051
+ if(y[i]==+1)
1052
+ {
1053
+ if(-G[i] > Gmax1) Gmax1 = -G[i];
1054
+ }
1055
+ else if(-G[i] > Gmax4) Gmax4 = -G[i];
1056
+ }
1057
+ if(!is_lower_bound(i))
1058
+ {
1059
+ if(y[i]==+1)
1060
+ {
1061
+ if(G[i] > Gmax2) Gmax2 = G[i];
1062
+ }
1063
+ else if(G[i] > Gmax3) Gmax3 = G[i];
1064
+ }
1065
+ }
1066
+
1067
+ if(unshrink == false && Math.max(Gmax1+Gmax2,Gmax3+Gmax4) <= eps*10)
1068
+ {
1069
+ unshrink = true;
1070
+ reconstruct_gradient();
1071
+ active_size = l;
1072
+ }
1073
+
1074
+ for(i=0;i<active_size;i++)
1075
+ if (be_shrunk(i, Gmax1, Gmax2, Gmax3, Gmax4))
1076
+ {
1077
+ active_size--;
1078
+ while (active_size > i)
1079
+ {
1080
+ if (!be_shrunk(active_size, Gmax1, Gmax2, Gmax3, Gmax4))
1081
+ {
1082
+ swap_index(i,active_size);
1083
+ break;
1084
+ }
1085
+ active_size--;
1086
+ }
1087
+ }
1088
+ }
1089
+
1090
+ double calculate_rho()
1091
+ {
1092
+ int nr_free1 = 0,nr_free2 = 0;
1093
+ double ub1 = INF, ub2 = INF;
1094
+ double lb1 = -INF, lb2 = -INF;
1095
+ double sum_free1 = 0, sum_free2 = 0;
1096
+
1097
+ for(int i=0;i<active_size;i++)
1098
+ {
1099
+ if(y[i]==+1)
1100
+ {
1101
+ if(is_lower_bound(i))
1102
+ ub1 = Math.min(ub1,G[i]);
1103
+ else if(is_upper_bound(i))
1104
+ lb1 = Math.max(lb1,G[i]);
1105
+ else
1106
+ {
1107
+ ++nr_free1;
1108
+ sum_free1 += G[i];
1109
+ }
1110
+ }
1111
+ else
1112
+ {
1113
+ if(is_lower_bound(i))
1114
+ ub2 = Math.min(ub2,G[i]);
1115
+ else if(is_upper_bound(i))
1116
+ lb2 = Math.max(lb2,G[i]);
1117
+ else
1118
+ {
1119
+ ++nr_free2;
1120
+ sum_free2 += G[i];
1121
+ }
1122
+ }
1123
+ }
1124
+
1125
+ double r1,r2;
1126
+ if(nr_free1 > 0)
1127
+ r1 = sum_free1/nr_free1;
1128
+ else
1129
+ r1 = (ub1+lb1)/2;
1130
+
1131
+ if(nr_free2 > 0)
1132
+ r2 = sum_free2/nr_free2;
1133
+ else
1134
+ r2 = (ub2+lb2)/2;
1135
+
1136
+ si.r = (r1+r2)/2;
1137
+ return (r1-r2)/2;
1138
+ }
1139
+ }
1140
+
1141
+ //
1142
+ // Q matrices for various formulations
1143
+ //
1144
+ class SVC_Q extends Kernel
1145
+ {
1146
+ private final byte[] y;
1147
+ private final Cache cache;
1148
+ private final double[] QD;
1149
+
1150
+ SVC_Q(Problem prob, Parameter param, byte[] y_)
1151
+ {
1152
+ super(prob.l, prob.x, param);
1153
+ y = (byte[])y_.clone();
1154
+ cache = new Cache(prob.l,(long)(param.cache_size*(1<<20)));
1155
+ QD = new double[prob.l];
1156
+ for(int i=0;i<prob.l;i++)
1157
+ QD[i] = kernel_function(i,i);
1158
+ }
1159
+
1160
+ float[] get_Q(int i, int len)
1161
+ {
1162
+ float[][] data = new float[1][];
1163
+ int start, j;
1164
+ if((start = cache.get_data(i,data,len)) < len)
1165
+ {
1166
+ for(j=start;j<len;j++)
1167
+ data[0][j] = (float)(y[i]*y[j]*kernel_function(i,j));
1168
+ }
1169
+ return data[0];
1170
+ }
1171
+
1172
+ double[] get_QD()
1173
+ {
1174
+ return QD;
1175
+ }
1176
+
1177
+ void swap_index(int i, int j)
1178
+ {
1179
+ cache.swap_index(i,j);
1180
+ super.swap_index(i,j);
1181
+ do {byte _=y[i]; y[i]=y[j]; y[j]=_;} while(false);
1182
+ do {double _=QD[i]; QD[i]=QD[j]; QD[j]=_;} while(false);
1183
+ }
1184
+ }
1185
+
1186
+ class ONE_CLASS_Q extends Kernel
1187
+ {
1188
+ private final Cache cache;
1189
+ private final double[] QD;
1190
+
1191
+ ONE_CLASS_Q(Problem prob, Parameter param)
1192
+ {
1193
+ super(prob.l, prob.x, param);
1194
+ cache = new Cache(prob.l,(long)(param.cache_size*(1<<20)));
1195
+ QD = new double[prob.l];
1196
+ for(int i=0;i<prob.l;i++)
1197
+ QD[i] = kernel_function(i,i);
1198
+ }
1199
+
1200
+ float[] get_Q(int i, int len)
1201
+ {
1202
+ float[][] data = new float[1][];
1203
+ int start, j;
1204
+ if((start = cache.get_data(i,data,len)) < len)
1205
+ {
1206
+ for(j=start;j<len;j++)
1207
+ data[0][j] = (float)kernel_function(i,j);
1208
+ }
1209
+ return data[0];
1210
+ }
1211
+
1212
+ double[] get_QD()
1213
+ {
1214
+ return QD;
1215
+ }
1216
+
1217
+ void swap_index(int i, int j)
1218
+ {
1219
+ cache.swap_index(i,j);
1220
+ super.swap_index(i,j);
1221
+ do {double _=QD[i]; QD[i]=QD[j]; QD[j]=_;} while(false);
1222
+ }
1223
+ }
1224
+
1225
+ class SVR_Q extends Kernel
1226
+ {
1227
+ private final int l;
1228
+ private final Cache cache;
1229
+ private final byte[] sign;
1230
+ private final int[] index;
1231
+ private int next_buffer;
1232
+ private float[][] buffer;
1233
+ private final double[] QD;
1234
+
1235
+ SVR_Q(Problem prob, Parameter param)
1236
+ {
1237
+ super(prob.l, prob.x, param);
1238
+ l = prob.l;
1239
+ cache = new Cache(l,(long)(param.cache_size*(1<<20)));
1240
+ QD = new double[2*l];
1241
+ sign = new byte[2*l];
1242
+ index = new int[2*l];
1243
+ for(int k=0;k<l;k++)
1244
+ {
1245
+ sign[k] = 1;
1246
+ sign[k+l] = -1;
1247
+ index[k] = k;
1248
+ index[k+l] = k;
1249
+ QD[k] = kernel_function(k,k);
1250
+ QD[k+l] = QD[k];
1251
+ }
1252
+ buffer = new float[2][2*l];
1253
+ next_buffer = 0;
1254
+ }
1255
+
1256
+ void swap_index(int i, int j)
1257
+ {
1258
+ do {byte _=sign[i]; sign[i]=sign[j]; sign[j]=_;} while(false);
1259
+ do {int _=index[i]; index[i]=index[j]; index[j]=_;} while(false);
1260
+ do {double _=QD[i]; QD[i]=QD[j]; QD[j]=_;} while(false);
1261
+ }
1262
+
1263
+ float[] get_Q(int i, int len)
1264
+ {
1265
+ float[][] data = new float[1][];
1266
+ int j, real_i = index[i];
1267
+ if(cache.get_data(real_i,data,l) < l)
1268
+ {
1269
+ for(j=0;j<l;j++)
1270
+ data[0][j] = (float)kernel_function(real_i,j);
1271
+ }
1272
+
1273
+ // reorder and copy
1274
+ float buf[] = buffer[next_buffer];
1275
+ next_buffer = 1 - next_buffer;
1276
+ byte si = sign[i];
1277
+ for(j=0;j<len;j++)
1278
+ buf[j] = (float) si * sign[j] * data[0][index[j]];
1279
+ return buf;
1280
+ }
1281
+
1282
+ double[] get_QD()
1283
+ {
1284
+ return QD;
1285
+ }
1286
+ }
1287
+
1288
+ public class Svm {
1289
+ //
1290
+ // construct and solve various formulations
1291
+ //
1292
+ public static final int LIBSVM_VERSION=314;
1293
+ public static final Random rand = new Random();
1294
+
1295
+ private static PrintInterface svm_print_stdout = new PrintInterface()
1296
+ {
1297
+ public void print(String s)
1298
+ {
1299
+ System.out.print(s);
1300
+ System.out.flush();
1301
+ }
1302
+ };
1303
+
1304
+ private static PrintInterface svm_print_string = svm_print_stdout;
1305
+
1306
+ static void info(String s)
1307
+ {
1308
+ // svm_print_string.print(s);
1309
+ }
1310
+
1311
+ private static void solve_c_svc(Problem prob, Parameter param,
1312
+ double[] alpha, Solver.SolutionInfo si,
1313
+ double Cp, double Cn)
1314
+ {
1315
+ int l = prob.l;
1316
+ double[] minus_ones = new double[l];
1317
+ byte[] y = new byte[l];
1318
+
1319
+ int i;
1320
+
1321
+ for(i=0;i<l;i++)
1322
+ {
1323
+ alpha[i] = 0;
1324
+ minus_ones[i] = -1;
1325
+ if(prob.y[i] > 0) y[i] = +1; else y[i] = -1;
1326
+ }
1327
+
1328
+ Solver s = new Solver();
1329
+ s.Solve(l, new SVC_Q(prob,param,y), minus_ones, y,
1330
+ alpha, Cp, Cn, param.eps, si, param.shrinking);
1331
+
1332
+ double sum_alpha=0;
1333
+ for(i=0;i<l;i++)
1334
+ sum_alpha += alpha[i];
1335
+
1336
+ if (Cp==Cn)
1337
+ Svm.info("nu = "+sum_alpha/(Cp*prob.l)+"\n");
1338
+
1339
+ for(i=0;i<l;i++)
1340
+ alpha[i] *= y[i];
1341
+ }
1342
+
1343
+ private static void solve_nu_svc(Problem prob, Parameter param,
1344
+ double[] alpha, Solver.SolutionInfo si)
1345
+ {
1346
+ int i;
1347
+ int l = prob.l;
1348
+ double nu = param.nu;
1349
+
1350
+ byte[] y = new byte[l];
1351
+
1352
+ for(i=0;i<l;i++)
1353
+ if(prob.y[i]>0)
1354
+ y[i] = +1;
1355
+ else
1356
+ y[i] = -1;
1357
+
1358
+ double sum_pos = nu*l/2;
1359
+ double sum_neg = nu*l/2;
1360
+
1361
+ for(i=0;i<l;i++)
1362
+ if(y[i] == +1)
1363
+ {
1364
+ alpha[i] = Math.min(1.0,sum_pos);
1365
+ sum_pos -= alpha[i];
1366
+ }
1367
+ else
1368
+ {
1369
+ alpha[i] = Math.min(1.0,sum_neg);
1370
+ sum_neg -= alpha[i];
1371
+ }
1372
+
1373
+ double[] zeros = new double[l];
1374
+
1375
+ for(i=0;i<l;i++)
1376
+ zeros[i] = 0;
1377
+
1378
+ Solver_NU s = new Solver_NU();
1379
+ s.Solve(l, new SVC_Q(prob,param,y), zeros, y,
1380
+ alpha, 1.0, 1.0, param.eps, si, param.shrinking);
1381
+ double r = si.r;
1382
+
1383
+ Svm.info("C = "+1/r+"\n");
1384
+
1385
+ for(i=0;i<l;i++)
1386
+ alpha[i] *= y[i]/r;
1387
+
1388
+ si.rho /= r;
1389
+ si.obj /= (r*r);
1390
+ si.upper_bound_p = 1/r;
1391
+ si.upper_bound_n = 1/r;
1392
+ }
1393
+
1394
+ private static void solve_one_class(Problem prob, Parameter param,
1395
+ double[] alpha, Solver.SolutionInfo si)
1396
+ {
1397
+ int l = prob.l;
1398
+ double[] zeros = new double[l];
1399
+ byte[] ones = new byte[l];
1400
+ int i;
1401
+
1402
+ int n = (int)(param.nu*prob.l); // # of alpha's at upper bound
1403
+
1404
+ for(i=0;i<n;i++)
1405
+ alpha[i] = 1;
1406
+ if(n<prob.l)
1407
+ alpha[n] = param.nu * prob.l - n;
1408
+ for(i=n+1;i<l;i++)
1409
+ alpha[i] = 0;
1410
+
1411
+ for(i=0;i<l;i++)
1412
+ {
1413
+ zeros[i] = 0;
1414
+ ones[i] = 1;
1415
+ }
1416
+
1417
+ Solver s = new Solver();
1418
+ s.Solve(l, new ONE_CLASS_Q(prob,param), zeros, ones,
1419
+ alpha, 1.0, 1.0, param.eps, si, param.shrinking);
1420
+ }
1421
+
1422
+ private static void solve_epsilon_svr(Problem prob, Parameter param,
1423
+ double[] alpha, Solver.SolutionInfo si)
1424
+ {
1425
+ int l = prob.l;
1426
+ double[] alpha2 = new double[2*l];
1427
+ double[] linear_term = new double[2*l];
1428
+ byte[] y = new byte[2*l];
1429
+ int i;
1430
+
1431
+ for(i=0;i<l;i++)
1432
+ {
1433
+ alpha2[i] = 0;
1434
+ linear_term[i] = param.p - prob.y[i];
1435
+ y[i] = 1;
1436
+
1437
+ alpha2[i+l] = 0;
1438
+ linear_term[i+l] = param.p + prob.y[i];
1439
+ y[i+l] = -1;
1440
+ }
1441
+
1442
+ Solver s = new Solver();
1443
+ s.Solve(2*l, new SVR_Q(prob,param), linear_term, y,
1444
+ alpha2, param.C, param.C, param.eps, si, param.shrinking);
1445
+
1446
+ double sum_alpha = 0;
1447
+ for(i=0;i<l;i++)
1448
+ {
1449
+ alpha[i] = alpha2[i] - alpha2[i+l];
1450
+ sum_alpha += Math.abs(alpha[i]);
1451
+ }
1452
+ Svm.info("nu = "+sum_alpha/(param.C*l)+"\n");
1453
+ }
1454
+
1455
+ private static void solve_nu_svr(Problem prob, Parameter param,
1456
+ double[] alpha, Solver.SolutionInfo si)
1457
+ {
1458
+ int l = prob.l;
1459
+ double C = param.C;
1460
+ double[] alpha2 = new double[2*l];
1461
+ double[] linear_term = new double[2*l];
1462
+ byte[] y = new byte[2*l];
1463
+ int i;
1464
+
1465
+ double sum = C * param.nu * l / 2;
1466
+ for(i=0;i<l;i++)
1467
+ {
1468
+ alpha2[i] = alpha2[i+l] = Math.min(sum,C);
1469
+ sum -= alpha2[i];
1470
+
1471
+ linear_term[i] = - prob.y[i];
1472
+ y[i] = 1;
1473
+
1474
+ linear_term[i+l] = prob.y[i];
1475
+ y[i+l] = -1;
1476
+ }
1477
+
1478
+ Solver_NU s = new Solver_NU();
1479
+ s.Solve(2*l, new SVR_Q(prob,param), linear_term, y,
1480
+ alpha2, C, C, param.eps, si, param.shrinking);
1481
+
1482
+ Svm.info("epsilon = "+(-si.r)+"\n");
1483
+
1484
+ for(i=0;i<l;i++)
1485
+ alpha[i] = alpha2[i] - alpha2[i+l];
1486
+ }
1487
+
1488
+ //
1489
+ // decision_function
1490
+ //
1491
+ static class decision_function
1492
+ {
1493
+ double[] alpha;
1494
+ double rho;
1495
+ };
1496
+
1497
+ static decision_function svm_train_one(
1498
+ Problem prob, Parameter param,
1499
+ double Cp, double Cn)
1500
+ {
1501
+ double[] alpha = new double[prob.l];
1502
+ Solver.SolutionInfo si = new Solver.SolutionInfo();
1503
+ switch(param.svm_type)
1504
+ {
1505
+ case Parameter.C_SVC:
1506
+ solve_c_svc(prob,param,alpha,si,Cp,Cn);
1507
+ break;
1508
+ case Parameter.NU_SVC:
1509
+ solve_nu_svc(prob,param,alpha,si);
1510
+ break;
1511
+ case Parameter.ONE_CLASS:
1512
+ solve_one_class(prob,param,alpha,si);
1513
+ break;
1514
+ case Parameter.EPSILON_SVR:
1515
+ solve_epsilon_svr(prob,param,alpha,si);
1516
+ break;
1517
+ case Parameter.NU_SVR:
1518
+ solve_nu_svr(prob,param,alpha,si);
1519
+ break;
1520
+ }
1521
+
1522
+ Svm.info("obj = "+si.obj+", rho = "+si.rho+"\n");
1523
+
1524
+ // output SVs
1525
+
1526
+ int nSV = 0;
1527
+ int nBSV = 0;
1528
+ for(int i=0;i<prob.l;i++)
1529
+ {
1530
+ if(Math.abs(alpha[i]) > 0)
1531
+ {
1532
+ ++nSV;
1533
+ if(prob.y[i] > 0)
1534
+ {
1535
+ if(Math.abs(alpha[i]) >= si.upper_bound_p)
1536
+ ++nBSV;
1537
+ }
1538
+ else
1539
+ {
1540
+ if(Math.abs(alpha[i]) >= si.upper_bound_n)
1541
+ ++nBSV;
1542
+ }
1543
+ }
1544
+ }
1545
+
1546
+ Svm.info("nSV = "+nSV+", nBSV = "+nBSV+"\n");
1547
+
1548
+ decision_function f = new decision_function();
1549
+ f.alpha = alpha;
1550
+ f.rho = si.rho;
1551
+ return f;
1552
+ }
1553
+
1554
+ // Platt's binary SVM Probablistic Output: an improvement from Lin et al.
1555
+ private static void sigmoid_train(int l, double[] dec_values, double[] labels,
1556
+ double[] probAB)
1557
+ {
1558
+ double A, B;
1559
+ double prior1=0, prior0 = 0;
1560
+ int i;
1561
+
1562
+ for (i=0;i<l;i++)
1563
+ if (labels[i] > 0) prior1+=1;
1564
+ else prior0+=1;
1565
+
1566
+ int max_iter=100; // Maximal number of iterations
1567
+ double min_step=1e-10; // Minimal step taken in line search
1568
+ double sigma=1e-12; // For numerically strict PD of Hessian
1569
+ double eps=1e-5;
1570
+ double hiTarget=(prior1+1.0)/(prior1+2.0);
1571
+ double loTarget=1/(prior0+2.0);
1572
+ double[] t= new double[l];
1573
+ double fApB,p,q,h11,h22,h21,g1,g2,det,dA,dB,gd,stepsize;
1574
+ double newA,newB,newf,d1,d2;
1575
+ int iter;
1576
+
1577
+ // Initial Point and Initial Fun Value
1578
+ A=0.0; B=Math.log((prior0+1.0)/(prior1+1.0));
1579
+ double fval = 0.0;
1580
+
1581
+ for (i=0;i<l;i++)
1582
+ {
1583
+ if (labels[i]>0) t[i]=hiTarget;
1584
+ else t[i]=loTarget;
1585
+ fApB = dec_values[i]*A+B;
1586
+ if (fApB>=0)
1587
+ fval += t[i]*fApB + Math.log(1+Math.exp(-fApB));
1588
+ else
1589
+ fval += (t[i] - 1)*fApB +Math.log(1+Math.exp(fApB));
1590
+ }
1591
+ for (iter=0;iter<max_iter;iter++)
1592
+ {
1593
+ // Update Gradient and Hessian (use H' = H + sigma I)
1594
+ h11=sigma; // numerically ensures strict PD
1595
+ h22=sigma;
1596
+ h21=0.0;g1=0.0;g2=0.0;
1597
+ for (i=0;i<l;i++)
1598
+ {
1599
+ fApB = dec_values[i]*A+B;
1600
+ if (fApB >= 0)
1601
+ {
1602
+ p=Math.exp(-fApB)/(1.0+Math.exp(-fApB));
1603
+ q=1.0/(1.0+Math.exp(-fApB));
1604
+ }
1605
+ else
1606
+ {
1607
+ p=1.0/(1.0+Math.exp(fApB));
1608
+ q=Math.exp(fApB)/(1.0+Math.exp(fApB));
1609
+ }
1610
+ d2=p*q;
1611
+ h11+=dec_values[i]*dec_values[i]*d2;
1612
+ h22+=d2;
1613
+ h21+=dec_values[i]*d2;
1614
+ d1=t[i]-p;
1615
+ g1+=dec_values[i]*d1;
1616
+ g2+=d1;
1617
+ }
1618
+
1619
+ // Stopping Criteria
1620
+ if (Math.abs(g1)<eps && Math.abs(g2)<eps)
1621
+ break;
1622
+
1623
+ // Finding Newton direction: -inv(H') * g
1624
+ det=h11*h22-h21*h21;
1625
+ dA=-(h22*g1 - h21 * g2) / det;
1626
+ dB=-(-h21*g1+ h11 * g2) / det;
1627
+ gd=g1*dA+g2*dB;
1628
+
1629
+
1630
+ stepsize = 1; // Line Search
1631
+ while (stepsize >= min_step)
1632
+ {
1633
+ newA = A + stepsize * dA;
1634
+ newB = B + stepsize * dB;
1635
+
1636
+ // New function value
1637
+ newf = 0.0;
1638
+ for (i=0;i<l;i++)
1639
+ {
1640
+ fApB = dec_values[i]*newA+newB;
1641
+ if (fApB >= 0)
1642
+ newf += t[i]*fApB + Math.log(1+Math.exp(-fApB));
1643
+ else
1644
+ newf += (t[i] - 1)*fApB +Math.log(1+Math.exp(fApB));
1645
+ }
1646
+ // Check sufficient decrease
1647
+ if (newf<fval+0.0001*stepsize*gd)
1648
+ {
1649
+ A=newA;B=newB;fval=newf;
1650
+ break;
1651
+ }
1652
+ else
1653
+ stepsize = stepsize / 2.0;
1654
+ }
1655
+
1656
+ if (stepsize < min_step)
1657
+ {
1658
+ Svm.info("Line search fails in two-class probability estimates\n");
1659
+ break;
1660
+ }
1661
+ }
1662
+
1663
+ if (iter>=max_iter)
1664
+ Svm.info("Reaching maximal iterations in two-class probability estimates\n");
1665
+ probAB[0]=A;probAB[1]=B;
1666
+ }
1667
+
1668
+ private static double sigmoid_predict(double decision_value, double A, double B)
1669
+ {
1670
+ double fApB = decision_value*A+B;
1671
+ if (fApB >= 0)
1672
+ return Math.exp(-fApB)/(1.0+Math.exp(-fApB));
1673
+ else
1674
+ return 1.0/(1+Math.exp(fApB)) ;
1675
+ }
1676
+
1677
+ // Method 2 from the multiclass_prob paper by Wu, Lin, and Weng
1678
+ private static void multiclass_probability(int k, double[][] r, double[] p)
1679
+ {
1680
+ int t,j;
1681
+ int iter = 0, max_iter=Math.max(100,k);
1682
+ double[][] Q=new double[k][k];
1683
+ double[] Qp=new double[k];
1684
+ double pQp, eps=0.005/k;
1685
+
1686
+ for (t=0;t<k;t++)
1687
+ {
1688
+ p[t]=1.0/k; // Valid if k = 1
1689
+ Q[t][t]=0;
1690
+ for (j=0;j<t;j++)
1691
+ {
1692
+ Q[t][t]+=r[j][t]*r[j][t];
1693
+ Q[t][j]=Q[j][t];
1694
+ }
1695
+ for (j=t+1;j<k;j++)
1696
+ {
1697
+ Q[t][t]+=r[j][t]*r[j][t];
1698
+ Q[t][j]=-r[j][t]*r[t][j];
1699
+ }
1700
+ }
1701
+ for (iter=0;iter<max_iter;iter++)
1702
+ {
1703
+ // stopping condition, recalculate QP,pQP for numerical accuracy
1704
+ pQp=0;
1705
+ for (t=0;t<k;t++)
1706
+ {
1707
+ Qp[t]=0;
1708
+ for (j=0;j<k;j++)
1709
+ Qp[t]+=Q[t][j]*p[j];
1710
+ pQp+=p[t]*Qp[t];
1711
+ }
1712
+ double max_error=0;
1713
+ for (t=0;t<k;t++)
1714
+ {
1715
+ double error=Math.abs(Qp[t]-pQp);
1716
+ if (error>max_error)
1717
+ max_error=error;
1718
+ }
1719
+ if (max_error<eps) break;
1720
+
1721
+ for (t=0;t<k;t++)
1722
+ {
1723
+ double diff=(-Qp[t]+pQp)/Q[t][t];
1724
+ p[t]+=diff;
1725
+ pQp=(pQp+diff*(diff*Q[t][t]+2*Qp[t]))/(1+diff)/(1+diff);
1726
+ for (j=0;j<k;j++)
1727
+ {
1728
+ Qp[j]=(Qp[j]+diff*Q[t][j])/(1+diff);
1729
+ p[j]/=(1+diff);
1730
+ }
1731
+ }
1732
+ }
1733
+ if (iter>=max_iter)
1734
+ Svm.info("Exceeds max_iter in multiclass_prob\n");
1735
+ }
1736
+
1737
+ // Cross-validation decision values for probability estimates
1738
+ private static void svm_binary_svc_probability(Problem prob, Parameter param, double Cp, double Cn, double[] probAB)
1739
+ {
1740
+ int i;
1741
+ int nr_fold = 5;
1742
+ int[] perm = new int[prob.l];
1743
+ double[] dec_values = new double[prob.l];
1744
+
1745
+ // random shuffle
1746
+ for(i=0;i<prob.l;i++) perm[i]=i;
1747
+ for(i=0;i<prob.l;i++)
1748
+ {
1749
+ int j = i+rand.nextInt(prob.l-i);
1750
+ do {int _=perm[i]; perm[i]=perm[j]; perm[j]=_;} while(false);
1751
+ }
1752
+ for(i=0;i<nr_fold;i++)
1753
+ {
1754
+ int begin = i*prob.l/nr_fold;
1755
+ int end = (i+1)*prob.l/nr_fold;
1756
+ int j,k;
1757
+ Problem subprob = new Problem();
1758
+
1759
+ subprob.l = prob.l-(end-begin);
1760
+ subprob.x = new Node[subprob.l][];
1761
+ subprob.y = new double[subprob.l];
1762
+
1763
+ k=0;
1764
+ for(j=0;j<begin;j++)
1765
+ {
1766
+ subprob.x[k] = prob.x[perm[j]];
1767
+ subprob.y[k] = prob.y[perm[j]];
1768
+ ++k;
1769
+ }
1770
+ for(j=end;j<prob.l;j++)
1771
+ {
1772
+ subprob.x[k] = prob.x[perm[j]];
1773
+ subprob.y[k] = prob.y[perm[j]];
1774
+ ++k;
1775
+ }
1776
+ int p_count=0,n_count=0;
1777
+ for(j=0;j<k;j++)
1778
+ if(subprob.y[j]>0)
1779
+ p_count++;
1780
+ else
1781
+ n_count++;
1782
+
1783
+ if(p_count==0 && n_count==0)
1784
+ for(j=begin;j<end;j++)
1785
+ dec_values[perm[j]] = 0;
1786
+ else if(p_count > 0 && n_count == 0)
1787
+ for(j=begin;j<end;j++)
1788
+ dec_values[perm[j]] = 1;
1789
+ else if(p_count == 0 && n_count > 0)
1790
+ for(j=begin;j<end;j++)
1791
+ dec_values[perm[j]] = -1;
1792
+ else
1793
+ {
1794
+ Parameter subparam = (Parameter)param.clone();
1795
+ subparam.probability=0;
1796
+ subparam.C=1.0;
1797
+ subparam.nr_weight=2;
1798
+ subparam.weight_label = new int[2];
1799
+ subparam.weight = new double[2];
1800
+ subparam.weight_label[0]=+1;
1801
+ subparam.weight_label[1]=-1;
1802
+ subparam.weight[0]=Cp;
1803
+ subparam.weight[1]=Cn;
1804
+ Model submodel = svm_train(subprob,subparam);
1805
+ for(j=begin;j<end;j++)
1806
+ {
1807
+ double[] dec_value=new double[1];
1808
+ svm_predict_values(submodel,prob.x[perm[j]],dec_value);
1809
+ dec_values[perm[j]]=dec_value[0];
1810
+ // ensure +1 -1 order; reason not using CV subroutine
1811
+ dec_values[perm[j]] *= submodel.label[0];
1812
+ }
1813
+ }
1814
+ }
1815
+ sigmoid_train(prob.l,dec_values,prob.y,probAB);
1816
+ }
1817
+
1818
+ // Return parameter of a Laplace distribution
1819
+ private static double svm_svr_probability(Problem prob, Parameter param)
1820
+ {
1821
+ int i;
1822
+ int nr_fold = 5;
1823
+ double[] ymv = new double[prob.l];
1824
+ double mae = 0;
1825
+
1826
+ Parameter newparam = (Parameter)param.clone();
1827
+ newparam.probability = 0;
1828
+ svm_cross_validation(prob,newparam,nr_fold,ymv);
1829
+ for(i=0;i<prob.l;i++)
1830
+ {
1831
+ ymv[i]=prob.y[i]-ymv[i];
1832
+ mae += Math.abs(ymv[i]);
1833
+ }
1834
+ mae /= prob.l;
1835
+ double std=Math.sqrt(2*mae*mae);
1836
+ int count=0;
1837
+ mae=0;
1838
+ for(i=0;i<prob.l;i++)
1839
+ if (Math.abs(ymv[i]) > 5*std)
1840
+ count=count+1;
1841
+ else
1842
+ mae+=Math.abs(ymv[i]);
1843
+ mae /= (prob.l-count);
1844
+ Svm.info("Prob. model for test data: target value = predicted value + z,\nz: Laplace distribution e^(-|z|/sigma)/(2sigma),sigma="+mae+"\n");
1845
+ return mae;
1846
+ }
1847
+
1848
+ // label: label name, start: begin of each class, count: #data of classes, perm: indices to the original data
1849
+ // perm, length l, must be allocated before calling this subroutine
1850
+ private static void svm_group_classes(Problem prob, int[] nr_class_ret, int[][] label_ret, int[][] start_ret, int[][] count_ret, int[] perm)
1851
+ {
1852
+ int l = prob.l;
1853
+ int max_nr_class = 16;
1854
+ int nr_class = 0;
1855
+ int[] label = new int[max_nr_class];
1856
+ int[] count = new int[max_nr_class];
1857
+ int[] data_label = new int[l];
1858
+ int i;
1859
+
1860
+ for(i=0;i<l;i++)
1861
+ {
1862
+ int this_label = (int)(prob.y[i]);
1863
+ int j;
1864
+ for(j=0;j<nr_class;j++)
1865
+ {
1866
+ if(this_label == label[j])
1867
+ {
1868
+ ++count[j];
1869
+ break;
1870
+ }
1871
+ }
1872
+ data_label[i] = j;
1873
+ if(j == nr_class)
1874
+ {
1875
+ if(nr_class == max_nr_class)
1876
+ {
1877
+ max_nr_class *= 2;
1878
+ int[] new_data = new int[max_nr_class];
1879
+ System.arraycopy(label,0,new_data,0,label.length);
1880
+ label = new_data;
1881
+ new_data = new int[max_nr_class];
1882
+ System.arraycopy(count,0,new_data,0,count.length);
1883
+ count = new_data;
1884
+ }
1885
+ label[nr_class] = this_label;
1886
+ count[nr_class] = 1;
1887
+ ++nr_class;
1888
+ }
1889
+ }
1890
+
1891
+ int[] start = new int[nr_class];
1892
+ start[0] = 0;
1893
+ for(i=1;i<nr_class;i++)
1894
+ start[i] = start[i-1]+count[i-1];
1895
+ for(i=0;i<l;i++)
1896
+ {
1897
+ perm[start[data_label[i]]] = i;
1898
+ ++start[data_label[i]];
1899
+ }
1900
+ start[0] = 0;
1901
+ for(i=1;i<nr_class;i++)
1902
+ start[i] = start[i-1]+count[i-1];
1903
+
1904
+ nr_class_ret[0] = nr_class;
1905
+ label_ret[0] = label;
1906
+ start_ret[0] = start;
1907
+ count_ret[0] = count;
1908
+ }
1909
+
1910
+ //
1911
+ // Interface functions
1912
+ //
1913
+ public static Model svm_train(Problem prob, Parameter param)
1914
+ {
1915
+ Model model = new Model();
1916
+ model.param = param;
1917
+
1918
+ if(param.svm_type == Parameter.ONE_CLASS ||
1919
+ param.svm_type == Parameter.EPSILON_SVR ||
1920
+ param.svm_type == Parameter.NU_SVR)
1921
+ {
1922
+ // regression or one-class-svm
1923
+ model.nr_class = 2;
1924
+ model.label = null;
1925
+ model.nSV = null;
1926
+ model.probA = null; model.probB = null;
1927
+ model.sv_coef = new double[1][];
1928
+
1929
+ if(param.probability == 1 &&
1930
+ (param.svm_type == Parameter.EPSILON_SVR ||
1931
+ param.svm_type == Parameter.NU_SVR))
1932
+ {
1933
+ model.probA = new double[1];
1934
+ model.probA[0] = svm_svr_probability(prob,param);
1935
+ }
1936
+
1937
+ decision_function f = svm_train_one(prob,param,0,0);
1938
+ model.rho = new double[1];
1939
+ model.rho[0] = f.rho;
1940
+
1941
+ int nSV = 0;
1942
+ int i;
1943
+ for(i=0;i<prob.l;i++)
1944
+ if(Math.abs(f.alpha[i]) > 0) ++nSV;
1945
+ model.l = nSV;
1946
+ model.SV = new Node[nSV][];
1947
+ model.sv_coef[0] = new double[nSV];
1948
+ model.sv_indices = new int[nSV];
1949
+ int j = 0;
1950
+ for(i=0;i<prob.l;i++)
1951
+ if(Math.abs(f.alpha[i]) > 0)
1952
+ {
1953
+ model.SV[j] = prob.x[i];
1954
+ model.sv_coef[0][j] = f.alpha[i];
1955
+ model.sv_indices[j] = i+1;
1956
+ ++j;
1957
+ }
1958
+ }
1959
+ else
1960
+ {
1961
+ // classification
1962
+ int l = prob.l;
1963
+ int[] tmp_nr_class = new int[1];
1964
+ int[][] tmp_label = new int[1][];
1965
+ int[][] tmp_start = new int[1][];
1966
+ int[][] tmp_count = new int[1][];
1967
+ int[] perm = new int[l];
1968
+
1969
+ // group training data of the same class
1970
+ svm_group_classes(prob,tmp_nr_class,tmp_label,tmp_start,tmp_count,perm);
1971
+ int nr_class = tmp_nr_class[0];
1972
+ int[] label = tmp_label[0];
1973
+ int[] start = tmp_start[0];
1974
+ int[] count = tmp_count[0];
1975
+
1976
+ if(nr_class == 1)
1977
+ Svm.info("WARNING: training data in only one class. See README for details.\n");
1978
+
1979
+ Node[][] x = new Node[l][];
1980
+ int i;
1981
+ for(i=0;i<l;i++)
1982
+ x[i] = prob.x[perm[i]];
1983
+
1984
+ // calculate weighted C
1985
+
1986
+ double[] weighted_C = new double[nr_class];
1987
+ for(i=0;i<nr_class;i++)
1988
+ weighted_C[i] = param.C;
1989
+ for(i=0;i<param.nr_weight;i++)
1990
+ {
1991
+ int j;
1992
+ for(j=0;j<nr_class;j++)
1993
+ if(param.weight_label[i] == label[j])
1994
+ break;
1995
+ if(j == nr_class)
1996
+ System.err.print("WARNING: class label "+param.weight_label[i]+" specified in weight is not found\n");
1997
+ else
1998
+ weighted_C[j] *= param.weight[i];
1999
+ }
2000
+
2001
+ // train k*(k-1)/2 models
2002
+
2003
+ boolean[] nonzero = new boolean[l];
2004
+ for(i=0;i<l;i++)
2005
+ nonzero[i] = false;
2006
+ decision_function[] f = new decision_function[nr_class*(nr_class-1)/2];
2007
+
2008
+ double[] probA=null,probB=null;
2009
+ if (param.probability == 1)
2010
+ {
2011
+ probA=new double[nr_class*(nr_class-1)/2];
2012
+ probB=new double[nr_class*(nr_class-1)/2];
2013
+ }
2014
+
2015
+ int p = 0;
2016
+ for(i=0;i<nr_class;i++)
2017
+ for(int j=i+1;j<nr_class;j++)
2018
+ {
2019
+ Problem sub_prob = new Problem();
2020
+ int si = start[i], sj = start[j];
2021
+ int ci = count[i], cj = count[j];
2022
+ sub_prob.l = ci+cj;
2023
+ sub_prob.x = new Node[sub_prob.l][];
2024
+ sub_prob.y = new double[sub_prob.l];
2025
+ int k;
2026
+ for(k=0;k<ci;k++)
2027
+ {
2028
+ sub_prob.x[k] = x[si+k];
2029
+ sub_prob.y[k] = +1;
2030
+ }
2031
+ for(k=0;k<cj;k++)
2032
+ {
2033
+ sub_prob.x[ci+k] = x[sj+k];
2034
+ sub_prob.y[ci+k] = -1;
2035
+ }
2036
+
2037
+ if(param.probability == 1)
2038
+ {
2039
+ double[] probAB=new double[2];
2040
+ svm_binary_svc_probability(sub_prob,param,weighted_C[i],weighted_C[j],probAB);
2041
+ probA[p]=probAB[0];
2042
+ probB[p]=probAB[1];
2043
+ }
2044
+
2045
+ f[p] = svm_train_one(sub_prob,param,weighted_C[i],weighted_C[j]);
2046
+ for(k=0;k<ci;k++)
2047
+ if(!nonzero[si+k] && Math.abs(f[p].alpha[k]) > 0)
2048
+ nonzero[si+k] = true;
2049
+ for(k=0;k<cj;k++)
2050
+ if(!nonzero[sj+k] && Math.abs(f[p].alpha[ci+k]) > 0)
2051
+ nonzero[sj+k] = true;
2052
+ ++p;
2053
+ }
2054
+
2055
+ // build output
2056
+
2057
+ model.nr_class = nr_class;
2058
+
2059
+ model.label = new int[nr_class];
2060
+ for(i=0;i<nr_class;i++)
2061
+ model.label[i] = label[i];
2062
+
2063
+ model.rho = new double[nr_class*(nr_class-1)/2];
2064
+ for(i=0;i<nr_class*(nr_class-1)/2;i++)
2065
+ model.rho[i] = f[i].rho;
2066
+
2067
+ if(param.probability == 1)
2068
+ {
2069
+ model.probA = new double[nr_class*(nr_class-1)/2];
2070
+ model.probB = new double[nr_class*(nr_class-1)/2];
2071
+ for(i=0;i<nr_class*(nr_class-1)/2;i++)
2072
+ {
2073
+ model.probA[i] = probA[i];
2074
+ model.probB[i] = probB[i];
2075
+ }
2076
+ }
2077
+ else
2078
+ {
2079
+ model.probA=null;
2080
+ model.probB=null;
2081
+ }
2082
+
2083
+ int nnz = 0;
2084
+ int[] nz_count = new int[nr_class];
2085
+ model.nSV = new int[nr_class];
2086
+ for(i=0;i<nr_class;i++)
2087
+ {
2088
+ int nSV = 0;
2089
+ for(int j=0;j<count[i];j++)
2090
+ if(nonzero[start[i]+j])
2091
+ {
2092
+ ++nSV;
2093
+ ++nnz;
2094
+ }
2095
+ model.nSV[i] = nSV;
2096
+ nz_count[i] = nSV;
2097
+ }
2098
+
2099
+ Svm.info("Total nSV = "+nnz+"\n");
2100
+
2101
+ model.l = nnz;
2102
+ model.SV = new Node[nnz][];
2103
+ model.sv_indices = new int[nnz];
2104
+ p = 0;
2105
+ for(i=0;i<l;i++)
2106
+ if(nonzero[i])
2107
+ {
2108
+ model.SV[p] = x[i];
2109
+ model.sv_indices[p++] = perm[i] + 1;
2110
+ }
2111
+
2112
+ int[] nz_start = new int[nr_class];
2113
+ nz_start[0] = 0;
2114
+ for(i=1;i<nr_class;i++)
2115
+ nz_start[i] = nz_start[i-1]+nz_count[i-1];
2116
+
2117
+ model.sv_coef = new double[nr_class-1][];
2118
+ for(i=0;i<nr_class-1;i++)
2119
+ model.sv_coef[i] = new double[nnz];
2120
+
2121
+ p = 0;
2122
+ for(i=0;i<nr_class;i++)
2123
+ for(int j=i+1;j<nr_class;j++)
2124
+ {
2125
+ // classifier (i,j): coefficients with
2126
+ // i are in sv_coef[j-1][nz_start[i]...],
2127
+ // j are in sv_coef[i][nz_start[j]...]
2128
+
2129
+ int si = start[i];
2130
+ int sj = start[j];
2131
+ int ci = count[i];
2132
+ int cj = count[j];
2133
+
2134
+ int q = nz_start[i];
2135
+ int k;
2136
+ for(k=0;k<ci;k++)
2137
+ if(nonzero[si+k])
2138
+ model.sv_coef[j-1][q++] = f[p].alpha[k];
2139
+ q = nz_start[j];
2140
+ for(k=0;k<cj;k++)
2141
+ if(nonzero[sj+k])
2142
+ model.sv_coef[i][q++] = f[p].alpha[ci+k];
2143
+ ++p;
2144
+ }
2145
+ }
2146
+ return model;
2147
+ }
2148
+
2149
+ // Stratified cross validation
2150
+ public static void svm_cross_validation(Problem prob, Parameter param, int nr_fold, double[] target)
2151
+ {
2152
+ int i;
2153
+ int[] fold_start = new int[nr_fold+1];
2154
+ int l = prob.l;
2155
+ int[] perm = new int[l];
2156
+
2157
+ // stratified cv may not give leave-one-out rate
2158
+ // Each class to l folds -> some folds may have zero elements
2159
+ if((param.svm_type == Parameter.C_SVC ||
2160
+ param.svm_type == Parameter.NU_SVC) && nr_fold < l)
2161
+ {
2162
+ int[] tmp_nr_class = new int[1];
2163
+ int[][] tmp_label = new int[1][];
2164
+ int[][] tmp_start = new int[1][];
2165
+ int[][] tmp_count = new int[1][];
2166
+
2167
+ svm_group_classes(prob,tmp_nr_class,tmp_label,tmp_start,tmp_count,perm);
2168
+
2169
+ int nr_class = tmp_nr_class[0];
2170
+ int[] start = tmp_start[0];
2171
+ int[] count = tmp_count[0];
2172
+
2173
+ // random shuffle and then data grouped by fold using the array perm
2174
+ int[] fold_count = new int[nr_fold];
2175
+ int c;
2176
+ int[] index = new int[l];
2177
+ for(i=0;i<l;i++)
2178
+ index[i]=perm[i];
2179
+ for (c=0; c<nr_class; c++)
2180
+ for(i=0;i<count[c];i++)
2181
+ {
2182
+ int j = i+rand.nextInt(count[c]-i);
2183
+ do {int _=index[start[c]+j]; index[start[c]+j]=index[start[c]+i]; index[start[c]+i]=_;} while(false);
2184
+ }
2185
+ for(i=0;i<nr_fold;i++)
2186
+ {
2187
+ fold_count[i] = 0;
2188
+ for (c=0; c<nr_class;c++)
2189
+ fold_count[i]+=(i+1)*count[c]/nr_fold-i*count[c]/nr_fold;
2190
+ }
2191
+ fold_start[0]=0;
2192
+ for (i=1;i<=nr_fold;i++)
2193
+ fold_start[i] = fold_start[i-1]+fold_count[i-1];
2194
+ for (c=0; c<nr_class;c++)
2195
+ for(i=0;i<nr_fold;i++)
2196
+ {
2197
+ int begin = start[c]+i*count[c]/nr_fold;
2198
+ int end = start[c]+(i+1)*count[c]/nr_fold;
2199
+ for(int j=begin;j<end;j++)
2200
+ {
2201
+ perm[fold_start[i]] = index[j];
2202
+ fold_start[i]++;
2203
+ }
2204
+ }
2205
+ fold_start[0]=0;
2206
+ for (i=1;i<=nr_fold;i++)
2207
+ fold_start[i] = fold_start[i-1]+fold_count[i-1];
2208
+ }
2209
+ else
2210
+ {
2211
+ for(i=0;i<l;i++) perm[i]=i;
2212
+ for(i=0;i<l;i++)
2213
+ {
2214
+ int j = i+rand.nextInt(l-i);
2215
+ do {int _=perm[i]; perm[i]=perm[j]; perm[j]=_;} while(false);
2216
+ }
2217
+ for(i=0;i<=nr_fold;i++)
2218
+ fold_start[i]=i*l/nr_fold;
2219
+ }
2220
+
2221
+ for(i=0;i<nr_fold;i++)
2222
+ {
2223
+ int begin = fold_start[i];
2224
+ int end = fold_start[i+1];
2225
+ int j,k;
2226
+ Problem subprob = new Problem();
2227
+
2228
+ subprob.l = l-(end-begin);
2229
+ subprob.x = new Node[subprob.l][];
2230
+ subprob.y = new double[subprob.l];
2231
+
2232
+ k=0;
2233
+ for(j=0;j<begin;j++)
2234
+ {
2235
+ subprob.x[k] = prob.x[perm[j]];
2236
+ subprob.y[k] = prob.y[perm[j]];
2237
+ ++k;
2238
+ }
2239
+ for(j=end;j<l;j++)
2240
+ {
2241
+ subprob.x[k] = prob.x[perm[j]];
2242
+ subprob.y[k] = prob.y[perm[j]];
2243
+ ++k;
2244
+ }
2245
+ Model submodel = svm_train(subprob,param);
2246
+ if(param.probability==1 &&
2247
+ (param.svm_type == Parameter.C_SVC ||
2248
+ param.svm_type == Parameter.NU_SVC))
2249
+ {
2250
+ double[] prob_estimates= new double[svm_get_nr_class(submodel)];
2251
+ for(j=begin;j<end;j++)
2252
+ target[perm[j]] = svm_predict_probability(submodel,prob.x[perm[j]],prob_estimates);
2253
+ }
2254
+ else
2255
+ for(j=begin;j<end;j++)
2256
+ target[perm[j]] = svm_predict(submodel,prob.x[perm[j]]);
2257
+ }
2258
+ }
2259
+
2260
+ public static int svm_get_svm_type(Model model)
2261
+ {
2262
+ return model.param.svm_type;
2263
+ }
2264
+
2265
+ public static int svm_get_nr_class(Model model)
2266
+ {
2267
+ return model.nr_class;
2268
+ }
2269
+
2270
+ public static void svm_get_labels(Model model, int[] label)
2271
+ {
2272
+ if (model.label != null)
2273
+ for(int i=0;i<model.nr_class;i++)
2274
+ label[i] = model.label[i];
2275
+ }
2276
+
2277
+ public static void svm_get_sv_indices(Model model, int[] indices)
2278
+ {
2279
+ if (model.sv_indices != null)
2280
+ for(int i=0;i<model.l;i++)
2281
+ indices[i] = model.sv_indices[i];
2282
+ }
2283
+
2284
+ public static int svm_get_nr_sv(Model model)
2285
+ {
2286
+ return model.l;
2287
+ }
2288
+
2289
+ public static double svm_get_svr_probability(Model model)
2290
+ {
2291
+ if ((model.param.svm_type == Parameter.EPSILON_SVR || model.param.svm_type == Parameter.NU_SVR) &&
2292
+ model.probA!=null)
2293
+ return model.probA[0];
2294
+ else
2295
+ {
2296
+ System.err.print("Model doesn't contain information for SVR probability inference\n");
2297
+ return 0;
2298
+ }
2299
+ }
2300
+
2301
+ public static double svm_predict_values(Model model, Node[] x, double[] dec_values)
2302
+ {
2303
+ int i;
2304
+ if(model.param.svm_type == Parameter.ONE_CLASS ||
2305
+ model.param.svm_type == Parameter.EPSILON_SVR ||
2306
+ model.param.svm_type == Parameter.NU_SVR)
2307
+ {
2308
+ double[] sv_coef = model.sv_coef[0];
2309
+ double sum = 0;
2310
+ for(i=0;i<model.l;i++)
2311
+ sum += sv_coef[i] * Kernel.k_function(x,model.SV[i],model.param);
2312
+ sum -= model.rho[0];
2313
+ dec_values[0] = sum;
2314
+
2315
+ if(model.param.svm_type == Parameter.ONE_CLASS)
2316
+ return (sum>0)?1:-1;
2317
+ else
2318
+ return sum;
2319
+ }
2320
+ else
2321
+ {
2322
+ int nr_class = model.nr_class;
2323
+ int l = model.l;
2324
+
2325
+ double[] kvalue = new double[l];
2326
+ for(i=0;i<l;i++)
2327
+ kvalue[i] = Kernel.k_function(x,model.SV[i],model.param);
2328
+
2329
+ int[] start = new int[nr_class];
2330
+ start[0] = 0;
2331
+ for(i=1;i<nr_class;i++)
2332
+ start[i] = start[i-1]+model.nSV[i-1];
2333
+
2334
+ int[] vote = new int[nr_class];
2335
+ for(i=0;i<nr_class;i++)
2336
+ vote[i] = 0;
2337
+
2338
+ int p=0;
2339
+ for(i=0;i<nr_class;i++)
2340
+ for(int j=i+1;j<nr_class;j++)
2341
+ {
2342
+ double sum = 0;
2343
+ int si = start[i];
2344
+ int sj = start[j];
2345
+ int ci = model.nSV[i];
2346
+ int cj = model.nSV[j];
2347
+
2348
+ int k;
2349
+ double[] coef1 = model.sv_coef[j-1];
2350
+ double[] coef2 = model.sv_coef[i];
2351
+ for(k=0;k<ci;k++)
2352
+ sum += coef1[si+k] * kvalue[si+k];
2353
+ for(k=0;k<cj;k++)
2354
+ sum += coef2[sj+k] * kvalue[sj+k];
2355
+ sum -= model.rho[p];
2356
+ dec_values[p] = sum;
2357
+
2358
+ if(dec_values[p] > 0)
2359
+ ++vote[i];
2360
+ else
2361
+ ++vote[j];
2362
+ p++;
2363
+ }
2364
+
2365
+ int vote_max_idx = 0;
2366
+ for(i=1;i<nr_class;i++)
2367
+ if(vote[i] > vote[vote_max_idx])
2368
+ vote_max_idx = i;
2369
+
2370
+ return model.label[vote_max_idx];
2371
+ }
2372
+ }
2373
+
2374
+ public static double svm_predict(Model model, Node[] x)
2375
+ {
2376
+ int nr_class = model.nr_class;
2377
+ double[] dec_values;
2378
+ if(model.param.svm_type == Parameter.ONE_CLASS ||
2379
+ model.param.svm_type == Parameter.EPSILON_SVR ||
2380
+ model.param.svm_type == Parameter.NU_SVR)
2381
+ dec_values = new double[1];
2382
+ else
2383
+ dec_values = new double[nr_class*(nr_class-1)/2];
2384
+ double pred_result = svm_predict_values(model, x, dec_values);
2385
+ return pred_result;
2386
+ }
2387
+
2388
+ public static double svm_predict_probability(Model model, Node[] x, double[] prob_estimates)
2389
+ {
2390
+ if ((model.param.svm_type == Parameter.C_SVC || model.param.svm_type == Parameter.NU_SVC) &&
2391
+ model.probA!=null && model.probB!=null)
2392
+ {
2393
+ int i;
2394
+ int nr_class = model.nr_class;
2395
+ double[] dec_values = new double[nr_class*(nr_class-1)/2];
2396
+ svm_predict_values(model, x, dec_values);
2397
+
2398
+ double min_prob=1e-7;
2399
+ double[][] pairwise_prob=new double[nr_class][nr_class];
2400
+
2401
+ int k=0;
2402
+ for(i=0;i<nr_class;i++)
2403
+ for(int j=i+1;j<nr_class;j++)
2404
+ {
2405
+ pairwise_prob[i][j]=Math.min(Math.max(sigmoid_predict(dec_values[k],model.probA[k],model.probB[k]),min_prob),1-min_prob);
2406
+ pairwise_prob[j][i]=1-pairwise_prob[i][j];
2407
+ k++;
2408
+ }
2409
+ multiclass_probability(nr_class,pairwise_prob,prob_estimates);
2410
+
2411
+ int prob_max_idx = 0;
2412
+ for(i=1;i<nr_class;i++)
2413
+ if(prob_estimates[i] > prob_estimates[prob_max_idx])
2414
+ prob_max_idx = i;
2415
+ return model.label[prob_max_idx];
2416
+ }
2417
+ else
2418
+ return svm_predict(model, x);
2419
+ }
2420
+
2421
+ static final String svm_type_table[] =
2422
+ {
2423
+ "c_svc","nu_svc","one_class","epsilon_svr","nu_svr",
2424
+ };
2425
+
2426
+ static final String kernel_type_table[]=
2427
+ {
2428
+ "linear","polynomial","rbf","sigmoid","precomputed"
2429
+ };
2430
+
2431
+ public static void svm_save_model(String model_file_name, Model model) throws IOException
2432
+ {
2433
+ DataOutputStream fp = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(model_file_name)));
2434
+ svm_save_model(fp, model);
2435
+ }
2436
+
2437
+ public static void svm_save_model(DataOutputStream fp, Model model) throws IOException
2438
+ {
2439
+ Parameter param = model.param;
2440
+
2441
+ fp.writeBytes("svm_type "+svm_type_table[param.svm_type]+"\n");
2442
+ fp.writeBytes("kernel_type "+kernel_type_table[param.kernel_type]+"\n");
2443
+
2444
+ if(param.kernel_type == Parameter.POLY)
2445
+ fp.writeBytes("degree "+param.degree+"\n");
2446
+
2447
+ if(param.kernel_type == Parameter.POLY ||
2448
+ param.kernel_type == Parameter.RBF ||
2449
+ param.kernel_type == Parameter.SIGMOID)
2450
+ fp.writeBytes("gamma "+param.gamma+"\n");
2451
+
2452
+ if(param.kernel_type == Parameter.POLY ||
2453
+ param.kernel_type == Parameter.SIGMOID)
2454
+ fp.writeBytes("coef0 "+param.coef0+"\n");
2455
+
2456
+ int nr_class = model.nr_class;
2457
+ int l = model.l;
2458
+ fp.writeBytes("nr_class "+nr_class+"\n");
2459
+ fp.writeBytes("total_sv "+l+"\n");
2460
+
2461
+ {
2462
+ fp.writeBytes("rho");
2463
+ for(int i=0;i<nr_class*(nr_class-1)/2;i++)
2464
+ fp.writeBytes(" "+model.rho[i]);
2465
+ fp.writeBytes("\n");
2466
+ }
2467
+
2468
+ if(model.label != null)
2469
+ {
2470
+ fp.writeBytes("label");
2471
+ for(int i=0;i<nr_class;i++)
2472
+ fp.writeBytes(" "+model.label[i]);
2473
+ fp.writeBytes("\n");
2474
+ }
2475
+
2476
+ if(model.probA != null) // regression has probA only
2477
+ {
2478
+ fp.writeBytes("probA");
2479
+ for(int i=0;i<nr_class*(nr_class-1)/2;i++)
2480
+ fp.writeBytes(" "+model.probA[i]);
2481
+ fp.writeBytes("\n");
2482
+ }
2483
+ if(model.probB != null)
2484
+ {
2485
+ fp.writeBytes("probB");
2486
+ for(int i=0;i<nr_class*(nr_class-1)/2;i++)
2487
+ fp.writeBytes(" "+model.probB[i]);
2488
+ fp.writeBytes("\n");
2489
+ }
2490
+
2491
+ if(model.nSV != null)
2492
+ {
2493
+ fp.writeBytes("nr_sv");
2494
+ for(int i=0;i<nr_class;i++)
2495
+ fp.writeBytes(" "+model.nSV[i]);
2496
+ fp.writeBytes("\n");
2497
+ }
2498
+
2499
+ fp.writeBytes("SV\n");
2500
+ double[][] sv_coef = model.sv_coef;
2501
+ Node[][] SV = model.SV;
2502
+
2503
+ for(int i=0;i<l;i++)
2504
+ {
2505
+ for(int j=0;j<nr_class-1;j++)
2506
+ fp.writeBytes(sv_coef[j][i]+" ");
2507
+
2508
+ Node[] p = SV[i];
2509
+ if(param.kernel_type == Parameter.PRECOMPUTED)
2510
+ fp.writeBytes("0:"+(int)(p[0].value));
2511
+ else
2512
+ for(int j=0;j<p.length;j++)
2513
+ fp.writeBytes(p[j].index+":"+p[j].value+" ");
2514
+ fp.writeBytes("\n");
2515
+ }
2516
+
2517
+ fp.close();
2518
+ }
2519
+
2520
+ private static double atof(String s)
2521
+ {
2522
+ return Double.valueOf(s).doubleValue();
2523
+ }
2524
+
2525
+ private static int atoi(String s)
2526
+ {
2527
+ return Integer.parseInt(s);
2528
+ }
2529
+
2530
+ public static Model svm_load_model(String model_file_name) throws IOException
2531
+ {
2532
+ return svm_load_model(new BufferedReader(new FileReader(model_file_name)));
2533
+ }
2534
+
2535
+ public static Model svm_load_model(BufferedReader fp) throws IOException
2536
+ {
2537
+ // read parameters
2538
+
2539
+ Model model = new Model();
2540
+ Parameter param = new Parameter();
2541
+ model.param = param;
2542
+ model.rho = null;
2543
+ model.probA = null;
2544
+ model.probB = null;
2545
+ model.label = null;
2546
+ model.nSV = null;
2547
+
2548
+ while(true)
2549
+ {
2550
+ String cmd = fp.readLine();
2551
+ String arg = cmd.substring(cmd.indexOf(' ')+1);
2552
+
2553
+ if(cmd.startsWith("svm_type"))
2554
+ {
2555
+ int i;
2556
+ for(i=0;i<svm_type_table.length;i++)
2557
+ {
2558
+ if(arg.indexOf(svm_type_table[i])!=-1)
2559
+ {
2560
+ param.svm_type=i;
2561
+ break;
2562
+ }
2563
+ }
2564
+ if(i == svm_type_table.length)
2565
+ {
2566
+ System.err.print("unknown svm type.\n");
2567
+ return null;
2568
+ }
2569
+ }
2570
+ else if(cmd.startsWith("kernel_type"))
2571
+ {
2572
+ int i;
2573
+ for(i=0;i<kernel_type_table.length;i++)
2574
+ {
2575
+ if(arg.indexOf(kernel_type_table[i])!=-1)
2576
+ {
2577
+ param.kernel_type=i;
2578
+ break;
2579
+ }
2580
+ }
2581
+ if(i == kernel_type_table.length)
2582
+ {
2583
+ System.err.print("unknown kernel function.\n");
2584
+ return null;
2585
+ }
2586
+ }
2587
+ else if(cmd.startsWith("degree"))
2588
+ param.degree = atoi(arg);
2589
+ else if(cmd.startsWith("gamma"))
2590
+ param.gamma = atof(arg);
2591
+ else if(cmd.startsWith("coef0"))
2592
+ param.coef0 = atof(arg);
2593
+ else if(cmd.startsWith("nr_class"))
2594
+ model.nr_class = atoi(arg);
2595
+ else if(cmd.startsWith("total_sv"))
2596
+ model.l = atoi(arg);
2597
+ else if(cmd.startsWith("rho"))
2598
+ {
2599
+ int n = model.nr_class * (model.nr_class-1)/2;
2600
+ model.rho = new double[n];
2601
+ StringTokenizer st = new StringTokenizer(arg);
2602
+ for(int i=0;i<n;i++)
2603
+ model.rho[i] = atof(st.nextToken());
2604
+ }
2605
+ else if(cmd.startsWith("label"))
2606
+ {
2607
+ int n = model.nr_class;
2608
+ model.label = new int[n];
2609
+ StringTokenizer st = new StringTokenizer(arg);
2610
+ for(int i=0;i<n;i++)
2611
+ model.label[i] = atoi(st.nextToken());
2612
+ }
2613
+ else if(cmd.startsWith("probA"))
2614
+ {
2615
+ int n = model.nr_class*(model.nr_class-1)/2;
2616
+ model.probA = new double[n];
2617
+ StringTokenizer st = new StringTokenizer(arg);
2618
+ for(int i=0;i<n;i++)
2619
+ model.probA[i] = atof(st.nextToken());
2620
+ }
2621
+ else if(cmd.startsWith("probB"))
2622
+ {
2623
+ int n = model.nr_class*(model.nr_class-1)/2;
2624
+ model.probB = new double[n];
2625
+ StringTokenizer st = new StringTokenizer(arg);
2626
+ for(int i=0;i<n;i++)
2627
+ model.probB[i] = atof(st.nextToken());
2628
+ }
2629
+ else if(cmd.startsWith("nr_sv"))
2630
+ {
2631
+ int n = model.nr_class;
2632
+ model.nSV = new int[n];
2633
+ StringTokenizer st = new StringTokenizer(arg);
2634
+ for(int i=0;i<n;i++)
2635
+ model.nSV[i] = atoi(st.nextToken());
2636
+ }
2637
+ else if(cmd.startsWith("SV"))
2638
+ {
2639
+ break;
2640
+ }
2641
+ else
2642
+ {
2643
+ System.err.print("unknown text in model file: ["+cmd+"]\n");
2644
+ return null;
2645
+ }
2646
+ }
2647
+
2648
+ // read sv_coef and SV
2649
+
2650
+ int m = model.nr_class - 1;
2651
+ int l = model.l;
2652
+ model.sv_coef = new double[m][l];
2653
+ model.SV = new Node[l][];
2654
+
2655
+ for(int i=0;i<l;i++)
2656
+ {
2657
+ String line = fp.readLine();
2658
+ StringTokenizer st = new StringTokenizer(line," \t\n\r\f:");
2659
+
2660
+ for(int k=0;k<m;k++)
2661
+ model.sv_coef[k][i] = atof(st.nextToken());
2662
+ int n = st.countTokens()/2;
2663
+ model.SV[i] = new Node[n];
2664
+ for(int j=0;j<n;j++)
2665
+ {
2666
+ model.SV[i][j] = new Node();
2667
+ model.SV[i][j].index = atoi(st.nextToken());
2668
+ model.SV[i][j].value = atof(st.nextToken());
2669
+ }
2670
+ }
2671
+
2672
+ fp.close();
2673
+ return model;
2674
+ }
2675
+
2676
+ public static String svm_check_parameter(Problem prob, Parameter param)
2677
+ {
2678
+ // svm_type
2679
+
2680
+ int svm_type = param.svm_type;
2681
+ if(svm_type != Parameter.C_SVC &&
2682
+ svm_type != Parameter.NU_SVC &&
2683
+ svm_type != Parameter.ONE_CLASS &&
2684
+ svm_type != Parameter.EPSILON_SVR &&
2685
+ svm_type != Parameter.NU_SVR)
2686
+ return "unknown svm type";
2687
+
2688
+ // kernel_type, degree
2689
+
2690
+ int kernel_type = param.kernel_type;
2691
+ if(kernel_type != Parameter.LINEAR &&
2692
+ kernel_type != Parameter.POLY &&
2693
+ kernel_type != Parameter.RBF &&
2694
+ kernel_type != Parameter.SIGMOID &&
2695
+ kernel_type != Parameter.PRECOMPUTED)
2696
+ return "unknown kernel type";
2697
+
2698
+ if(param.gamma < 0)
2699
+ return "gamma < 0";
2700
+
2701
+ if(param.degree < 0)
2702
+ return "degree of polynomial kernel < 0";
2703
+
2704
+ // cache_size,eps,C,nu,p,shrinking
2705
+
2706
+ if(param.cache_size <= 0)
2707
+ return "cache_size <= 0";
2708
+
2709
+ if(param.eps <= 0)
2710
+ return "eps <= 0";
2711
+
2712
+ if(svm_type == Parameter.C_SVC ||
2713
+ svm_type == Parameter.EPSILON_SVR ||
2714
+ svm_type == Parameter.NU_SVR)
2715
+ if(param.C <= 0)
2716
+ return "C <= 0";
2717
+
2718
+ if(svm_type == Parameter.NU_SVC ||
2719
+ svm_type == Parameter.ONE_CLASS ||
2720
+ svm_type == Parameter.NU_SVR)
2721
+ if(param.nu <= 0 || param.nu > 1)
2722
+ return "nu <= 0 or nu > 1";
2723
+
2724
+ if(svm_type == Parameter.EPSILON_SVR)
2725
+ if(param.p < 0)
2726
+ return "p < 0";
2727
+
2728
+ if(param.shrinking != 0 &&
2729
+ param.shrinking != 1)
2730
+ return "shrinking != 0 and shrinking != 1";
2731
+
2732
+ if(param.probability != 0 &&
2733
+ param.probability != 1)
2734
+ return "probability != 0 and probability != 1";
2735
+
2736
+ if(param.probability == 1 &&
2737
+ svm_type == Parameter.ONE_CLASS)
2738
+ return "one-class SVM probability output not supported yet";
2739
+
2740
+ // check whether nu-svc is feasible
2741
+
2742
+ if(svm_type == Parameter.NU_SVC)
2743
+ {
2744
+ int l = prob.l;
2745
+ int max_nr_class = 16;
2746
+ int nr_class = 0;
2747
+ int[] label = new int[max_nr_class];
2748
+ int[] count = new int[max_nr_class];
2749
+
2750
+ int i;
2751
+ for(i=0;i<l;i++)
2752
+ {
2753
+ int this_label = (int)prob.y[i];
2754
+ int j;
2755
+ for(j=0;j<nr_class;j++)
2756
+ if(this_label == label[j])
2757
+ {
2758
+ ++count[j];
2759
+ break;
2760
+ }
2761
+
2762
+ if(j == nr_class)
2763
+ {
2764
+ if(nr_class == max_nr_class)
2765
+ {
2766
+ max_nr_class *= 2;
2767
+ int[] new_data = new int[max_nr_class];
2768
+ System.arraycopy(label,0,new_data,0,label.length);
2769
+ label = new_data;
2770
+
2771
+ new_data = new int[max_nr_class];
2772
+ System.arraycopy(count,0,new_data,0,count.length);
2773
+ count = new_data;
2774
+ }
2775
+ label[nr_class] = this_label;
2776
+ count[nr_class] = 1;
2777
+ ++nr_class;
2778
+ }
2779
+ }
2780
+
2781
+ for(i=0;i<nr_class;i++)
2782
+ {
2783
+ int n1 = count[i];
2784
+ for(int j=i+1;j<nr_class;j++)
2785
+ {
2786
+ int n2 = count[j];
2787
+ if(param.nu*(n1+n2)/2 > Math.min(n1,n2))
2788
+ return "specified nu is infeasible";
2789
+ }
2790
+ }
2791
+ }
2792
+
2793
+ return null;
2794
+ }
2795
+
2796
+ public static int svm_check_probability_model(Model model)
2797
+ {
2798
+ if (((model.param.svm_type == Parameter.C_SVC || model.param.svm_type == Parameter.NU_SVC) &&
2799
+ model.probA!=null && model.probB!=null) ||
2800
+ ((model.param.svm_type == Parameter.EPSILON_SVR || model.param.svm_type == Parameter.NU_SVR) &&
2801
+ model.probA!=null))
2802
+ return 1;
2803
+ else
2804
+ return 0;
2805
+ }
2806
+
2807
+ public static void svm_set_print_string_function(PrintInterface print_func)
2808
+ {
2809
+ if (print_func == null)
2810
+ svm_print_string = svm_print_stdout;
2811
+ else
2812
+ svm_print_string = print_func;
2813
+ }
2814
+ }