Libjulia / c-api : how to unbox SparseMatrixCSC

A new question about the libjulia c-api. My project is fully functional and I’m able to interact with the c-api in my VS2017 / C++ solution.

I work on a C++ wrapper of the ACME package that (intensively) use sparse matrix, but I can’t see how to “unbox” the SparseMatrixCSC with the c-api.

The package contains two major circuit functions that create : one “incidence” matrix (sparse), and two “topology” matrices (sparse). The first one return a SparseMatrixCSC of integers. The second return a tuple of two SparseMatrixCSC of Integer. The “topomat” function call the “incidence” function that is used to produce the two topologicals matrices.

My C/C++ method call the “topomat” function to get a tuple :

jl_value_t* topo = jl_eval_string((ACME_ID + ".topomat(" + id + ")").c_str());

I get a jl_value_t* pointer that is a tuple - alright. As I know, the tuple contains two matrices :

size_t num_mat = jl_nfields(topo);

Return 2. OK.

jl_value_t* mat1 = jl_get_nth_field(topo, 0);
jl_value_t* mat2 = jl_get_nth_field(topo, 1);

Return 2 valid pointers where jl_typeof_str return => SparseMatrixCSC

But now, I’m confused. How to use thèses pointers?

A call to :

jl_is_mutable(mat1) 

return true.

If I call :

jl_value_t* eltype = (jl_value_t*)jl_array_eltype(mat1);

Return a value pointer that is now typed : bitstype

If I call :

jl_value_t *ty = jl_typeof(val1);
int sz = jl_datatype_size(ty);

I get a sz integer of => 20

But I really don’t know how to work with a 2D array (matrix) with my pointers. The current Embedded documentation talk about the C->libjulia 1D array.

If someone can help me with a little example of code, I would be delighted.

I haven’t tested this, but you could get e.g. the colptr like this:

jl_value_t* mat1 = jl_get_nth_field(topo, 0);
jl_array_t* colptr = (jl_array_t*)jl_get_nth_field(mat1, 0);
int_t* colptr_raw = (int_t*)jl_array_data(colptr);
size_t colptr_n = jl_array_len(colptr);

colptr_raw can then be used as a C array with length colptr_n. Same for the other spare matrix fields, except for the element types.

Thank you for you answer, barche, but this process doesn’t work.

jl_array_t* colptr = (jl_array_t*)jl_get_nth_field(mat1, 0);

return a bad structure :

jl_array_t* colptr (0x0402b0f8)
{
    data=0x00000009 
    length=67241042 
    flags={how=1 ndims=130 pooled=0 ...}
    elsize=0
    offset=67241170
    nrows=521
    maxsize=67240850
    ncols=67240850
}

The raw data pointer is a bad address : 0x00000009

The first matrix (mat1) is a sparse matrix with full size:

julia> full(tv)
9×16 Array{Int32,2}:
 1  1  0   0  0   0  0  0  0   0  0  0   0  0  0  0
 1  0  1   0  0   0  0  0  0   0  0  0   0  0  0  0
 0  0  0  -1  1   0  0  0  0   0  0  0   0  0  0  0
 0  0  0  -1  0   1  1  0  0   0  0  0   0  0  0  0
 1  0  0  -1  0   1  0  1  0   0  0  0   0  0  0  0
 0  0  0  -1  0   1  0  0  1   0  1  0   0  0  0  0
 1  0  0  -1  0   1  0  0  0   1  0  1   0  0  0  0
 0  0  0   1  0  -1  0  0  0  -1  0  0  -1  1  1  0
 0  0  0   0  0   0  0  0  0   0  0  0   0  1  0  1

The second matrix (mat2) :

julia> full(ti)
7×16 Array{Int32,2}:
 1  -1  -1  0  0  0   0  -1  0  0   0  -1  0  0   0   0
 0   0   0  1  1  0   1   1  0  0   1   1  0  0  -1   0
 0   0   0  0  0  1  -1  -1  0  0  -1  -1  0  0   1   0
 0   0   0  0  0  0   0   0  1  0  -1   0  0  0   0   0
 0   0   0  0  0  0   0   0  0  1   0  -1  0  0   1   0
 0   0   0  0  0  0   0   0  0  0   0   0  1  0   1   0
 0   0   0  0  0  0   0   0  0  0   0   0  0  1  -1  -1

Matrices come from the “birdie” circuit example of the ACME package.

Oops, sorry, the colptr is the third field, not the first, I was confused by the printing output. Dumping the type yields:

SparseMatrixCSC{Tv,Ti<:Integer} <: AbstractSparseArray{Tv,Ti<:Integer,2}
  m::Int64
  n::Int64
  colptr::Array{Ti,1}
  rowval::Array{Ti,1}
  nzval::Array{Tv,1}

So it should be

jl_array_t* colptr = (jl_array_t*)jl_get_nth_field(mat1, 2);
1 Like

So simple :slight_smile:

So, for someone who want to get the SparseMatrixCSC fields, the answer is :

int m = jl_unbox_int32(jl_get_nth_field(mat1, 0));
int n = jl_unbox_int32(jl_get_nth_field(mat1, 1));
jl_array_t* colptr = (jl_array_t*)jl_get_nth_field(mat1, 2);
jl_array_t* rowval = (jl_array_t*)jl_get_nth_field(mat1, 3);
jl_array_t* nzval  = (jl_array_t*)jl_get_nth_field(mat1, 4);

In my case, m = 9, n = 16, colptr, rowval & nzval are valid jl_array_t pointers.

Thank’s to barche !

This code will segfault. Be sure to root the variables.

Also, you don’t need to use any API, you can directly declare it as a C struct

struct jl_sparse_matrix_csc_t {
    int64_t m;
    int64_t n;
    jl_array_t *colptr;
    jl_array_t *rowval;
    jl_array_t *nzval;
};

And cast the pointer to this. You can do the same with the tuple too.

2 Likes

Thank you yuyichao. You’re right, but not “segfault” here, the code compile and run under VS2017 even if I use jl_unbox_int32 with int64 for input.

But you’re right, the better way is to use the jl_sparse_matrix_csc_t !

Thank you very much for your answer too !!!

Well, it won’t segfault every time. But it will once in a few thousand times when the GC decided to give you a bad day.