/* -*- mode: C -*- */ /* vim:set ts=4 sw=4 sts=4 et */ /* IGraph library. Copyright (C) 2003-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_types.h" #include "igraph_spmatrix.h" #include "igraph_memory.h" #include "igraph_random.h" #include "igraph_error.h" #include "config.h" #include #include /* memcpy & co. */ #include /** * \section igraph_spmatrix_constructor_and_destructor Sparse matrix constructors * and destructors. */ /** * \ingroup matrix * \function igraph_spmatrix_init * \brief Initializes a sparse matrix. * * * Every sparse matrix needs to be initialized before using it, this is done * by calling this function. A matrix has to be destroyed if it is not * needed any more, see \ref igraph_spmatrix_destroy(). * \param m Pointer to a not yet initialized sparse matrix object to be * initialized. * \param nrow The number of rows in the matrix. * \param ncol The number of columns in the matrix. * \return Error code. * * Time complexity: operating system dependent. */ int igraph_spmatrix_init(igraph_spmatrix_t *m, long int nrow, long int ncol) { assert(m != NULL); IGRAPH_VECTOR_INIT_FINALLY(&m->ridx, 0); IGRAPH_VECTOR_INIT_FINALLY(&m->cidx, ncol + 1); IGRAPH_VECTOR_INIT_FINALLY(&m->data, 0); IGRAPH_FINALLY_CLEAN(3); m->nrow = nrow; m->ncol = ncol; return 0; } /** * \ingroup matrix * \function igraph_spmatrix_destroy * \brief Destroys a sparse matrix object. * * * This function frees all the memory allocated for a sparse matrix * object. The destroyed object needs to be reinitialized before using * it again. * \param m The matrix to destroy. * * Time complexity: operating system dependent. */ void igraph_spmatrix_destroy(igraph_spmatrix_t *m) { assert(m != NULL); igraph_vector_destroy(&m->ridx); igraph_vector_destroy(&m->cidx); igraph_vector_destroy(&m->data); } /** * \ingroup matrix * \function igraph_spmatrix_copy * \brief Copies a sparse matrix. * * * Creates a sparse matrix object by copying another one. * \param to Pointer to an uninitialized sparse matrix object. * \param from The initialized sparse matrix object to copy. * \return Error code, \c IGRAPH_ENOMEM if there * isn't enough memory to allocate the new sparse matrix. * * Time complexity: O(n), the number * of elements in the matrix. */ int igraph_spmatrix_copy(igraph_spmatrix_t *to, const igraph_spmatrix_t *from) { assert(from != NULL); assert(to != NULL); to->nrow = from->nrow; to->ncol = from->ncol; IGRAPH_CHECK(igraph_vector_copy(&to->ridx, &from->ridx)); IGRAPH_CHECK(igraph_vector_copy(&to->cidx, &from->cidx)); IGRAPH_CHECK(igraph_vector_copy(&to->data, &from->data)); return 0; } /** * \section igraph_spmatrix_accessing_elements Accessing elements of a sparse matrix */ /** * \ingroup matrix * \function igraph_spmatrix_e * \brief Accessing an element of a sparse matrix. * * Note that there are no range checks right now. * \param m The matrix object. * \param row The index of the row, starting with zero. * \param col The index of the column, starting with zero. * * Time complexity: O(log n), where n is the number of nonzero elements in * the requested column. */ igraph_real_t igraph_spmatrix_e(const igraph_spmatrix_t *m, long int row, long int col) { long int start, end; assert(m != NULL); start = (long) VECTOR(m->cidx)[col]; end = (long) VECTOR(m->cidx)[col + 1] - 1; if (end < start) { return 0; } /* Elements residing in column col are between m->data[start] and * m->data[end], inclusive, ordered by row index */ while (start < end - 1) { long int mid = (start + end) / 2; if (VECTOR(m->ridx)[mid] > row) { end = mid; } else if (VECTOR(m->ridx)[mid] < row) { start = mid; } else { start = mid; break; } } if (VECTOR(m->ridx)[start] == row) { return VECTOR(m->data)[start]; } if (VECTOR(m->ridx)[start] != row && VECTOR(m->ridx)[end] == row) { return VECTOR(m->data)[end]; } return 0; } /** * \ingroup matrix * \function igraph_spmatrix_set * \brief Setting an element of a sparse matrix. * * Note that there are no range checks right now. * \param m The matrix object. * \param row The index of the row, starting with zero. * \param col The index of the column, starting with zero. * \param value The new value. * * Time complexity: O(log n), where n is the number of nonzero elements in * the requested column. */ int igraph_spmatrix_set(igraph_spmatrix_t *m, long int row, long int col, igraph_real_t value) { long int start, end; assert(m != NULL); start = (long) VECTOR(m->cidx)[col]; end = (long) VECTOR(m->cidx)[col + 1] - 1; if (end < start) { /* First element in the column */ if (value == 0.0) { return 0; } IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start, row)); IGRAPH_CHECK(igraph_vector_insert(&m->data, start, value)); for (start = col + 1; start < m->ncol + 1; start++) { VECTOR(m->cidx)[start]++; } return 0; } /* Elements residing in column col are between m->data[start] and * m->data[end], inclusive, ordered by row index */ while (start < end - 1) { long int mid = (start + end) / 2; if (VECTOR(m->ridx)[mid] > row) { end = mid; } else if (VECTOR(m->ridx)[mid] < row) { start = mid; } else { start = mid; break; } } if (VECTOR(m->ridx)[start] == row) { /* Overwriting a value - or deleting it if it has been overwritten by zero */ if (value == 0) { igraph_vector_remove(&m->ridx, start); igraph_vector_remove(&m->data, start); for (start = col + 1; start < m->ncol + 1; start++) { VECTOR(m->cidx)[start]--; } } else { VECTOR(m->data)[start] = value; } return 0; } else if (VECTOR(m->ridx)[end] == row) { /* Overwriting a value - or deleting it if it has been overwritten by zero */ if (value == 0) { igraph_vector_remove(&m->ridx, end); igraph_vector_remove(&m->data, end); for (start = col + 1; start < m->ncol + 1; start++) { VECTOR(m->cidx)[start]--; } } else { VECTOR(m->data)[end] = value; } return 0; } /* New element has to be inserted, but only if not a zero is * being written into the matrix */ if (value != 0.0) { if (VECTOR(m->ridx)[end] < row) { IGRAPH_CHECK(igraph_vector_insert(&m->ridx, end + 1, row)); IGRAPH_CHECK(igraph_vector_insert(&m->data, end + 1, value)); } else if (VECTOR(m->ridx)[start] < row) { IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start + 1, row)); IGRAPH_CHECK(igraph_vector_insert(&m->data, start + 1, value)); } else { IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start, row)); IGRAPH_CHECK(igraph_vector_insert(&m->data, start, value)); } for (start = col + 1; start < m->ncol + 1; start++) { VECTOR(m->cidx)[start]++; } } return 0; } /** * \ingroup matrix * \function igraph_spmatrix_add_e * \brief Adding a real value to an element of a sparse matrix. * * Note that there are no range checks right now. This is implemented to avoid * double lookup of a given element in the matrix by using \ref igraph_spmatrix_e() * and \ref igraph_spmatrix_set() consecutively. * * \param m The matrix object. * \param row The index of the row, starting with zero. * \param col The index of the column, starting with zero. * \param value The value to add. * * Time complexity: O(log n), where n is the number of nonzero elements in * the requested column. */ int igraph_spmatrix_add_e(igraph_spmatrix_t *m, long int row, long int col, igraph_real_t value) { long int start, end; assert(m != NULL); start = (long) VECTOR(m->cidx)[col]; end = (long) VECTOR(m->cidx)[col + 1] - 1; if (end < start) { /* First element in the column */ if (value == 0.0) { return 0; } IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start, row)); IGRAPH_CHECK(igraph_vector_insert(&m->data, start, value)); for (start = col + 1; start < m->ncol + 1; start++) { VECTOR(m->cidx)[start]++; } return 0; } /* Elements residing in column col are between m->data[start] and * m->data[end], inclusive, ordered by row index */ while (start < end - 1) { long int mid = (start + end) / 2; if (VECTOR(m->ridx)[mid] > row) { end = mid; } else if (VECTOR(m->ridx)[mid] < row) { start = mid; } else { start = mid; break; } } if (VECTOR(m->ridx)[start] == row) { /* Overwriting a value */ if (VECTOR(m->data)[start] == -1) { igraph_vector_remove(&m->ridx, start); igraph_vector_remove(&m->data, start); for (start = col + 1; start < m->ncol + 1; start++) { VECTOR(m->cidx)[start]--; } } else { VECTOR(m->data)[start] += value; } return 0; } else if (VECTOR(m->ridx)[end] == row) { /* Overwriting a value */ if (VECTOR(m->data)[end] == -1) { igraph_vector_remove(&m->ridx, end); igraph_vector_remove(&m->data, end); for (start = col + 1; start < m->ncol + 1; start++) { VECTOR(m->cidx)[start]--; } } else { VECTOR(m->data)[end] += value; } return 0; } /* New element has to be inserted, but only if not a zero is * being added to a zero element of the matrix */ if (value != 0.0) { if (VECTOR(m->ridx)[end] < row) { IGRAPH_CHECK(igraph_vector_insert(&m->ridx, end + 1, row)); IGRAPH_CHECK(igraph_vector_insert(&m->data, end + 1, value)); } else if (VECTOR(m->ridx)[start] < row) { IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start + 1, row)); IGRAPH_CHECK(igraph_vector_insert(&m->data, start + 1, value)); } else { IGRAPH_CHECK(igraph_vector_insert(&m->ridx, start, row)); IGRAPH_CHECK(igraph_vector_insert(&m->data, start, value)); } for (start = col + 1; start < m->ncol + 1; start++) { VECTOR(m->cidx)[start]++; } } return 0; } /** * \function igraph_spmatrix_add_col_values * \brief Adds the values of a column to another column. * * \param to The index of the column to be added to * \param from The index of the column to be added * \return Error code. */ int igraph_spmatrix_add_col_values(igraph_spmatrix_t *m, long int to, long int from) { long int i; /* TODO: I think this implementation could be speeded up if I don't use * igraph_spmatrix_add_e directly -- but maybe it's not worth the fuss */ for (i = (long int) VECTOR(m->cidx)[from]; i < VECTOR(m->cidx)[from + 1]; i++) { IGRAPH_CHECK(igraph_spmatrix_add_e(m, (long int) VECTOR(m->ridx)[i], to, VECTOR(m->data)[i])); } return 0; } /** * \ingroup matrix * \function igraph_spmatrix_resize * \brief Resizes a sparse matrix. * * * This function resizes a sparse matrix by adding more elements to it. * The matrix retains its data even after resizing it, except for the data * which lies outside the new boundaries (if the new size is smaller). * \param m Pointer to an already initialized sparse matrix object. * \param nrow The number of rows in the resized matrix. * \param ncol The number of columns in the resized matrix. * \return Error code. * * Time complexity: O(n). * n is the number of elements in the old matrix. */ int igraph_spmatrix_resize(igraph_spmatrix_t *m, long int nrow, long int ncol) { long int i, j, ci, ei, mincol; assert(m != NULL); /* Iterating through the matrix data and deleting unnecessary data. */ /* At the same time, we create the new indices as well */ if (nrow < m->nrow) { ei = j = 0; mincol = (m->ncol < ncol) ? m->ncol : ncol; for (ci = 0; ci < mincol; ci++) { for (; ei < VECTOR(m->cidx)[ci + 1]; ei++) { if (VECTOR(m->ridx)[ei] < nrow) { VECTOR(m->ridx)[j] = VECTOR(m->ridx)[ei]; VECTOR(m->data)[j] = VECTOR(m->data)[ei]; j++; } } VECTOR(m->cidx)[ci] = j; } /* Contract the row index and the data vector */ IGRAPH_CHECK(igraph_vector_resize(&m->ridx, j)); IGRAPH_CHECK(igraph_vector_resize(&m->cidx, j)); } /* Updating cidx */ IGRAPH_CHECK(igraph_vector_resize(&m->cidx, ncol + 1)); for (i = m->ncol + 1; i < ncol + 1; i++) { VECTOR(m->cidx)[i] = VECTOR(m->cidx)[m->ncol]; } m->nrow = nrow; m->ncol = ncol; return 0; } /** * \ingroup matrix * \function igraph_spmatrix_count_nonzero * \brief The number of non-zero elements in a sparse matrix. * * \param m Pointer to an initialized sparse matrix object. * \return The size of the matrix. * * Time complexity: O(1). */ long int igraph_spmatrix_count_nonzero(const igraph_spmatrix_t *m) { assert(m != NULL); return igraph_vector_size(&m->data); } /** * \ingroup matrix * \function igraph_spmatrix_size * \brief The number of elements in a sparse matrix. * * \param m Pointer to an initialized sparse matrix object. * \return The size of the matrix. * * Time complexity: O(1). */ long int igraph_spmatrix_size(const igraph_spmatrix_t *m) { assert(m != NULL); return (m->nrow) * (m->ncol); } /** * \ingroup matrix * \function igraph_spmatrix_nrow * \brief The number of rows in a sparse matrix. * * \param m Pointer to an initialized sparse matrix object. * \return The number of rows in the matrix. * * Time complexity: O(1). */ long int igraph_spmatrix_nrow(const igraph_spmatrix_t *m) { assert(m != NULL); return m->nrow; } /** * \ingroup matrix * \function igraph_spmatrix_ncol * \brief The number of columns in a sparse matrix. * * \param m Pointer to an initialized sparse matrix object. * \return The number of columns in the sparse matrix. * * Time complexity: O(1). */ long int igraph_spmatrix_ncol(const igraph_spmatrix_t *m) { assert(m != NULL); return m->ncol; } /** * \ingroup matrix * \brief Copies a sparse matrix to a regular C array. * * * The matrix is copied columnwise, as this is the format most * programs and languages use. * The C array should be of sufficient size, there are (of course) no * range checks done. * \param m Pointer to an initialized sparse matrix object. * \param to Pointer to a C array, the place to copy the data to. * \return Error code. * * Time complexity: O(n), * n is the number of * elements in the matrix. */ int igraph_spmatrix_copy_to(const igraph_spmatrix_t *m, igraph_real_t *to) { long int c, dest_idx, idx; memset(to, 0, sizeof(igraph_real_t) * (size_t) igraph_spmatrix_size(m)); for (c = 0, dest_idx = 0; c < m->ncol; c++, dest_idx += m->nrow) { for (idx = (long int) VECTOR(m->cidx)[c]; idx < VECTOR(m->cidx)[c + 1]; idx++) { to[dest_idx + (long)VECTOR(m->ridx)[idx]] = VECTOR(m->data)[idx]; } } return 0; } /** * \ingroup matrix * \brief Sets all element in a sparse matrix to zero. * * \param m Pointer to an initialized matrix object. * \return Error code, always returns with success. * * Time complexity: O(n), * n is the number of columns in the matrix */ int igraph_spmatrix_null(igraph_spmatrix_t *m) { assert(m != NULL); igraph_vector_clear(&m->data); igraph_vector_clear(&m->ridx); igraph_vector_null(&m->cidx); return 0; } /** * \ingroup matrix * \function igraph_spmatrix_add_cols * \brief Adds columns to a sparse matrix. * \param m The sparse matrix object. * \param n The number of columns to add. * \return Error code. * * Time complexity: O(1). */ int igraph_spmatrix_add_cols(igraph_spmatrix_t *m, long int n) { igraph_spmatrix_resize(m, m->nrow, m->ncol + n); return 0; } /** * \ingroup matrix * \function igraph_spmatrix_add_rows * \brief Adds rows to a sparse matrix. * \param m The sparse matrix object. * \param n The number of rows to add. * \return Error code. * * Time complexity: O(1). */ int igraph_spmatrix_add_rows(igraph_spmatrix_t *m, long int n) { igraph_spmatrix_resize(m, m->nrow + n, m->ncol); return 0; } /** * \function igraph_spmatrix_clear_row * \brief Clears a row in the matrix (sets all of its elements to zero) * \param m The matrix. * \param row The index of the row to be cleared. * * Time complexity: O(n), the number of nonzero elements in the matrix. */ int igraph_spmatrix_clear_row(igraph_spmatrix_t *m, long int row) { long int ci, ei, i, j, nremove = 0, nremove_old = 0; igraph_vector_t permvec; assert(m != NULL); IGRAPH_VECTOR_INIT_FINALLY(&permvec, igraph_vector_size(&m->data)); for (ci = 0, i = 0, j = 1; ci < m->ncol; ci++) { for (ei = (long int) VECTOR(m->cidx)[ci]; ei < VECTOR(m->cidx)[ci + 1]; ei++) { if (VECTOR(m->ridx)[ei] == row) { /* this element will be deleted, so all elements in cidx from the * column index of this element will have to be decreased by one */ nremove++; } else { /* this element will be kept */ VECTOR(permvec)[i] = j; j++; } i++; } if (ci > 0) { VECTOR(m->cidx)[ci] -= nremove_old; } nremove_old = nremove; } VECTOR(m->cidx)[m->ncol] -= nremove; igraph_vector_permdelete(&m->ridx, &permvec, nremove); igraph_vector_permdelete(&m->data, &permvec, nremove); igraph_vector_destroy(&permvec); IGRAPH_FINALLY_CLEAN(1); return 0; } int igraph_i_spmatrix_clear_row_fast(igraph_spmatrix_t *m, long int row) { long int ei, n; assert(m != NULL); n = igraph_vector_size(&m->data); for (ei = 0; ei < n; ei++) { if (VECTOR(m->ridx)[ei] == row) { VECTOR(m->data)[ei] = 0.0; } } return 0; } int igraph_i_spmatrix_cleanup(igraph_spmatrix_t *m) { long int ci, ei, i, j, nremove = 0, nremove_old = 0; igraph_vector_t permvec; assert(m != NULL); IGRAPH_VECTOR_INIT_FINALLY(&permvec, igraph_vector_size(&m->data)); for (ci = 0, i = 0, j = 1; ci < m->ncol; ci++) { for (ei = (long int) VECTOR(m->cidx)[ci]; ei < VECTOR(m->cidx)[ci + 1]; ei++) { if (VECTOR(m->data)[ei] == 0.0) { /* this element will be deleted, so all elements in cidx from the * column index of this element will have to be decreased by one */ nremove++; } else { /* this element will be kept */ VECTOR(permvec)[i] = j; j++; } i++; } if (ci > 0) { VECTOR(m->cidx)[ci] -= nremove_old; } nremove_old = nremove; } VECTOR(m->cidx)[m->ncol] -= nremove; igraph_vector_permdelete(&m->ridx, &permvec, nremove); igraph_vector_permdelete(&m->data, &permvec, nremove); igraph_vector_destroy(&permvec); IGRAPH_FINALLY_CLEAN(1); return 0; } /** * \function igraph_spmatrix_clear_col * \brief Clears a column in the matrix (sets all of its elements to zero) * \param m The matrix. * \param col The index of the column to be cleared. * \return Error code. The current implementation always succeeds. * * Time complexity: TODO */ int igraph_spmatrix_clear_col(igraph_spmatrix_t *m, long int col) { long int i, n; assert(m != NULL); n = (long)VECTOR(m->cidx)[col + 1] - (long)VECTOR(m->cidx)[col]; if (n == 0) { return 0; } igraph_vector_remove_section(&m->ridx, (long int) VECTOR(m->cidx)[col], (long int) VECTOR(m->cidx)[col + 1]); igraph_vector_remove_section(&m->data, (long int) VECTOR(m->cidx)[col], (long int) VECTOR(m->cidx)[col + 1]); for (i = col + 1; i <= m->ncol; i++) { VECTOR(m->cidx)[i] -= n; } return 0; } /** * \function igraph_spmatrix_scale * \brief Multiplies each element of the sparse matrix by a constant. * \param m The matrix. * \param by The constant. * * Time complexity: O(n), the number of elements in the matrix. */ void igraph_spmatrix_scale(igraph_spmatrix_t *m, igraph_real_t by) { assert(m != NULL); igraph_vector_scale(&m->data, by); } /** * \function igraph_spmatrix_colsums * \brief Calculates the column sums of the matrix. * \param m The matrix. * \param res An initialized \c igraph_vector_t, the result will be stored here. * The vector will be resized as needed. * * Time complexity: O(n), the number of nonzero elements in the matrix. */ int igraph_spmatrix_colsums(const igraph_spmatrix_t *m, igraph_vector_t *res) { long int i, c; assert(m != NULL); IGRAPH_CHECK(igraph_vector_resize(res, m->ncol)); igraph_vector_null(res); for (c = 0; c < m->ncol; c++) { for (i = (long int) VECTOR(m->cidx)[c]; i < VECTOR(m->cidx)[c + 1]; i++) { VECTOR(*res)[c] += VECTOR(m->data)[i]; } } return 0; } /** * \function igraph_spmatrix_rowsums * \brief Calculates the row sums of the matrix. * \param m The matrix. * \param res An initialized \c igraph_vector_t, the result will be stored here. * The vector will be resized as needed. * * Time complexity: O(n), the number of nonzero elements in the matrix. */ int igraph_spmatrix_rowsums(const igraph_spmatrix_t *m, igraph_vector_t *res) { long int i, n; assert(m != NULL); IGRAPH_CHECK(igraph_vector_resize(res, m->nrow)); n = igraph_vector_size(&m->data); igraph_vector_null(res); for (i = 0; i < n; i++) { VECTOR(*res)[(long int)VECTOR(m->ridx)[i]] += VECTOR(m->data)[i]; } return 0; } /** * \function igraph_spmatrix_max_nonzero * \brief Returns the maximum nonzero element of a matrix. * If the matrix is empty, zero is returned. * * \param m the matrix object. * \param ridx the row index of the maximum element if not \c NULL. * \param cidx the column index of the maximum element if not \c NULL. * * Time complexity: O(n), the number of nonzero elements in the matrix. */ igraph_real_t igraph_spmatrix_max_nonzero(const igraph_spmatrix_t *m, igraph_real_t *ridx, igraph_real_t *cidx) { igraph_real_t res; long int i, n, maxidx; assert(m != NULL); n = igraph_vector_size(&m->data); if (n == 0) { return 0.0; } maxidx = -1; for (i = 0; i < n; i++) if (VECTOR(m->data)[i] != 0.0 && (maxidx == -1 || VECTOR(m->data)[i] >= VECTOR(m->data)[maxidx])) { maxidx = i; } if (maxidx == -1) { return 0.0; } res = VECTOR(m->data)[maxidx]; if (ridx != 0) { *ridx = VECTOR(m->ridx)[maxidx]; } if (cidx != 0) { igraph_vector_binsearch(&m->cidx, maxidx, &i); while (VECTOR(m->cidx)[i + 1] == VECTOR(m->cidx)[i]) { i++; } *cidx = (igraph_real_t)i; } return res; } /** * \function igraph_spmatrix_max * \brief Returns the maximum element of a matrix. * If the matrix is empty, zero is returned. * * \param m the matrix object. * \param ridx the row index of the maximum element if not \c NULL. * \param cidx the column index of the maximum element if not \c NULL. * * Time complexity: O(n), the number of nonzero elements in the matrix. */ igraph_real_t igraph_spmatrix_max(const igraph_spmatrix_t *m, igraph_real_t *ridx, igraph_real_t *cidx) { igraph_real_t res; long int i, j, k, maxidx; assert(m != NULL); i = igraph_vector_size(&m->data); if (i == 0) { return 0.0; } maxidx = (long)igraph_vector_which_max(&m->data); res = VECTOR(m->data)[maxidx]; if (res >= 0.0 || i == m->nrow * m->ncol) { if (ridx != 0) { *ridx = VECTOR(m->ridx)[maxidx]; } if (cidx != 0) { igraph_vector_binsearch(&m->cidx, maxidx, &i); i--; while (i < m->ncol - 1 && VECTOR(m->cidx)[i + 1] == VECTOR(m->cidx)[i]) { i++; } *cidx = (igraph_real_t)i; } return res; } /* the maximal nonzero element is negative and there is at least a * single zero */ res = 0.0; if (cidx != 0 || ridx != 0) { for (i = 0; i < m->ncol; i++) { if (VECTOR(m->cidx)[i + 1] - VECTOR(m->cidx)[i] < m->nrow) { if (cidx != 0) { *cidx = i; } if (ridx != 0) { for (j = (long int) VECTOR(m->cidx)[i], k = 0; j < VECTOR(m->cidx)[i + 1]; j++, k++) { if (VECTOR(m->ridx)[j] != k) { *ridx = k; break; } } } break; } } } return res; } int igraph_i_spmatrix_get_col_nonzero_indices(const igraph_spmatrix_t *m, igraph_vector_t *res, long int col) { long int i, n; assert(m != NULL); n = (long int) (VECTOR(m->cidx)[col + 1] - VECTOR(m->cidx)[col]); IGRAPH_CHECK(igraph_vector_resize(res, n)); for (i = (long int) VECTOR(m->cidx)[col], n = 0; i < VECTOR(m->cidx)[col + 1]; i++, n++) if (VECTOR(m->data)[i] != 0.0) { VECTOR(*res)[n] = VECTOR(m->ridx)[i]; } return 0; } /** * \section igraph_spmatrix_iterating Iterating over the non-zero elements of a sparse matrix * * The \type igraph_spmatrix_iter_t type represents an iterator that can * be used to step over the non-zero elements of a sparse matrix in columnwise * order efficiently. In general, you shouldn't modify the elements of the matrix * while iterating over it; doing so will probably invalidate the iterator, but * there are no checks to prevent you from doing this. * * To access the row index of the current element of the iterator, use its * \c ri field. Similarly, the \c ci field stores the column index of the current * element and the \c value field stores the value of the element. */ /** * \function igraph_spmatrix_iter_create * \brief Creates a sparse matrix iterator corresponding to the given matrix. * * \param mit pointer to the matrix iterator being initialized * \param m pointer to the matrix we will be iterating over * \return Error code. The current implementation is always successful. * * Time complexity: O(1). */ int igraph_spmatrix_iter_create(igraph_spmatrix_iter_t *mit, const igraph_spmatrix_t *m) { mit->m = m; IGRAPH_CHECK(igraph_spmatrix_iter_reset(mit)); return 0; } /** * \function igraph_spmatrix_iter_reset * \brief Resets a sparse matrix iterator. * * * After resetting, the iterator will point to the first nonzero element (if any). * * \param mit pointer to the matrix iterator being reset * \return Error code. The current implementation is always successful. * * Time complexity: O(1). */ int igraph_spmatrix_iter_reset(igraph_spmatrix_iter_t *mit) { assert(mit->m); if (igraph_spmatrix_count_nonzero(mit->m) == 0) { mit->pos = mit->ri = mit->ci = -1L; mit->value = -1; return 0; } mit->ci = 0; mit->pos = -1; IGRAPH_CHECK(igraph_spmatrix_iter_next(mit)); return 0; } /** * \function igraph_spmatrix_iter_next * \brief Moves a sparse matrix iterator to the next nonzero element. * * * You should call this function only if \ref igraph_spmatrix_iter_end() * returns FALSE (0). * * \param mit pointer to the matrix iterator being moved * \return Error code. The current implementation is always successful. * * Time complexity: O(1). */ int igraph_spmatrix_iter_next(igraph_spmatrix_iter_t *mit) { mit->pos++; if (igraph_spmatrix_iter_end(mit)) { return 0; } mit->ri = (long int)VECTOR(mit->m->ridx)[mit->pos]; mit->value = VECTOR(mit->m->data)[mit->pos]; while (VECTOR(mit->m->cidx)[mit->ci + 1] <= mit->pos) { mit->ci++; } return 0; } /** * \function igraph_spmatrix_iter_end * \brief Checks whether there are more elements in the iterator. * * * You should call this function before calling \ref igraph_spmatrix_iter_next() * to make sure you have more elements in the iterator. * * \param mit pointer to the matrix iterator being checked * \return TRUE (1) if there are more elements in the iterator, * FALSE (0) otherwise. * * Time complexity: O(1). */ igraph_bool_t igraph_spmatrix_iter_end(igraph_spmatrix_iter_t *mit) { return mit->pos >= igraph_spmatrix_count_nonzero(mit->m); } /** * \function igraph_spmatrix_iter_destroy * \brief Frees the memory used by the iterator. * * * The current implementation does not allocate any memory upon * creation, so this function does nothing. However, since there is * no guarantee that future implementations will not allocate any * memory in \ref igraph_spmatrix_iter_create(), you are still * required to call this function whenever you are done with the * iterator. * * \param mit pointer to the matrix iterator being destroyed * * Time complexity: O(1). */ void igraph_spmatrix_iter_destroy(igraph_spmatrix_iter_t *mit) { IGRAPH_UNUSED(mit); /* Nothing to do at the moment */ } #ifndef USING_R /** * \function igraph_spmatrix_print * \brief Prints a sparse matrix. * * Prints a sparse matrix to the standard output. Only the non-zero entries * are printed. * * \return Error code. * * Time complexity: O(n), the number of non-zero elements. */ int igraph_spmatrix_print(const igraph_spmatrix_t* matrix) { return igraph_spmatrix_fprint(matrix, stdout); } #endif /** * \function igraph_spmatrix_fprint * \brief Prints a sparse matrix to the given file. * * Prints a sparse matrix to the given file. Only the non-zero entries * are printed. * * \return Error code. * * Time complexity: O(n), the number of non-zero elements. */ int igraph_spmatrix_fprint(const igraph_spmatrix_t* matrix, FILE *file) { igraph_spmatrix_iter_t mit; IGRAPH_CHECK(igraph_spmatrix_iter_create(&mit, matrix)); IGRAPH_FINALLY(igraph_spmatrix_iter_destroy, &mit); while (!igraph_spmatrix_iter_end(&mit)) { fprintf(file, "[%ld, %ld] = %.4f\n", (long int)mit.ri, (long int)mit.ci, mit.value); igraph_spmatrix_iter_next(&mit); } igraph_spmatrix_iter_destroy(&mit); IGRAPH_FINALLY_CLEAN(1); return 0; }