robotology / osqp-eigen

Simple Eigen-C++ wrapper for OSQP library
https://robotology.github.io/osqp-eigen/
BSD 3-Clause "New" or "Revised" License
395 stars 118 forks source link

The createOsqpSparseMatrix does not correctly handle empty sparse matrix. #142

Open Levi-Armstrong opened 1 year ago

Levi-Armstrong commented 1 year ago

When setting the hessian which has zero nonZero entries it creates an osqp hessian with one value initialized to garbage.

Current:

OSQP Hessian:
     nzmax:1
         m:19
         n:19
         p:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
         i:93825007825568
         x:4.64e-310

Expected:

OSQP Hessian:
     nzmax:0
         m:19
         n:19
         p:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
         i:
         x:
traversaro commented 1 year ago

Thanks @Levi-Armstrong for reporting the problem. fyi @GiulioRomualdi @S-Dafarra

Levi-Armstrong commented 1 year ago

I believe this line should be updated to the following.

osqpSparseMatrix = csc_spalloc(rows, cols, numberOfNonZeroCoeff, numberOfNonZeroCoeff != 0, 0);
Levi-Armstrong commented 1 year ago

It looks like it may be a bug in 0.6.X

csc* csc_spalloc(c_int m, c_int n, c_int nzmax, c_int values, c_int triplet) {
  csc *A = csc_calloc(1, sizeof(csc)); /* allocate the csc struct */

  if (!A) return OSQP_NULL;            /* out of memory */

  A->m     = m;                        /* define dimensions and nzmax */
  A->n     = n;
  A->nzmax = nzmax = c_max(nzmax, 1);
  A->nz    = triplet ? 0 : -1;         /* allocate triplet or comp.col */
  A->p     = csc_malloc(triplet ? nzmax : n + 1, sizeof(c_int));
  A->i     = csc_malloc(nzmax,  sizeof(c_int));
  A->x     = values ? csc_malloc(nzmax,  sizeof(c_float)) : OSQP_NULL;
  if (!A->p || !A->i || (values && !A->x)){
    csc_spfree(A);
    return OSQP_NULL;
  } else return A;
}

Master:

OSQPCscMatrix* csc_spalloc(OSQPInt m,
                           OSQPInt n,
                           OSQPInt nzmax,
                           OSQPInt values,
                           OSQPInt triplet) {
  OSQPCscMatrix* A = c_calloc(1, sizeof(OSQPCscMatrix)); /* allocate the OSQPCscMatrix struct */

  if (!A) return OSQP_NULL;            /* out of memory */

  A->m     = m;                        /* define dimensions and nzmax */
  A->n     = n;
  A->nzmax = nzmax = c_max(nzmax, 0);
  A->nz    = triplet ? 0 : -1;         /* allocate triplet or comp.col */
  A->p     = csc_malloc(triplet ? nzmax : n + 1, sizeof(OSQPInt));
  A->i     = values ? csc_malloc(nzmax,  sizeof(OSQPInt)) : OSQP_NULL;
  A->x     = values ? csc_malloc(nzmax,  sizeof(OSQPFloat)) : OSQP_NULL;
  if (!A->p || (values && !A->i ) || (values && !A->x)){
    csc_spfree(A);
    return OSQP_NULL;
  } else return A;
}
Levi-Armstrong commented 1 year ago

I created this method and used it within osqp_eigen to solve the issue.

static void* csc_malloc(c_int n, c_int size) {
  return c_malloc(n * size); // NOLINT
}

static void* csc_calloc(c_int n, c_int size) {
  return c_calloc(n, size); // NOLINT
}

csc* csc_spalloc_fix(c_int m, c_int n, c_int nzmax, c_int values, c_int triplet) {
  csc *A = (csc*)(csc_calloc(1, sizeof(csc))); /* allocate the csc struct */ // NOLINT

  if (!A) return OSQP_NULL;            /* out of memory */ // NOLINT

  A->m     = m;                        /* define dimensions and nzmax */
  A->n     = n;
  A->nzmax = nzmax = c_max(nzmax, 0);
  A->nz    = triplet ? 0 : -1;         /* allocate triplet or comp.col */ // NOLINT
  A->p     = (c_int*)(csc_malloc(triplet ? nzmax : n + 1, sizeof(c_int))); // NOLINT
  A->i     = values ? (c_int*)(csc_malloc(nzmax,  sizeof(c_int))) : OSQP_NULL; // NOLINT
  A->x     = values ? (c_float*)(csc_malloc(nzmax,  sizeof(c_float))) : OSQP_NULL; // NOLINT
  if (!A->p || (values && !A->i) || (values && !A->x)){ // NOLINT
    csc_spfree(A);
    return OSQP_NULL;
  } else return A; // NOLINT
}
Levi-Armstrong commented 1 year ago

Then updated the call below.

osqpSparseMatrix = csc_spalloc_fix(rows, cols, numberOfNonZeroCoeff, 1, 0);