SHOGUN  v2.0.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Mosek.cpp
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 3 of the License, or
5  * (at your option) any later version.
6  *
7  * Written (W) 2012 Fernando José Iglesias García
8  * Copyright (C) 2012 Fernando José Iglesias García
9  */
10 
11 #ifdef USE_MOSEK
12 
13 //#define DEBUG_MOSEK
14 //#define DEBUG_SOLUTION
15 
18 #include <shogun/lib/SGVector.h>
19 
20 using namespace shogun;
21 
22 CMosek::CMosek()
23 : CSGObject()
24 {
25 }
26 
27 CMosek::CMosek(int32_t num_con, int32_t num_var)
28 : CSGObject()
29 {
30  // Create MOSEK environment
31  m_rescode = MSK_makeenv(&m_env, NULL, NULL, NULL, NULL);
32 
33 #ifdef DEBUG_MOSEK
34  // Direct the environment's log stream to SG_PRINT
35  if ( m_rescode == MSK_RES_OK )
36  {
37  m_rescode = MSK_linkfunctoenvstream(m_env, MSK_STREAM_LOG,
38  NULL, CMosek::print);
39  }
40 #endif
41 
42  // Initialize the environment
43  if ( m_rescode == MSK_RES_OK )
44  {
45  m_rescode = MSK_initenv(m_env);
46  }
47 
48  // Create an optimization task
49  if ( m_rescode == MSK_RES_OK )
50  {
51  m_rescode = MSK_maketask(m_env, num_con, num_var, &m_task);
52  }
53 
54 #ifdef DEBUG_MOSEK
55  // Direct the task's log stream to SG_PRINT
56  if ( m_rescode == MSK_RES_OK )
57  {
58  m_rescode = MSK_linkfunctotaskstream(m_task, MSK_STREAM_LOG,
59  NULL, CMosek::print);
60  }
61 #endif
62 }
63 
64 CMosek::~CMosek()
65 {
66  delete_problem();
67 }
68 
69 void MSKAPI CMosek::print(void* handle, char str[])
70 {
71  SG_SPRINT("%s", str);
72 }
73 
74 MSKrescodee CMosek::init_sosvm(int32_t M, int32_t N,
75  int32_t num_aux, int32_t num_aux_con,
79 {
80  // Give an estimate of the size of the input data to increase the
81  // speed of inputting
82  int32_t num_var = M+N+num_aux;
83  int32_t num_con = N*N+num_aux_con;
84  // NOTE: However, to input this step is completely optional and MOSEK
85  // will automatically allocate more resources if necessary
86  m_rescode = MSK_putmaxnumvar(m_task, num_var);
87  // Neither the number of constraints nor the number of non-zero elements
88  // is known a priori, rough estimates are given here
89  m_rescode = MSK_putmaxnumcon(m_task, num_con);
90  // A = [-dPsi(y) | -I_N ] with M+N columns => max. M+1 nnz per row
91  m_rescode = MSK_putmaxnumanz(m_task, (M+1)*N*N);
92 
93  // Append optimization variables initialized to zero
94  m_rescode = MSK_append(m_task, MSK_ACC_VAR, num_var);
95  // Append empty constraints initialized with no bounds
96  m_rescode = MSK_append(m_task, MSK_ACC_CON, num_con);
97  // Set the constant term in the objective equal to zero
98  m_rescode = MSK_putcfix(m_task, 0.0);
99 
100  for ( int32_t j = 0 ; j < num_var && m_rescode == MSK_RES_OK ; ++j )
101  {
102  // Set the linear term c_j in the objective
103  if ( j < M+num_aux )
104  m_rescode = MSK_putcj(m_task, j, 0.0);
105  else
106  m_rescode = MSK_putcj(m_task, j, 1.0);
107 
108  // Set the bounds on x_j: blx[j] <= x_j <= bux[j]
109  // TODO set bounds lb and ub given by init_opt for w
110  if ( j < M )
111  {
112  m_rescode = MSK_putbound(m_task, MSK_ACC_VAR, j,
113  MSK_BK_FR, -MSK_INFINITY, +MSK_INFINITY);
114  }
115 
116  // The slack and the auxiliary variables are required to be positive
117  if ( j >= M )
118  {
119  m_rescode = MSK_putbound(m_task, MSK_ACC_VAR, j,
120  MSK_BK_LO, 0.0, +MSK_INFINITY);
121  }
122  }
123 
124  // Input the matrix Q^0 for the objective
125  //
126  // NOTE: In MOSEK we minimize x'*Q^0*x. C != Q0 but Q0 is
127  // just an extended version of C with zeros that make no
128  // difference in MOSEK's sparse representation
129  m_rescode = wrapper_putqobj(C);
130 
131  // Input the matrix A and the vector b for the contraints A*x <= b
132  m_rescode = wrapper_putaveclist(m_task, A);
133  m_rescode = wrapper_putboundlist(m_task, b);
134 
135  REQUIRE(m_rescode == MSK_RES_OK, "MOSEK Error in CMosek::init_sosvm(). "
136  "Enable DEBUG_MOSEK for details.\n");
137 
138  return m_rescode;
139 }
140 
141 MSKrescodee CMosek::add_constraint_sosvm(
143  index_t con_idx,
144  index_t train_idx,
145  int32_t num_aux,
146  float64_t bi)
147 {
148  // Count the number of non-zero element in dPsi
149  int32_t nnz = CMath::get_num_nonzero(dPsi.vector, dPsi.vlen);
150  // Indices of the non-zero elements in the row of A to add
151  SGVector< index_t > asub(nnz+1); // +1 because of the -1 element
152  // Values of the non-zero elements
153  SGVector< float64_t > aval(nnz+1);
154  // Next element to add in asub and aval
155  index_t idx = 0;
156 
157  for ( int32_t i = 0 ; i < dPsi.vlen ; ++i )
158  {
159  if ( dPsi[i] != 0 )
160  {
161  asub[idx] = i;
162  aval[idx] = dPsi[i];
163  ++idx;
164  }
165  }
166 
167  ASSERT(idx == nnz);
168 
169  asub[idx] = dPsi.vlen + num_aux + train_idx;
170  aval[idx] = -1;
171 
172  m_rescode = MSK_putavec(m_task, MSK_ACC_CON, con_idx, nnz+1,
173  asub.vector, aval.vector);
174 
175  if ( m_rescode == MSK_RES_OK )
176  {
177  m_rescode = MSK_putbound(m_task, MSK_ACC_CON, con_idx,
178  MSK_BK_UP, -MSK_INFINITY, bi);
179  }
180 
181  return m_rescode;
182 }
183 
184 MSKrescodee CMosek::wrapper_putaveclist(
185  MSKtask_t & task,
187 {
188  // Indices to the rows of A to replace, all the rows
190  for ( index_t i = 0 ; i < A.num_rows ; ++i )
191  sub[i] = i;
192 
193  // Non-zero elements of A
194  int32_t nnza = CMath::get_num_nonzero(A.matrix, A.num_rows*A.num_cols);
195  SGVector< float64_t > aval(nnza);
196  // For each of the rows, indices to non-zero elements
197  SGVector< index_t > asub(nnza);
198  // For each row, pointer to the first non-zero element
199  // in aval
201  // Next position to write in aval and asub
202  index_t idx = 0;
203  // Switch if the first non-zero element in each row
204  // has been found
205  bool first_nnz_found = false;
206 
207  for ( index_t i = 0 ; i < A.num_rows ; ++i )
208  {
209  first_nnz_found = false;
210 
211  for ( index_t j = 0 ; j < A.num_cols ; ++j )
212  {
213  if ( A(i,j) )
214  {
215  aval[idx] = A(i,j);
216  asub[idx] = j;
217 
218  if ( !first_nnz_found )
219  {
220  ptrb[i] = idx;
221  first_nnz_found = true;
222  }
223 
224  ++idx;
225  }
226  }
227 
228  // Handle rows whose elements are all zero
229  // TODO does it make sense that a row in A has all its elements
230  // equal to zero?
231  if ( !first_nnz_found )
232  ptrb[i] = ( i ? ptrb[i-1] : 0 );
233  }
234 
235  // For each row, pointer to the last+1 non-zero element
236  // in aval
238  for ( index_t i = 0 ; i < A.num_rows-1 ; ++i )
239  ptre[i] = ptrb[i+1];
240 
241  if ( A.num_rows > 0 )
242  ptre[A.num_rows-1] = nnza;
243 
244  MSKrescodee ret = MSK_putaveclist(task, MSK_ACC_CON, A.num_rows, sub.vector,
245  ptrb.vector, ptre.vector,
246  asub.vector, aval.vector);
247 
248  REQUIRE(ret == MSK_RES_OK, "MOSEK Error in CMosek::wrapper_putaveclist(). "
249  "Enable DEBUG_MOSEK for details.\n");
250 
251  return ret;
252 }
253 
254 MSKrescodee CMosek::wrapper_putboundlist(MSKtask_t & task, SGVector< float64_t > b)
255 {
256  // Indices to the bounds that should be replaced, b.vlen bounds starting
257  // from zero
258  SGVector< index_t > sub(b.vlen);
259  for ( index_t i = 0 ; i < b.vlen ; ++i )
260  sub[i] = i;
261 
262  // Type of the bounds and lower bound values
263  MSKboundkeye* bk = SG_MALLOC(MSKboundkeye, b.vlen);
265  for ( index_t i = 0 ; i < b.vlen ; ++i )
266  {
267  bk[i] = MSK_BK_UP;
268  bl[i] = -MSK_INFINITY;
269  }
270 
271  MSKrescodee ret = MSK_putboundlist(task, MSK_ACC_CON, b.vlen, sub.vector,
272  bk, bl.vector, b.vector);
273 
274  SG_FREE(bk);
275 
276  REQUIRE(ret == MSK_RES_OK, "MOSEK Error in CMosek::wrapper_putboundlist(). "
277  "Enable DEBUG_MOSEK for details.\n");
278 
279  return ret;
280 }
281 
282 MSKrescodee CMosek::wrapper_putqobj(SGMatrix< float64_t > Q0) const
283 {
284  // Shorthands for the dimensions of the matrix
285  index_t N = Q0.num_rows;
286  index_t M = Q0.num_cols;
287 
288  // Count the number of non-zero elements in the lower
289  // triangular part of the matrix
290  int32_t nnz = 0;
291  for ( index_t i = 0 ; i < N ; ++i )
292  for ( index_t j = i ; j < M ; ++j )
293  nnz += ( Q0[j + i*M] ? 1 : 0 );
294 
295  // i subscript for non-zero elements of Q0
296  SGVector< index_t > qosubi(nnz);
297  // j subscript for non-zero elements of Q0
298  SGVector< index_t > qosubj(nnz);
299  // Values of non-zero elements of Q0
300  SGVector< float64_t > qoval(nnz);
301  // Next position to write in the vectors
302  index_t idx = 0;
303 
304  for ( index_t i = 0 ; i < N ; ++i )
305  for ( index_t j = i ; j < M ; ++j )
306  {
307  if ( Q0[j + i*M] )
308  {
309  qosubi[idx] = i;
310  qosubj[idx] = j;
311  qoval[idx] = Q0[j + i*M];
312 
313  ++idx;
314  }
315  }
316 
317  return MSK_putqobj(m_task, nnz, qosubi.vector,
318  qosubj.vector, qoval.vector);
319 }
320 
321 MSKrescodee CMosek::optimize(SGVector< float64_t > sol)
322 {
323  m_rescode = MSK_optimize(m_task);
324 
325 #ifdef DEBUG_MOSEK
326  // Print a summary containing information about the solution
327  MSK_solutionsummary(m_task, MSK_STREAM_LOG);
328 #endif
329 
330  // Read the solution
331  if ( m_rescode == MSK_RES_OK )
332  {
333  // Solution status
334  MSKsolstae solsta;
335  // FIXME posible solutions are:
336  // MSK_SOL_ITR: the interior solution
337  // MSK_SOL_BAS: the basic solution
338  // MSK_SOL_ITG: the integer solution
339  MSK_getsolutionstatus(m_task, MSK_SOL_ITR, NULL, &solsta);
340 
341  switch (solsta)
342  {
343  case MSK_SOL_STA_OPTIMAL:
344  case MSK_SOL_STA_NEAR_OPTIMAL:
345  MSK_getsolutionslice(m_task,
346  // Request the interior solution
347  MSK_SOL_ITR,
348  // of the optimization vector
349  MSK_SOL_ITEM_XX,
350  0,
351  sol.vlen,
352  sol.vector);
353 #ifdef DEBUG_SOLUTION
354  sol.display_vector("Solution");
355 #endif
356  break;
357  case MSK_SOL_STA_DUAL_INFEAS_CER:
358  case MSK_SOL_STA_PRIM_INFEAS_CER:
359  case MSK_SOL_STA_NEAR_DUAL_INFEAS_CER:
360  case MSK_SOL_STA_NEAR_PRIM_INFEAS_CER:
361 #ifdef DEBUG_MOSEK
362  SG_PRINT("Primal or dual infeasibility "
363  "certificate found\n");
364 #endif
365  break;
366  case MSK_SOL_STA_UNKNOWN:
367 #ifdef DEBUG_MOSEK
368  SG_PRINT("Undetermined solution status\n");
369 #endif
370  break;
371  default:
372 #ifdef DEBUG_MOSEK
373  SG_PRINT("Other solution status\n");
374 #endif
375  break; // to avoid compile error when DEBUG_MOSEK
376  // is not defined
377  }
378  }
379 
380  // In case any error occurred, print the appropriate error message
381  if ( m_rescode != MSK_RES_OK )
382  {
383  char symname[MSK_MAX_STR_LEN];
384  char desc[MSK_MAX_STR_LEN];
385 
386  MSK_getcodedesc(m_rescode, symname, desc);
387 
388  SG_PRINT("An error occurred optimizing with MOSEK\n");
389  SG_PRINT("ERROR %s - '%s'\n", symname, desc);
390  }
391 
392  return m_rescode;
393 }
394 
395 void CMosek::delete_problem()
396 {
397  MSK_deletetask(&m_task);
398  MSK_deleteenv(&m_env);
399 }
400 
401 void CMosek::display_problem()
402 {
403  int32_t num_var, num_con;
404  m_rescode = MSK_getnumvar(m_task, &num_var);
405  m_rescode = MSK_getnumcon(m_task, &num_con);
406 
407  SG_PRINT("\nMatrix Q^0:\n");
408  for ( int32_t i = 0 ; i < num_var ; ++i )
409  {
410  for ( int32_t j = 0 ; j < num_var ; ++j )
411  {
412  float64_t qij;
413  m_rescode = MSK_getqobjij(m_task, i, j, &qij);
414  if ( qij != 0.0 )
415  SG_PRINT("(%d,%d)\t%.2f\n", i, j, qij);
416  }
417  }
418  SG_PRINT("\n");
419 
420  SG_PRINT("\nVector c:\n");
421  SGVector< float64_t > c(num_var);
422  m_rescode = MSK_getc(m_task, c.vector);
423  c.display_vector();
424 
425  SG_PRINT("\n\nMatrix A:\n");
426  for ( int32_t i = 0 ; i < num_con ; ++i )
427  {
428  for ( int32_t j = 0 ; j < num_var ; ++j )
429  {
430  float64_t aij;
431  m_rescode = MSK_getaij(m_task, i, j, &aij);
432  if ( aij != 0.0 )
433  SG_PRINT("(%d,%d)\t%.2f\n", i, j, aij);
434  }
435  }
436  SG_PRINT("\n");
437 
438  SG_PRINT("\nConstraint Bounds, vector b:\n");
439  for ( int32_t i = 0 ; i < num_con ; ++i )
440  {
441  MSKboundkeye bk;
442  float64_t bl, bu;
443  m_rescode = MSK_getbound(m_task, MSK_ACC_CON, i, &bk, &bl, &bu);
444 
445  SG_PRINT("%6.2f %6.2f\n", bl, bu);
446  }
447 
448  SG_PRINT("\nVariable Bounds, vectors lb and ub:\n");
449  for ( int32_t i = 0 ; i < num_var ; ++i )
450  {
451  MSKboundkeye bk;
452  float64_t bl, bu;
453  m_rescode = MSK_getbound(m_task, MSK_ACC_VAR, i, &bk, &bl, &bu);
454 
455  SG_PRINT("%6.2f %6.2f\n", bl, bu);
456  }
457  SG_PRINT("\n");
458 }
459 
460 
461 #endif /* USE_MOSEK */

SHOGUN Machine Learning Toolbox - Documentation