GenSVM
gensvm_update.c
Go to the documentation of this file.
1 
27 #include "gensvm_update.h"
28 
33 #ifndef GENSVM_BLOCK_SIZE
34  #define GENSVM_BLOCK_SIZE 512
35 #endif
36 
56 double gensvm_calculate_omega(struct GenModel *model, struct GenData *data,
57  long i)
58 {
59  long j;
60  double h, omega = 0.0,
61  p = model->p;
62 
63  for (j=0; j<model->K; j++) {
64  if (j == (data->y[i]-1))
65  continue;
66  h = matrix_get(model->H, model->K, i, j);
67  omega += pow(h, p);
68  }
69  omega = (1.0/p)*pow(omega, 1.0/p - 1.0);
70 
71  return omega;
72 }
73 
89 bool gensvm_majorize_is_simple(struct GenModel *model, struct GenData *data,
90  long i)
91 {
92  long j;
93  double h, value = 0;
94  for (j=0; j<model->K; j++) {
95  if (j == (data->y[i]-1))
96  continue;
97  h = matrix_get(model->H, model->K, i, j);
98  value += h > 0;
99  if (value > 1)
100  return false;
101  }
102  return true;
103 }
104 
126 void gensvm_calculate_ab_non_simple(struct GenModel *model, long i, long j,
127  double *a, double *b_aq)
128 {
129  double q = matrix_get(model->Q, model->K, i, j);
130  double p = model->p;
131  double kappa = model->kappa;
132  const double a2g2 = 0.25*p*(2.0*p - 1.0)*pow((kappa+1.0)/2.0,p-2.0);
133 
134  if (2.0 - model->p < 1e-2) {
135  if (q <= - kappa) {
136  *b_aq = 0.5 - kappa/2.0 - q;
137  } else if ( q <= 1.0) {
138  *b_aq = pow(1.0 - q, 3.0)/(2.0*pow(kappa + 1.0, 2.0));
139  } else {
140  *b_aq = 0;
141  }
142  *a = 1.5;
143  } else {
144  if (q <= (p + kappa - 1.0)/(p - 2.0)) {
145  *a = 0.25*pow(p, 2.0)*pow(0.5 - kappa/2.0 - q, p - 2.0);
146  } else if (q <= 1.0) {
147  *a = a2g2;
148  } else {
149  *a = 0.25*pow(p, 2.0)*pow((p/(p - 2.0))*(0.5 -
150  kappa/2.0 - q), p - 2.0);
151  *b_aq = (*a)*(2.0*q + kappa - 1.0)/(p - 2.0) +
152  0.5*p*pow(p/(p - 2.0)*(0.5 - kappa/2.0 - q),
153  p - 1.0);
154  }
155  if (q <= -kappa) {
156  *b_aq = 0.5*p*pow(0.5 - kappa/2.0 - q, p - 1.0);
157  } else if ( q <= 1.0) {
158  *b_aq = p*pow(1.0 - q, 2.0*p - 1.0)/pow(2*kappa+2.0, p);
159  }
160  }
161 }
162 
183 void gensvm_calculate_ab_simple(struct GenModel *model, long i, long j,
184  double *a, double *b_aq)
185 {
186  double q = matrix_get(model->Q, model->K, i, j);
187 
188  if (q <= - model->kappa) {
189  *a = 0.25/(0.5 - model->kappa/2.0 - q);
190  *b_aq = 0.5;
191  } else if (q <= 1.0) {
192  *a = 1.0/(2.0*model->kappa + 2.0);
193  *b_aq = (1.0 - q)*(*a);
194  } else {
195  *a = -0.25/(0.5 - model->kappa/2.0 - q);
196  *b_aq = 0;
197  }
198 }
199 
228 double gensvm_get_alpha_beta(struct GenModel *model, struct GenData *data,
229  long i, double *beta)
230 {
231  bool simple;
232  long j, K = model->K;
233  double omega, a, b_aq = 0.0,
234  alpha = 0.0;
235  double *uu_row = NULL;
236  const double in = 1.0/((double) model->n);
237 
238  simple = gensvm_majorize_is_simple(model, data, i);
239  omega = simple ? 1.0 : gensvm_calculate_omega(model, data, i);
240 
241  Memset(beta, double, K-1);
242  for (j=0; j<K; j++) {
243  // skip the class y_i = k
244  if (j == (data->y[i]-1))
245  continue;
246 
247  // calculate the a_ijk and (b_ijk - a_ijk q_i^(kj)) values
248  if (simple) {
249  gensvm_calculate_ab_simple(model, i, j, &a, &b_aq);
250  } else {
251  gensvm_calculate_ab_non_simple(model, i, j, &a, &b_aq);
252  }
253 
254  // daxpy on beta and UU
255  // daxpy does: y = a*x + y
256  // so y = beta, UU_row = x, a = factor
257  b_aq *= model->rho[i] * omega * in;
258  uu_row = &model->UU[((data->y[i]-1)*K+j)*(K-1)];
259  cblas_daxpy(K-1, b_aq, uu_row, 1, beta, 1);
260 
261  // increment Avalue
262  alpha += a;
263  }
264  alpha *= omega * model->rho[i] * in;
265  return alpha;
266 }
267 
323 void gensvm_get_update(struct GenModel *model, struct GenData *data,
324  struct GenWork *work)
325 {
326  int status;
327  long i, j;
328 
329  long m = model->m;
330  long K = model->K;
331 
332  // compute the ZAZ and ZB matrices
333  gensvm_get_ZAZ_ZB(model, data, work);
334 
335  // Calculate right-hand side of system we want to solve
336  // dsymm performs ZB := 1.0 * (ZAZ) * Vbar + 1.0 * ZB
337  // the right-hand side is thus stored in ZB after this call
338  // Note: LDB and LDC are second dimensions of the matrices due to
339  // Row-Major order
340  cblas_dsymm(CblasRowMajor, CblasLeft, CblasUpper, m+1, K-1, 1,
341  work->ZAZ, m+1, model->V, K-1, 1.0, work->ZB, K-1);
342 
343  // Calculate left-hand side of system we want to solve
344  // Add lambda to all diagonal elements except the first one. Recall
345  // that ZAZ is of size m+1 and is symmetric.
346  for (i=m+2; i<=m*(m+2); i+=m+2)
347  work->ZAZ[i] += model->lambda;
348 
349  // Lapack uses column-major order, so we transform the ZB matrix to
350  // correspond to this.
351  for (i=0; i<m+1; i++)
352  for (j=0; j<K-1; j++)
353  work->ZBc[j*(m+1)+i] = work->ZB[i*(K-1)+j];
354 
355  // Solve the system using dposv. Note that above the upper triangular
356  // part has always been used in row-major order for ZAZ. This
357  // corresponds to the lower triangular part in column-major order.
358  status = dposv('L', m+1, K-1, work->ZAZ, m+1, work->ZBc, m+1);
359 
360  // Use dsysv as fallback, for when the ZAZ matrix is not positive
361  // semi-definite for some reason (perhaps due to rounding errors).
362  // This step shouldn't be necessary but is included for safety.
363  if (status != 0) {
364  err("[GenSVM Warning]: Received nonzero status from "
365  "dposv: %i\n", status);
366  int *IPIV = Malloc(int, m+1);
367  double *WORK = Malloc(double, 1);
368  status = dsysv('L', m+1, K-1, work->ZAZ, m+1, IPIV, work->ZBc,
369  m+1, WORK, -1);
370 
371  int LWORK = WORK[0];
372  WORK = Realloc(WORK, double, LWORK);
373  status = dsysv('L', m+1, K-1, work->ZAZ, m+1, IPIV, work->ZBc,
374  m+1, WORK, LWORK);
375  if (status != 0)
376  err("[GenSVM Warning]: Received nonzero "
377  "status from dsysv: %i\n", status);
378  free(WORK);
379  WORK = NULL;
380  free(IPIV);
381  IPIV = NULL;
382  }
383 
384  // the solution is now stored in ZBc, in column-major order. Here we
385  // convert this back to row-major order
386  for (i=0; i<m+1; i++)
387  for (j=0; j<K-1; j++)
388  work->ZB[i*(K-1)+j] = work->ZBc[j*(m+1)+i];
389 
390  // copy the old V to Vbar and the new solution to V
391  for (i=0; i<m+1; i++) {
392  for (j=0; j<K-1; j++) {
393  matrix_set(model->Vbar, K-1, i, j,
394  matrix_get(model->V, K-1, i, j));
395  matrix_set(model->V, K-1, i, j,
396  matrix_get(work->ZB, K-1, i, j));
397  }
398  }
399 }
400 
418 void gensvm_get_ZAZ_ZB_dense(struct GenModel *model, struct GenData *data,
419  struct GenWork *work)
420 {
421  long i;
422  double alpha, sqalpha;
423 
424  long n = model->n;
425  long m = model->m;
426  long K = model->K;
427 
428  // generate Z'*A*Z and Z'*B by rank 1 operations
429  for (i=0; i<n; i++) {
430  alpha = gensvm_get_alpha_beta(model, data, i, work->beta);
431 
432  // calculate row of matrix LZ, which is a scalar
433  // multiplication of sqrt(alpha_i) and row z_i' of Z
434  // Note that we use the fact that the first column of Z is
435  // always 1, by only computing the product for m values and
436  // copying the first element over.
437  sqalpha = sqrt(alpha);
438  work->LZ[i*(m+1)] = sqalpha;
439  cblas_daxpy(m, sqalpha, &data->Z[i*(m+1)+1], 1,
440  &work->LZ[i*(m+1)+1], 1);
441 
442  // rank 1 update of matrix Z'*B
443  // Note: LDA is the second dimension of ZB because of
444  // Row-Major order
445  cblas_dger(CblasRowMajor, m+1, K-1, 1, &data->Z[i*(m+1)], 1,
446  work->beta, 1, work->ZB, K-1);
447  }
448 
449  // calculate Z'*A*Z by symmetric multiplication of LZ with itself
450  // (ZAZ = (LZ)' * (LZ)
451  cblas_dsyrk(CblasRowMajor, CblasUpper, CblasTrans, m+1, n, 1.0,
452  work->LZ, m+1, 0.0, work->ZAZ, m+1);
453 }
454 
481 void gensvm_get_ZAZ_ZB_sparse(struct GenModel *model, struct GenData *data,
482  struct GenWork *work)
483 {
484  long *Zia = NULL,
485  *Zja = NULL;
486  long b, i, j, k, K, jj, kk, jj_start, jj_end, blk_start, blk_end,
487  rem_size, n_blocks, n_row = data->spZ->n_row,
488  n_col = data->spZ->n_col;
489  double temp, alpha, z_ij, *vals = NULL;
490 
491  K = model->K;
492  Zia = data->spZ->ia;
493  Zja = data->spZ->ja;
494  vals = data->spZ->values;
495 
496  // calculate ZAZ using blocks of rows of Z. This helps avoiding
497  // rounding errors, which increases precision, and in turn helps
498  // convergence of the IM algorithm.
499  // see also: http://stackoverflow.com/q/40286989/
500  n_blocks = floor(n_row / GENSVM_BLOCK_SIZE);
501  rem_size = n_row % GENSVM_BLOCK_SIZE;
502 
503  for (b=0; b<=n_blocks; b++) {
504  blk_start = b * GENSVM_BLOCK_SIZE;
505  blk_end = blk_start;
506  blk_end += (b == n_blocks) ? rem_size : GENSVM_BLOCK_SIZE;
507 
508  Memset(work->tmpZAZ, double, n_col*n_col);
509  for (i=blk_start; i<blk_end; i++) {
510  alpha = gensvm_get_alpha_beta(model, data, i,
511  work->beta);
512  jj_start = Zia[i];
513  jj_end = Zia[i+1];
514 
515  for (jj=jj_start; jj<jj_end; jj++) {
516  j = Zja[jj];
517  z_ij = vals[jj];
518  cblas_daxpy(K-1, z_ij, work->beta, 1,
519  &work->ZB[j*(K-1)], 1);
520  z_ij *= alpha;
521  for (kk=jj; kk<jj_end; kk++) {
522  matrix_add(work->tmpZAZ, n_col, j,
523  Zja[kk],
524  z_ij * vals[kk]);
525  }
526  }
527  }
528 
529  // copy the intermediate results over to the actual ZAZ matrix
530  for (j=0; j<n_col; j++) {
531  for (k=j; k<n_col; k++) {
532  temp = matrix_get(work->tmpZAZ, n_col, j, k);
533  matrix_add(work->ZAZ, n_col, j, k, temp);
534  }
535  }
536  }
537 }
538 
552 void gensvm_get_ZAZ_ZB(struct GenModel *model, struct GenData *data,
553  struct GenWork *work)
554 {
555  gensvm_reset_work(work);
556 
557  if (data->Z == NULL)
558  gensvm_get_ZAZ_ZB_sparse(model, data, work);
559  else
560  gensvm_get_ZAZ_ZB_dense(model, data, work);
561 }
562 
592 int dposv(char UPLO, int N, int NRHS, double *A, int LDA, double *B,
593  int LDB)
594 {
595  extern void dposv_(char *UPLO, int *Np, int *NRHSp, double *A,
596  int *LDAp, double *B, int *LDBp, int *INFOp);
597  int INFO;
598  dposv_(&UPLO, &N, &NRHS, A, &LDA, B, &LDB, &INFO);
599  return INFO;
600 }
601 
637 int dsysv(char UPLO, int N, int NRHS, double *A, int LDA, int *IPIV,
638  double *B, int LDB, double *WORK, int LWORK)
639 {
640  extern void dsysv_(char *UPLO, int *Np, int *NRHSp, double *A,
641  int *LDAp, int *IPIV, double *B, int *LDBp,
642  double *WORK, int *LWORK, int *INFOp);
643  int INFO;
644  dsysv_(&UPLO, &N, &NRHS, A, &LDA, IPIV, B, &LDB, WORK, &LWORK, &INFO);
645  return INFO;
646 }
double * H
Huber weighted error matrix.
Definition: gensvm_base.h:126
double * LZ
n x (m+1) working matrix for the Z&#39;*A*Z calculation
Definition: gensvm_base.h:159
long * ja
column indices, should be of length nnz
Definition: gensvm_sparse.h:67
long n_col
number of columns of the original matrix
Definition: gensvm_sparse.h:60
void err(const char *fmt,...)
Parse a formatted string and write it to standard error.
Definition: gensvm_print.c:84
int dposv(char UPLO, int N, int NRHS, double *A, int LDA, double *B, int LDB)
Solve AX = B where A is symmetric positive definite.
double p
parameter for the L-p norm in the loss function
Definition: gensvm_base.h:103
double * UU
simplex difference matrix
Definition: gensvm_base.h:122
void gensvm_calculate_ab_simple(struct GenModel *model, long i, long j, double *a, double *b_aq)
Compute majorization coefficients for simple instances.
bool gensvm_majorize_is_simple(struct GenModel *model, struct GenData *data, long i)
Check if we can do simple majorization for a given instance.
Definition: gensvm_update.c:89
#define Memset(var, type, size)
Definition: gensvm_memory.h:61
#define matrix_get(M, cols, i, j)
void gensvm_get_update(struct GenModel *model, struct GenData *data, struct GenWork *work)
Perform a single step of the majorization algorithm to update V.
double * Z
Definition: gensvm_base.h:68
A structure to hold the GenSVM workspace.
Definition: gensvm_base.h:151
double * ZBc
(K-1) x (m+1) working matrix for the Z&#39;*B calculation
Definition: gensvm_base.h:163
void gensvm_calculate_ab_non_simple(struct GenModel *model, long i, long j, double *a, double *b_aq)
Compute majorization coefficients for non-simple instance.
#define Malloc(type, size)
Definition: gensvm_memory.h:48
double * V
augmented weight matrix
Definition: gensvm_base.h:115
#define matrix_add(M, cols, i, j, val)
double * Q
error matrix
Definition: gensvm_base.h:124
double * ZAZ
(m+1) x (m+1) working matrix for the Z&#39;*A*Z calculation
Definition: gensvm_base.h:165
long * y
array of class labels, 1..K
Definition: gensvm_base.h:66
A structure to represent the data.
Definition: gensvm_base.h:57
double * values
actual nonzero values, should be of length nnz
Definition: gensvm_sparse.h:63
void gensvm_get_ZAZ_ZB(struct GenModel *model, struct GenData *data, struct GenWork *work)
Wrapper around calculation of Z&#39;*A*Z and Z&#39;*B for sparse and dense.
A structure to represent a single GenSVM model.
Definition: gensvm_base.h:92
void gensvm_reset_work(struct GenWork *work)
Reset all matrices of a GenWork instance.
Definition: gensvm_base.c:302
double * ZB
(m+1) x (K-1) working matrix for the Z&#39;*B calculation
Definition: gensvm_base.h:161
void gensvm_get_ZAZ_ZB_sparse(struct GenModel *model, struct GenData *data, struct GenWork *work)
Calculate Z&#39;*A*Z and Z&#39;*B for sparse matrices.
#define Realloc(var, type, size)
Definition: gensvm_memory.h:55
double * Vbar
Definition: gensvm_base.h:117
double * tmpZAZ
(m+1) x (m+1) temporary working matrix for the Z&#39;*A*Z calculation
Definition: gensvm_base.h:167
long n
number of instances in the dataset
Definition: gensvm_base.h:97
Header file for gensvm_update.c.
int dsysv(char UPLO, int N, int NRHS, double *A, int LDA, int *IPIV, double *B, int LDB, double *WORK, int LWORK)
Solve a system of equations AX = B where A is symmetric.
double * rho
vector of instance weights
Definition: gensvm_base.h:128
double gensvm_calculate_omega(struct GenModel *model, struct GenData *data, long i)
Calculate the value of omega for a single instance.
Definition: gensvm_update.c:56
double gensvm_get_alpha_beta(struct GenModel *model, struct GenData *data, long i, double *beta)
Compute the alpha_i and beta_i for an instance.
double kappa
parameter for the Huber hinge function
Definition: gensvm_base.h:105
long K
number of classes in the dataset
Definition: gensvm_base.h:95
void gensvm_get_ZAZ_ZB_dense(struct GenModel *model, struct GenData *data, struct GenWork *work)
Calculate Z&#39;*A*Z and Z&#39;*B for dense matrices.
double kappa
kappa parameter for the GenModel
Definition: gensvm_task.h:66
#define matrix_set(M, cols, i, j, val)
#define GENSVM_BLOCK_SIZE
Definition: gensvm_update.c:34
long m
number of predictor variables in the dataset
Definition: gensvm_base.h:99
long * ia
cumulative row lengths, should be of length n_row+1
Definition: gensvm_sparse.h:65
struct GenSparse * spZ
sparse representation of the augmented data matrix
Definition: gensvm_base.h:71
double * beta
K-1 working vector for a row of the B matrix.
Definition: gensvm_base.h:171
double p
p parameter for the GenModel
Definition: gensvm_task.h:64
double lambda
regularization parameter in the loss function
Definition: gensvm_base.h:107
long n_row
number of rows of the original matrix
Definition: gensvm_sparse.h:58