/************************************************************************* This function performs in-place conversion to SKS format. INPUT PARAMETERS S - sparse matrix in any format. OUTPUT PARAMETERS S - sparse matrix in SKS format. NOTE: this function has no effect when called with matrix which is already in SKS mode. NOTE: in-place conversion involves allocation of temporary arrays. If you perform a lot of repeated in- place conversions, it may lead to memory fragmentation. Consider using out-of-place SparseCopyToSKSBuf() function in this case. -- ALGLIB PROJECT -- Copyright 15.01.2014 by Bochkanov Sergey *************************************************************************/ public static void sparseconverttosks(sparsematrix s) { int[] tridx = new int[0]; int[] tdidx = new int[0]; int[] tuidx = new int[0]; double[] tvals = new double[0]; int n = 0; int t0 = 0; int t1 = 0; int i = 0; int j = 0; int k = 0; double v = 0; alglib.ap.assert((s.matrixtype==0 || s.matrixtype==1) || s.matrixtype==2, "SparseConvertToSKS: invalid matrix type"); alglib.ap.assert(s.m==s.n, "SparseConvertToSKS: rectangular matrices are not supported"); n = s.n; if( s.matrixtype==2 ) { // // Already in SKS mode // return; } // // Generate internal copy of SKS matrix // apserv.ivectorsetlengthatleast(ref tdidx, n+1); apserv.ivectorsetlengthatleast(ref tuidx, n+1); for(i=0; i<=n; i++) { tdidx[i] = 0; tuidx[i] = 0; } t0 = 0; t1 = 0; while( sparseenumerate(s, ref t0, ref t1, ref i, ref j, ref v) ) { if( j<i ) { tdidx[i] = Math.Max(tdidx[i], i-j); } else { tuidx[j] = Math.Max(tuidx[j], j-i); } } apserv.ivectorsetlengthatleast(ref tridx, n+1); tridx[0] = 0; for(i=1; i<=n; i++) { tridx[i] = tridx[i-1]+tdidx[i-1]+1+tuidx[i-1]; } apserv.rvectorsetlengthatleast(ref tvals, tridx[n]); k = tridx[n]; for(i=0; i<=k-1; i++) { tvals[i] = 0.0; } t0 = 0; t1 = 0; while( sparseenumerate(s, ref t0, ref t1, ref i, ref j, ref v) ) { if( j<=i ) { tvals[tridx[i]+tdidx[i]-(i-j)] = v; } else { tvals[tridx[j+1]-(j-i)] = v; } } for(i=0; i<=n-1; i++) { tdidx[n] = Math.Max(tdidx[n], tdidx[i]); tuidx[n] = Math.Max(tuidx[n], tuidx[i]); } s.matrixtype = 2; s.ninitialized = 0; s.nfree = 0; s.m = n; s.n = n; alglib.ap.swap(ref s.didx, ref tdidx); alglib.ap.swap(ref s.uidx, ref tuidx); alglib.ap.swap(ref s.ridx, ref tridx); alglib.ap.swap(ref s.vals, ref tvals); }
/************************************************************************* This function calculates matrix-matrix product S*A, when S is symmetric matrix. Matrix S must be stored in CRS format (exception will be thrown otherwise). INPUT PARAMETERS S - sparse M*M matrix in CRS format (you MUST convert it to CRS before calling this function). A - array[N][K], input dense matrix. For performance reasons we make only quick checks - we check that array size is at least N, but we do not check for NAN's or INF's. K - number of columns of matrix (A). B - output buffer, possibly preallocated. In case buffer size is too small to store result, this buffer is automatically resized. OUTPUT PARAMETERS B - array[M][K], S*A NOTE: this function throws exception when called for non-CRS matrix. You must convert your matrix with SparseConvertToCRS() before using this function. -- ALGLIB PROJECT -- Copyright 14.10.2011 by Bochkanov Sergey *************************************************************************/ public static void sparsesmm(sparsematrix s, bool isupper, double[,] a, int k, ref double[,] b) { int i = 0; int j = 0; int k0 = 0; int id = 0; int lt = 0; int rt = 0; double v = 0; double vb = 0; double va = 0; int i_ = 0; alglib.ap.assert(s.matrixtype == 1, "SparseSMM: incorrect matrix type (convert your matrix to CRS)"); alglib.ap.assert(s.ninitialized == s.ridx[s.m], "SparseSMM: some rows/elements of the CRS matrix were not initialized (you must initialize everything you promised to SparseCreateCRS)"); alglib.ap.assert(alglib.ap.rows(a) >= s.n, "SparseSMM: Rows(X)<N"); alglib.ap.assert(s.m == s.n, "SparseSMM: matrix is non-square"); apserv.rmatrixsetlengthatleast(ref b, s.m, k); for (i = 0; i <= s.m - 1; i++) { for (j = 0; j <= k - 1; j++) { b[i, j] = 0; } } if (k > linalgswitch) { for (i = 0; i <= s.m - 1; i++) { for (j = 0; j <= k - 1; j++) { if (s.didx[i] != s.uidx[i]) { id = s.didx[i]; b[i, j] = b[i, j] + s.vals[id] * a[s.idx[id], j]; } if (isupper) { lt = s.uidx[i]; rt = s.ridx[i + 1]; vb = 0; va = a[i, j]; for (k0 = lt; k0 <= rt - 1; k0++) { id = s.idx[k0]; v = s.vals[k0]; vb = vb + a[id, j] * v; b[id, j] = b[id, j] + va * v; } b[i, j] = b[i, j] + vb; } else { lt = s.ridx[i]; rt = s.didx[i]; vb = 0; va = a[i, j]; for (k0 = lt; k0 <= rt - 1; k0++) { id = s.idx[k0]; v = s.vals[k0]; vb = vb + a[id, j] * v; b[id, j] = b[id, j] + va * v; } b[i, j] = b[i, j] + vb; } } } } else { for (i = 0; i <= s.m - 1; i++) { if (s.didx[i] != s.uidx[i]) { id = s.didx[i]; v = s.vals[id]; for (i_ = 0; i_ <= k - 1; i_++) { b[i, i_] = b[i, i_] + v * a[s.idx[id], i_]; } } if (isupper) { lt = s.uidx[i]; rt = s.ridx[i + 1]; for (j = lt; j <= rt - 1; j++) { id = s.idx[j]; v = s.vals[j]; for (i_ = 0; i_ <= k - 1; i_++) { b[i, i_] = b[i, i_] + v * a[id, i_]; } for (i_ = 0; i_ <= k - 1; i_++) { b[id, i_] = b[id, i_] + v * a[i, i_]; } } } else { lt = s.ridx[i]; rt = s.didx[i]; for (j = lt; j <= rt - 1; j++) { id = s.idx[j]; v = s.vals[j]; for (i_ = 0; i_ <= k - 1; i_++) { b[i, i_] = b[i, i_] + v * a[id, i_]; } for (i_ = 0; i_ <= k - 1; i_++) { b[id, i_] = b[id, i_] + v * a[i, i_]; } } } } } }
/************************************************************************* This function return average length of chain at hash-table. -- ALGLIB PROJECT -- Copyright 14.10.2011 by Bochkanov Sergey *************************************************************************/ public static double sparsegetaveragelengthofchain(sparsematrix s) { double result = 0; int nchains = 0; int talc = 0; int l = 0; int i = 0; int ind0 = 0; int ind1 = 0; int hashcode = 0; // //if matrix represent in CRS then return zero and exit // if (s.matrixtype == 1) { result = 0; return result; } nchains = 0; talc = 0; l = alglib.ap.len(s.vals); for (i = 0; i <= l - 1; i++) { ind0 = 2 * i; if (s.idx[ind0] != -1) { nchains = nchains + 1; hashcode = hash(s.idx[ind0], s.idx[ind0 + 1], l); while (true) { talc = talc + 1; ind1 = 2 * hashcode; if (s.idx[ind0] == s.idx[ind1] && s.idx[ind0 + 1] == s.idx[ind1 + 1]) { break; } hashcode = (hashcode + 1) % l; } } } if (nchains == 0) { result = 0; } else { result = (double)talc / (double)nchains; } return result; }
/************************************************************************* This function simultaneously calculates two matrix-vector products: S*x and S^T*x. S must be square (non-rectangular) matrix stored in CRS format (exception will be thrown otherwise). INPUT PARAMETERS S - sparse N*N matrix in CRS format (you MUST convert it to CRS before calling this function). X - array[N], input vector. For performance reasons we make only quick checks - we check that array size is at least N, but we do not check for NAN's or INF's. Y0 - output buffer, possibly preallocated. In case buffer size is too small to store result, this buffer is automatically resized. Y1 - output buffer, possibly preallocated. In case buffer size is too small to store result, this buffer is automatically resized. OUTPUT PARAMETERS Y0 - array[N], S*x Y1 - array[N], S^T*x NOTE: this function throws exception when called for non-CRS matrix. You must convert your matrix with SparseConvertToCRS() before using this function. It also throws exception when S is non-square. -- ALGLIB PROJECT -- Copyright 14.10.2011 by Bochkanov Sergey *************************************************************************/ public static void sparsemv2(sparsematrix s, double[] x, ref double[] y0, ref double[] y1) { int l = 0; double tval = 0; int i = 0; int j = 0; double vx = 0; double vs = 0; int vi = 0; int j0 = 0; int j1 = 0; alglib.ap.assert(s.matrixtype == 1, "SparseMV2: incorrect matrix type (convert your matrix to CRS)"); alglib.ap.assert(s.ninitialized == s.ridx[s.m], "SparseMV: some rows/elements of the CRS matrix were not initialized (you must initialize everything you promised to SparseCreateCRS)"); alglib.ap.assert(s.m == s.n, "SparseMV2: matrix is non-square"); l = alglib.ap.len(x); alglib.ap.assert(l >= s.n, "SparseMV2: Length(X)<N"); apserv.rvectorsetlengthatleast(ref y0, l); apserv.rvectorsetlengthatleast(ref y1, l); for (i = 0; i <= s.n - 1; i++) { y1[i] = 0; } for (i = 0; i <= s.m - 1; i++) { tval = 0; vx = x[i]; j0 = s.ridx[i]; j1 = s.ridx[i + 1] - 1; for (j = j0; j <= j1; j++) { vi = s.idx[j]; vs = s.vals[j]; tval = tval + x[vi] * vs; y1[vi] = y1[vi] + vx * vs; } y0[i] = tval; } }
/************************************************************************* This function calculates matrix-matrix product S^T*A. Matrix S must be stored in CRS format (exception will be thrown otherwise). INPUT PARAMETERS S - sparse M*N matrix in CRS format (you MUST convert it to CRS before calling this function). A - array[M][K], input dense matrix. For performance reasons we make only quick checks - we check that array size is at least M, but we do not check for NAN's or INF's. K - number of columns of matrix (A). B - output buffer, possibly preallocated. In case buffer size is too small to store result, this buffer is automatically resized. OUTPUT PARAMETERS B - array[N][K], S^T*A NOTE: this function throws exception when called for non-CRS matrix. You must convert your matrix with SparseConvertToCRS() before using this function. -- ALGLIB PROJECT -- Copyright 14.10.2011 by Bochkanov Sergey *************************************************************************/ public static void sparsemtm(sparsematrix s, double[,] a, int k, ref double[,] b) { int i = 0; int j = 0; int k0 = 0; int lt = 0; int rt = 0; int ct = 0; double v = 0; int i_ = 0; alglib.ap.assert(s.matrixtype == 1, "SparseMTM: incorrect matrix type (convert your matrix to CRS)"); alglib.ap.assert(s.ninitialized == s.ridx[s.m], "SparseMTM: some rows/elements of the CRS matrix were not initialized (you must initialize everything you promised to SparseCreateCRS)"); alglib.ap.assert(alglib.ap.rows(a) >= s.m, "SparseMTM: Rows(A)<M"); alglib.ap.assert(k > 0, "SparseMTM: K<=0"); apserv.rmatrixsetlengthatleast(ref b, s.n, k); for (i = 0; i <= s.n - 1; i++) { for (j = 0; j <= k - 1; j++) { b[i, j] = 0; } } if (k < linalgswitch) { for (i = 0; i <= s.m - 1; i++) { lt = s.ridx[i]; rt = s.ridx[i + 1]; for (k0 = lt; k0 <= rt - 1; k0++) { v = s.vals[k0]; ct = s.idx[k0]; for (j = 0; j <= k - 1; j++) { b[ct, j] = b[ct, j] + v * a[i, j]; } } } } else { for (i = 0; i <= s.m - 1; i++) { lt = s.ridx[i]; rt = s.ridx[i + 1]; for (j = lt; j <= rt - 1; j++) { v = s.vals[j]; ct = s.idx[j]; for (i_ = 0; i_ <= k - 1; i_++) { b[ct, i_] = b[ct, i_] + v * a[i, i_]; } } } } }
/************************************************************************* This function returns S[i,j] - element of the sparse matrix. Matrix can be in any mode (Hash-Table or CRS), but this function is less efficient for CRS matrices. Hash-Table matrices can find element in O(1) time, while CRS matrices need O(RS) time, where RS is an number of non-zero elements in a row. INPUT PARAMETERS S - sparse M*N matrix in Hash-Table representation. Exception will be thrown for CRS matrix. I - row index of the element to modify, 0<=I<M J - column index of the element to modify, 0<=J<N RESULT value of S[I,J] or zero (in case no element with such index is found) -- ALGLIB PROJECT -- Copyright 14.10.2011 by Bochkanov Sergey *************************************************************************/ public static double sparseget(sparsematrix s, int i, int j) { double result = 0; int hashcode = 0; int k = 0; int k0 = 0; int k1 = 0; alglib.ap.assert(i >= 0, "SparseGet: I<0"); alglib.ap.assert(i < s.m, "SparseGet: I>=M"); alglib.ap.assert(j >= 0, "SparseGet: J<0"); alglib.ap.assert(j < s.n, "SparseGet: J>=N"); k = alglib.ap.len(s.vals); result = 0; if (s.matrixtype == 0) { hashcode = hash(i, j, k); while (true) { if (s.idx[2 * hashcode] == -1) { return result; } if (s.idx[2 * hashcode] == i && s.idx[2 * hashcode + 1] == j) { result = s.vals[hashcode]; return result; } hashcode = (hashcode + 1) % k; } } if (s.matrixtype == 1) { alglib.ap.assert(s.ninitialized == s.ridx[s.m], "SparseGet: some rows/elements of the CRS matrix were not initialized (you must initialize everything you promised to SparseCreateCRS)"); k0 = s.ridx[i]; k1 = s.ridx[i + 1] - 1; for (k = k0; k <= k1; k++) { if (s.idx[k] == j) { result = s.vals[k]; return result; } } return result; } return result; }
/************************************************************************* This function calculates matrix-vector product S*x. Matrix S must be stored in CRS format (exception will be thrown otherwise). INPUT PARAMETERS S - sparse M*N matrix in CRS format (you MUST convert it to CRS before calling this function). X - array[N], input vector. For performance reasons we make only quick checks - we check that array size is at least N, but we do not check for NAN's or INF's. Y - output buffer, possibly preallocated. In case buffer size is too small to store result, this buffer is automatically resized. OUTPUT PARAMETERS Y - array[M], S*x NOTE: this function throws exception when called for non-CRS matrix. You must convert your matrix with SparseConvertToCRS() before using this function. -- ALGLIB PROJECT -- Copyright 14.10.2011 by Bochkanov Sergey *************************************************************************/ public static void sparsemv(sparsematrix s, double[] x, ref double[] y) { double tval = 0; int i = 0; int j = 0; int lt = 0; int rt = 0; alglib.ap.assert(s.matrixtype == 1, "SparseMV: incorrect matrix type (convert your matrix to CRS)"); alglib.ap.assert(s.ninitialized == s.ridx[s.m], "SparseMV: some rows/elements of the CRS matrix were not initialized (you must initialize everything you promised to SparseCreateCRS)"); alglib.ap.assert(alglib.ap.len(x) >= s.n, "SparseMV: length(X)<N"); apserv.rvectorsetlengthatleast(ref y, s.m); for (i = 0; i <= s.m - 1; i++) { tval = 0; lt = s.ridx[i]; rt = s.ridx[i + 1]; for (j = lt; j <= rt - 1; j++) { tval = tval + x[s.idx[j]] * s.vals[j]; } y[i] = tval; } }
/************************************************************************* The function returns number of rows of a sparse matrix. RESULT: number of rows of a sparse matrix. -- ALGLIB PROJECT -- Copyright 23.08.2012 by Bochkanov Sergey *************************************************************************/ public static int sparsegetnrows(sparsematrix s) { int result = 0; result = s.m; return result; }
/************************************************************************* The function returns number of columns of a sparse matrix. RESULT: number of columns of a sparse matrix. -- ALGLIB PROJECT -- Copyright 23.08.2012 by Bochkanov Sergey *************************************************************************/ public static int sparsegetncols(sparsematrix s) { int result = 0; result = s.n; return result; }
/************************************************************************* This function checks matrix storage format and returns True when matrix is stored using SKS representation. INPUT PARAMETERS: S - sparse matrix. RESULT: True if matrix type is SKS False if matrix type is not SKS -- ALGLIB PROJECT -- Copyright 20.07.2012 by Bochkanov Sergey *************************************************************************/ public static bool sparseissks(sparsematrix s) { bool result = new bool(); alglib.ap.assert((s.matrixtype==0 || s.matrixtype==1) || s.matrixtype==2, "SparseIsSKS: invalid matrix type"); result = s.matrixtype==2; return result; }
/************************************************************************* The function frees all memory occupied by sparse matrix. Sparse matrix structure becomes unusable after this call. OUTPUT PARAMETERS S - sparse matrix to delete -- ALGLIB PROJECT -- Copyright 24.07.2012 by Bochkanov Sergey *************************************************************************/ public static void sparsefree(sparsematrix s) { s.matrixtype = -1; s.m = 0; s.n = 0; s.nfree = 0; s.ninitialized = 0; s.tablesize = 0; }
/************************************************************************* This function returns type of the matrix storage format. INPUT PARAMETERS: S - sparse matrix. RESULT: sparse storage format used by matrix: 0 - Hash-table 1 - CRS (compressed row storage) 2 - SKS (skyline) NOTE: future versions of ALGLIB may include additional sparse storage formats. -- ALGLIB PROJECT -- Copyright 20.07.2012 by Bochkanov Sergey *************************************************************************/ public static int sparsegetmatrixtype(sparsematrix s) { int result = 0; alglib.ap.assert((s.matrixtype==0 || s.matrixtype==1) || s.matrixtype==2, "SparseGetMatrixType: invalid matrix type"); result = s.matrixtype; return result; }
/************************************************************************* This function performs out-of-place conversion to SKS format. S0 is copied to S1 and converted on-the-fly. Memory allocated in S1 is reused to maximum extent possible. INPUT PARAMETERS S0 - sparse matrix in any format. OUTPUT PARAMETERS S1 - sparse matrix in SKS format. NOTE: if S0 is stored as SKS, it is just copied without conversion. -- ALGLIB PROJECT -- Copyright 20.07.2012 by Bochkanov Sergey *************************************************************************/ public static void sparsecopytosksbuf(sparsematrix s0, sparsematrix s1) { double v = 0; int n = 0; int t0 = 0; int t1 = 0; int i = 0; int j = 0; int k = 0; alglib.ap.assert((s0.matrixtype==0 || s0.matrixtype==1) || s0.matrixtype==2, "SparseCopyToSKSBuf: invalid matrix type"); alglib.ap.assert(s0.m==s0.n, "SparseCopyToSKSBuf: rectangular matrices are not supported"); n = s0.n; if( s0.matrixtype==2 ) { // // Already SKS, just copy // sparsecopybuf(s0, s1); return; } // // Generate copy of matrix in the SKS format // apserv.ivectorsetlengthatleast(ref s1.didx, n+1); apserv.ivectorsetlengthatleast(ref s1.uidx, n+1); for(i=0; i<=n; i++) { s1.didx[i] = 0; s1.uidx[i] = 0; } t0 = 0; t1 = 0; while( sparseenumerate(s0, ref t0, ref t1, ref i, ref j, ref v) ) { if( j<i ) { s1.didx[i] = Math.Max(s1.didx[i], i-j); } else { s1.uidx[j] = Math.Max(s1.uidx[j], j-i); } } apserv.ivectorsetlengthatleast(ref s1.ridx, n+1); s1.ridx[0] = 0; for(i=1; i<=n; i++) { s1.ridx[i] = s1.ridx[i-1]+s1.didx[i-1]+1+s1.uidx[i-1]; } apserv.rvectorsetlengthatleast(ref s1.vals, s1.ridx[n]); k = s1.ridx[n]; for(i=0; i<=k-1; i++) { s1.vals[i] = 0.0; } t0 = 0; t1 = 0; while( sparseenumerate(s0, ref t0, ref t1, ref i, ref j, ref v) ) { if( j<=i ) { s1.vals[s1.ridx[i]+s1.didx[i]-(i-j)] = v; } else { s1.vals[s1.ridx[j+1]-(j-i)] = v; } } for(i=0; i<=n-1; i++) { s1.didx[n] = Math.Max(s1.didx[n], s1.didx[i]); s1.uidx[n] = Math.Max(s1.uidx[n], s1.uidx[i]); } s1.matrixtype = 2; s1.ninitialized = 0; s1.nfree = 0; s1.m = n; s1.n = n; }
/************************************************************************* This function performs out-of-place conversion to SKS storage format. S0 is copied to S1 and converted on-the-fly. INPUT PARAMETERS S0 - sparse matrix in any format. OUTPUT PARAMETERS S1 - sparse matrix in SKS format. NOTE: if S0 is stored as SKS, it is just copied without conversion. NOTE: this function de-allocates memory occupied by S1 before starting conversion. If you perform a lot of repeated conversions, it may lead to memory fragmentation. In this case we recommend you to use SparseCopyToSKSBuf() function which re-uses memory in S1 as much as possible. -- ALGLIB PROJECT -- Copyright 20.07.2012 by Bochkanov Sergey *************************************************************************/ public static void sparsecopytosks(sparsematrix s0, sparsematrix s1) { alglib.ap.assert((s0.matrixtype==0 || s0.matrixtype==1) || s0.matrixtype==2, "SparseCopyToSKS: invalid matrix type"); sparsecopytosksbuf(s0, s1); }
/************************************************************************* This function adds value to S[i,j] - element of the sparse matrix. Matrix must be in a Hash-Table mode. In case S[i,j] already exists in the table, V i added to its value. In case S[i,j] is non-existent, it is inserted in the table. Table automatically grows when necessary. INPUT PARAMETERS S - sparse M*N matrix in Hash-Table representation. Exception will be thrown for CRS matrix. I - row index of the element to modify, 0<=I<M J - column index of the element to modify, 0<=J<N V - value to add, must be finite number OUTPUT PARAMETERS S - modified matrix NOTE 1: when S[i,j] is exactly zero after modification, it is deleted from the table. -- ALGLIB PROJECT -- Copyright 14.10.2011 by Bochkanov Sergey *************************************************************************/ public static void sparseadd(sparsematrix s, int i, int j, double v) { int hashcode = 0; int tcode = 0; int k = 0; alglib.ap.assert(s.matrixtype == 0, "SparseAdd: matrix must be in the Hash-Table mode to do this operation"); alglib.ap.assert(i >= 0, "SparseAdd: I<0"); alglib.ap.assert(i < s.m, "SparseAdd: I>=M"); alglib.ap.assert(j >= 0, "SparseAdd: J<0"); alglib.ap.assert(j < s.n, "SparseAdd: J>=N"); alglib.ap.assert(math.isfinite(v), "SparseAdd: V is not finite number"); if ((double)(v) == (double)(0)) { return; } tcode = -1; k = alglib.ap.len(s.vals); if ((double)((1 - maxloadfactor) * k) >= (double)(s.nfree)) { sparseresizematrix(s); k = alglib.ap.len(s.vals); } hashcode = hash(i, j, k); while (true) { if (s.idx[2 * hashcode] == -1) { if (tcode != -1) { hashcode = tcode; } s.vals[hashcode] = v; s.idx[2 * hashcode] = i; s.idx[2 * hashcode + 1] = j; if (tcode == -1) { s.nfree = s.nfree - 1; } return; } else { if (s.idx[2 * hashcode] == i && s.idx[2 * hashcode + 1] == j) { s.vals[hashcode] = s.vals[hashcode] + v; if ((double)(s.vals[hashcode]) == (double)(0)) { s.idx[2 * hashcode] = -2; } return; } // //is it deleted element? // if (tcode == -1 && s.idx[2 * hashcode] == -2) { tcode = hashcode; } // //next step // hashcode = (hashcode + 1) % k; } } }
/************************************************************************* The function returns number of strictly lower triangular non-zero elements in the matrix. It counts SYMBOLICALLY non-zero elements, i.e. entries in the sparse matrix data structure. If some element has zero numerical value, it is still counted. This function has different cost for different types of matrices: * for hash-based matrices it involves complete pass over entire hash-table with O(NNZ) cost, where NNZ is number of non-zero elements * for CRS and SKS matrix types cost of counting is O(N) (N - matrix size). RESULT: number of non-zero elements strictly below main diagonal -- ALGLIB PROJECT -- Copyright 12.02.2014 by Bochkanov Sergey *************************************************************************/ public static int sparsegetlowercount(sparsematrix s) { int result = 0; int sz = 0; int i0 = 0; int i = 0; result = -1; if( s.matrixtype==0 ) { // // Hash-table matrix // result = 0; sz = s.tablesize; for(i0=0; i0<=sz-1; i0++) { i = s.idx[2*i0]; if( i>=0 && s.idx[2*i0+1]<i ) { result = result+1; } } return result; } if( s.matrixtype==1 ) { // // CRS matrix // alglib.ap.assert(s.ninitialized==s.ridx[s.m], "SparseGetUpperCount: some rows/elements of the CRS matrix were not initialized (you must initialize everything you promised to SparseCreateCRS)"); result = 0; sz = s.m; for(i=0; i<=sz-1; i++) { result = result+(s.didx[i]-s.ridx[i]); } return result; } if( s.matrixtype==2 ) { // // SKS matrix // alglib.ap.assert(s.m==s.n, "SparseGetUpperCount: non-square SKS matrices are not supported"); result = 0; sz = s.m; for(i=0; i<=sz-1; i++) { result = result+s.didx[i]; } return result; } alglib.ap.assert(false, "SparseGetUpperCount: internal error"); return result; }
/************************************************************************* This function modifies S[i,j] - element of the sparse matrix. Matrix must be in a Hash-Table mode. In case new value of S[i,j] is zero, this element is deleted from the table. INPUT PARAMETERS S - sparse M*N matrix in Hash-Table representation. Exception will be thrown for CRS matrix. I - row index of the element to modify, 0<=I<M J - column index of the element to modify, 0<=J<N V - value to set, must be finite number, can be zero OUTPUT PARAMETERS S - modified matrix NOTE: this function has no effect when called with zero V for non- existent element. -- ALGLIB PROJECT -- Copyright 14.10.2011 by Bochkanov Sergey *************************************************************************/ public static void sparseset(sparsematrix s, int i, int j, double v) { int hashcode = 0; int tcode = 0; int k = 0; alglib.ap.assert(i >= 0, "SparseSet: I<0"); alglib.ap.assert(i < s.m, "SparseSet: I>=M"); alglib.ap.assert(j >= 0, "SparseSet: J<0"); alglib.ap.assert(j < s.n, "SparseSet: J>=N"); alglib.ap.assert(math.isfinite(v), "SparseSet: V is not finite number"); // // Hash-table matrix // if (s.matrixtype == 0) { tcode = -1; k = alglib.ap.len(s.vals); if ((double)((1 - maxloadfactor) * k) >= (double)(s.nfree)) { sparseresizematrix(s); k = alglib.ap.len(s.vals); } hashcode = hash(i, j, k); while (true) { if (s.idx[2 * hashcode] == -1) { if ((double)(v) != (double)(0)) { if (tcode != -1) { hashcode = tcode; } s.vals[hashcode] = v; s.idx[2 * hashcode] = i; s.idx[2 * hashcode + 1] = j; if (tcode == -1) { s.nfree = s.nfree - 1; } } return; } else { if (s.idx[2 * hashcode] == i && s.idx[2 * hashcode + 1] == j) { if ((double)(v) == (double)(0)) { s.idx[2 * hashcode] = -2; } else { s.vals[hashcode] = v; } return; } if (tcode == -1 && s.idx[2 * hashcode] == -2) { tcode = hashcode; } // // next step // hashcode = (hashcode + 1) % k; } } } // // CRS matrix // if (s.matrixtype == 1) { alglib.ap.assert((double)(v) != (double)(0), "SparseSet: CRS format does not allow you to write zero elements"); alglib.ap.assert(s.ridx[i] <= s.ninitialized, "SparseSet: too few initialized elements at some row (you have promised more when called SparceCreateCRS)"); alglib.ap.assert(s.ridx[i + 1] > s.ninitialized, "SparseSet: too many initialized elements at some row (you have promised less when called SparceCreateCRS)"); alglib.ap.assert(s.ninitialized == s.ridx[i] || s.idx[s.ninitialized - 1] < j, "SparseSet: incorrect column order (you must fill every row from left to right)"); s.vals[s.ninitialized] = v; s.idx[s.ninitialized] = j; s.ninitialized = s.ninitialized + 1; // //if matrix has been created then //initiale 'S.UIdx' and 'S.DIdx' // if (s.ninitialized == s.ridx[s.m]) { sparseinitduidx(s); } } }
/************************************************************************* Procedure for initialization 'S.DIdx' and 'S.UIdx' -- ALGLIB PROJECT -- Copyright 14.10.2011 by Bochkanov Sergey *************************************************************************/ private static void sparseinitduidx(sparsematrix s) { int i = 0; int j = 0; int lt = 0; int rt = 0; alglib.ap.assert(s.matrixtype==1, "SparseInitDUIdx: internal error, incorrect matrix type"); apserv.ivectorsetlengthatleast(ref s.didx, s.m); apserv.ivectorsetlengthatleast(ref s.uidx, s.m); for(i=0; i<=s.m-1; i++) { s.uidx[i] = -1; s.didx[i] = -1; lt = s.ridx[i]; rt = s.ridx[i+1]; for(j=lt; j<=rt-1; j++) { if( i<s.idx[j] && s.uidx[i]==-1 ) { s.uidx[i] = j; break; } else { if( i==s.idx[j] ) { s.didx[i] = j; } } } if( s.uidx[i]==-1 ) { s.uidx[i] = s.ridx[i+1]; } if( s.didx[i]==-1 ) { s.didx[i] = s.uidx[i]; } } }
/************************************************************************* This function converts matrix to CRS format. Some algorithms (linear algebra ones, for example) require matrices in CRS format. INPUT PARAMETERS S - sparse M*N matrix. OUTPUT PARAMETERS S - matrix in CRS format NOTE: this function has no effect when called with matrix which is already in CRS mode. -- ALGLIB PROJECT -- Copyright 14.10.2011 by Bochkanov Sergey *************************************************************************/ public static void sparseconverttocrs(sparsematrix s) { int i = 0; double[] tvals = new double[0]; int[] tidx = new int[0]; int[] temp = new int[0]; int nonne = 0; int k = 0; alglib.ap.assert(s.matrixtype == 0 || s.matrixtype == 1, "SparseConvertToCRS: invalid matrix type"); if (s.matrixtype == 1) { return; } s.matrixtype = 1; nonne = 0; k = alglib.ap.len(s.vals); alglib.ap.swap(ref s.vals, ref tvals); alglib.ap.swap(ref s.idx, ref tidx); s.ridx = new int[s.m + 1]; for (i = 0; i <= s.m; i++) { s.ridx[i] = 0; } temp = new int[s.m]; for (i = 0; i <= s.m - 1; i++) { temp[i] = 0; } // // Number of elements per row // for (i = 0; i <= k - 1; i++) { if (tidx[2 * i] >= 0) { s.ridx[tidx[2 * i] + 1] = s.ridx[tidx[2 * i] + 1] + 1; nonne = nonne + 1; } } // // Fill RIdx (offsets of rows) // for (i = 0; i <= s.m - 1; i++) { s.ridx[i + 1] = s.ridx[i + 1] + s.ridx[i]; } // // Allocate memory // s.vals = new double[nonne]; s.idx = new int[nonne]; for (i = 0; i <= k - 1; i++) { if (tidx[2 * i] >= 0) { s.vals[s.ridx[tidx[2 * i]] + temp[tidx[2 * i]]] = tvals[i]; s.idx[s.ridx[tidx[2 * i]] + temp[tidx[2 * i]]] = tidx[2 * i + 1]; temp[tidx[2 * i]] = temp[tidx[2 * i]] + 1; } } // // Set NInitialized // s.ninitialized = s.ridx[s.m]; // //sorting of elements // for (i = 0; i <= s.m - 1; i++) { tsort.tagsortmiddleir(ref s.idx, ref s.vals, s.ridx[i], s.ridx[i + 1] - s.ridx[i]); } // //initialization 'S.UIdx' and 'S.DIdx' // sparseinitduidx(s); }
/************************************************************************* This function is used to enumerate all elements of the sparse matrix. Before first call user initializes T0 and T1 counters by zero. These counters are used to remember current position in a matrix; after each call they are updated by the function. Subsequent calls to this function return non-zero elements of the sparse matrix, one by one. If you enumerate CRS matrix, matrix is traversed from left to right, from top to bottom. In case you enumerate matrix stored as Hash table, elements are returned in random order. EXAMPLE > T0=0 > T1=0 > while SparseEnumerate(S,T0,T1,I,J,V) do > ....do something with I,J,V INPUT PARAMETERS S - sparse M*N matrix in Hash-Table or CRS representation. T0 - internal counter T1 - internal counter OUTPUT PARAMETERS T0 - new value of the internal counter T1 - new value of the internal counter I - row index of non-zero element, 0<=I<M. J - column index of non-zero element, 0<=J<N V - value of the T-th element RESULT True in case of success (next non-zero element was retrieved) False in case all non-zero elements were enumerated -- ALGLIB PROJECT -- Copyright 14.03.2012 by Bochkanov Sergey *************************************************************************/ public static bool sparseenumerate(sparsematrix s, ref int t0, ref int t1, ref int i, ref int j, ref double v) { bool result = new bool(); int counter = 0; int sz = 0; int i0 = 0; i = 0; j = 0; v = 0; if( t0<0 || (s.matrixtype==1 && t1<0) ) { result = false; return result; } // // Hash-table matrix // if( s.matrixtype==0 ) { sz = alglib.ap.len(s.vals); for(i0=t0; i0<=sz-1; i0++) { if( s.idx[2*i0]==-1 || s.idx[2*i0]==-2 ) { continue; } else { i = s.idx[2*i0]; j = s.idx[2*i0+1]; v = s.vals[i0]; t0 = i0+1; result = true; return result; } } t0 = 0; result = false; return result; } // // CRS matrix // if( s.matrixtype==1 && t0<s.ninitialized ) { alglib.ap.assert(s.ninitialized==s.ridx[s.m], "SparseEnumerate: some rows/elements of the CRS matrix were not initialized (you must initialize everything you promised to SparseCreateCRS)"); while( t0>s.ridx[t1+1]-1 && t1<s.m ) { t1 = t1+1; } i = t1; j = s.idx[t0]; v = s.vals[t0]; t0 = t0+1; result = true; return result; } t0 = 0; t1 = 0; result = false; return result; }
/************************************************************************* This function calculates matrix-vector product S^T*x. Matrix S must be stored in CRS format (exception will be thrown otherwise). INPUT PARAMETERS S - sparse M*N matrix in CRS format (you MUST convert it to CRS before calling this function). X - array[M], input vector. For performance reasons we make only quick checks - we check that array size is at least M, but we do not check for NAN's or INF's. Y - output buffer, possibly preallocated. In case buffer size is too small to store result, this buffer is automatically resized. OUTPUT PARAMETERS Y - array[N], S^T*x NOTE: this function throws exception when called for non-CRS matrix. You must convert your matrix with SparseConvertToCRS() before using this function. -- ALGLIB PROJECT -- Copyright 14.10.2011 by Bochkanov Sergey *************************************************************************/ public static void sparsemtv(sparsematrix s, double[] x, ref double[] y) { int i = 0; int j = 0; int lt = 0; int rt = 0; int ct = 0; double v = 0; alglib.ap.assert(s.matrixtype == 1, "SparseMTV: incorrect matrix type (convert your matrix to CRS)"); alglib.ap.assert(s.ninitialized == s.ridx[s.m], "SparseMTV: some rows/elements of the CRS matrix were not initialized (you must initialize everything you promised to SparseCreateCRS)"); alglib.ap.assert(alglib.ap.len(x) >= s.m, "SparseMTV: Length(X)<M"); apserv.rvectorsetlengthatleast(ref y, s.n); for (i = 0; i <= s.n - 1; i++) { y[i] = 0; } for (i = 0; i <= s.m - 1; i++) { lt = s.ridx[i]; rt = s.ridx[i + 1]; v = x[i]; for (j = lt; j <= rt - 1; j++) { ct = s.idx[j]; y[ct] = y[ct] + v * s.vals[j]; } } }
/************************************************************************* This function rewrites existing (non-zero) element. It returns True if element exists or False, when it is called for non-existing (zero) element. The purpose of this function is to provide convenient thread-safe way to modify sparse matrix. Such modification (already existing element is rewritten) is guaranteed to be thread-safe without any synchronization, as long as different threads modify different elements. INPUT PARAMETERS S - sparse M*N matrix in Hash-Table or CRS representation. I - row index of non-zero element to modify, 0<=I<M J - column index of non-zero element to modify, 0<=J<N V - value to rewrite, must be finite number OUTPUT PARAMETERS S - modified matrix -- ALGLIB PROJECT -- Copyright 14.03.2012 by Bochkanov Sergey *************************************************************************/ public static bool sparserewriteexisting(sparsematrix s, int i, int j, double v) { bool result = new bool(); int hashcode = 0; int k = 0; int k0 = 0; int k1 = 0; alglib.ap.assert(0<=i && i<s.m, "SparseRewriteExisting: invalid argument I(either I<0 or I>=S.M)"); alglib.ap.assert(0<=j && j<s.n, "SparseRewriteExisting: invalid argument J(either J<0 or J>=S.N)"); alglib.ap.assert(math.isfinite(v), "SparseRewriteExisting: invalid argument V(either V is infinite or V is NaN)"); result = false; // // Hash-table matrix // if( s.matrixtype==0 ) { k = alglib.ap.len(s.vals); hashcode = hash(i, j, k); while( true ) { if( s.idx[2*hashcode]==-1 ) { return result; } if( s.idx[2*hashcode]==i && s.idx[2*hashcode+1]==j ) { s.vals[hashcode] = v; result = true; return result; } hashcode = (hashcode+1)%k; } } // // CRS matrix // if( s.matrixtype==1 ) { alglib.ap.assert(s.ninitialized==s.ridx[s.m], "SparseRewriteExisting: some rows/elements of the CRS matrix were not initialized (you must initialize everything you promised to SparseCreateCRS)"); k0 = s.ridx[i]; k1 = s.ridx[i+1]-1; for(k=k0; k<=k1; k++) { if( s.idx[k]==j ) { s.vals[k] = v; result = true; return result; } } return result; } return result; }
/************************************************************************* This function calculates matrix-vector product S*x, when S is symmetric matrix. Matrix S must be stored in CRS format (exception will be thrown otherwise). INPUT PARAMETERS S - sparse M*M matrix in CRS format (you MUST convert it to CRS before calling this function). X - array[N], input vector. For performance reasons we make only quick checks - we check that array size is at least N, but we do not check for NAN's or INF's. Y - output buffer, possibly preallocated. In case buffer size is too small to store result, this buffer is automatically resized. OUTPUT PARAMETERS Y - array[M], S*x NOTE: this function throws exception when called for non-CRS matrix. You must convert your matrix with SparseConvertToCRS() before using this function. -- ALGLIB PROJECT -- Copyright 14.10.2011 by Bochkanov Sergey *************************************************************************/ public static void sparsesmv(sparsematrix s, bool isupper, double[] x, ref double[] y) { int i = 0; int j = 0; int id = 0; int lt = 0; int rt = 0; double v = 0; double vy = 0; double vx = 0; alglib.ap.assert(s.matrixtype == 1, "SparseSMV: incorrect matrix type (convert your matrix to CRS)"); alglib.ap.assert(s.ninitialized == s.ridx[s.m], "SparseSMV: some rows/elements of the CRS matrix were not initialized (you must initialize everything you promised to SparseCreateCRS)"); alglib.ap.assert(alglib.ap.len(x) >= s.n, "SparseSMV: length(X)<N"); alglib.ap.assert(s.m == s.n, "SparseSMV: non-square matrix"); apserv.rvectorsetlengthatleast(ref y, s.m); for (i = 0; i <= s.m - 1; i++) { y[i] = 0; } for (i = 0; i <= s.m - 1; i++) { if (s.didx[i] != s.uidx[i]) { y[i] = y[i] + s.vals[s.didx[i]] * x[s.idx[s.didx[i]]]; } if (isupper) { lt = s.uidx[i]; rt = s.ridx[i + 1]; vy = 0; vx = x[i]; for (j = lt; j <= rt - 1; j++) { id = s.idx[j]; v = s.vals[j]; vy = vy + x[id] * v; y[id] = y[id] + vx * v; } y[i] = y[i] + vy; } else { lt = s.ridx[i]; rt = s.didx[i]; vy = 0; vx = x[i]; for (j = lt; j <= rt - 1; j++) { id = s.idx[j]; v = s.vals[j]; vy = vy + x[id] * v; y[id] = y[id] + vx * v; } y[i] = y[i] + vy; } } }
/************************************************************************* This function creates sparse matrix in a Hash-Table format. This function creates Hast-Table matrix, which can be converted to CRS format after its initialization is over. Typical usage scenario for a sparse matrix is: 1. creation in a Hash-Table format 2. insertion of the matrix elements 3. conversion to the CRS representation 4. matrix is passed to some linear algebra algorithm Some information about different matrix formats can be found below, in the "NOTES" section. INPUT PARAMETERS M - number of rows in a matrix, M>=1 N - number of columns in a matrix, N>=1 K - K>=0, expected number of non-zero elements in a matrix. K can be inexact approximation, can be less than actual number of elements (table will grow when needed) or even zero). It is important to understand that although hash-table may grow automatically, it is better to provide good estimate of data size. OUTPUT PARAMETERS S - sparse M*N matrix in Hash-Table representation. All elements of the matrix are zero. NOTE 1. Sparse matrices can be stored using either Hash-Table representation or Compressed Row Storage representation. Hast-table is better suited for querying and dynamic operations (thus, it is used for matrix initialization), but it is inefficient when you want to make some linear algebra operations. From the other side, CRS is better suited for linear algebra operations, but initialization is less convenient - you have to tell row sizes at the initialization, and you can fill matrix only row by row, from left to right. CRS is also very inefficient when you want to find matrix element by its index. Thus, Hash-Table representation does not support linear algebra operations, while CRS format does not support modification of the table. Tables below outline information about these two formats: OPERATIONS WITH MATRIX HASH CRS create + + read element + + modify element + add value to element + A*x (dense vector) + A'*x (dense vector) + A*X (dense matrix) + A'*X (dense matrix) + NOTE 2. Hash-tables use memory inefficiently, and they have to keep some amount of the "spare memory" in order to have good performance. Hash table for matrix with K non-zero elements will need C*K*(8+2*sizeof(int)) bytes, where C is a small constant, about 1.5-2 in magnitude. CRS storage, from the other side, is more memory-efficient, and needs just K*(8+sizeof(int))+M*sizeof(int) bytes, where M is a number of rows in a matrix. When you convert from the Hash-Table to CRS representation, all unneeded memory will be freed. -- ALGLIB PROJECT -- Copyright 14.10.2011 by Bochkanov Sergey *************************************************************************/ public static void sparsecreate(int m, int n, int k, sparsematrix s) { int i = 0; int sz = 0; alglib.ap.assert(m > 0, "SparseCreate: M<=0"); alglib.ap.assert(n > 0, "SparseCreate: N<=0"); alglib.ap.assert(k >= 0, "SparseCreate: K<0"); sz = (int)Math.Round(k / desiredloadfactor + additional); s.matrixtype = 0; s.m = m; s.n = n; s.nfree = sz; s.vals = new double[sz]; s.idx = new int[2 * sz]; for (i = 0; i <= sz - 1; i++) { s.idx[2 * i] = -1; } }
/************************************************************************* This function simultaneously calculates two matrix-matrix products: S*A and S^T*A. S must be square (non-rectangular) matrix stored in CRS format (exception will be thrown otherwise). INPUT PARAMETERS S - sparse N*N matrix in CRS format (you MUST convert it to CRS before calling this function). A - array[N][K], input dense matrix. For performance reasons we make only quick checks - we check that array size is at least N, but we do not check for NAN's or INF's. K - number of columns of matrix (A). B0 - output buffer, possibly preallocated. In case buffer size is too small to store result, this buffer is automatically resized. B1 - output buffer, possibly preallocated. In case buffer size is too small to store result, this buffer is automatically resized. OUTPUT PARAMETERS B0 - array[N][K], S*A B1 - array[N][K], S^T*A NOTE: this function throws exception when called for non-CRS matrix. You must convert your matrix with SparseConvertToCRS() before using this function. It also throws exception when S is non-square. -- ALGLIB PROJECT -- Copyright 14.10.2011 by Bochkanov Sergey *************************************************************************/ public static void sparsemm2(sparsematrix s, double[,] a, int k, ref double[,] b0, ref double[,] b1) { int i = 0; int j = 0; int k0 = 0; int lt = 0; int rt = 0; int ct = 0; double v = 0; double tval = 0; int i_ = 0; alglib.ap.assert(s.matrixtype == 1, "SparseMM2: incorrect matrix type (convert your matrix to CRS)"); alglib.ap.assert(s.ninitialized == s.ridx[s.m], "SparseMM2: some rows/elements of the CRS matrix were not initialized (you must initialize everything you promised to SparseCreateCRS)"); alglib.ap.assert(s.m == s.n, "SparseMM2: matrix is non-square"); alglib.ap.assert(alglib.ap.rows(a) >= s.n, "SparseMM2: Rows(A)<N"); alglib.ap.assert(k > 0, "SparseMM2: K<=0"); apserv.rmatrixsetlengthatleast(ref b0, s.m, k); apserv.rmatrixsetlengthatleast(ref b1, s.n, k); for (i = 0; i <= s.n - 1; i++) { for (j = 0; j <= k - 1; j++) { b1[i, j] = 0; } } if (k < linalgswitch) { for (i = 0; i <= s.m - 1; i++) { for (j = 0; j <= k - 1; j++) { tval = 0; lt = s.ridx[i]; rt = s.ridx[i + 1]; v = a[i, j]; for (k0 = lt; k0 <= rt - 1; k0++) { ct = s.idx[k0]; b1[ct, j] = b1[ct, j] + s.vals[k0] * v; tval = tval + s.vals[k0] * a[ct, j]; } b0[i, j] = tval; } } } else { for (i = 0; i <= s.m - 1; i++) { for (j = 0; j <= k - 1; j++) { b0[i, j] = 0; } } for (i = 0; i <= s.m - 1; i++) { lt = s.ridx[i]; rt = s.ridx[i + 1]; for (j = lt; j <= rt - 1; j++) { v = s.vals[j]; ct = s.idx[j]; for (i_ = 0; i_ <= k - 1; i_++) { b0[i, i_] = b0[i, i_] + v * a[ct, i_]; } for (i_ = 0; i_ <= k - 1; i_++) { b1[ct, i_] = b1[ct, i_] + v * a[i, i_]; } } } } }
/************************************************************************* This function creates sparse matrix in a CRS format (expert function for situations when you are running out of memory). This function creates CRS matrix. Typical usage scenario for a CRS matrix is: 1. creation (you have to tell number of non-zero elements at each row at this moment) 2. insertion of the matrix elements (row by row, from left to right) 3. matrix is passed to some linear algebra algorithm This function is a memory-efficient alternative to SparseCreate(), but it is more complex because it requires you to know in advance how large your matrix is. Some information about different matrix formats can be found below, in the "NOTES" section. INPUT PARAMETERS M - number of rows in a matrix, M>=1 N - number of columns in a matrix, N>=1 NER - number of elements at each row, array[M], NER[i]>=0 OUTPUT PARAMETERS S - sparse M*N matrix in CRS representation. You have to fill ALL non-zero elements by calling SparseSet() BEFORE you try to use this matrix. NOTE 1. Sparse matrices can be stored using either Hash-Table representation or Compressed Row Storage representation. Hast-table is better suited for querying and dynamic operations (thus, it is used for matrix initialization), but it is inefficient when you want to make some linear algebra operations. From the other side, CRS is better suited for linear algebra operations, but initialization is less convenient - you have to tell row sizes at the initialization, and you can fill matrix only row by row, from left to right. CRS is also very inefficient when you want to find matrix element by its index. Thus, Hash-Table representation does not support linear algebra operations, while CRS format does not support modification of the table. Tables below outline information about these two formats: OPERATIONS WITH MATRIX HASH CRS create + + read element + + modify element + add value to element + A*x (dense vector) + A'*x (dense vector) + A*X (dense matrix) + A'*X (dense matrix) + NOTE 2. Hash-tables use memory inefficiently, and they have to keep some amount of the "spare memory" in order to have good performance. Hash table for matrix with K non-zero elements will need C*K*(8+2*sizeof(int)) bytes, where C is a small constant, about 1.5-2 in magnitude. CRS storage, from the other side, is more memory-efficient, and needs just K*(8+sizeof(int))+M*sizeof(int) bytes, where M is a number of rows in a matrix. When you convert from the Hash-Table to CRS representation, all unneeded memory will be freed. -- ALGLIB PROJECT -- Copyright 14.10.2011 by Bochkanov Sergey *************************************************************************/ public static void sparsecreatecrs(int m, int n, int[] ner, sparsematrix s) { int i = 0; int noe = 0; alglib.ap.assert(m > 0, "SparseCreateCRS: M<=0"); alglib.ap.assert(n > 0, "SparseCreateCRS: N<=0"); alglib.ap.assert(alglib.ap.len(ner) >= m, "SparseCreateCRS: Length(NER)<M"); noe = 0; s.matrixtype = 1; s.ninitialized = 0; s.m = m; s.n = n; s.ridx = new int[s.m + 1]; s.ridx[0] = 0; for (i = 0; i <= s.m - 1; i++) { alglib.ap.assert(ner[i] >= 0, "SparseCreateCRS: NER[] contains negative elements"); noe = noe + ner[i]; s.ridx[i + 1] = s.ridx[i] + ner[i]; } s.vals = new double[noe]; s.idx = new int[noe]; if (noe == 0) { sparseinitduidx(s); } }
/************************************************************************* This procedure resizes Hash-Table matrix. It can be called when you have deleted too many elements from the matrix, and you want to free unneeded memory. -- ALGLIB PROJECT -- Copyright 14.10.2011 by Bochkanov Sergey *************************************************************************/ public static void sparseresizematrix(sparsematrix s) { int k = 0; int k1 = 0; int i = 0; double[] tvals = new double[0]; int[] tidx = new int[0]; alglib.ap.assert(s.matrixtype == 0, "SparseResizeMatrix: incorrect matrix type"); // //initialization for length and number of non-null elementd // k = alglib.ap.len(s.vals); k1 = 0; // //calculating number of non-null elements // for (i = 0; i <= k - 1; i++) { if (s.idx[2 * i] >= 0) { k1 = k1 + 1; } } // //initialization value for free space // s.nfree = (int)Math.Round(k1 / desiredloadfactor * growfactor + additional) - k1; tvals = new double[s.nfree + k1]; tidx = new int[2 * (s.nfree + k1)]; alglib.ap.swap(ref s.vals, ref tvals); alglib.ap.swap(ref s.idx, ref tidx); for (i = 0; i <= s.nfree + k1 - 1; i++) { s.idx[2 * i] = -1; } for (i = 0; i <= k - 1; i++) { if (tidx[2 * i] >= 0) { sparseset(s, tidx[2 * i], tidx[2 * i + 1], tvals[i]); } } }
/************************************************************************* This function copies S0 to S1. NOTE: this function does not verify its arguments, it just copies all fields of the structure. -- ALGLIB PROJECT -- Copyright 14.10.2011 by Bochkanov Sergey *************************************************************************/ public static void sparsecopy(sparsematrix s0, sparsematrix s1) { int l = 0; int i = 0; s1.matrixtype = s0.matrixtype; s1.m = s0.m; s1.n = s0.n; s1.nfree = s0.nfree; s1.ninitialized = s0.ninitialized; // //initialization for arrays // l = alglib.ap.len(s0.vals); s1.vals = new double[l]; for (i = 0; i <= l - 1; i++) { s1.vals[i] = s0.vals[i]; } l = alglib.ap.len(s0.ridx); s1.ridx = new int[l]; for (i = 0; i <= l - 1; i++) { s1.ridx[i] = s0.ridx[i]; } l = alglib.ap.len(s0.idx); s1.idx = new int[l]; for (i = 0; i <= l - 1; i++) { s1.idx[i] = s0.idx[i]; } // //initalization for CRS-parameters // l = alglib.ap.len(s0.uidx); s1.uidx = new int[l]; for (i = 0; i <= l - 1; i++) { s1.uidx[i] = s0.uidx[i]; } l = alglib.ap.len(s0.didx); s1.didx = new int[l]; for (i = 0; i <= l - 1; i++) { s1.didx[i] = s0.didx[i]; } }
/************************************************************************* Procedure for initialization 'S.DIdx' and 'S.UIdx' -- ALGLIB PROJECT -- Copyright 14.10.2011 by Bochkanov Sergey *************************************************************************/ private static void sparseinitduidx(sparsematrix s) { int i = 0; int j = 0; int lt = 0; int rt = 0; s.didx = new int[s.m]; s.uidx = new int[s.m]; for (i = 0; i <= s.m - 1; i++) { s.uidx[i] = -1; s.didx[i] = -1; lt = s.ridx[i]; rt = s.ridx[i + 1]; for (j = lt; j <= rt - 1; j++) { if (i < s.idx[j] && s.uidx[i] == -1) { s.uidx[i] = j; break; } else { if (i == s.idx[j]) { s.didx[i] = j; } } } if (s.uidx[i] == -1) { s.uidx[i] = s.ridx[i + 1]; } if (s.didx[i] == -1) { s.didx[i] = s.uidx[i]; } } }
/************************************************************************* This function performs out-of-place conversion to CRS format. S0 is copied to S1 and converted on-the-fly. Memory allocated in S1 is reused to maximum extent possible. INPUT PARAMETERS S0 - sparse matrix in any format. S1 - matrix which may contain some pre-allocated memory, or can be just uninitialized structure. OUTPUT PARAMETERS S1 - sparse matrix in CRS format. NOTE: if S0 is stored as CRS, it is just copied without conversion. -- ALGLIB PROJECT -- Copyright 20.07.2012 by Bochkanov Sergey *************************************************************************/ public static void sparsecopytocrsbuf(sparsematrix s0, sparsematrix s1) { int[] temp = new int[0]; int nonne = 0; int i = 0; int j = 0; int k = 0; int offs0 = 0; int offs1 = 0; int m = 0; alglib.ap.assert((s0.matrixtype==0 || s0.matrixtype==1) || s0.matrixtype==2, "SparseCopyToCRSBuf: invalid matrix type"); m = s0.m; if( s0.matrixtype==0 ) { // // Convert from hash-table to CRS // Done like ConvertToCRS function // s1.matrixtype = 1; s1.m = s0.m; s1.n = s0.n; s1.nfree = s0.nfree; nonne = 0; k = s0.tablesize; apserv.ivectorsetlengthatleast(ref s1.ridx, s1.m+1); for(i=0; i<=s1.m; i++) { s1.ridx[i] = 0; } temp = new int[s1.m]; for(i=0; i<=s1.m-1; i++) { temp[i] = 0; } // // Number of elements per row // for(i=0; i<=k-1; i++) { if( s0.idx[2*i]>=0 ) { s1.ridx[s0.idx[2*i]+1] = s1.ridx[s0.idx[2*i]+1]+1; nonne = nonne+1; } } // // Fill RIdx (offsets of rows) // for(i=0; i<=s1.m-1; i++) { s1.ridx[i+1] = s1.ridx[i+1]+s1.ridx[i]; } // // Allocate memory // apserv.rvectorsetlengthatleast(ref s1.vals, nonne); apserv.ivectorsetlengthatleast(ref s1.idx, nonne); for(i=0; i<=k-1; i++) { if( s0.idx[2*i]>=0 ) { s1.vals[s1.ridx[s0.idx[2*i]]+temp[s0.idx[2*i]]] = s0.vals[i]; s1.idx[s1.ridx[s0.idx[2*i]]+temp[s0.idx[2*i]]] = s0.idx[2*i+1]; temp[s0.idx[2*i]] = temp[s0.idx[2*i]]+1; } } // // Set NInitialized // s1.ninitialized = s1.ridx[s1.m]; // // Sorting of elements // for(i=0; i<=s1.m-1; i++) { tsort.tagsortmiddleir(ref s1.idx, ref s1.vals, s1.ridx[i], s1.ridx[i+1]-s1.ridx[i]); } // // Initialization 'S.UIdx' and 'S.DIdx' // sparseinitduidx(s1); return; } if( s0.matrixtype==1 ) { // // Already CRS, just copy // sparsecopybuf(s0, s1); return; } if( s0.matrixtype==2 ) { alglib.ap.assert(s0.m==s0.n, "SparseCopyToCRS: non-square SKS matrices are not supported"); // // From SKS to CRS. // s1.m = s0.m; s1.n = s0.n; s1.matrixtype = 1; // // Fill RIdx by number of elements per row: // RIdx[I+1] stores number of elements in I-th row. // // Convert RIdx from row sizes to row offsets. // Set NInitialized // apserv.ivectorsetlengthatleast(ref s1.ridx, m+1); s1.ridx[0] = 0; for(i=1; i<=m; i++) { s1.ridx[i] = 1; } nonne = 0; for(i=0; i<=m-1; i++) { s1.ridx[i+1] = s0.didx[i]+s1.ridx[i+1]; for(j=i-s0.uidx[i]; j<=i-1; j++) { s1.ridx[j+1] = s1.ridx[j+1]+1; } nonne = nonne+s0.didx[i]+1+s0.uidx[i]; } for(i=0; i<=m-1; i++) { s1.ridx[i+1] = s1.ridx[i+1]+s1.ridx[i]; } s1.ninitialized = s1.ridx[m]; // // Allocate memory and move elements to Vals/Idx. // Initially, elements are sorted by rows, and are sorted within row too. // No additional post-sorting is required. // temp = new int[m]; for(i=0; i<=m-1; i++) { temp[i] = 0; } apserv.rvectorsetlengthatleast(ref s1.vals, nonne); apserv.ivectorsetlengthatleast(ref s1.idx, nonne); for(i=0; i<=m-1; i++) { // // copy subdiagonal and diagonal parts of I-th block // offs0 = s0.ridx[i]; offs1 = s1.ridx[i]+temp[i]; k = s0.didx[i]+1; for(j=0; j<=k-1; j++) { s1.vals[offs1+j] = s0.vals[offs0+j]; s1.idx[offs1+j] = i-s0.didx[i]+j; } temp[i] = temp[i]+s0.didx[i]+1; // // Copy superdiagonal part of I-th block // offs0 = s0.ridx[i]+s0.didx[i]+1; k = s0.uidx[i]; for(j=0; j<=k-1; j++) { offs1 = s1.ridx[i-k+j]+temp[i-k+j]; s1.vals[offs1] = s0.vals[offs0+j]; s1.idx[offs1] = i; temp[i-k+j] = temp[i-k+j]+1; } } // // Initialization 'S.UIdx' and 'S.DIdx' // sparseinitduidx(s1); return; } alglib.ap.assert(false, "SparseCopyToCRSBuf: unexpected matrix type"); }