Convert Matrix to column major order format

949 views Asked by At

I was using Rsymphony and decided that I would like to use CPLEX. I am working through the cplexAPI in R and would rather use my existing structure for building a constraint matrix. The documentation says that the API takes the constraint matrix in column major order format with only the non-zero elements. So I need a way to (elegantly, I hope) convert a matrix into this form.

For example:

3.2 Creating and solving a mixed integer programming (MIP) problem In the following, an example MIP will be created and solved: ...

subject to:

constraints

Currently, this is implemented as a matrix

> mat = matrix(c(-1, 1, 1, 10, 
+                 1, -3, 1, 0, 
+                 0, 1, 0, -3.5),byrow = TRUE, nrow=3,ncol=4)
> mat
     [,1] [,2] [,3] [,4]
[1,]   -1    1    1 10.0
[2,]    1   -3    1  0.0
[3,]    0    1    0 -3.5

From the documentation, I need the following elements.

The constraint matrix is passed in column major order format. Be careful here: all indices start with 0! Begin indices of rows.

beg <- c(0, 2, 5, 7)

You will note that working from the top left down is how this works. In the first position (0) the second column begins with the third non-zero element, the third with the fifth, etc.

Number of non-zero elements per row.

cnt <- c(2, 3, 2, 2)

Again, what I would have said was columns.

Column indices.

ind <- c(0, 1, 0, 1, 2, 0, 1, 0, 2)

This is the column index of each non-zero element.

Non-zero elements.

val <- c(-1.0, 1.0, 1.0, -3.0, 1.0, 1.0, 1.0, 10.0, -3.5)

I am stumped on finding a way to get this done.

I know I can create val easily by:

c(mat)
[1] -1.0  1.0  0.0  1.0 -3.0  1.0  1.0  1.0  0.0 10.0  0.0 -3.5
val2 <- mat[mat !=0]
val <- c(-1.0, 1.0, 1.0, -3.0, 1.0, 1.0, 1.0, 10.0, -3.5)
val2 & val
[1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE

I can create cnt easily by:

cnt <- apply(mat,2,function(x) Matrix::nnzero(x, na.counted = NA))
cnt
[1] 2 3 2 2

Thanks to @42-

ind2 <- row(mat)[which(mat != 0)]-1
ind <- c(0, 1, 0, 1, 2, 0, 1, 0, 2)
ind2 == ind
[1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE

Now for beg.

Restating the pieces,

mat
     [,1] [,2] [,3] [,4]
[1,]   -1    1    1 10.0
[2,]    1   -3    1  0.0
[3,]    0    1    0 -3.5
beg <- c(0, 2, 5, 7)
val <- c(-1.0, 1.0, 1.0, -3.0, 1.0, 1.0, 1.0, 10.0, -3.5)

beg is an index that counts along the position of the non-zero element in val. So in mat[1,1] is the first non-zero element in mat and the first (0) element in val. In mat[,2] the first non-zero elment is the third element (2) in val. In mat[,3] the first non-zero element is the sixth (5) element in val and in mat[,4] the first non-zero element is the eighth (7) element of val.

Clear as mud? Me too.

1

There are 1 answers

3
IRTFM On

It's not exactly clear to me what you are hoping for but here is a method to generate the row and column numbers that would index the non-zero entries in that matrix using zero-based indexing (which is not normal for R-matrix indexing although as you have seen that rule does not apply to sparse matrices constructed with Matrix-package functions.):

row(mat)[which(mat != 0)]-1
#[1] 0 1 0 1 2 0 1 0 2
col(mat)[which(mat != 0)]-1
#[1] 0 0 1 1 1 2 2 3 3