Coverage Report

Created: 2026-08-13 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/igraph/src/core/sparsemat.c
Line
Count
Source
1
/*
2
   igraph library.
3
   Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
4
   334 Harvard street, Cambridge, MA 02139 USA
5
6
   This program is free software; you can redistribute it and/or modify
7
   it under the terms of the GNU General Public License as published by
8
   the Free Software Foundation; either version 2 of the License, or
9
   (at your option) any later version.
10
11
   This program is distributed in the hope that it will be useful,
12
   but WITHOUT ANY WARRANTY; without even the implied warranty of
13
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
   GNU General Public License for more details.
15
16
   You should have received a copy of the GNU General Public License
17
   along with this program; if not, write to the Free Software
18
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
   02110-1301 USA
20
21
*/
22
23
#include "igraph_sparsemat.h"
24
25
#include "igraph_memory.h"
26
#include "igraph_types.h"
27
28
#include "internal/hacks.h"    /* IGRAPH_STATIC_ASSERT */
29
30
#include <limits.h>
31
#include <string.h>
32
33
#include <cs/cs.h>
34
#undef cs  /* because otherwise it messes up the name of the 'cs' member in igraph_sparsemat_t */
35
36
/* Returns the number of potential nonzero elements in the given sparse matrix.
37
 * The returned value can be used to iterate over A->cs->x no matter whether the
38
 * matrix is in triplet or column-compressed form */
39
0
static CS_INT igraph_i_sparsemat_count_elements(const igraph_sparsemat_t* A) {
40
0
    return A->cs->nz < 0 ? A->cs->p[A->cs->n] : A->cs->nz;
41
0
}
42
43
/**
44
 * \section about_sparsemat About sparse matrices
45
 *
46
 * <para>
47
 * The <code>igraph_sparsemat_t</code> data type stores sparse matrices,
48
 * i.e. matrices in which the majority of the elements are zero.
49
 * </para>
50
 *
51
 * <para>The data type is essentially a wrapper to some of the
52
 * functions in the CXSparse library, by Tim Davis, see
53
 * http://faculty.cse.tamu.edu/davis/suitesparse.html
54
 * </para>
55
 *
56
 * <para>
57
 * Matrices can be stored in two formats: triplet and
58
 * column-compressed. The triplet format is intended for sparse matrix
59
 * initialization, as it is easy to add new (non-zero) elements to
60
 * it. Most of the computations are done on sparse matrices in
61
 * column-compressed format, after the user has converted the triplet
62
 * matrix to column-compressed, via \ref igraph_sparsemat_compress().
63
 * </para>
64
 *
65
 * <para>
66
 * Both formats are dynamic, in the sense that new elements can be
67
 * added to them, possibly resulting the allocation of more memory.
68
 * </para>
69
 *
70
 * <para>
71
 * Row and column indices follow the C convention and are zero-based.
72
 * </para>
73
 *
74
 * <para>
75
 * \example examples/simple/igraph_sparsemat.c
76
 * \example examples/simple/igraph_sparsemat3.c
77
 * \example examples/simple/igraph_sparsemat4.c
78
 * \example examples/simple/igraph_sparsemat6.c
79
 * \example examples/simple/igraph_sparsemat7.c
80
 * \example examples/simple/igraph_sparsemat8.c
81
 * </para>
82
 */
83
84
/**
85
 * \function igraph_sparsemat_init
86
 * \brief Initializes a sparse matrix, in triplet format.
87
 *
88
 * This is the most common way to create a sparse matrix, together
89
 * with the \ref igraph_sparsemat_entry() function, which can be used to
90
 * add the non-zero elements one by one. Once done, the user can call
91
 * \ref igraph_sparsemat_compress() to convert the matrix to
92
 * column-compressed, to allow computations with it.
93
 *
94
 * </para><para>The user must call \ref igraph_sparsemat_destroy() on
95
 * the matrix to deallocate the memory, once the matrix is no more
96
 * needed.
97
 * \param A Pointer to a not yet initialized sparse matrix.
98
 * \param rows The number of rows in the matrix.
99
 * \param cols The number of columns.
100
 * \param nzmax The maximum number of non-zero elements in the
101
 *    matrix. It is not compulsory to get this right, but it is
102
 *    useful for the allocation of the proper amount of memory.
103
 * \return Error code.
104
 *
105
 * Time complexity: TODO.
106
 */
107
108
igraph_error_t igraph_sparsemat_init(igraph_sparsemat_t *A, igraph_int_t rows,
109
34.4k
        igraph_int_t cols, igraph_int_t nzmax) {
110
34.4k
    IGRAPH_STATIC_ASSERT(sizeof(igraph_int_t) == sizeof(CS_INT));
111
34.4k
    IGRAPH_STATIC_ASSERT(sizeof(igraph_real_t) == sizeof(CS_ENTRY));
112
113
34.4k
    if (rows < 0) {
114
0
        IGRAPH_ERROR("Negative number of rows", IGRAPH_EINVAL);
115
0
    }
116
34.4k
    if (cols < 0) {
117
0
        IGRAPH_ERROR("Negative number of columns", IGRAPH_EINVAL);
118
0
    }
119
120
34.4k
    A->cs = cs_spalloc( rows, cols, nzmax, /*values=*/ 1,
121
34.4k
                        /*triplet=*/ 1);
122
34.4k
    if (!A->cs) {
123
0
        IGRAPH_ERROR("Cannot allocate memory for sparse matrix", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */
124
0
    }
125
126
34.4k
    return IGRAPH_SUCCESS;
127
34.4k
}
128
129
/**
130
 * \function igraph_sparsemat_init_copy
131
 * \brief Copies a sparse matrix.
132
 *
133
 * Create a sparse matrix object, by copying another one. The source
134
 * matrix can be either in triplet or column-compressed format.
135
 *
136
 * </para><para>
137
 * Exactly the same amount of memory will be allocated to the
138
 * copy matrix, as it is currently for the original one.
139
 * \param to Pointer to an uninitialized sparse matrix, the copy will
140
 *    be created here.
141
 * \param from The sparse matrix to copy.
142
 * \return Error code.
143
 *
144
 * Time complexity: O(n+nzmax), the number of columns plus the maximum
145
 * number of non-zero elements.
146
 */
147
148
igraph_error_t igraph_sparsemat_init_copy(
149
    igraph_sparsemat_t *to, const igraph_sparsemat_t *from
150
0
) {
151
152
0
    CS_INT ne = from->cs->nz == -1 ? from->cs->n + 1 : from->cs->nzmax;
153
154
0
    to->cs = cs_spalloc(from->cs->m, from->cs->n, from->cs->nzmax,
155
0
                        /*values=*/ 1,
156
0
                        /*triplet=*/ igraph_sparsemat_is_triplet(from));
157
158
0
    to->cs->nzmax = from->cs->nzmax;
159
0
    to->cs->m     = from->cs->m;
160
0
    to->cs->n     = from->cs->n;
161
0
    to->cs->nz    = from->cs->nz;
162
163
0
    memcpy(to->cs->p, from->cs->p, sizeof(CS_INT) * (size_t) ne);
164
0
    memcpy(to->cs->i, from->cs->i, sizeof(CS_INT) * (size_t) (from->cs->nzmax));
165
0
    memcpy(to->cs->x, from->cs->x, sizeof(CS_ENTRY) * (size_t) (from->cs->nzmax));
166
167
0
    return IGRAPH_SUCCESS;
168
0
}
169
170
/**
171
 * \function igraph_sparsemat_destroy
172
 * \brief Deallocates memory used by a sparse matrix.
173
 *
174
 * One destroyed, the sparse matrix must be initialized again, before
175
 * calling any other operation on it.
176
 * \param A The sparse matrix to destroy.
177
 *
178
 * Time complexity: O(1).
179
 */
180
181
68.9k
void igraph_sparsemat_destroy(igraph_sparsemat_t *A) {
182
68.9k
    cs_spfree(A->cs);
183
68.9k
}
184
185
/**
186
 * \function igraph_sparsemat_realloc
187
 * \brief Allocates more (or less) memory for a sparse matrix.
188
 *
189
 * Sparse matrices automatically allocate more memory, as needed. To
190
 * control memory allocation, the user can call this function, to
191
 * allocate memory for a given number of non-zero elements.
192
 *
193
 * \param A The sparse matrix, it can be in triplet or
194
 *    column-compressed format.
195
 * \param nzmax The new maximum number of non-zero elements.
196
 * \return Error code.
197
 *
198
 * Time complexity: TODO.
199
 */
200
201
20.6k
igraph_error_t igraph_sparsemat_realloc(igraph_sparsemat_t *A, igraph_int_t nzmax) {
202
20.6k
    if (!cs_sprealloc(A->cs, nzmax)) {
203
0
        IGRAPH_ERROR("Could not allocate more memory for sparse matrix.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */
204
0
    }
205
20.6k
    return IGRAPH_SUCCESS;
206
20.6k
}
207
208
/**
209
 * \function igraph_sparsemat_nrow
210
 * \brief Number of rows.
211
 *
212
 * \param A The input matrix, in triplet or column-compressed format.
213
 * \return The number of rows in the \p A matrix.
214
 *
215
 * Time complexity: O(1).
216
 */
217
218
20.6k
igraph_int_t igraph_sparsemat_nrow(const igraph_sparsemat_t *A) {
219
20.6k
    return A->cs->m;
220
20.6k
}
221
222
/**
223
 * \function igraph_sparsemat_ncol
224
 * \brief Number of columns.
225
 *
226
 * \param A The input matrix, in triplet or column-compressed format.
227
 * \return The number of columns in the \p A matrix.
228
 *
229
 * Time complexity: O(1).
230
 */
231
232
20.6k
igraph_int_t igraph_sparsemat_ncol(const igraph_sparsemat_t *A) {
233
20.6k
    return A->cs->n;
234
20.6k
}
235
236
/**
237
 * \function igraph_sparsemat_type
238
 * \brief Type of a sparse matrix (triplet or column-compressed).
239
 *
240
 * Gives whether a sparse matrix is stored in the triplet format or in
241
 * column-compressed format.
242
 * \param A The input matrix.
243
 * \return Either \c IGRAPH_SPARSEMAT_CC or \c
244
 * IGRAPH_SPARSEMAT_TRIPLET.
245
 *
246
 * Time complexity: O(1).
247
 */
248
249
0
igraph_sparsemat_type_t igraph_sparsemat_type(const igraph_sparsemat_t *A) {
250
0
    return igraph_sparsemat_is_cc(A) ? IGRAPH_SPARSEMAT_CC : IGRAPH_SPARSEMAT_TRIPLET;
251
0
}
252
253
/**
254
 * \function igraph_sparsemat_is_triplet
255
 * \brief Is this sparse matrix in triplet format?
256
 *
257
 * Decides whether a sparse matrix is in triplet format.
258
 * \param A The input matrix.
259
 * \return One if the input matrix is in triplet format, zero
260
 * otherwise.
261
 *
262
 * Time complexity: O(1).
263
 */
264
265
2.62M
igraph_bool_t igraph_sparsemat_is_triplet(const igraph_sparsemat_t *A) {
266
2.62M
    return A->cs->nz >= 0;
267
2.62M
}
268
269
/**
270
 * \function igraph_sparsemat_is_cc
271
 * \brief Is this sparse matrix in column-compressed format?
272
 *
273
 * Decides whether a sparse matrix is in column-compressed format.
274
 * \param A The input matrix.
275
 * \return One if the input matrix is in column-compressed format, zero
276
 * otherwise.
277
 *
278
 * Time complexity: O(1).
279
 */
280
281
55.1k
igraph_bool_t igraph_sparsemat_is_cc(const igraph_sparsemat_t *A) {
282
55.1k
    return A->cs->nz < 0;
283
55.1k
}
284
285
/**
286
 * \function igraph_sparsemat_permute
287
 * \brief Permutes the rows and columns of a sparse matrix.
288
 *
289
 * \param A The input matrix, it must be in column-compressed format.
290
 * \param p Integer vector, giving the permutation of the rows.
291
 * \param q Integer vector, the permutation of the columns.
292
 * \param res Pointer to an uninitialized sparse matrix, the result is
293
 *   stored here.
294
 * \return Error code.
295
 *
296
 * Time complexity: O(m+n+nz), the number of rows plus the number of
297
 * columns plus the number of non-zero elements in the matrix.
298
 */
299
300
igraph_error_t igraph_sparsemat_permute(const igraph_sparsemat_t *A,
301
                                        const igraph_vector_int_t *p,
302
                                        const igraph_vector_int_t *q,
303
0
                                        igraph_sparsemat_t *res) {
304
305
0
    CS_INT nrow = A->cs->m, ncol = A->cs->n;
306
0
    CS_INT* pinv;
307
0
    CS_INT i;
308
309
0
    if (nrow != igraph_vector_int_size(p)) {
310
0
        IGRAPH_ERROR("Invalid row permutation length.", IGRAPH_FAILURE);
311
0
    }
312
0
    if (ncol != igraph_vector_int_size(q)) {
313
0
        IGRAPH_ERROR("Invalid column permutation length.", IGRAPH_FAILURE);
314
0
    }
315
316
    /* We invert the permutation by hand */
317
0
    pinv = IGRAPH_CALLOC(nrow, CS_INT);
318
0
    if (pinv == 0) {
319
0
        IGRAPH_ERROR("Cannot allocate index vector for permutation.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */
320
0
    }
321
0
    IGRAPH_FINALLY(igraph_free, pinv);
322
0
    for (i = 0; i < nrow; i++) {
323
0
        pinv[ VECTOR(*p)[i] ] = i;
324
0
    }
325
326
    /* And call the permutation routine */
327
0
    res->cs = cs_permute(A->cs, pinv, (const CS_INT*) VECTOR(*q), /*values=*/ 1);
328
0
    if (!res->cs) {
329
0
        IGRAPH_ERROR("Cannot index sparse matrix", IGRAPH_FAILURE);
330
0
    }
331
332
0
    IGRAPH_FREE(pinv);
333
0
    IGRAPH_FINALLY_CLEAN(1);
334
335
0
    return IGRAPH_SUCCESS;
336
0
}
337
338
static igraph_error_t igraph_i_sparsemat_index_rows(const igraph_sparsemat_t *A,
339
                                         const igraph_vector_int_t *p,
340
                                         igraph_sparsemat_t *res,
341
0
                                         igraph_real_t *constres) {
342
343
0
    igraph_sparsemat_t II, II2;
344
0
    CS_INT nrow = A->cs->m;
345
0
    igraph_int_t idx_rows = igraph_vector_int_size(p);
346
0
    igraph_int_t k;
347
348
    /* Create index matrix */
349
0
    IGRAPH_CHECK(igraph_sparsemat_init(&II2, idx_rows, nrow, idx_rows));
350
0
    IGRAPH_FINALLY(igraph_sparsemat_destroy, &II2);
351
0
    for (k = 0; k < idx_rows; k++) {
352
0
        IGRAPH_CHECK(igraph_sparsemat_entry(&II2, k, VECTOR(*p)[k], 1.0));
353
0
    }
354
0
    IGRAPH_CHECK(igraph_sparsemat_compress(&II2, &II));
355
0
    igraph_sparsemat_destroy(&II2);
356
0
    IGRAPH_FINALLY_CLEAN(1);
357
0
    IGRAPH_FINALLY(igraph_sparsemat_destroy, &II);
358
359
    /* Multiply */
360
0
    IGRAPH_CHECK(igraph_sparsemat_multiply(&II, A, res));
361
0
    igraph_sparsemat_destroy(&II);
362
0
    IGRAPH_FINALLY_CLEAN(1);
363
364
0
    if (constres) {
365
0
        if (res->cs->p[1] != 0) {
366
0
            *constres = res->cs->x[0];
367
0
        } else {
368
0
            *constres = 0.0;
369
0
        }
370
0
    }
371
372
0
    return IGRAPH_SUCCESS;
373
0
}
374
375
static igraph_error_t igraph_i_sparsemat_index_cols(const igraph_sparsemat_t *A,
376
                                         const igraph_vector_int_t *q,
377
                                         igraph_sparsemat_t *res,
378
0
                                         igraph_real_t *constres) {
379
380
0
    igraph_sparsemat_t JJ, JJ2;
381
0
    CS_INT ncol = A->cs->n;
382
0
    igraph_int_t idx_cols = igraph_vector_int_size(q);
383
0
    igraph_int_t k;
384
385
    /* Create index matrix */
386
0
    IGRAPH_CHECK(igraph_sparsemat_init(&JJ2, ncol, idx_cols, idx_cols));
387
0
    IGRAPH_FINALLY(igraph_sparsemat_destroy, &JJ2);
388
0
    for (k = 0; k < idx_cols; k++) {
389
0
        IGRAPH_CHECK(igraph_sparsemat_entry(&JJ2, VECTOR(*q)[k], k, 1.0));
390
0
    }
391
0
    IGRAPH_CHECK(igraph_sparsemat_compress(&JJ2, &JJ));
392
0
    igraph_sparsemat_destroy(&JJ2);
393
0
    IGRAPH_FINALLY_CLEAN(1);
394
0
    IGRAPH_FINALLY(igraph_sparsemat_destroy, &JJ);
395
396
    /* Multiply */
397
0
    IGRAPH_CHECK(igraph_sparsemat_multiply(A, &JJ, res));
398
0
    igraph_sparsemat_destroy(&JJ);
399
0
    IGRAPH_FINALLY_CLEAN(1);
400
401
0
    if (constres) {
402
0
        if (res->cs->p [1] != 0) {
403
0
            *constres = res->cs->x [0];
404
0
        } else {
405
0
            *constres = 0.0;
406
0
        }
407
0
    }
408
409
0
    return IGRAPH_SUCCESS;
410
0
}
411
412
/**
413
 * \function igraph_sparsemat_index
414
 * \brief Extracts a submatrix or a single element.
415
 *
416
 * This function indexes into a sparse matrix.
417
 * It serves two purposes. First, it can extract
418
 * submatrices from a sparse matrix. Second, as a special case, it can
419
 * extract a single element from a sparse matrix.
420
 *
421
 * \param A The input matrix, it must be in column-compressed format.
422
 * \param p An integer vector, or a null pointer. The selected row
423
 *    index or indices. A null pointer selects all rows.
424
 * \param q An integer vector, or a null pointer. The selected column
425
 *    index or indices. A null pointer selects all columns.
426
 * \param res Pointer to an uninitialized sparse matrix, or a null
427
 *    pointer. If not a null pointer, then the selected submatrix is
428
 *    stored here.
429
 * \param constres Pointer to a real variable or a null pointer. If
430
 *    not a null pointer, then the first non-zero element in the
431
 *    selected submatrix is stored here, if there is one. Otherwise
432
 *    zero is stored here. This behavior is handy if one
433
 *    wants to select a single entry from the matrix.
434
 * \return Error code.
435
 *
436
 * Time complexity: TODO.
437
 */
438
439
igraph_error_t igraph_sparsemat_index(const igraph_sparsemat_t *A,
440
                           const igraph_vector_int_t *p,
441
                           const igraph_vector_int_t *q,
442
                           igraph_sparsemat_t *res,
443
0
                           igraph_real_t *constres) {
444
445
0
    igraph_sparsemat_t II, JJ, II2, JJ2, tmp;
446
0
    CS_INT nrow = A->cs->m;
447
0
    CS_INT ncol = A->cs->n;
448
0
    igraph_int_t idx_rows = p ? igraph_vector_int_size(p) : -1;
449
0
    igraph_int_t idx_cols = q ? igraph_vector_int_size(q) : -1;
450
0
    igraph_int_t k;
451
452
0
    igraph_sparsemat_t *myres = res, mres;
453
454
0
    if (!p && !q) {
455
0
        IGRAPH_ERROR("No index vectors", IGRAPH_EINVAL);
456
0
    }
457
458
0
    if (!res && (idx_rows != 1 || idx_cols != 1)) {
459
0
        IGRAPH_ERROR("Sparse matrix indexing: must give `res' if not a "
460
0
                     "single element is selected", IGRAPH_EINVAL);
461
0
    }
462
463
0
    if (!q) {
464
0
        return igraph_i_sparsemat_index_rows(A, p, res, constres);
465
0
    }
466
0
    if (!p) {
467
0
        return igraph_i_sparsemat_index_cols(A, q, res, constres);
468
0
    }
469
470
0
    if (!res) {
471
0
        myres = &mres;
472
0
    }
473
474
    /* Create first index matrix */
475
0
    IGRAPH_CHECK(igraph_sparsemat_init(&II2, idx_rows, nrow, idx_rows));
476
0
    IGRAPH_FINALLY(igraph_sparsemat_destroy, &II2);
477
0
    for (k = 0; k < idx_rows; k++) {
478
0
        IGRAPH_CHECK(igraph_sparsemat_entry(&II2, k, VECTOR(*p)[k], 1.0));
479
0
    }
480
0
    IGRAPH_CHECK(igraph_sparsemat_compress(&II2, &II));
481
0
    igraph_sparsemat_destroy(&II2);
482
0
    IGRAPH_FINALLY_CLEAN(1);
483
0
    IGRAPH_FINALLY(igraph_sparsemat_destroy, &II);
484
485
    /* Create second index matrix */
486
0
    IGRAPH_CHECK(igraph_sparsemat_init(&JJ2, ncol, idx_cols, idx_cols));
487
0
    IGRAPH_FINALLY(igraph_sparsemat_destroy, &JJ2);
488
0
    for (k = 0; k < idx_cols; k++) {
489
0
        IGRAPH_CHECK(igraph_sparsemat_entry(&JJ2, VECTOR(*q)[k], k, 1.0));
490
0
    }
491
0
    IGRAPH_CHECK(igraph_sparsemat_compress(&JJ2, &JJ));
492
0
    igraph_sparsemat_destroy(&JJ2);
493
0
    IGRAPH_FINALLY_CLEAN(1);
494
0
    IGRAPH_FINALLY(igraph_sparsemat_destroy, &JJ);
495
496
    /* Multiply */
497
0
    IGRAPH_CHECK(igraph_sparsemat_multiply(&II, A, &tmp));
498
0
    igraph_sparsemat_destroy(&II);
499
0
    IGRAPH_FINALLY_CLEAN(1);
500
0
    IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp);
501
0
    IGRAPH_CHECK(igraph_sparsemat_multiply(&tmp, &JJ, myres));
502
0
    igraph_sparsemat_destroy(&tmp);
503
0
    igraph_sparsemat_destroy(&JJ);
504
0
    IGRAPH_FINALLY_CLEAN(2);
505
506
0
    if (constres) {
507
0
        if (myres->cs->p [1] != 0) {
508
0
            *constres = myres->cs->x [0];
509
0
        } else {
510
0
            *constres = 0.0;
511
0
        }
512
0
    }
513
514
0
    if (!res) {
515
0
        igraph_sparsemat_destroy(myres);
516
0
    }
517
518
0
    return IGRAPH_SUCCESS;
519
0
}
520
521
/**
522
 * \function igraph_sparsemat_entry
523
 * \brief Adds an element to a sparse matrix.
524
 *
525
 * This function can be used to add the entries to a sparse matrix,
526
 * after initializing it with \ref igraph_sparsemat_init(). If you add
527
 * multiple entries in the same position, they will all be saved, and
528
 * the resulting value is the sum of all entries in that position.
529
 *
530
 * \param A The input matrix, it must be in triplet format.
531
 * \param row The row index of the entry to add.
532
 * \param col The column index of the entry to add.
533
 * \param elem The value of the entry.
534
 * \return Error code.
535
 *
536
 * Time complexity: O(1) on average.
537
 */
538
539
igraph_error_t igraph_sparsemat_entry(igraph_sparsemat_t *A,
540
1.58M
        igraph_int_t row, igraph_int_t col, igraph_real_t elem) {
541
1.58M
    if (!igraph_sparsemat_is_triplet(A)) {
542
0
        IGRAPH_ERROR("Entries can only be added to sparse matrices that are in triplet format.",
543
0
                     IGRAPH_EINVAL);
544
0
    }
545
546
1.58M
    if (!cs_entry(A->cs, row, col, elem)) {
547
0
        IGRAPH_ERROR("Cannot add entry to sparse matrix.",
548
0
                     IGRAPH_FAILURE);
549
0
    }
550
551
1.58M
    return IGRAPH_SUCCESS;
552
1.58M
}
553
554
/**
555
 * \function igraph_sparsemat_compress
556
 * \brief Converts a sparse matrix to column-compressed format.
557
 *
558
 * Converts a sparse matrix from triplet format to column-compressed format.
559
 * Almost all sparse matrix operations require that the matrix is in
560
 * column-compressed format.
561
 *
562
 * \param A The input matrix, it must be in triplet format.
563
 * \param res Pointer to an uninitialized sparse matrix object, the
564
 *    compressed version of \p A is stored here.
565
 * \return Error code.
566
 *
567
 * Time complexity: O(nz) where \c nz is the number of non-zero elements.
568
 */
569
570
igraph_error_t igraph_sparsemat_compress(const igraph_sparsemat_t *A,
571
34.4k
                              igraph_sparsemat_t *res) {
572
573
34.4k
    if (! igraph_sparsemat_is_triplet(A)) {
574
0
        IGRAPH_ERROR("Sparse matrix to compress is not in triplet format.", IGRAPH_EINVAL);
575
0
    }
576
34.4k
    res->cs = cs_compress(A->cs);
577
34.4k
    if (!res->cs) {
578
0
        IGRAPH_ERROR("Cannot compress sparse matrix", IGRAPH_FAILURE);
579
0
    }
580
581
34.4k
    return IGRAPH_SUCCESS;
582
34.4k
}
583
584
static igraph_real_t igraph_i_sparsemat_get_cc(
585
    const igraph_sparsemat_t *A, igraph_int_t row, igraph_int_t col
586
0
) {
587
    /* elements in column 'col' are at indices
588
     * A->cs->p[col] .. A->cs->p[col+1] (open from right) in
589
     * A->cs->x .
590
     *
591
     * Their corresponding row indices are in A->cs->i .
592
     */
593
594
0
    CS_INT lo = A->cs->p[col];
595
0
    CS_INT hi = A->cs->p[col + 1];
596
0
    igraph_real_t result = 0.0;
597
598
    /* TODO: this could be faster with binary search if A->cs->i
599
     * is sorted, which I think should be */
600
0
    for (; lo < hi; lo++) {
601
0
        if (A->cs->i[lo] == row) {
602
0
            result += A->cs->x[lo];
603
0
        }
604
0
    }
605
606
0
    return result;
607
0
}
608
609
static igraph_real_t igraph_i_sparsemat_get_triplet(
610
    const igraph_sparsemat_t *A, igraph_int_t row, igraph_int_t col
611
0
) {
612
0
    igraph_sparsemat_iterator_t it;
613
0
    igraph_real_t result = 0.0;
614
615
0
    igraph_sparsemat_iterator_init(&it, A);
616
0
    while (!igraph_sparsemat_iterator_end(&it)) {
617
0
        if (
618
0
            igraph_sparsemat_iterator_row(&it) == row &&
619
0
            igraph_sparsemat_iterator_col(&it) == col
620
0
        ) {
621
0
            result += igraph_sparsemat_iterator_get(&it);
622
0
        }
623
0
        igraph_sparsemat_iterator_next(&it);
624
0
    }
625
626
0
    return result;
627
0
}
628
629
/**
630
 * \function igraph_sparsemat_get
631
 * \brief Return the value of a single element from a sparse matrix.
632
 *
633
 * \param A The input matrix, in triplet or column-compressed format.
634
 * \param row The row index
635
 * \param col The column index
636
 * \return The value of the cell with the given row and column indices in the
637
 *         matrix; zero if the indices are out of bounds.
638
 *
639
 * Time complexity: TODO.
640
 */
641
igraph_real_t igraph_sparsemat_get(
642
    const igraph_sparsemat_t *A, igraph_int_t row, igraph_int_t col
643
0
) {
644
0
    if (row < 0 || col < 0 || row >= A->cs->m || col >= A->cs->n) {
645
0
        return 0.0;
646
0
    } else if (igraph_sparsemat_is_cc(A)) {
647
0
        return igraph_i_sparsemat_get_cc(A, row, col);
648
0
    } else {
649
0
        return igraph_i_sparsemat_get_triplet(A, row, col);
650
0
    }
651
0
}
652
653
/**
654
 * \function igraph_sparsemat_transpose
655
 * \brief Transposes a sparse matrix.
656
 *
657
 * \param A The input matrix, column-compressed or triple format.
658
 * \param res Pointer to an uninitialized sparse matrix, the result is
659
 *    stored here.
660
 * \return Error code.
661
 *
662
 * Time complexity: TODO.
663
 */
664
665
igraph_error_t igraph_sparsemat_transpose(
666
    const igraph_sparsemat_t *A, igraph_sparsemat_t *res
667
0
) {
668
669
0
    if (igraph_sparsemat_is_cc(A)) {
670
        /* column-compressed */
671
0
        res->cs = cs_transpose(A->cs, /* values = */ 1);
672
0
        if (!res->cs) {
673
0
            IGRAPH_ERROR("Cannot transpose sparse matrix", IGRAPH_FAILURE);
674
0
        }
675
0
    } else {
676
        /* triplets */
677
0
        CS_INT *tmp;
678
0
        IGRAPH_CHECK(igraph_sparsemat_init_copy(res, A));
679
0
        tmp = res->cs->p;
680
0
        res->cs->p = res->cs->i;
681
0
        res->cs->i = tmp;
682
0
    }
683
0
    return IGRAPH_SUCCESS;
684
0
}
685
686
0
static igraph_error_t igraph_i_sparsemat_is_symmetric_cc(const igraph_sparsemat_t *A, igraph_bool_t *result) {
687
0
    igraph_sparsemat_t t, tt;
688
0
    igraph_bool_t res;
689
0
    igraph_int_t nz;
690
691
0
    IGRAPH_CHECK(igraph_sparsemat_transpose(A, &t));
692
0
    IGRAPH_FINALLY(igraph_sparsemat_destroy, &t);
693
0
    IGRAPH_CHECK(igraph_sparsemat_dupl(&t));
694
0
    IGRAPH_CHECK(igraph_sparsemat_transpose(&t, &tt));
695
0
    igraph_sparsemat_destroy(&t);
696
0
    IGRAPH_FINALLY_CLEAN(1);
697
0
    IGRAPH_FINALLY(igraph_sparsemat_destroy, &tt);
698
0
    IGRAPH_CHECK(igraph_sparsemat_transpose(&tt, &t));
699
0
    IGRAPH_FINALLY(igraph_sparsemat_destroy, &t);
700
701
0
    nz = t.cs->p[t.cs->n];
702
0
    res = memcmp(t.cs->i, tt.cs->i, sizeof(CS_INT) * (size_t) nz) == 0;
703
0
    res = res && memcmp(t.cs->p, tt.cs->p, sizeof(CS_INT) *
704
0
                        (size_t)(t.cs->n + 1)) == 0;
705
0
    res = res && memcmp(t.cs->x, tt.cs->x, sizeof(CS_ENTRY) * (size_t)nz) == 0;
706
707
0
    igraph_sparsemat_destroy(&t);
708
0
    igraph_sparsemat_destroy(&tt);
709
0
    IGRAPH_FINALLY_CLEAN(2);
710
711
0
    *result = res;
712
713
0
    return IGRAPH_SUCCESS;
714
0
}
715
716
0
static igraph_error_t igraph_i_sparsemat_is_symmetric_triplet(const igraph_sparsemat_t *A, igraph_bool_t *result) {
717
0
    igraph_sparsemat_t tmp;
718
719
0
    IGRAPH_CHECK(igraph_sparsemat_compress(A, &tmp));
720
0
    IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp);
721
0
    IGRAPH_CHECK(igraph_i_sparsemat_is_symmetric_cc(&tmp, result));
722
0
    igraph_sparsemat_destroy(&tmp);
723
0
    IGRAPH_FINALLY_CLEAN(1);
724
725
0
    return IGRAPH_SUCCESS;
726
0
}
727
728
/**
729
 * \function igraph_sparsemat_is_symmetric
730
 * \brief Returns whether a sparse matrix is symmetric.
731
 *
732
 * \param A The input matrix
733
 * \param result Pointer to an \c igraph_bool_t ; the result is provided here.
734
 * \return Error code.
735
 */
736
737
0
igraph_error_t igraph_sparsemat_is_symmetric(const igraph_sparsemat_t *A, igraph_bool_t *result) {
738
0
    if (A->cs->m != A->cs->n) {
739
0
        *result = false;
740
0
    } else if (igraph_sparsemat_is_cc(A)) {
741
0
        IGRAPH_CHECK(igraph_i_sparsemat_is_symmetric_cc(A, result));
742
0
    } else {
743
0
        IGRAPH_CHECK(igraph_i_sparsemat_is_symmetric_triplet(A, result));
744
0
    }
745
0
    return IGRAPH_SUCCESS;
746
0
}
747
748
/**
749
 * \function igraph_sparsemat_dupl
750
 * \brief Removes duplicate elements from a sparse matrix.
751
 *
752
 * It is possible that a column-compressed sparse matrix stores a
753
 * single matrix entry in multiple pieces. The entry is then the sum
754
 * of all its pieces. (Some functions create matrices like this.) This
755
 * function eliminates the multiple pieces.
756
 *
757
 * \param A The input matrix, in column-compressed format.
758
 * \return Error code.
759
 *
760
 * Time complexity: TODO.
761
 */
762
763
34.4k
igraph_error_t igraph_sparsemat_dupl(igraph_sparsemat_t *A) {
764
765
34.4k
    if (! igraph_sparsemat_is_cc(A)) {
766
0
        IGRAPH_ERROR("Sparse matrix must be in compressed format in order to remove duplicates.", IGRAPH_EINVAL);
767
0
    }
768
769
34.4k
    if (!cs_dupl(A->cs)) {
770
0
        IGRAPH_ERROR("Cannot remove duplicates from sparse matrix.", IGRAPH_FAILURE);
771
0
    }
772
773
34.4k
    return IGRAPH_SUCCESS;
774
34.4k
}
775
776
struct fkeep_wrapper_data {
777
    igraph_int_t (*fkeep) (igraph_int_t, igraph_int_t, igraph_real_t, void*);
778
    void* data;
779
};
780
781
0
static CS_INT fkeep_wrapper(CS_INT row, CS_INT col, double value, void* data) {
782
0
    return ((struct fkeep_wrapper_data*)data)->fkeep(
783
0
        row, col, value, ((struct fkeep_wrapper_data*)data)->data
784
0
    );
785
0
}
786
787
/**
788
 * \function igraph_sparsemat_fkeep
789
 * \brief Filters the elements of a sparse matrix.
790
 *
791
 * This function can be used to filter the (non-zero) elements of a
792
 * sparse matrix. For all entries, it calls the supplied function and
793
 * depending on the return values either keeps, or deleted the element
794
 * from the matrix.
795
 *
796
 * \param A The input matrix, in column-compressed format.
797
 * \param fkeep The filter function. It must take four arguments: the
798
 *    first is an \c igraph_int_t, the row index of the entry, the second is
799
 *    another \c igraph_int_t, the column index. The third is \c igraph_real_t,
800
 *    the value of the entry. The fourth element is a \c void pointer,
801
 *    the \p other argument is passed here. The function must return
802
 *    an \c int. If this is zero, then the entry is deleted, otherwise
803
 *    it is kept.
804
 * \param other A \c void pointer that is passed to the filtering
805
 * function.
806
 * \return Error code.
807
 *
808
 * Time complexity: TODO.
809
 */
810
811
igraph_error_t igraph_sparsemat_fkeep(
812
    igraph_sparsemat_t *A,
813
    igraph_int_t (*fkeep)(igraph_int_t, igraph_int_t, igraph_real_t, void*),
814
    void *other
815
0
) {
816
0
    struct fkeep_wrapper_data wrapper_data = {
817
0
        /* .fkeep = */ fkeep,
818
0
        /* .data = */ other
819
0
    };
820
821
0
    IGRAPH_ASSERT(A);
822
0
    IGRAPH_ASSERT(fkeep);
823
0
    if (!igraph_sparsemat_is_cc(A)) {
824
0
        IGRAPH_ERROR("The sparse matrix is not in compressed format.", IGRAPH_EINVAL);
825
0
    }
826
0
    if (cs_fkeep(A->cs, fkeep_wrapper, &wrapper_data) < 0) {
827
0
        IGRAPH_ERROR("External function cs_keep has returned an unknown error while filtering the matrix.", IGRAPH_FAILURE);
828
0
    }
829
830
0
    return IGRAPH_SUCCESS;
831
0
}
832
833
/**
834
 * \function igraph_sparsemat_dropzeros
835
 * \brief Drops the zero elements from a sparse matrix.
836
 *
837
 * As a result of matrix operations, some of the entries in a sparse
838
 * matrix might be zero. This function removes these entries.
839
 *
840
 * \param A The input matrix, it must be in column-compressed format.
841
 * \return Error code.
842
 *
843
 * Time complexity: TODO.
844
 */
845
846
0
igraph_error_t igraph_sparsemat_dropzeros(igraph_sparsemat_t *A) {
847
848
0
    if (!cs_dropzeros(A->cs)) {
849
0
        IGRAPH_ERROR("Cannot drop zeros from sparse matrix", IGRAPH_FAILURE);
850
0
    }
851
852
0
    return IGRAPH_SUCCESS;
853
0
}
854
855
/**
856
 * \function igraph_sparsemat_droptol
857
 * \brief Drops the almost zero elements from a sparse matrix.
858
 *
859
 * This function is similar to \ref igraph_sparsemat_dropzeros(), but it
860
 * also drops entries that are closer to zero than the given tolerance
861
 * threshold.
862
 *
863
 * \param A The input matrix, it must be in column-compressed format.
864
 * \param tol Real number, giving the tolerance threshold.
865
 * \return Error code.
866
 *
867
 * Time complexity: TODO.
868
 */
869
870
0
igraph_error_t igraph_sparsemat_droptol(igraph_sparsemat_t *A, igraph_real_t tol) {
871
872
0
    IGRAPH_ASSERT(A);
873
0
    if (!igraph_sparsemat_is_cc(A)) {
874
0
        IGRAPH_ERROR("The sparse matrix is not in compressed format.", IGRAPH_EINVAL);
875
0
    }
876
0
    if (cs_droptol(A->cs, tol) < 0) {
877
0
        IGRAPH_ERROR("External function cs_droptol has returned an unknown error.", IGRAPH_FAILURE);
878
0
    }
879
880
0
    return IGRAPH_SUCCESS;
881
0
}
882
883
/**
884
 * \function igraph_sparsemat_multiply
885
 * \brief Matrix multiplication.
886
 *
887
 * Multiplies two sparse matrices.
888
 *
889
 * \param A The first input matrix (left hand side), in
890
 *   column-compressed format.
891
 * \param B The second input matrix (right hand side), in
892
 *   column-compressed format.
893
 * \param res Pointer to an uninitialized sparse matrix, the result is
894
 *   stored here.
895
 * \return Error code.
896
 *
897
 * Time complexity: TODO.
898
 */
899
900
igraph_error_t igraph_sparsemat_multiply(const igraph_sparsemat_t *A,
901
                              const igraph_sparsemat_t *B,
902
0
                              igraph_sparsemat_t *res) {
903
904
0
    res->cs = cs_multiply(A->cs, B->cs);
905
0
    if (!res->cs) {
906
0
        IGRAPH_ERROR("Cannot multiply matrices", IGRAPH_FAILURE);
907
0
    }
908
909
0
    return IGRAPH_SUCCESS;
910
0
}
911
912
/**
913
 * \function igraph_sparsemat_add
914
 * \brief Sum of two sparse matrices.
915
 *
916
 * \param A The first input matrix, in column-compressed format.
917
 * \param B The second input matrix, in column-compressed format.
918
 * \param alpha Real value, \p A is multiplied by \p alpha before the
919
 *    addition.
920
 * \param beta Real value, \p B is multiplied by \p beta before the
921
 *    addition.
922
 * \param res Pointer to an uninitialized sparse matrix, the result
923
 *    is stored here.
924
 * \return Error code.
925
 *
926
 * Time complexity: TODO.
927
 */
928
929
igraph_error_t igraph_sparsemat_add(const igraph_sparsemat_t *A,
930
                         const igraph_sparsemat_t *B,
931
                         igraph_real_t alpha,
932
                         igraph_real_t beta,
933
0
                         igraph_sparsemat_t *res) {
934
935
0
    res->cs = cs_add(A->cs, B->cs, alpha, beta);
936
0
    if (!res->cs) {
937
0
        IGRAPH_ERROR("Cannot add matrices", IGRAPH_FAILURE);
938
0
    }
939
940
0
    return IGRAPH_SUCCESS;
941
0
}
942
943
/**
944
 * \function igraph_sparsemat_gaxpy
945
 * \brief Matrix-vector product, added to another vector.
946
 *
947
 * \param A The input matrix, in column-compressed format.
948
 * \param x The input vector, its size must match the number of
949
 *    columns in \p A.
950
 * \param res This vector is added to the matrix-vector product
951
 *    and it is overwritten by the result.
952
 * \return Error code.
953
 *
954
 * Time complexity: TODO.
955
 */
956
957
igraph_error_t igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A,
958
                           const igraph_vector_t *x,
959
0
                           igraph_vector_t *res) {
960
961
0
    if (A->cs->n != igraph_vector_size(x) ||
962
0
        A->cs->m != igraph_vector_size(res)) {
963
0
        IGRAPH_ERROR("Invalid matrix/vector size for multiplication",
964
0
                     IGRAPH_EINVAL);
965
0
    }
966
967
0
    if (! (cs_gaxpy(A->cs, VECTOR(*x), VECTOR(*res)))) {
968
0
        IGRAPH_ERROR("Cannot perform sparse matrix vector multiplication",
969
0
                     IGRAPH_FAILURE);
970
0
    }
971
972
0
    return IGRAPH_SUCCESS;
973
0
}
974
975
/**
976
 * \function igraph_sparsemat_lsolve
977
 * \brief Solves a lower-triangular linear system.
978
 *
979
 * Solve the Lx=b linear equation system, where the L coefficient
980
 * matrix is square and lower-triangular, with a zero-free diagonal.
981
 *
982
 * \param L The input matrix, in column-compressed format.
983
 * \param b The right hand side of the linear system.
984
 * \param res An initialized vector, the result is stored here.
985
 * \return Error code.
986
 *
987
 * Time complexity: TODO.
988
 */
989
990
igraph_error_t igraph_sparsemat_lsolve(const igraph_sparsemat_t *L,
991
                            const igraph_vector_t *b,
992
0
                            igraph_vector_t *res) {
993
994
0
    if (L->cs->m != L->cs->n) {
995
0
        IGRAPH_ERROR("Cannot perform lower triangular solve on non-square matrix.", IGRAPH_EINVAL);
996
0
    }
997
998
0
    if (res != b) {
999
0
        IGRAPH_CHECK(igraph_vector_update(res, b));
1000
0
    }
1001
1002
0
    if (! cs_lsolve(L->cs, VECTOR(*res))) {
1003
0
        IGRAPH_ERROR("Cannot perform lower triangular solve.", IGRAPH_FAILURE);
1004
0
    }
1005
1006
0
    return IGRAPH_SUCCESS;
1007
0
}
1008
1009
/**
1010
 * \function igraph_sparsemat_ltsolve
1011
 * \brief Solves an upper-triangular linear system.
1012
 *
1013
 * Solve the L'x=b linear equation system, where the L
1014
 * matrix is square and lower-triangular, with a zero-free diagonal.
1015
 *
1016
 * \param L The input matrix, in column-compressed format.
1017
 * \param b The right hand side of the linear system.
1018
 * \param res An initialized vector, the result is stored here.
1019
 * \return Error code.
1020
 *
1021
 * Time complexity: TODO.
1022
 */
1023
1024
igraph_error_t igraph_sparsemat_ltsolve(const igraph_sparsemat_t *L,
1025
                             const igraph_vector_t *b,
1026
0
                             igraph_vector_t *res) {
1027
1028
0
    if (L->cs->m != L->cs->n) {
1029
0
        IGRAPH_ERROR(
1030
0
            "Cannot perform transposed lower triangular solve on non-square matrix.",
1031
0
            IGRAPH_EINVAL
1032
0
        );
1033
0
    }
1034
1035
0
    if (res != b) {
1036
0
        IGRAPH_CHECK(igraph_vector_update(res, b));
1037
0
    }
1038
1039
0
    if (!cs_ltsolve(L->cs, VECTOR(*res))) {
1040
0
        IGRAPH_ERROR("Cannot perform lower triangular solve.", IGRAPH_FAILURE);
1041
0
    }
1042
1043
0
    return IGRAPH_SUCCESS;
1044
0
}
1045
1046
/**
1047
 * \function igraph_sparsemat_usolve
1048
 * \brief Solves an upper-triangular linear system.
1049
 *
1050
 * Solves the Ux=b upper triangular system.
1051
 *
1052
 * \param U The input matrix, in column-compressed format.
1053
 * \param b The right hand side of the linear system.
1054
 * \param res An initialized vector, the result is stored here.
1055
 * \return Error code.
1056
 *
1057
 * Time complexity: TODO.
1058
 */
1059
1060
igraph_error_t igraph_sparsemat_usolve(const igraph_sparsemat_t *U,
1061
                            const igraph_vector_t *b,
1062
0
                            igraph_vector_t *res) {
1063
1064
0
    if (U->cs->m != U->cs->n) {
1065
0
        IGRAPH_ERROR(
1066
0
            "Cannot perform upper triangular solve on non-square matrix.",
1067
0
            IGRAPH_EINVAL
1068
0
        );
1069
0
    }
1070
1071
0
    if (res != b) {
1072
0
        IGRAPH_CHECK(igraph_vector_update(res, b));
1073
0
    }
1074
1075
0
    if (! cs_usolve(U->cs, VECTOR(*res))) {
1076
0
        IGRAPH_ERROR("Cannot perform upper triangular solve.", IGRAPH_FAILURE);
1077
0
    }
1078
1079
0
    return IGRAPH_SUCCESS;
1080
0
}
1081
1082
/**
1083
 * \function igraph_sparsemat_utsolve
1084
 * \brief Solves a lower-triangular linear system.
1085
 *
1086
 * This is the same as \ref igraph_sparsemat_usolve(), but U'x=b is
1087
 * solved, where the apostrophe denotes the transpose.
1088
 *
1089
 * \param U The input matrix, in column-compressed format.
1090
 * \param b The right hand side of the linear system.
1091
 * \param res An initialized vector, the result is stored here.
1092
 * \return Error code.
1093
 *
1094
 * Time complexity: TODO.
1095
 */
1096
1097
igraph_error_t igraph_sparsemat_utsolve(const igraph_sparsemat_t *U,
1098
                             const igraph_vector_t *b,
1099
0
                             igraph_vector_t *res) {
1100
1101
0
    if (U->cs->m != U->cs->n) {
1102
0
        IGRAPH_ERROR(
1103
0
            "Cannot perform transposed upper triangular solve on non-square matrix.",
1104
0
            IGRAPH_EINVAL
1105
0
        );
1106
0
    }
1107
1108
0
    if (res != b) {
1109
0
        IGRAPH_CHECK(igraph_vector_update(res, b));
1110
0
    }
1111
1112
0
    if (!cs_utsolve(U->cs, VECTOR(*res))) {
1113
0
        IGRAPH_ERROR("Cannot perform transposed upper triangular solve.",
1114
0
                     IGRAPH_FAILURE);
1115
0
    }
1116
1117
0
    return IGRAPH_SUCCESS;
1118
0
}
1119
1120
/**
1121
 * \function igraph_sparsemat_cholsol
1122
 * \brief Solves a symmetric linear system via Cholesky decomposition.
1123
 *
1124
 * Solve Ax=b, where A is a symmetric positive definite matrix.
1125
 *
1126
 * \param A The input matrix, in column-compressed format.
1127
 * \param b The right hand side.
1128
 * \param res An initialized vector, the result is stored here.
1129
 * \param order An integer giving the ordering method to use for the
1130
 *    factorization. Zero is the natural ordering; if it is one, then
1131
 *    the fill-reducing minimum-degree ordering of A+A' is used.
1132
 * \return Error code.
1133
 *
1134
 * Time complexity: TODO.
1135
 */
1136
1137
igraph_error_t igraph_sparsemat_cholsol(const igraph_sparsemat_t *A,
1138
                             const igraph_vector_t *b,
1139
                             igraph_vector_t *res,
1140
0
                             igraph_int_t order) {
1141
1142
0
    if (A->cs->m != A->cs->n) {
1143
0
        IGRAPH_ERROR(
1144
0
            "Cannot perform sparse symmetric solve on non-square matrix.",
1145
0
            IGRAPH_EINVAL
1146
0
        );
1147
0
    }
1148
1149
0
    if (res != b) {
1150
0
        IGRAPH_CHECK(igraph_vector_update(res, b));
1151
0
    }
1152
1153
0
    if (! cs_cholsol(order, A->cs, VECTOR(*res))) {
1154
0
        IGRAPH_ERROR("Cannot perform sparse symmetric solve.", IGRAPH_FAILURE);
1155
0
    }
1156
1157
0
    return IGRAPH_SUCCESS;
1158
0
}
1159
1160
/**
1161
 * \function igraph_sparsemat_lusol
1162
 * \brief Solves a linear system via LU decomposition.
1163
 *
1164
 * Solve Ax=b, via LU factorization of A.
1165
 *
1166
 * \param A The input matrix, in column-compressed format.
1167
 * \param b The right hand side of the equation.
1168
 * \param res An initialized vector, the result is stored here.
1169
 * \param order The ordering method to use, zero means the natural
1170
 *    ordering, one means the fill-reducing minimum-degree ordering of
1171
 *    A+A', two means the ordering of A'*A, after removing the dense
1172
 *    rows from A. Three means the ordering of A'*A.
1173
 * \param tol Real number, the tolerance limit to use for the numeric
1174
 *    LU factorization.
1175
 * \return Error code.
1176
 *
1177
 * Time complexity: TODO.
1178
 */
1179
1180
igraph_error_t igraph_sparsemat_lusol(const igraph_sparsemat_t *A,
1181
                           const igraph_vector_t *b,
1182
                           igraph_vector_t *res,
1183
                           igraph_int_t order,
1184
0
                           igraph_real_t tol) {
1185
1186
0
    if (A->cs->m != A->cs->n) {
1187
0
        IGRAPH_ERROR("Cannot perform LU solve on non-square matrix.", IGRAPH_EINVAL);
1188
0
    }
1189
1190
0
    if (res != b) {
1191
0
        IGRAPH_CHECK(igraph_vector_update(res, b));
1192
0
    }
1193
1194
0
    if (! cs_lusol(order, A->cs, VECTOR(*res), tol)) {
1195
0
        IGRAPH_ERROR("Cannot perform LU solve.", IGRAPH_FAILURE);
1196
0
    }
1197
1198
0
    return IGRAPH_SUCCESS;
1199
0
}
1200
1201
0
#define CHECK(x) if ((x)<0) { IGRAPH_ERROR("Cannot write to file", IGRAPH_EFILE); }
1202
1203
/**
1204
 * \function igraph_sparsemat_print
1205
 * \brief Prints a sparse matrix to a file.
1206
 *
1207
 * Only the non-zero entries are printed. This function serves more as
1208
 * a debugging utility, as currently there is no function that could
1209
 * read back the printed matrix from the file.
1210
 *
1211
 * \param A The input matrix, triplet or column-compressed format.
1212
 * \param outstream The stream to print it to.
1213
 * \return Error code.
1214
 *
1215
 * Time complexity: O(nz) for triplet matrices, O(n+nz) for
1216
 * column-compressed matrices. nz is the number of non-zero elements,
1217
 * n is the number columns in the matrix.
1218
 */
1219
1220
igraph_error_t igraph_sparsemat_print(const igraph_sparsemat_t *A,
1221
0
                           FILE *outstream) {
1222
1223
0
    if (igraph_sparsemat_is_cc(A)) {
1224
        /* CC */
1225
0
        CS_INT j, p;
1226
0
        for (j = 0; j < A->cs->n; j++) {
1227
0
            CHECK(fprintf(outstream, "col " CS_ID ": locations " CS_ID " to " CS_ID "\n",
1228
0
                          j, A->cs->p[j], A->cs->p[j + 1] - 1));
1229
0
            for (p = A->cs->p[j]; p < A->cs->p[j + 1]; p++) {
1230
0
                CHECK(fprintf(outstream, CS_ID " : %g\n", A->cs->i[p], A->cs->x[p]));
1231
0
            }
1232
0
        }
1233
0
    } else {
1234
        /* Triplet */
1235
0
        CS_INT p;
1236
0
        for (p = 0; p < A->cs->nz; p++) {
1237
0
            CHECK(fprintf(outstream, CS_ID " " CS_ID " : %g\n",
1238
0
                          A->cs->i[p], A->cs->p[p], A->cs->x[p]));
1239
0
        }
1240
0
    }
1241
1242
0
    return IGRAPH_SUCCESS;
1243
0
}
1244
1245
#undef CHECK
1246
1247
static igraph_error_t igraph_i_sparsemat_eye_triplet(
1248
    igraph_sparsemat_t *A, igraph_int_t n, igraph_int_t nzmax,
1249
    igraph_real_t value
1250
0
) {
1251
0
    igraph_int_t i;
1252
1253
0
    IGRAPH_CHECK(igraph_sparsemat_init(A, n, n, nzmax));
1254
1255
0
    for (i = 0; i < n; i++) {
1256
0
        IGRAPH_CHECK(igraph_sparsemat_entry(A, i, i, value));
1257
0
    }
1258
1259
0
    return IGRAPH_SUCCESS;
1260
0
}
1261
1262
static igraph_error_t igraph_i_sparsemat_eye_cc(
1263
    igraph_sparsemat_t *A, igraph_int_t n, igraph_real_t value
1264
0
) {
1265
0
    igraph_int_t i;
1266
1267
0
    A->cs = cs_spalloc(n, n, n, /*values=*/ 1, /*triplet=*/ 0);
1268
0
    if (!A->cs) {
1269
0
        IGRAPH_ERROR("Cannot create eye sparse matrix", IGRAPH_FAILURE);
1270
0
    }
1271
1272
0
    for (i = 0; i < n; i++) {
1273
0
        A->cs->p [i] = i;
1274
0
        A->cs->i [i] = i;
1275
0
        A->cs->x [i] = value;
1276
0
    }
1277
0
    A->cs->p [n] = n;
1278
1279
0
    return IGRAPH_SUCCESS;
1280
0
}
1281
1282
/**
1283
 * \function igraph_sparsemat_init_eye
1284
 * \brief Creates a sparse identity matrix.
1285
 *
1286
 * \param A An uninitialized sparse matrix, the result is stored
1287
 *   here.
1288
 * \param n The number of rows and number of columns in the matrix.
1289
 * \param nzmax The maximum number of non-zero elements, this
1290
 *   essentially gives the amount of memory that will be allocated for
1291
 *   matrix elements.
1292
 * \param value The value to store in the diagonal.
1293
 * \param compress Whether to create a column-compressed matrix. If
1294
 *   false, then a triplet matrix is created.
1295
 * \return Error code.
1296
 *
1297
 * Time complexity: O(n).
1298
 */
1299
1300
igraph_error_t igraph_sparsemat_init_eye(
1301
    igraph_sparsemat_t *A, igraph_int_t n, igraph_int_t nzmax,
1302
    igraph_real_t value, igraph_bool_t compress
1303
0
) {
1304
0
    if (compress) {
1305
0
        return igraph_i_sparsemat_eye_cc(A, n, value);
1306
0
    } else {
1307
0
        return igraph_i_sparsemat_eye_triplet(A, n, nzmax, value);
1308
0
    }
1309
0
}
1310
1311
static igraph_error_t igraph_i_sparsemat_init_diag_triplet(
1312
    igraph_sparsemat_t *A, igraph_int_t nzmax, const igraph_vector_t *values
1313
0
) {
1314
1315
0
    CS_INT i, n = igraph_vector_size(values);
1316
1317
0
    IGRAPH_CHECK(igraph_sparsemat_init(A, n, n, nzmax));
1318
1319
0
    for (i = 0; i < n; i++) {
1320
0
        IGRAPH_CHECK(igraph_sparsemat_entry(A, i, i, VECTOR(*values)[i]));
1321
0
    }
1322
1323
0
    return IGRAPH_SUCCESS;
1324
1325
0
}
1326
1327
static igraph_error_t igraph_i_sparsemat_init_diag_cc(igraph_sparsemat_t *A,
1328
0
                                      const igraph_vector_t *values) {
1329
1330
0
    CS_INT i, n = igraph_vector_size(values);
1331
1332
0
    A->cs = cs_spalloc(n, n, n, /*values=*/ 1, /*triplet=*/ 0);
1333
0
    if (!A->cs) {
1334
0
        IGRAPH_ERROR("Cannot create eye sparse matrix", IGRAPH_FAILURE);
1335
0
    }
1336
1337
0
    for (i = 0; i < n; i++) {
1338
0
        A->cs->p [i] = i;
1339
0
        A->cs->i [i] = i;
1340
0
        A->cs->x [i] = VECTOR(*values)[i];
1341
0
    }
1342
0
    A->cs->p [n] = n;
1343
1344
0
    return IGRAPH_SUCCESS;
1345
1346
0
}
1347
1348
/**
1349
 * \function igraph_sparsemat_init_diag
1350
 * \brief Creates a sparse diagonal matrix.
1351
 *
1352
 * \param A An uninitialized sparse matrix, the result is stored
1353
 *    here.
1354
 * \param nzmax The maximum number of non-zero elements, this
1355
 *   essentially gives the amount of memory that will be allocated for
1356
 *   matrix elements.
1357
 * \param values The values to store in the diagonal, the size of the
1358
 *    matrix defined by the length of this vector.
1359
 * \param compress Whether to create a column-compressed matrix. If
1360
 *   false, then a triplet matrix is created.
1361
 * \return Error code.
1362
 *
1363
 * Time complexity: O(n), the length of the diagonal vector.
1364
 */
1365
1366
igraph_error_t igraph_sparsemat_init_diag(
1367
    igraph_sparsemat_t *A, igraph_int_t nzmax, const igraph_vector_t *values,
1368
    igraph_bool_t compress
1369
0
) {
1370
0
    if (compress) {
1371
0
        return (igraph_i_sparsemat_init_diag_cc(A, values));
1372
0
    } else {
1373
0
        return (igraph_i_sparsemat_init_diag_triplet(A, nzmax, values));
1374
0
    }
1375
0
}
1376
1377
static igraph_error_t igraph_i_sparsemat_arpack_multiply(igraph_real_t *to,
1378
                                              const igraph_real_t *from,
1379
                                              int n,
1380
0
                                              void *extra) {
1381
0
    igraph_sparsemat_t *A = extra;
1382
0
    const igraph_vector_t vfrom = igraph_vector_view(from, n);
1383
0
    igraph_vector_t vto = igraph_vector_view(to, n);
1384
1385
0
    igraph_vector_null(&vto);
1386
0
    IGRAPH_CHECK(igraph_sparsemat_gaxpy(A, &vfrom, &vto));
1387
1388
0
    return IGRAPH_SUCCESS;
1389
0
}
1390
1391
typedef struct igraph_i_sparsemat_arpack_rssolve_data_t {
1392
    igraph_sparsemat_symbolic_t *dis;
1393
    igraph_sparsemat_numeric_t *din;
1394
    igraph_real_t tol;
1395
    igraph_sparsemat_solve_t method;
1396
} igraph_i_sparsemat_arpack_rssolve_data_t;
1397
1398
static igraph_error_t igraph_i_sparsemat_arpack_solve(igraph_real_t *to,
1399
                                           const igraph_real_t *from,
1400
                                           int n,
1401
0
                                           void *extra) {
1402
1403
0
    igraph_i_sparsemat_arpack_rssolve_data_t *data = extra;
1404
0
    const igraph_vector_t vfrom = igraph_vector_view(from, n);
1405
0
    igraph_vector_t vto = igraph_vector_view(to, n);
1406
1407
0
    if (data->method == IGRAPH_SPARSEMAT_SOLVE_LU) {
1408
0
        IGRAPH_CHECK(igraph_sparsemat_luresol(data->dis, data->din, &vfrom,
1409
0
                                              &vto));
1410
0
    } else if (data->method == IGRAPH_SPARSEMAT_SOLVE_QR) {
1411
0
        IGRAPH_CHECK(igraph_sparsemat_qrresol(data->dis, data->din, &vfrom,
1412
0
                                              &vto));
1413
1414
0
    }
1415
1416
0
    return IGRAPH_SUCCESS;
1417
0
}
1418
1419
/**
1420
 * \function igraph_sparsemat_arpack_rssolve
1421
 * \brief Eigenvalues and eigenvectors of a symmetric sparse matrix via ARPACK.
1422
 *
1423
 * \param A The input matrix, must be column-compressed.
1424
 * \param options It is passed to \ref igraph_arpack_rssolve(). Supply
1425
 *    \c NULL here to use the defaults. See \ref igraph_arpack_options_t for the
1426
 *    details. If \c mode is 1, then ARPACK uses regular mode, if \c mode is 3,
1427
 *    then shift and invert mode is used and the \c sigma structure member defines
1428
 *    the shift.
1429
 * \param storage Storage for ARPACK. See \ref
1430
 *    igraph_arpack_rssolve() and \ref igraph_arpack_storage_t for
1431
 *    details.
1432
 * \param values An initialized vector or a null pointer, the
1433
 *    eigenvalues are stored here.
1434
 * \param vectors An initialised matrix, or a null pointer, the
1435
 *    eigenvectors are stored here, in the columns.
1436
 * \param solvemethod The method to solve the linear system, if \c
1437
 *    mode is 3, i.e. the shift and invert mode is used.
1438
 *    Possible values:
1439
 *    \clist
1440
 *      \cli IGRAPH_SPARSEMAT_SOLVE_LU
1441
 *           The linear system is solved using LU decomposition.
1442
 *      \cli IGRAPH_SPARSEMAT_SOLVE_QR
1443
 *           The linear system is solved using QR decomposition.
1444
 *    \endclist
1445
 * \return Error code.
1446
 *
1447
 * Time complexity: TODO.
1448
 */
1449
1450
igraph_error_t igraph_sparsemat_arpack_rssolve(const igraph_sparsemat_t *A,
1451
                                    igraph_arpack_options_t *options,
1452
                                    igraph_arpack_storage_t *storage,
1453
                                    igraph_vector_t *values,
1454
                                    igraph_matrix_t *vectors,
1455
0
                                    igraph_sparsemat_solve_t solvemethod) {
1456
1457
0
    igraph_int_t n = igraph_sparsemat_nrow(A);
1458
1459
0
    if (n != igraph_sparsemat_ncol(A)) {
1460
0
        IGRAPH_ERROR("Non-square matrix for ARPACK.", IGRAPH_EINVAL);
1461
0
    }
1462
1463
0
    if (n > INT_MAX) {
1464
0
        IGRAPH_ERROR("Matrix too large for ARPACK.", IGRAPH_EOVERFLOW);
1465
0
    }
1466
1467
0
    if (options == 0) {
1468
0
        options = igraph_arpack_options_get_default();
1469
0
    }
1470
1471
0
    options->n = (int) n;
1472
1473
0
    if (options->mode == 1) {
1474
0
        IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_sparsemat_arpack_multiply,
1475
0
                                           (void*) A, options, storage,
1476
0
                                           values, vectors));
1477
0
    } else if (options->mode == 3) {
1478
0
        igraph_real_t sigma = options->sigma;
1479
0
        igraph_sparsemat_t OP, eye;
1480
0
        igraph_sparsemat_symbolic_t symb;
1481
0
        igraph_sparsemat_numeric_t num;
1482
0
        igraph_i_sparsemat_arpack_rssolve_data_t data;
1483
        /*-----------------------------------*/
1484
        /* We need to factor the (A-sigma*I) */
1485
        /*-----------------------------------*/
1486
1487
        /* Create (A-sigma*I) */
1488
0
        IGRAPH_CHECK(igraph_sparsemat_init_eye(&eye, /*n=*/ n, /*nzmax=*/ n,
1489
0
                                          /*value=*/ -sigma, /*compress=*/ 1));
1490
0
        IGRAPH_FINALLY(igraph_sparsemat_destroy, &eye);
1491
0
        IGRAPH_CHECK(igraph_sparsemat_add(/*A=*/ A, /*B=*/ &eye, /*alpha=*/ 1.0,
1492
0
                     /*beta=*/ 1.0, /*res=*/ &OP));
1493
0
        igraph_sparsemat_destroy(&eye);
1494
0
        IGRAPH_FINALLY_CLEAN(1);
1495
0
        IGRAPH_FINALLY(igraph_sparsemat_destroy, &OP);
1496
1497
0
        if (solvemethod == IGRAPH_SPARSEMAT_SOLVE_LU) {
1498
            /* Symbolic analysis */
1499
0
            IGRAPH_CHECK(igraph_sparsemat_symblu(/*order=*/ 0, &OP, &symb));
1500
0
            IGRAPH_FINALLY(igraph_sparsemat_symbolic_destroy, &symb);
1501
            /* Numeric LU factorization */
1502
0
            IGRAPH_CHECK(igraph_sparsemat_lu(&OP, &symb, &num, /*tol=*/ 0));
1503
0
            IGRAPH_FINALLY(igraph_sparsemat_numeric_destroy, &num);
1504
0
        } else if (solvemethod == IGRAPH_SPARSEMAT_SOLVE_QR) {
1505
            /* Symbolic analysis */
1506
0
            IGRAPH_CHECK(igraph_sparsemat_symbqr(/*order=*/ 0, &OP, &symb));
1507
0
            IGRAPH_FINALLY(igraph_sparsemat_symbolic_destroy, &symb);
1508
            /* Numeric QR factorization */
1509
0
            IGRAPH_CHECK(igraph_sparsemat_qr(&OP, &symb, &num));
1510
0
            IGRAPH_FINALLY(igraph_sparsemat_numeric_destroy, &num);
1511
0
        }
1512
1513
0
        data.dis = &symb;
1514
0
        data.din = &num;
1515
0
        data.tol = options->tol;
1516
0
        data.method = solvemethod;
1517
0
        IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_sparsemat_arpack_solve,
1518
0
                                           (void*) &data, options, storage,
1519
0
                                           values, vectors));
1520
1521
0
        igraph_sparsemat_numeric_destroy(&num);
1522
0
        igraph_sparsemat_symbolic_destroy(&symb);
1523
0
        igraph_sparsemat_destroy(&OP);
1524
0
        IGRAPH_FINALLY_CLEAN(3);
1525
0
    }
1526
1527
0
    return IGRAPH_SUCCESS;
1528
0
}
1529
1530
/**
1531
 * \function igraph_sparsemat_arpack_rnsolve
1532
 * \brief Eigenvalues and eigenvectors of a nonsymmetric sparse matrix via ARPACK.
1533
 *
1534
 * Eigenvalues and/or eigenvectors of a nonsymmetric sparse matrix.
1535
 *
1536
 * \param A The input matrix, in column-compressed mode.
1537
 * \param options ARPACK options, it is passed to \ref
1538
 *    igraph_arpack_rnsolve(). Supply \c NULL here to use the defaults.
1539
 *    See also \ref igraph_arpack_options_t for details.
1540
 * \param storage Storage for ARPACK, this is passed to \ref
1541
 *    igraph_arpack_rnsolve(). See \ref igraph_arpack_storage_t for
1542
 *    details.
1543
 * \param values An initialized matrix, or a null pointer. If not a
1544
 *    null pointer, then the eigenvalues are stored here, the first
1545
 *    column is the real part, the second column is the imaginary
1546
 *    part.
1547
 * \param vectors An initialized matrix, or a null pointer. If not a
1548
 *    null pointer, then the eigenvectors are stored here, please see
1549
 *    \ref igraph_arpack_rnsolve() for the format.
1550
 * \return Error code.
1551
 *
1552
 * Time complexity: TODO.
1553
 */
1554
1555
igraph_error_t igraph_sparsemat_arpack_rnsolve(const igraph_sparsemat_t *A,
1556
                                    igraph_arpack_options_t *options,
1557
                                    igraph_arpack_storage_t *storage,
1558
                                    igraph_matrix_t *values,
1559
0
                                    igraph_matrix_t *vectors) {
1560
1561
0
    igraph_int_t n = igraph_sparsemat_nrow(A);
1562
1563
0
    if (n > INT_MAX) {
1564
0
        IGRAPH_ERROR("Matrix too large for ARPACK.", IGRAPH_EOVERFLOW);
1565
0
    }
1566
1567
0
    if (n != igraph_sparsemat_ncol(A)) {
1568
0
        IGRAPH_ERROR("Non-square matrix for ARPACK.", IGRAPH_EINVAL);
1569
0
    }
1570
1571
0
    if (options == 0) {
1572
0
        options = igraph_arpack_options_get_default();
1573
0
    }
1574
1575
0
    options->n = (int) n;
1576
1577
0
    return igraph_arpack_rnsolve(igraph_i_sparsemat_arpack_multiply,
1578
0
                                 (void*) A, options, storage,
1579
0
                                 values, vectors);
1580
0
}
1581
1582
/**
1583
 * \function igraph_sparsemat_symbqr
1584
 * \brief Symbolic QR decomposition.
1585
 *
1586
 * QR decomposition of sparse matrices involves two steps, the first
1587
 * is calling this function, and then \ref
1588
 * igraph_sparsemat_qr().
1589
 *
1590
 * \param order The ordering to use: 0 means natural ordering, 1 means
1591
 *   minimum degree ordering of A+A', 2 is minimum degree ordering of
1592
 *   A'A after removing the dense rows from A, and 3 is the minimum
1593
 *   degree ordering of A'A.
1594
 * \param A The input matrix, in column-compressed format.
1595
 * \param dis The result of the symbolic analysis is stored here. Once
1596
 *    not needed anymore, it must be destroyed by calling \ref
1597
 *    igraph_sparsemat_symbolic_destroy().
1598
 * \return Error code.
1599
 *
1600
 * Time complexity: TODO.
1601
 */
1602
1603
igraph_error_t igraph_sparsemat_symbqr(igraph_int_t order, const igraph_sparsemat_t *A,
1604
0
                            igraph_sparsemat_symbolic_t *dis) {
1605
1606
0
    dis->symbolic = cs_sqr(order, A->cs, /*qr=*/ 1);
1607
0
    if (!dis->symbolic) {
1608
0
        IGRAPH_ERROR("Cannot do symbolic QR decomposition", IGRAPH_FAILURE);
1609
0
    }
1610
1611
0
    return IGRAPH_SUCCESS;
1612
0
}
1613
1614
/**
1615
 * \function igraph_sparsemat_symblu
1616
 * \brief Symbolic LU decomposition.
1617
 *
1618
 * LU decomposition of sparse matrices involves two steps, the first
1619
 * is calling this function, and then \ref igraph_sparsemat_lu().
1620
 *
1621
 * \param order The ordering to use: 0 means natural ordering, 1 means
1622
 *   minimum degree ordering of A+A', 2 is minimum degree ordering of
1623
 *   A'A after removing the dense rows from A, and 3 is the minimum
1624
 *   degree ordering of A'A.
1625
 * \param A The input matrix, in column-compressed format.
1626
 * \param dis The result of the symbolic analysis is stored here. Once
1627
 *    not needed anymore, it must be destroyed by calling \ref
1628
 *    igraph_sparsemat_symbolic_destroy().
1629
 * \return Error code.
1630
 *
1631
 * Time complexity: TODO.
1632
 */
1633
1634
igraph_error_t igraph_sparsemat_symblu(igraph_int_t order, const igraph_sparsemat_t *A,
1635
0
                            igraph_sparsemat_symbolic_t *dis) {
1636
1637
0
    dis->symbolic = cs_sqr(order, A->cs, /*qr=*/ 0);
1638
0
    if (!dis->symbolic) {
1639
0
        IGRAPH_ERROR("Cannot do symbolic LU decomposition", IGRAPH_FAILURE);
1640
0
    }
1641
1642
0
    return IGRAPH_SUCCESS;
1643
0
}
1644
1645
/**
1646
 * \function igraph_sparsemat_lu
1647
 * \brief LU decomposition of a sparse matrix.
1648
 *
1649
 * Performs numeric sparse LU decomposition of a matrix.
1650
 *
1651
 * \param A The input matrix, in column-compressed format.
1652
 * \param dis The symbolic analysis for LU decomposition, coming from
1653
 *    a call to the \ref igraph_sparsemat_symblu() function.
1654
 * \param din The numeric decomposition, the result is stored here. It
1655
 *    can be used to solve linear systems with changing right hand
1656
 *    side vectors, by calling \ref igraph_sparsemat_luresol(). Once
1657
 *    not needed any more, it must be destroyed by calling \ref
1658
 *    igraph_sparsemat_symbolic_destroy() on it.
1659
 * \param tol The tolerance for the numeric LU decomposition.
1660
 * \return Error code.
1661
 *
1662
 * Time complexity: TODO.
1663
 */
1664
1665
igraph_error_t igraph_sparsemat_lu(const igraph_sparsemat_t *A,
1666
                        const igraph_sparsemat_symbolic_t *dis,
1667
0
                        igraph_sparsemat_numeric_t *din, double tol) {
1668
0
    din->numeric = cs_lu(A->cs, dis->symbolic, tol);
1669
0
    if (!din->numeric) {
1670
0
        IGRAPH_ERROR("Cannot do LU decomposition", IGRAPH_FAILURE);
1671
0
    }
1672
0
    return IGRAPH_SUCCESS;
1673
0
}
1674
1675
/**
1676
 * \function igraph_sparsemat_qr
1677
 * \brief QR decomposition of a sparse matrix.
1678
 *
1679
 * Numeric QR decomposition of a sparse matrix.
1680
 *
1681
 * \param A The input matrix, in column-compressed format.
1682
 * \param dis The result of the symbolic QR analysis, from the
1683
 *    function \ref igraph_sparsemat_symbqr().
1684
 * \param din The result of the decomposition is stored here, it can
1685
 *    be used to solve many linear systems with the same coefficient
1686
 *    matrix and changing right hand sides, using the \ref
1687
 *    igraph_sparsemat_qrresol() function. Once not needed any more,
1688
 *    one should call \ref igraph_sparsemat_numeric_destroy() on it to
1689
 *    free the allocated memory.
1690
 * \return Error code.
1691
 *
1692
 * Time complexity: TODO.
1693
 */
1694
1695
igraph_error_t igraph_sparsemat_qr(const igraph_sparsemat_t *A,
1696
                        const igraph_sparsemat_symbolic_t *dis,
1697
0
                        igraph_sparsemat_numeric_t *din) {
1698
0
    din->numeric = cs_qr(A->cs, dis->symbolic);
1699
0
    if (!din->numeric) {
1700
0
        IGRAPH_ERROR("Cannot do QR decomposition", IGRAPH_FAILURE);
1701
0
    }
1702
0
    return IGRAPH_SUCCESS;
1703
0
}
1704
1705
/**
1706
 * \function igraph_sparsemat_luresol
1707
 * \brief Solves a linear system using a precomputed LU decomposition.
1708
 *
1709
 * Uses the LU decomposition of a matrix to solve linear systems.
1710
 *
1711
 * \param dis The symbolic analysis of the coefficient matrix, the
1712
 *    result of \ref igraph_sparsemat_symblu().
1713
 * \param din The LU decomposition, the result of a call to \ref
1714
 *    igraph_sparsemat_lu().
1715
 * \param b A vector that defines the right hand side of the linear
1716
 *    equation system.
1717
 * \param res An initialized vector, the solution of the linear system
1718
 *    is stored here.
1719
 * \return Error code.
1720
 *
1721
 * Time complexity: TODO.
1722
 */
1723
1724
igraph_error_t igraph_sparsemat_luresol(const igraph_sparsemat_symbolic_t *dis,
1725
                             const igraph_sparsemat_numeric_t *din,
1726
                             const igraph_vector_t *b,
1727
0
                             igraph_vector_t *res) {
1728
0
    igraph_int_t n = din->numeric->L->n;
1729
0
    igraph_real_t *workspace;
1730
1731
0
    if (res != b) {
1732
0
        IGRAPH_CHECK(igraph_vector_update(res, b));
1733
0
    }
1734
1735
0
    workspace = IGRAPH_CALLOC(n, igraph_real_t);
1736
0
    if (!workspace) {
1737
0
        IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */
1738
0
    }
1739
0
    IGRAPH_FINALLY(igraph_free, workspace);
1740
1741
0
    if (!cs_ipvec(din->numeric->pinv, VECTOR(*res), workspace, n)) {
1742
0
        IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_FAILURE);
1743
0
    }
1744
0
    if (!cs_lsolve(din->numeric->L, workspace)) {
1745
0
        IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_FAILURE);
1746
0
    }
1747
0
    if (!cs_usolve(din->numeric->U, workspace)) {
1748
0
        IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_FAILURE);
1749
0
    }
1750
0
    if (!cs_ipvec(dis->symbolic->q, workspace, VECTOR(*res), n)) {
1751
0
        IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_FAILURE);
1752
0
    }
1753
1754
0
    IGRAPH_FREE(workspace);
1755
0
    IGRAPH_FINALLY_CLEAN(1);
1756
1757
0
    return IGRAPH_SUCCESS;
1758
0
}
1759
1760
/**
1761
 * \function igraph_sparsemat_qrresol
1762
 * \brief Solves a linear system using a precomputed QR decomposition.
1763
 *
1764
 * Solves a linear system using a QR decomposition of its coefficient
1765
 * matrix.
1766
 *
1767
 * \param dis Symbolic analysis of the coefficient matrix, the result
1768
 *    of \ref igraph_sparsemat_symbqr().
1769
 * \param din The QR decomposition of the coefficient matrix, the
1770
 *    result of \ref igraph_sparsemat_qr().
1771
 * \param b Vector, giving the right hand side of the linear equation
1772
 *    system.
1773
 * \param res An initialized vector, the solution is stored here. It
1774
 *    is resized as needed.
1775
 * \return Error code.
1776
 *
1777
 * Time complexity: TODO.
1778
 */
1779
1780
igraph_error_t igraph_sparsemat_qrresol(const igraph_sparsemat_symbolic_t *dis,
1781
                             const igraph_sparsemat_numeric_t *din,
1782
                             const igraph_vector_t *b,
1783
0
                             igraph_vector_t *res) {
1784
0
    igraph_int_t n = din->numeric->L->n;
1785
0
    igraph_real_t *workspace;
1786
0
    igraph_int_t k;
1787
1788
0
    if (res != b) {
1789
0
        IGRAPH_CHECK(igraph_vector_update(res, b));
1790
0
    }
1791
1792
0
    workspace = IGRAPH_CALLOC(dis->symbolic ? dis->symbolic->m2 : 1,
1793
0
                              igraph_real_t);
1794
0
    if (!workspace) {
1795
0
        IGRAPH_ERROR("Cannot QR (re)solve sparse matrix", IGRAPH_FAILURE);
1796
0
    }
1797
0
    IGRAPH_FINALLY(igraph_free, workspace);
1798
1799
0
    if (!cs_ipvec(dis->symbolic->pinv, VECTOR(*res), workspace, n)) {
1800
0
        IGRAPH_ERROR("Cannot QR (re)solve sparse matrix", IGRAPH_FAILURE);
1801
0
    }
1802
0
    for (k = 0; k < n; k++) {
1803
0
        if (!cs_happly(din->numeric->L, k, din->numeric->B[k], workspace)) {
1804
0
            IGRAPH_ERROR("Cannot QR (re)solve sparse matrix", IGRAPH_FAILURE);
1805
0
        }
1806
0
    }
1807
0
    if (!cs_usolve(din->numeric->U, workspace)) {
1808
0
        IGRAPH_ERROR("Cannot QR (re)solve sparse matrix", IGRAPH_FAILURE);
1809
0
    }
1810
0
    if (!cs_ipvec(dis->symbolic->q, workspace, VECTOR(*res), n)) {
1811
0
        IGRAPH_ERROR("Cannot QR (re)solve sparse matrix", IGRAPH_FAILURE);
1812
0
    }
1813
1814
0
    IGRAPH_FREE(workspace);
1815
0
    IGRAPH_FINALLY_CLEAN(1);
1816
1817
0
    return IGRAPH_SUCCESS;
1818
0
}
1819
1820
/**
1821
 * \function igraph_sparsemat_symbolic_destroy
1822
 * \brief Deallocates memory after a symbolic decomposition.
1823
 *
1824
 * Frees the memory allocated by \ref igraph_sparsemat_symbqr() or
1825
 * \ref igraph_sparsemat_symblu().
1826
 *
1827
 * \param dis The symbolic analysis.
1828
 *
1829
 * Time complexity: O(1).
1830
 */
1831
1832
0
void igraph_sparsemat_symbolic_destroy(igraph_sparsemat_symbolic_t *dis) {
1833
0
    cs_sfree(dis->symbolic);
1834
0
    dis->symbolic = 0;
1835
0
}
1836
1837
/**
1838
 * \function igraph_sparsemat_numeric_destroy
1839
 * \brief Deallocates memory after a numeric decomposition.
1840
 *
1841
 * Frees the memoty allocated by \ref igraph_sparsemat_qr() or \ref
1842
 * igraph_sparsemat_lu().
1843
 *
1844
 * \param din The LU or QR decomposition.
1845
 *
1846
 * Time complexity: O(1).
1847
 */
1848
1849
0
void igraph_sparsemat_numeric_destroy(igraph_sparsemat_numeric_t *din) {
1850
0
    cs_nfree(din->numeric);
1851
0
    din->numeric = 0;
1852
0
}
1853
1854
/**
1855
 * \function igraph_matrix_as_sparsemat
1856
 * \brief Converts a dense matrix to a sparse matrix.
1857
 *
1858
 * \param res An uninitialized sparse matrix, the result is stored
1859
 *    here.
1860
 * \param mat The dense input matrix.
1861
 * \param tol The tolerance for zero comparisons. Values closer than
1862
 *    \p tol to zero are considered as zero, and will not be included
1863
 *    in the sparse matrix.
1864
 * \return Error code.
1865
 *
1866
 * \sa \ref igraph_sparsemat_as_matrix() for the reverse conversion.
1867
 *
1868
 * Time complexity: O(mn), the number of elements in the dense
1869
 * matrix.
1870
 */
1871
1872
igraph_error_t igraph_matrix_as_sparsemat(igraph_sparsemat_t *res,
1873
                               const igraph_matrix_t *mat,
1874
0
                               igraph_real_t tol) {
1875
0
    igraph_int_t nrow = igraph_matrix_nrow(mat);
1876
0
    igraph_int_t ncol = igraph_matrix_ncol(mat);
1877
0
    igraph_int_t i, j, nzmax = 0;
1878
1879
0
    for (i = 0; i < nrow; i++) {
1880
0
        for (j = 0; j < ncol; j++) {
1881
0
            if (fabs(MATRIX(*mat, i, j)) > tol) {
1882
0
                nzmax++;
1883
0
            }
1884
0
        }
1885
0
    }
1886
1887
0
    IGRAPH_CHECK(igraph_sparsemat_init(res, nrow, ncol, nzmax));
1888
1889
0
    for (i = 0; i < nrow; i++) {
1890
0
        for (j = 0; j < ncol; j++) {
1891
0
            if (fabs(MATRIX(*mat, i, j)) > tol) {
1892
0
                IGRAPH_CHECK(igraph_sparsemat_entry(res, i, j, MATRIX(*mat, i, j)));
1893
0
            }
1894
0
        }
1895
0
    }
1896
1897
0
    return IGRAPH_SUCCESS;
1898
0
}
1899
1900
static igraph_error_t igraph_i_sparsemat_as_matrix_cc(igraph_matrix_t *res,
1901
0
                                           const igraph_sparsemat_t *spmat) {
1902
1903
0
    igraph_int_t nrow = igraph_sparsemat_nrow(spmat);
1904
0
    igraph_int_t ncol = igraph_sparsemat_ncol(spmat);
1905
0
    CS_INT from = 0, to = 0;
1906
0
    CS_INT *p = spmat->cs->p;
1907
0
    CS_INT *i = spmat->cs->i;
1908
0
    CS_ENTRY *x = spmat->cs->x;
1909
0
    CS_INT elem_count = spmat->cs->p[ spmat->cs->n ];
1910
1911
0
    IGRAPH_CHECK(igraph_matrix_resize(res, nrow, ncol));
1912
0
    igraph_matrix_null(res);
1913
1914
0
    while (*p < elem_count) {
1915
0
        while (to < *(p + 1)) {
1916
0
            MATRIX(*res, *i, from) += *x;
1917
0
            to++;
1918
0
            i++;
1919
0
            x++;
1920
0
        }
1921
0
        from++;
1922
0
        p++;
1923
0
    }
1924
1925
0
    return IGRAPH_SUCCESS;
1926
0
}
1927
1928
static igraph_error_t igraph_i_sparsemat_as_matrix_triplet(igraph_matrix_t *res,
1929
0
                                                const igraph_sparsemat_t *spmat) {
1930
0
    igraph_int_t nrow = igraph_sparsemat_nrow(spmat);
1931
0
    igraph_int_t ncol = igraph_sparsemat_ncol(spmat);
1932
0
    CS_INT *i = spmat->cs->p;
1933
0
    CS_INT *j = spmat->cs->i;
1934
0
    CS_ENTRY *x = spmat->cs->x;
1935
0
    CS_INT nz = spmat->cs->nz;
1936
0
    CS_INT e;
1937
1938
0
    IGRAPH_CHECK(igraph_matrix_resize(res, nrow, ncol));
1939
0
    igraph_matrix_null(res);
1940
1941
0
    for (e = 0; e < nz; e++, i++, j++, x++) {
1942
0
        MATRIX(*res, *j, *i) += *x;
1943
0
    }
1944
1945
0
    return IGRAPH_SUCCESS;
1946
0
}
1947
1948
/**
1949
 * \function igraph_sparsemat_as_matrix
1950
 * \brief Converts a sparse matrix to a dense matrix.
1951
 *
1952
 * \param res Pointer to an initialized matrix, the result is stored
1953
 *    here. It will be resized to the required size.
1954
 * \param spmat The input sparse matrix, in triplet or
1955
 *    column-compressed format.
1956
 * \return Error code.
1957
 *
1958
 * \sa \ref igraph_matrix_as_sparsemat() for the reverse conversion.
1959
 *
1960
 * Time complexity: O(mn), the number of elements in the dense
1961
 * matrix.
1962
 */
1963
1964
igraph_error_t igraph_sparsemat_as_matrix(igraph_matrix_t *res,
1965
0
                               const igraph_sparsemat_t *spmat) {
1966
0
    if (spmat->cs->nz < 0) {
1967
0
        return (igraph_i_sparsemat_as_matrix_cc(res, spmat));
1968
0
    } else {
1969
0
        return (igraph_i_sparsemat_as_matrix_triplet(res, spmat));
1970
0
    }
1971
0
}
1972
1973
/**
1974
 * \function igraph_sparsemat_max
1975
 * \brief Maximum of a sparse matrix.
1976
 *
1977
 * \param A The input matrix, column-compressed.
1978
 * \return The maximum in the input matrix, or <code>-IGRAPH_INFINITY</code>
1979
 *    if the matrix has zero elements.
1980
 *
1981
 * Time complexity: TODO.
1982
 */
1983
1984
0
igraph_real_t igraph_sparsemat_max(igraph_sparsemat_t *A) {
1985
0
    CS_INT i, n;
1986
0
    CS_ENTRY *ptr;
1987
0
    igraph_real_t res;
1988
1989
0
    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
1990
1991
0
    ptr = A->cs->x;
1992
0
    n = igraph_i_sparsemat_count_elements(A);
1993
0
    if (n == 0) {
1994
0
        return -IGRAPH_INFINITY;
1995
0
    }
1996
0
    res = *ptr;
1997
0
    for (i = 1; i < n; i++, ptr++) {
1998
0
        if (*ptr > res) {
1999
0
            res = *ptr;
2000
0
        }
2001
0
    }
2002
0
    return res;
2003
0
}
2004
2005
/* TODO: CC matrix don't actually need _dupl,
2006
   because the elements are right beside each other.
2007
   Same for max and minmax. */
2008
2009
/**
2010
 * \function igraph_sparsemat_min
2011
 * \brief Minimum of a sparse matrix.
2012
 *
2013
 * \param A The input matrix, column-compressed.
2014
 * \return The minimum in the input matrix, or \c IGRAPH_INFINITY
2015
 *    if the matrix has zero elements.
2016
 *
2017
 * Time complexity: TODO.
2018
 */
2019
2020
0
igraph_real_t igraph_sparsemat_min(igraph_sparsemat_t *A) {
2021
0
    CS_INT i, n;
2022
0
    CS_ENTRY *ptr;
2023
0
    igraph_real_t res;
2024
2025
0
    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
2026
2027
0
    ptr = A->cs->x;
2028
0
    n = igraph_i_sparsemat_count_elements(A);
2029
0
    if (n == 0) {
2030
0
        return IGRAPH_INFINITY;
2031
0
    }
2032
0
    res = *ptr;
2033
0
    for (i = 1; i < n; i++, ptr++) {
2034
0
        if (*ptr < res) {
2035
0
            res = *ptr;
2036
0
        }
2037
0
    }
2038
0
    return res;
2039
0
}
2040
2041
/**
2042
 * \function igraph_sparsemat_minmax
2043
 * \brief Minimum and maximum of a sparse matrix.
2044
 *
2045
 * \param A The input matrix, column-compressed.
2046
 * \param min The minimum in the input matrix is stored here, or \c
2047
 *    IGRAPH_INFINITY if the matrix has zero elements.
2048
 * \param max The maximum in the input matrix is stored here, or
2049
 *    <code>-IGRAPH_INFINITY</code> if the matrix has zero elements.
2050
 * \return Error code.
2051
 *
2052
 * Time complexity: TODO.
2053
 */
2054
2055
2056
igraph_error_t igraph_sparsemat_minmax(igraph_sparsemat_t *A,
2057
0
                            igraph_real_t *min, igraph_real_t *max) {
2058
0
    CS_INT i, n;
2059
0
    CS_ENTRY *ptr;
2060
2061
0
    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
2062
2063
0
    ptr = A->cs->x;
2064
0
    n = igraph_i_sparsemat_count_elements(A);
2065
0
    if (n == 0) {
2066
0
        *min = IGRAPH_INFINITY;
2067
0
        *max = -IGRAPH_INFINITY;
2068
0
        return IGRAPH_SUCCESS;
2069
0
    }
2070
0
    *min = *max = *ptr;
2071
0
    for (i = 1; i < n; i++, ptr++) {
2072
0
        if (*ptr > *max) {
2073
0
            *max = *ptr;
2074
0
        } else if (*ptr < *min) {
2075
0
            *min = *ptr;
2076
0
        }
2077
0
    }
2078
0
    return IGRAPH_SUCCESS;
2079
0
}
2080
2081
/**
2082
 * \function igraph_sparsemat_count_nonzero
2083
 * \brief Counts nonzero elements of a sparse matrix.
2084
 *
2085
 * \param A The input matrix, column-compressed.
2086
 * \return Error code.
2087
 *
2088
 * Time complexity: TODO.
2089
 */
2090
2091
0
igraph_int_t igraph_sparsemat_count_nonzero(igraph_sparsemat_t *A) {
2092
0
    CS_INT i, n;
2093
0
    CS_ENTRY *ptr;
2094
0
    igraph_int_t res = 0;
2095
2096
0
    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
2097
2098
0
    ptr = A->cs->x;
2099
0
    n = igraph_i_sparsemat_count_elements(A);
2100
0
    if (n == 0) {
2101
0
        return 0;
2102
0
    }
2103
0
    for (i = 0; i < n; i++, ptr++) {
2104
0
        if (*ptr) {
2105
0
            res++;
2106
0
        }
2107
0
    }
2108
0
    return res;
2109
0
}
2110
2111
/**
2112
 * \function igraph_sparsemat_count_nonzerotol
2113
 * \brief Counts nonzero elements of a sparse matrix, ignoring elements close to zero.
2114
 *
2115
 * Count the number of matrix entries that are closer to zero than \p tol.
2116
 *
2117
 * \param A The input matrix, column-compressed.
2118
 * \param tol The tolerance for zero comparisons.
2119
 * \return Error code.
2120
 *
2121
 * Time complexity: TODO.
2122
 */
2123
2124
igraph_int_t igraph_sparsemat_count_nonzerotol(igraph_sparsemat_t *A,
2125
0
        igraph_real_t tol) {
2126
0
    CS_INT i, n;
2127
0
    CS_ENTRY *ptr;
2128
0
    igraph_int_t res = 0;
2129
2130
0
    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
2131
2132
0
    ptr = A->cs->x;
2133
0
    n = igraph_i_sparsemat_count_elements(A);
2134
0
    if (n == 0) {
2135
0
        return 0;
2136
0
    }
2137
0
    for (i = 0; i < n; i++, ptr++) {
2138
0
        if (*ptr < - tol || *ptr > tol) {
2139
0
            res++;
2140
0
        }
2141
0
    }
2142
0
    return res;
2143
0
}
2144
2145
static igraph_error_t igraph_i_sparsemat_rowsums_triplet(const igraph_sparsemat_t *A,
2146
13.7k
                                              igraph_vector_t *res) {
2147
13.7k
    CS_INT i;
2148
13.7k
    CS_INT *pi = A->cs->i;
2149
13.7k
    CS_ENTRY *px = A->cs->x;
2150
2151
13.7k
    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m));
2152
13.7k
    igraph_vector_null(res);
2153
2154
646k
    for (i = 0; i < A->cs->nz; i++, pi++, px++) {
2155
633k
        VECTOR(*res)[ *pi ] += *px;
2156
633k
    }
2157
2158
13.7k
    return IGRAPH_SUCCESS;
2159
13.7k
}
2160
2161
static igraph_error_t igraph_i_sparsemat_rowsums_cc(const igraph_sparsemat_t *A,
2162
0
                                         igraph_vector_t *res) {
2163
0
    CS_INT ne = A->cs->p[A->cs->n];
2164
0
    CS_ENTRY *px = A->cs->x;
2165
0
    CS_INT *pi = A->cs->i;
2166
2167
0
    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m));
2168
0
    igraph_vector_null(res);
2169
2170
0
    for (; pi < A->cs->i + ne; pi++, px++) {
2171
0
        VECTOR(*res)[ *pi ] += *px;
2172
0
    }
2173
2174
0
    return IGRAPH_SUCCESS;
2175
0
}
2176
2177
/**
2178
 * \function igraph_sparsemat_rowsums
2179
 * \brief Row-wise sums.
2180
 *
2181
 * \param A The input matrix, in triplet or column-compressed format.
2182
 * \param res An initialized vector, the result is stored here. It
2183
 *    will be resized as needed.
2184
 * \return Error code.
2185
 *
2186
 * Time complexity: O(nz), the number of non-zero elements.
2187
 */
2188
2189
igraph_error_t igraph_sparsemat_rowsums(const igraph_sparsemat_t *A,
2190
13.7k
                             igraph_vector_t *res) {
2191
13.7k
    if (igraph_sparsemat_is_triplet(A)) {
2192
13.7k
        return igraph_i_sparsemat_rowsums_triplet(A, res);
2193
13.7k
    } else {
2194
0
        return igraph_i_sparsemat_rowsums_cc(A, res);
2195
0
    }
2196
13.7k
}
2197
2198
static igraph_error_t igraph_i_sparsemat_rowmins_triplet(const igraph_sparsemat_t *A,
2199
0
                                              igraph_vector_t *res) {
2200
0
    CS_INT i;
2201
0
    CS_INT *pi = A->cs->i;
2202
0
    CS_ENTRY *px = A->cs->x;
2203
2204
0
    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m));
2205
0
    igraph_vector_fill(res, IGRAPH_INFINITY);
2206
2207
0
    for (i = 0; i < A->cs->nz; i++, pi++, px++) {
2208
0
        if (*px < VECTOR(*res)[ *pi ]) {
2209
0
            VECTOR(*res)[ *pi ] = *px;
2210
0
        }
2211
0
    }
2212
2213
0
    return IGRAPH_SUCCESS;
2214
0
}
2215
2216
static igraph_error_t igraph_i_sparsemat_rowmins_cc(igraph_sparsemat_t *A,
2217
0
                                         igraph_vector_t *res) {
2218
0
    CS_INT ne;
2219
0
    CS_ENTRY *px;
2220
0
    CS_INT *pi;
2221
2222
0
    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
2223
2224
0
    ne = A->cs->p[A->cs->n];
2225
0
    px = A->cs->x;
2226
0
    pi = A->cs->i;
2227
2228
0
    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m));
2229
0
    igraph_vector_fill(res, IGRAPH_INFINITY);
2230
2231
0
    for (; pi < A->cs->i + ne; pi++, px++) {
2232
0
        if (*px < VECTOR(*res)[ *pi ]) {
2233
0
            VECTOR(*res)[ *pi ] = *px;
2234
0
        }
2235
0
    }
2236
2237
0
    return IGRAPH_SUCCESS;
2238
0
}
2239
2240
igraph_error_t igraph_sparsemat_rowmins(igraph_sparsemat_t *A,
2241
0
                             igraph_vector_t *res) {
2242
0
    if (igraph_sparsemat_is_triplet(A)) {
2243
0
        return igraph_i_sparsemat_rowmins_triplet(A, res);
2244
0
    } else {
2245
0
        return igraph_i_sparsemat_rowmins_cc(A, res);
2246
0
    }
2247
0
}
2248
2249
2250
static igraph_error_t igraph_i_sparsemat_rowmaxs_triplet(const igraph_sparsemat_t *A,
2251
0
                                              igraph_vector_t *res) {
2252
0
    CS_INT i;
2253
0
    CS_INT *pi = A->cs->i;
2254
0
    CS_ENTRY *px = A->cs->x;
2255
2256
0
    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m));
2257
0
    igraph_vector_fill(res, -IGRAPH_INFINITY);
2258
2259
0
    for (i = 0; i < A->cs->nz; i++, pi++, px++) {
2260
0
        if (*px > VECTOR(*res)[ *pi ]) {
2261
0
            VECTOR(*res)[ *pi ] = *px;
2262
0
        }
2263
0
    }
2264
2265
0
    return IGRAPH_SUCCESS;
2266
0
}
2267
2268
static igraph_error_t igraph_i_sparsemat_rowmaxs_cc(igraph_sparsemat_t *A,
2269
0
                                         igraph_vector_t *res) {
2270
0
    CS_INT ne;
2271
0
    CS_ENTRY *px;
2272
0
    CS_INT *pi;
2273
2274
0
    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
2275
2276
0
    ne = A->cs->p[A->cs->n];
2277
0
    px = A->cs->x;
2278
0
    pi = A->cs->i;
2279
2280
0
    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m));
2281
0
    igraph_vector_fill(res, -IGRAPH_INFINITY);
2282
2283
0
    for (; pi < A->cs->i + ne; pi++, px++) {
2284
0
        if (*px > VECTOR(*res)[ *pi ]) {
2285
0
            VECTOR(*res)[ *pi ] = *px;
2286
0
        }
2287
0
    }
2288
2289
0
    return IGRAPH_SUCCESS;
2290
0
}
2291
2292
igraph_error_t igraph_sparsemat_rowmaxs(igraph_sparsemat_t *A,
2293
0
                             igraph_vector_t *res) {
2294
0
    if (igraph_sparsemat_is_triplet(A)) {
2295
0
        return igraph_i_sparsemat_rowmaxs_triplet(A, res);
2296
0
    } else {
2297
0
        return igraph_i_sparsemat_rowmaxs_cc(A, res);
2298
0
    }
2299
0
}
2300
2301
static igraph_error_t igraph_i_sparsemat_colmins_triplet(const igraph_sparsemat_t *A,
2302
0
                                              igraph_vector_t *res) {
2303
0
    CS_INT i;
2304
0
    CS_INT *pp = A->cs->p;
2305
0
    CS_ENTRY *px = A->cs->x;
2306
2307
0
    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->n));
2308
0
    igraph_vector_fill(res, IGRAPH_INFINITY);
2309
2310
0
    for (i = 0; i < A->cs->nz; i++, pp++, px++) {
2311
0
        if (*px < VECTOR(*res)[ *pp ]) {
2312
0
            VECTOR(*res)[ *pp ] = *px;
2313
0
        }
2314
0
    }
2315
2316
0
    return IGRAPH_SUCCESS;
2317
0
}
2318
2319
static igraph_error_t igraph_i_sparsemat_colmins_cc(igraph_sparsemat_t *A,
2320
0
                                         igraph_vector_t *res) {
2321
0
    CS_INT n;
2322
0
    CS_ENTRY *px;
2323
0
    CS_INT *pp;
2324
0
    CS_INT *pi;
2325
0
    double *pr;
2326
2327
0
    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
2328
2329
0
    n = A->cs->n;
2330
0
    px = A->cs->x;
2331
0
    pp = A->cs->p;
2332
0
    pi = A->cs->i;
2333
2334
0
    IGRAPH_CHECK(igraph_vector_resize(res, n));
2335
0
    igraph_vector_fill(res, IGRAPH_INFINITY);
2336
0
    pr = VECTOR(*res);
2337
2338
0
    for (; pp < A->cs->p + n; pp++, pr++) {
2339
0
        for (; pi < A->cs->i + * (pp + 1); pi++, px++) {
2340
0
            if (*px < *pr) {
2341
0
                *pr = *px;
2342
0
            }
2343
0
        }
2344
0
    }
2345
0
    return IGRAPH_SUCCESS;
2346
0
}
2347
2348
igraph_error_t igraph_sparsemat_colmins(igraph_sparsemat_t *A,
2349
0
                             igraph_vector_t *res) {
2350
0
    if (igraph_sparsemat_is_triplet(A)) {
2351
0
        return igraph_i_sparsemat_colmins_triplet(A, res);
2352
0
    } else {
2353
0
        return igraph_i_sparsemat_colmins_cc(A, res);
2354
0
    }
2355
0
}
2356
2357
static igraph_error_t igraph_i_sparsemat_colmaxs_triplet(const igraph_sparsemat_t *A,
2358
0
                                              igraph_vector_t *res) {
2359
0
    CS_INT i;
2360
0
    CS_INT *pp = A->cs->p;
2361
0
    CS_ENTRY *px = A->cs->x;
2362
2363
0
    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->n));
2364
0
    igraph_vector_fill(res, -IGRAPH_INFINITY);
2365
2366
0
    for (i = 0; i < A->cs->nz; i++, pp++, px++) {
2367
0
        if (*px > VECTOR(*res)[ *pp ]) {
2368
0
            VECTOR(*res)[ *pp ] = *px;
2369
0
        }
2370
0
    }
2371
2372
0
    return IGRAPH_SUCCESS;
2373
0
}
2374
2375
static igraph_error_t igraph_i_sparsemat_colmaxs_cc(igraph_sparsemat_t *A,
2376
0
                                         igraph_vector_t *res) {
2377
0
    CS_INT n;
2378
0
    CS_ENTRY *px;
2379
0
    CS_INT *pp;
2380
0
    CS_INT *pi;
2381
0
    double *pr;
2382
2383
0
    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
2384
2385
0
    n = A->cs->n;
2386
0
    px = A->cs->x;
2387
0
    pp = A->cs->p;
2388
0
    pi = A->cs->i;
2389
2390
0
    IGRAPH_CHECK(igraph_vector_resize(res, n));
2391
0
    igraph_vector_fill(res, -IGRAPH_INFINITY);
2392
0
    pr = VECTOR(*res);
2393
2394
0
    for (; pp < A->cs->p + n; pp++, pr++) {
2395
0
        for (; pi < A->cs->i + * (pp + 1); pi++, px++) {
2396
0
            if (*px > *pr) {
2397
0
                *pr = *px;
2398
0
            }
2399
0
        }
2400
0
    }
2401
0
    return IGRAPH_SUCCESS;
2402
0
}
2403
2404
igraph_error_t igraph_sparsemat_colmaxs(igraph_sparsemat_t *A,
2405
0
                             igraph_vector_t *res) {
2406
0
    if (igraph_sparsemat_is_triplet(A)) {
2407
0
        return igraph_i_sparsemat_colmaxs_triplet(A, res);
2408
0
    } else {
2409
0
        return igraph_i_sparsemat_colmaxs_cc(A, res);
2410
0
    }
2411
0
}
2412
2413
static igraph_error_t igraph_i_sparsemat_which_min_rows_triplet(igraph_sparsemat_t *A,
2414
                                                     igraph_vector_t *res,
2415
0
                                                     igraph_vector_int_t *pos) {
2416
0
    CS_INT i;
2417
0
    CS_INT *pi = A->cs->i;
2418
0
    CS_INT *pp = A->cs->p;
2419
0
    CS_ENTRY *px = A->cs->x;
2420
2421
0
    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m));
2422
0
    IGRAPH_CHECK(igraph_vector_int_resize(pos, A->cs->m));
2423
0
    igraph_vector_fill(res, IGRAPH_INFINITY);
2424
0
    igraph_vector_int_null(pos);
2425
2426
0
    for (i = 0; i < A->cs->nz; i++, pi++, px++, pp++) {
2427
0
        if (*px < VECTOR(*res)[ *pi ]) {
2428
0
            VECTOR(*res)[ *pi ] = *px;
2429
0
            VECTOR(*pos)[ *pi ] = *pp;
2430
0
        }
2431
0
    }
2432
2433
0
    return IGRAPH_SUCCESS;
2434
0
}
2435
2436
static igraph_error_t igraph_i_sparsemat_which_min_rows_cc(igraph_sparsemat_t *A,
2437
                                                igraph_vector_t *res,
2438
0
                                                igraph_vector_int_t *pos) {
2439
0
    CS_INT n;
2440
0
    CS_ENTRY *px;
2441
0
    CS_INT *pp;
2442
0
    CS_INT *pi;
2443
0
    igraph_int_t j;
2444
2445
0
    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
2446
2447
0
    n = A->cs->n;
2448
0
    px = A->cs->x;
2449
0
    pp = A->cs->p;
2450
0
    pi = A->cs->i;
2451
2452
0
    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m));
2453
0
    IGRAPH_CHECK(igraph_vector_int_resize(pos, A->cs->m));
2454
0
    igraph_vector_fill(res, IGRAPH_INFINITY);
2455
0
    igraph_vector_int_null(pos);
2456
2457
0
    for (j = 0; pp < A->cs->p + n; pp++, j++) {
2458
0
        for (; pi < A->cs->i + * (pp + 1); pi++, px++) {
2459
0
            if (*px < VECTOR(*res)[ *pi ]) {
2460
0
                VECTOR(*res)[ *pi ] = *px;
2461
0
                VECTOR(*pos)[ *pi ] = j;
2462
0
            }
2463
0
        }
2464
0
    }
2465
2466
0
    return IGRAPH_SUCCESS;
2467
0
}
2468
2469
igraph_error_t igraph_sparsemat_which_min_rows(igraph_sparsemat_t *A,
2470
                                    igraph_vector_t *res,
2471
0
                                    igraph_vector_int_t *pos) {
2472
0
    if (igraph_sparsemat_is_triplet(A)) {
2473
0
        return igraph_i_sparsemat_which_min_rows_triplet(A, res, pos);
2474
0
    } else {
2475
0
        return igraph_i_sparsemat_which_min_rows_cc(A, res, pos);
2476
0
    }
2477
0
}
2478
2479
static igraph_error_t igraph_i_sparsemat_which_min_cols_triplet(igraph_sparsemat_t *A,
2480
                                                     igraph_vector_t *res,
2481
0
                                                     igraph_vector_int_t *pos) {
2482
2483
0
    CS_INT i;
2484
0
    CS_INT *pi = A->cs->i;
2485
0
    CS_INT *pp = A->cs->p;
2486
0
    CS_ENTRY *px = A->cs->x;
2487
2488
0
    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->n));
2489
0
    IGRAPH_CHECK(igraph_vector_int_resize(pos, A->cs->n));
2490
0
    igraph_vector_fill(res, IGRAPH_INFINITY);
2491
0
    igraph_vector_int_null(pos);
2492
2493
0
    for (i = 0; i < A->cs->nz; i++, pi++, pp++, px++) {
2494
0
        if (*px < VECTOR(*res)[ *pp ]) {
2495
0
            VECTOR(*res)[ *pp ] = *px;
2496
0
            VECTOR(*pos)[ *pp ] = *pi;
2497
0
        }
2498
0
    }
2499
2500
0
    return IGRAPH_SUCCESS;
2501
0
}
2502
2503
static igraph_error_t igraph_i_sparsemat_which_min_cols_cc(igraph_sparsemat_t *A,
2504
                                                igraph_vector_t *res,
2505
0
                                                igraph_vector_int_t *pos) {
2506
0
    CS_INT n, j, p;
2507
0
    CS_ENTRY *px;
2508
0
    double *pr;
2509
0
    igraph_int_t *ppos;
2510
2511
0
    IGRAPH_CHECK(igraph_sparsemat_dupl(A));
2512
2513
0
    n = A->cs->n;
2514
0
    px = A->cs->x;
2515
2516
0
    IGRAPH_CHECK(igraph_vector_resize(res, n));
2517
0
    igraph_vector_fill(res, IGRAPH_INFINITY);
2518
0
    pr = VECTOR(*res);
2519
0
    IGRAPH_CHECK(igraph_vector_int_resize(pos, n));
2520
0
    igraph_vector_int_null(pos);
2521
0
    ppos = VECTOR(*pos);
2522
2523
0
    for (j = 0; j < A->cs->n; j++, pr++, ppos++) {
2524
0
        for (p = A->cs->p[j]; p < A->cs->p[j + 1]; p++, px++) {
2525
0
            if (*px < *pr) {
2526
0
                *pr = *px;
2527
0
                *ppos = A->cs->i[p];
2528
0
            }
2529
0
        }
2530
0
    }
2531
0
    return IGRAPH_SUCCESS;
2532
0
}
2533
2534
igraph_error_t igraph_sparsemat_which_min_cols(igraph_sparsemat_t *A,
2535
                                    igraph_vector_t *res,
2536
0
                                    igraph_vector_int_t *pos) {
2537
0
    if (igraph_sparsemat_is_triplet(A)) {
2538
0
        return igraph_i_sparsemat_which_min_cols_triplet(A, res, pos);
2539
0
    } else {
2540
0
        return igraph_i_sparsemat_which_min_cols_cc(A, res, pos);
2541
0
    }
2542
0
}
2543
2544
static igraph_error_t igraph_i_sparsemat_colsums_triplet(const igraph_sparsemat_t *A,
2545
13.7k
                                              igraph_vector_t *res) {
2546
13.7k
    CS_INT i;
2547
13.7k
    CS_INT *pp = A->cs->p;
2548
13.7k
    CS_ENTRY *px = A->cs->x;
2549
2550
13.7k
    IGRAPH_CHECK(igraph_vector_resize(res, A->cs->n));
2551
13.7k
    igraph_vector_null(res);
2552
2553
646k
    for (i = 0; i < A->cs->nz; i++, pp++, px++) {
2554
633k
        VECTOR(*res)[ *pp ] += *px;
2555
633k
    }
2556
2557
13.7k
    return IGRAPH_SUCCESS;
2558
13.7k
}
2559
2560
static igraph_error_t igraph_i_sparsemat_colsums_cc(const igraph_sparsemat_t *A,
2561
0
                                         igraph_vector_t *res) {
2562
0
    CS_INT n = A->cs->n;
2563
0
    CS_ENTRY *px = A->cs->x;
2564
0
    CS_INT *pp = A->cs->p;
2565
0
    CS_INT *pi = A->cs->i;
2566
0
    double *pr;
2567
2568
0
    IGRAPH_CHECK(igraph_vector_resize(res, n));
2569
0
    igraph_vector_null(res);
2570
0
    pr = VECTOR(*res);
2571
2572
0
    for (; pp < A->cs->p + n; pp++, pr++) {
2573
0
        for (; pi < A->cs->i + * (pp + 1); pi++, px++) {
2574
0
            *pr += *px;
2575
0
        }
2576
0
    }
2577
0
    return IGRAPH_SUCCESS;
2578
0
}
2579
2580
/**
2581
 * \function igraph_sparsemat_colsums
2582
 * \brief Column-wise sums.
2583
 *
2584
 * \param A The input matrix, in triplet or column-compressed format.
2585
 * \param res An initialized vector, the result is stored here. It
2586
 *    will be resized as needed.
2587
 * \return Error code.
2588
 *
2589
 * Time complexity: O(nz) for triplet matrices, O(nz+n) for
2590
 * column-compressed ones, nz is the number of non-zero elements, n is
2591
 * the number of columns.
2592
 */
2593
2594
igraph_error_t igraph_sparsemat_colsums(const igraph_sparsemat_t *A,
2595
13.7k
                             igraph_vector_t *res) {
2596
13.7k
    if (igraph_sparsemat_is_triplet(A)) {
2597
13.7k
        return igraph_i_sparsemat_colsums_triplet(A, res);
2598
13.7k
    } else {
2599
0
        return igraph_i_sparsemat_colsums_cc(A, res);
2600
0
    }
2601
13.7k
}
2602
2603
/**
2604
 * \function igraph_sparsemat_scale
2605
 * \brief Scales a sparse matrix.
2606
 *
2607
 * Multiplies all elements of a sparse matrix, by the given factor.
2608
 *
2609
 * \param A The input matrix.
2610
 * \param by The scaling factor.
2611
 * \return Error code.
2612
 *
2613
 * Time complexity: O(nz), the number of non-zero elements in the
2614
 * matrix.
2615
 */
2616
2617
0
igraph_error_t igraph_sparsemat_scale(igraph_sparsemat_t *A, igraph_real_t by) {
2618
2619
0
    CS_ENTRY *px = A->cs->x;
2620
0
    CS_ENTRY *stop = px + igraph_i_sparsemat_count_elements(A);
2621
2622
0
    for (; px < stop; px++) {
2623
0
        *px *= by;
2624
0
    }
2625
2626
0
    return IGRAPH_SUCCESS;
2627
0
}
2628
2629
/**
2630
 * \function igraph_sparsemat_add_rows
2631
 * \brief Adds rows to a sparse matrix.
2632
 *
2633
 * The current matrix elements are retained and all elements in the
2634
 * new rows are zero.
2635
 * \param A The input matrix, in triplet or column-compressed format.
2636
 * \param n The number of rows to add.
2637
 * \return Error code.
2638
 *
2639
 * Time complexity: O(1).
2640
 */
2641
2642
0
igraph_error_t igraph_sparsemat_add_rows(igraph_sparsemat_t *A, igraph_int_t n) {
2643
0
    A->cs->m += n;
2644
0
    return IGRAPH_SUCCESS;
2645
0
}
2646
2647
/**
2648
 * \function igraph_sparsemat_add_cols
2649
 * \brief Adds columns to a sparse matrix.
2650
 *
2651
 * The current matrix elements are retained, and all elements in the
2652
 * new columns are zero.
2653
 * \param A The input matrix, in triplet or column-compressed format.
2654
 * \param n The number of columns to add.
2655
 * \return Error code.
2656
 *
2657
 * Time complexity: TODO.
2658
 */
2659
2660
0
igraph_error_t igraph_sparsemat_add_cols(igraph_sparsemat_t *A, igraph_int_t n) {
2661
0
    if (igraph_sparsemat_is_triplet(A)) {
2662
0
        A->cs->n += n;
2663
0
    } else {
2664
0
        CS_INT realloc_ok = 0, i;
2665
0
        CS_INT *newp = cs_realloc(A->cs->p, (A->cs->n + n + 1), sizeof(CS_INT), &realloc_ok);
2666
0
        if (!realloc_ok) {
2667
0
            IGRAPH_ERROR("Cannot add columns to sparse matrix", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */
2668
0
        }
2669
0
        if (newp != A->cs->p) {
2670
0
            A->cs->p = newp;
2671
0
        }
2672
0
        for (i = A->cs->n + 1; i < A->cs->n + n + 1; i++) {
2673
0
            A->cs->p[i] = A->cs->p[i - 1];
2674
0
        }
2675
0
        A->cs->n += n;
2676
0
    }
2677
0
    return IGRAPH_SUCCESS;
2678
0
}
2679
2680
/**
2681
 * \function igraph_sparsemat_resize
2682
 * \brief Resizes a sparse matrix and clears all the elements.
2683
 *
2684
 * This function resizes a sparse matrix. The resized sparse matrix
2685
 * will become empty, even if it contained nonzero entries.
2686
 *
2687
 * \param A The initialized sparse matrix to resize.
2688
 * \param nrow The new number of rows.
2689
 * \param ncol The new number of columns.
2690
 * \param nzmax The new maximum number of elements.
2691
 * \return Error code.
2692
 *
2693
 * Time complexity: O(nzmax), the maximum number of non-zero elements.
2694
 */
2695
2696
igraph_error_t igraph_sparsemat_resize(igraph_sparsemat_t *A, igraph_int_t nrow,
2697
20.6k
                            igraph_int_t ncol, igraph_int_t nzmax) {
2698
2699
20.6k
    if (igraph_sparsemat_is_cc(A)) {
2700
0
        igraph_sparsemat_t tmp;
2701
0
        IGRAPH_CHECK(igraph_sparsemat_init(&tmp, nrow, ncol, nzmax));
2702
0
        igraph_sparsemat_destroy(A);
2703
0
        *A = tmp;
2704
20.6k
    } else {
2705
20.6k
        IGRAPH_CHECK(igraph_sparsemat_realloc(A, nzmax));
2706
20.6k
        A->cs->m = nrow;
2707
20.6k
        A->cs->n = ncol;
2708
20.6k
        A->cs->nz = 0;
2709
20.6k
    }
2710
20.6k
    return IGRAPH_SUCCESS;
2711
20.6k
}
2712
2713
/**
2714
 * \function igraph_sparsemat_nonzero_storage
2715
 * \brief Returns number of stored entries of a sparse matrix.
2716
 *
2717
 * This function will return the number of stored entries of a sparse
2718
 * matrix. These entries can be zero, and multiple entries can be
2719
 * at the same position. Use \ref igraph_sparsemat_dupl() to sum
2720
 * duplicate entries, and \ref igraph_sparsemat_dropzeros() to remove
2721
 * zeros.
2722
 *
2723
 * \param A A sparse matrix in either triplet or compressed form.
2724
 * \return Number of stored entries.
2725
 *
2726
 * Time complexity: O(1).
2727
 */
2728
2729
0
igraph_int_t igraph_sparsemat_nonzero_storage(const igraph_sparsemat_t *A) {
2730
0
    return igraph_i_sparsemat_count_elements(A);
2731
0
}
2732
2733
2734
/**
2735
 * \function igraph_sparsemat_getelements
2736
 * \brief Returns all elements of a sparse matrix.
2737
 *
2738
 * This function will return the elements of a sparse matrix in three vectors.
2739
 * Two vectors will indicate where the elements are located, and one will
2740
 * specify the elements themselves.
2741
 *
2742
 * \param A A sparse matrix in either triplet or compressed form.
2743
 * \param i An initialized integer vector. This will store the rows of the
2744
 *          returned elements.
2745
 * \param j An initialized integer vector. For a triplet matrix this will
2746
 *          store the columns of the returned elements. For a compressed
2747
 *          matrix, if the column index is \c k, then <code>j[k]</code>
2748
 *          is the index in \p x of the start of the \c k-th column, and
2749
 *          the last element of \c j is the total number of elements.
2750
 *          The total number of elements in the \c k-th column is
2751
 *          therefore <code>j[k+1] - j[k]</code>. For example, if there
2752
 *          is one element in the first column, and five in the second,
2753
 *          \c j will be set to <code>{0, 1, 6}</code>.
2754
 * \param x An initialized vector. The elements will be placed here.
2755
 * \return Error code.
2756
 *
2757
 * Time complexity: O(n), the number of stored elements in the sparse matrix.
2758
 */
2759
2760
igraph_error_t igraph_sparsemat_getelements(const igraph_sparsemat_t *A,
2761
                                 igraph_vector_int_t *i,
2762
                                 igraph_vector_int_t *j,
2763
0
                                 igraph_vector_t *x) {
2764
0
    CS_INT nz = A->cs->nz;
2765
0
    if (nz < 0) {
2766
0
        nz = A->cs->p[A->cs->n];
2767
0
        IGRAPH_CHECK(igraph_vector_int_resize(i, nz));
2768
0
        IGRAPH_CHECK(igraph_vector_int_resize(j, A->cs->n + 1));
2769
0
        IGRAPH_CHECK(igraph_vector_resize(x, nz));
2770
0
        memcpy(VECTOR(*i), A->cs->i, (size_t) nz * sizeof(CS_INT));
2771
0
        memcpy(VECTOR(*j), A->cs->p, (size_t) (A->cs->n + 1) * sizeof(CS_INT));
2772
0
        memcpy(VECTOR(*x), A->cs->x, (size_t) nz * sizeof(CS_ENTRY));
2773
0
    } else {
2774
0
        IGRAPH_CHECK(igraph_vector_int_resize(i, nz));
2775
0
        IGRAPH_CHECK(igraph_vector_int_resize(j, nz));
2776
0
        IGRAPH_CHECK(igraph_vector_resize(x, nz));
2777
0
        memcpy(VECTOR(*i), A->cs->i, (size_t) nz * sizeof(CS_INT));
2778
0
        memcpy(VECTOR(*j), A->cs->p, (size_t) nz * sizeof(CS_INT));
2779
0
        memcpy(VECTOR(*x), A->cs->x, (size_t) nz * sizeof(CS_ENTRY));
2780
0
    }
2781
0
    return IGRAPH_SUCCESS;
2782
0
}
2783
2784
igraph_error_t igraph_sparsemat_scale_rows(igraph_sparsemat_t *A,
2785
0
                                const igraph_vector_t *fact) {
2786
0
    CS_INT *i = A->cs->i;
2787
0
    CS_ENTRY *x = A->cs->x;
2788
0
    CS_INT no_of_edges = igraph_i_sparsemat_count_elements(A);
2789
0
    CS_INT e;
2790
2791
0
    for (e = 0; e < no_of_edges; e++, x++, i++) {
2792
0
        igraph_real_t f = VECTOR(*fact)[*i];
2793
0
        (*x) *= f;
2794
0
    }
2795
2796
0
    return IGRAPH_SUCCESS;
2797
0
}
2798
2799
static igraph_error_t igraph_i_sparsemat_scale_cols_cc(igraph_sparsemat_t *A,
2800
0
                                            const igraph_vector_t *fact) {
2801
0
    CS_INT *i = A->cs->i;
2802
0
    CS_ENTRY *x = A->cs->x;
2803
0
    CS_INT no_of_edges = A->cs->p[A->cs->n];
2804
0
    CS_INT e;
2805
0
    CS_INT c = 0;        /* actual column */
2806
2807
0
    for (e = 0; e < no_of_edges; e++, x++, i++) {
2808
0
        igraph_real_t f;
2809
0
        while (c < A->cs->n && A->cs->p[c + 1] == e) {
2810
0
            c++;
2811
0
        }
2812
0
        f = VECTOR(*fact)[c];
2813
0
        (*x) *= f;
2814
0
    }
2815
2816
0
    return IGRAPH_SUCCESS;
2817
0
}
2818
2819
static igraph_error_t igraph_i_sparsemat_scale_cols_triplet(igraph_sparsemat_t *A,
2820
0
                                                 const igraph_vector_t *fact) {
2821
0
    CS_INT *j = A->cs->p;
2822
0
    CS_ENTRY *x = A->cs->x;
2823
0
    CS_INT no_of_edges = A->cs->nz;
2824
0
    CS_INT e;
2825
2826
0
    for (e = 0; e < no_of_edges; e++, x++, j++) {
2827
0
        igraph_real_t f = VECTOR(*fact)[*j];
2828
0
        (*x) *= f;
2829
0
    }
2830
2831
0
    return IGRAPH_SUCCESS;
2832
0
}
2833
2834
igraph_error_t igraph_sparsemat_scale_cols(igraph_sparsemat_t *A,
2835
0
                                const igraph_vector_t *fact) {
2836
0
    if (igraph_sparsemat_is_cc(A)) {
2837
0
        return igraph_i_sparsemat_scale_cols_cc(A, fact);
2838
0
    } else {
2839
0
        return igraph_i_sparsemat_scale_cols_triplet(A, fact);
2840
0
    }
2841
0
}
2842
2843
igraph_error_t igraph_sparsemat_multiply_by_dense(const igraph_sparsemat_t *A,
2844
                                       const igraph_matrix_t *B,
2845
0
                                       igraph_matrix_t *res) {
2846
2847
0
    igraph_int_t m = igraph_sparsemat_nrow(A);
2848
0
    igraph_int_t n = igraph_sparsemat_ncol(A);
2849
0
    igraph_int_t p = igraph_matrix_ncol(B);
2850
0
    igraph_int_t i;
2851
2852
0
    if (igraph_matrix_nrow(B) != n) {
2853
0
        IGRAPH_ERROR("Invalid dimensions in sparse-dense matrix product",
2854
0
                     IGRAPH_EINVAL);
2855
0
    }
2856
2857
0
    IGRAPH_CHECK(igraph_matrix_resize(res, m, p));
2858
0
    igraph_matrix_null(res);
2859
2860
0
    for (i = 0; i < p; i++) {
2861
0
        if (!(cs_gaxpy(A->cs, &MATRIX(*B, 0, i), &MATRIX(*res, 0, i)))) {
2862
0
            IGRAPH_ERROR("Cannot perform sparse-dense matrix multiplication",
2863
0
                         IGRAPH_FAILURE);
2864
0
        }
2865
0
    }
2866
2867
0
    return IGRAPH_SUCCESS;
2868
0
}
2869
2870
igraph_error_t igraph_sparsemat_dense_multiply(const igraph_matrix_t *A,
2871
                                    const igraph_sparsemat_t *B,
2872
0
                                    igraph_matrix_t *res) {
2873
0
    igraph_int_t m = igraph_matrix_nrow(A);
2874
0
    igraph_int_t n = igraph_matrix_ncol(A);
2875
0
    igraph_int_t p = igraph_sparsemat_ncol(B);
2876
0
    igraph_int_t r, c;
2877
0
    CS_INT *Bp = B->cs->p;
2878
2879
0
    if (igraph_sparsemat_nrow(B) != n) {
2880
0
        IGRAPH_ERROR("Invalid dimensions in dense-sparse matrix product",
2881
0
                     IGRAPH_EINVAL);
2882
0
    }
2883
2884
0
    if (!igraph_sparsemat_is_cc(B)) {
2885
0
        IGRAPH_ERROR("Dense-sparse product is only implemented for "
2886
0
                     "column-compressed sparse matrices", IGRAPH_EINVAL);
2887
0
    }
2888
2889
0
    IGRAPH_CHECK(igraph_matrix_resize(res, m, p));
2890
0
    igraph_matrix_null(res);
2891
2892
0
    for (c = 0; c < p; c++) {
2893
0
        for (r = 0; r < m; r++) {
2894
0
            igraph_int_t idx = *Bp;
2895
0
            while (idx < * (Bp + 1)) {
2896
0
                MATRIX(*res, r, c) += MATRIX(*A, r, B->cs->i[idx]) * B->cs->x[idx];
2897
0
                idx++;
2898
0
            }
2899
0
        }
2900
0
        Bp++;
2901
0
    }
2902
2903
0
    return IGRAPH_SUCCESS;
2904
0
}
2905
2906
2907
/**
2908
 * \function igraph_sparsemat_sort
2909
 * \brief Sorts all elements of a sparse matrix by row and column indices.
2910
 *
2911
 * This function will sort the elements of a sparse matrix such that iterating
2912
 * over the entries will return them sorted by column indices; elements in the
2913
 * same column are then sorted by row indices.
2914
 *
2915
 * \param A A sparse matrix in either triplet or compressed form.
2916
 * \param sorted An uninitialized sparse matrix; the result will be returned
2917
 *        here. The result will be in triplet form if the input was in triplet
2918
 *        form, otherwise it will be in compressed form. Note that sorting is
2919
 *        more efficient when the matrix is already in compressed form.
2920
 * \return Error code.
2921
 *
2922
 * Time complexity: TODO
2923
 */
2924
2925
igraph_error_t igraph_sparsemat_sort(const igraph_sparsemat_t *A,
2926
0
                          igraph_sparsemat_t *sorted) {
2927
0
    igraph_sparsemat_t tmp;
2928
0
    igraph_sparsemat_t tmp2;
2929
2930
0
    if (igraph_sparsemat_is_cc(A)) {
2931
        /* for column-compressed matrices, we will transpose the matrix twice,
2932
         * which will sort the indices as a side effect */
2933
0
        IGRAPH_CHECK(igraph_sparsemat_transpose(A, &tmp));
2934
0
        IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp);
2935
0
        IGRAPH_CHECK(igraph_sparsemat_transpose(&tmp, sorted));
2936
0
        igraph_sparsemat_destroy(&tmp);
2937
0
        IGRAPH_FINALLY_CLEAN(1);
2938
0
    } else {
2939
0
        igraph_sparsemat_iterator_t it;
2940
2941
        /* for triplet matrices, we convert it to compressed column representation,
2942
         * sort it, then we convert back */
2943
0
        IGRAPH_CHECK(igraph_sparsemat_compress(A, &tmp));
2944
0
        IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp);
2945
0
        IGRAPH_CHECK(igraph_sparsemat_sort(&tmp, &tmp2));
2946
2947
0
        igraph_sparsemat_destroy(&tmp);
2948
0
        tmp = tmp2;   /* tmp is still protected in the FINALLY stack */
2949
2950
0
        IGRAPH_CHECK(igraph_sparsemat_init(
2951
0
            sorted,
2952
0
            igraph_sparsemat_nrow(&tmp),
2953
0
            igraph_sparsemat_ncol(&tmp),
2954
0
            igraph_i_sparsemat_count_elements(&tmp)
2955
0
        ));
2956
0
        IGRAPH_FINALLY(igraph_sparsemat_destroy, sorted);
2957
2958
0
        IGRAPH_CHECK(igraph_sparsemat_iterator_init(&it, &tmp));
2959
0
        while (!igraph_sparsemat_iterator_end(&it)) {
2960
0
            IGRAPH_CHECK(igraph_sparsemat_entry(
2961
0
                sorted,
2962
0
                igraph_sparsemat_iterator_row(&it),
2963
0
                igraph_sparsemat_iterator_col(&it),
2964
0
                igraph_sparsemat_iterator_get(&it)
2965
0
            ));
2966
0
            igraph_sparsemat_iterator_next(&it);
2967
0
        }
2968
2969
0
        igraph_sparsemat_destroy(&tmp);
2970
0
        IGRAPH_FINALLY_CLEAN(2);  /* tmp + sorted */
2971
0
    }
2972
2973
0
    return IGRAPH_SUCCESS;
2974
0
}
2975
2976
/**
2977
 * \function igraph_sparsemat_getelements_sorted
2978
 * \brief Returns all elements of a sparse matrix, sorted by row and column indices.
2979
 *
2980
 * This function will sort a sparse matrix and return the elements in three
2981
 * vectors. Two vectors will indicate where the elements are located,
2982
 * and one will specify the elements themselves.
2983
 *
2984
 * </para><para>
2985
 * Sorting is done based on the \em indices of the elements, not their
2986
 * numeric values. The returned entries will be sorted by column indices;
2987
 * entries in the same column are then sorted by row indices.
2988
 *
2989
 * \param A A sparse matrix in either triplet or compressed form.
2990
 * \param i An initialized integer vector. This will store the rows of the
2991
 *          returned elements.
2992
 * \param j An initialized integer vector. For a triplet matrix this will
2993
 *          store the columns of the returned elements. For a compressed
2994
 *          matrix, if the column index is \c k, then <code>j[k]</code>
2995
 *          is the index in \p x of the start of the \c k-th column, and
2996
 *          the last element of \c j is the total number of elements.
2997
 *          The total number of elements in the \c k-th column is
2998
 *          therefore <code>j[k+1] - j[k]</code>. For example, if there
2999
 *          is one element in the first column, and five in the second,
3000
 *          \c j will be set to <code>{0, 1, 6}</code>.
3001
 * \param x An initialized vector. The elements will be placed here.
3002
 * \return Error code.
3003
 *
3004
 * Time complexity: TODO.
3005
 */
3006
3007
igraph_error_t igraph_sparsemat_getelements_sorted(const igraph_sparsemat_t *A,
3008
                                        igraph_vector_int_t *i,
3009
                                        igraph_vector_int_t *j,
3010
0
                                        igraph_vector_t *x) {
3011
0
    igraph_sparsemat_t tmp;
3012
0
    IGRAPH_CHECK(igraph_sparsemat_sort(A, &tmp));
3013
0
    IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp);
3014
0
    IGRAPH_CHECK(igraph_sparsemat_getelements(&tmp, i, j, x));
3015
0
    igraph_sparsemat_destroy(&tmp);
3016
0
    IGRAPH_FINALLY_CLEAN(1);
3017
3018
    /* TODO: in triplets format, we could in theory sort the entries without
3019
     * going through an extra sorting step (which temporarily converts the
3020
     * matrix into compressed format). This is not implemented yet. */
3021
3022
0
    return IGRAPH_SUCCESS;
3023
0
}
3024
3025
0
igraph_int_t igraph_sparsemat_nzmax(const igraph_sparsemat_t *A) {
3026
0
    return A->cs->nzmax;
3027
0
}
3028
3029
0
igraph_error_t igraph_sparsemat_neg(igraph_sparsemat_t *A) {
3030
0
    CS_INT i;
3031
0
    CS_INT nz = igraph_i_sparsemat_count_elements(A);
3032
0
    CS_ENTRY *px = A->cs->x;
3033
3034
0
    for (i = 0; i < nz; i++, px++) {
3035
0
        *px = - (*px);
3036
0
    }
3037
3038
0
    return IGRAPH_SUCCESS;
3039
0
}
3040
3041
/**
3042
 * \function igraph_sparsemat_normalize_cols
3043
 * \brief Normalizes the column sums of a sparse matrix to a given value.
3044
 *
3045
 * \param  sparsemat    The sparse matrix to normalize
3046
 * \param  allow_zeros  If false, zero-sum columns will be rejected with an error.
3047
 * \return \c IGRAPH_SUCCESS if everything was successful,
3048
 *         \c IGRAPH_EINVAL if there is at least one column with zero sum and it
3049
 *         is disallowed,
3050
 *         \c IGRAPH_ENOMEM for out-of-memory conditions
3051
 */
3052
3053
igraph_error_t igraph_sparsemat_normalize_cols(
3054
    igraph_sparsemat_t *sparsemat, igraph_bool_t allow_zeros
3055
0
) {
3056
0
    igraph_vector_t sum;
3057
0
    const igraph_int_t no_of_nodes = igraph_sparsemat_nrow(sparsemat);
3058
3059
0
    IGRAPH_VECTOR_INIT_FINALLY(&sum, no_of_nodes);
3060
3061
0
    IGRAPH_CHECK(igraph_sparsemat_colsums(sparsemat, &sum));
3062
0
    for (igraph_int_t i = 0; i < no_of_nodes; i++) {
3063
0
        if (VECTOR(sum)[i] != 0.0) {
3064
0
            VECTOR(sum)[i] = 1.0 / VECTOR(sum)[i];
3065
0
        } else if (!allow_zeros) {
3066
0
            IGRAPH_ERROR("Columns with zero sum are not allowed.", IGRAPH_EINVAL);
3067
0
        }
3068
0
    }
3069
0
    IGRAPH_CHECK(igraph_sparsemat_scale_cols(sparsemat, &sum));
3070
3071
0
    igraph_vector_destroy(&sum);
3072
0
    IGRAPH_FINALLY_CLEAN(1);
3073
3074
0
    return IGRAPH_SUCCESS;
3075
0
}
3076
3077
/**
3078
 * \function igraph_sparsemat_normalize_rows
3079
 * \brief Normalizes the row sums of a sparse matrix to a given value.
3080
 *
3081
 * \param  sparsemat    The sparse matrix to normalize
3082
 * \param  allow_zeros  If false, zero-sum rows will be rejected with an error.
3083
 * \return \c IGRAPH_SUCCESS if everything was successful,
3084
 *         \c IGRAPH_EINVAL if there is at least one row with zero sum and it
3085
 *         is disallowed,
3086
 *         \c IGRAPH_ENOMEM for out-of-memory conditions
3087
 */
3088
3089
igraph_error_t igraph_sparsemat_normalize_rows(
3090
    igraph_sparsemat_t *sparsemat, igraph_bool_t allow_zeros
3091
0
) {
3092
0
    igraph_vector_t sum;
3093
0
    const igraph_int_t no_of_nodes = igraph_sparsemat_nrow(sparsemat);
3094
3095
0
    IGRAPH_VECTOR_INIT_FINALLY(&sum, no_of_nodes);
3096
3097
0
    IGRAPH_CHECK(igraph_sparsemat_rowsums(sparsemat, &sum));
3098
0
    for (igraph_int_t i = 0; i < no_of_nodes; i++) {
3099
0
        if (VECTOR(sum)[i] != 0.0) {
3100
0
            VECTOR(sum)[i] = 1.0 / VECTOR(sum)[i];
3101
0
        } else if (!allow_zeros) {
3102
0
            IGRAPH_ERROR("Rows with zero sum are not allowed.", IGRAPH_EINVAL);
3103
0
        }
3104
0
    }
3105
0
    IGRAPH_CHECK(igraph_sparsemat_scale_rows(sparsemat, &sum));
3106
3107
0
    igraph_vector_destroy(&sum);
3108
0
    IGRAPH_FINALLY_CLEAN(1);
3109
3110
0
    return IGRAPH_SUCCESS;
3111
0
}
3112
3113
/**
3114
 * \function igraph_sparsemat_iterator_init
3115
 * \brief Initialize a sparse matrix iterator.
3116
 *
3117
 * \param it A pointer to an uninitialized sparse matrix iterator.
3118
 * \param sparsemat Pointer to the sparse matrix.
3119
 * \return Error code. This will always return \c IGRAPH_SUCCESS
3120
 *
3121
 * Time complexity: O(n), the number of columns of the sparse matrix.
3122
 */
3123
3124
igraph_error_t igraph_sparsemat_iterator_init(
3125
    igraph_sparsemat_iterator_t *it, const igraph_sparsemat_t *sparsemat
3126
34.4k
) {
3127
3128
34.4k
    it->mat = sparsemat;
3129
34.4k
    igraph_sparsemat_iterator_reset(it);
3130
34.4k
    return IGRAPH_SUCCESS;
3131
34.4k
}
3132
3133
/**
3134
 * \function igraph_sparsemat_iterator_reset
3135
 * \brief Reset a sparse matrix iterator to the first element.
3136
 *
3137
 * \param it A pointer to the sparse matrix iterator.
3138
 * \return Error code. This will always return \c IGRAPH_SUCCESS
3139
 *
3140
 * Time complexity: O(n), the number of columns of the sparse matrix.
3141
 */
3142
3143
34.4k
igraph_error_t igraph_sparsemat_iterator_reset(igraph_sparsemat_iterator_t *it) {
3144
34.4k
    it->pos = 0;
3145
34.4k
    it->col = 0;
3146
34.4k
    if (!igraph_sparsemat_is_triplet(it->mat)) {
3147
34.4k
        while (it->col < it->mat->cs->n &&
3148
34.4k
               it->mat->cs->p[it->col + 1] == it->pos) {
3149
0
            it->col ++;
3150
0
        }
3151
34.4k
    }
3152
34.4k
    return IGRAPH_SUCCESS;
3153
34.4k
}
3154
3155
/**
3156
 * \function igraph_sparsemat_iterator_end
3157
 * \brief Query if the iterator is past the last element.
3158
 *
3159
 * \param it A pointer to the sparse matrix iterator.
3160
 * \return true if the iterator is past the last element, false if it
3161
 *         points to an element in a sparse matrix.
3162
 *
3163
 * Time complexity: O(1).
3164
 */
3165
3166
igraph_bool_t
3167
1.60M
igraph_sparsemat_iterator_end(const igraph_sparsemat_iterator_t *it) {
3168
1.60M
    CS_INT nz = it->mat->cs->nz == -1 ? it->mat->cs->p[it->mat->cs->n] :
3169
1.60M
                it->mat->cs->nz;
3170
1.60M
    return it->pos >= nz;
3171
1.60M
}
3172
3173
/**
3174
 * \function igraph_sparsemat_iterator_row
3175
 * \brief Return the row of the iterator.
3176
 *
3177
 * \param it A pointer to the sparse matrix iterator.
3178
 * \return The row of the element at the current iterator position.
3179
 *
3180
 * Time complexity: O(1).
3181
 */
3182
3183
942k
igraph_int_t igraph_sparsemat_iterator_row(const igraph_sparsemat_iterator_t *it) {
3184
942k
    return it->mat->cs->i[it->pos];
3185
942k
}
3186
3187
/**
3188
 * \function igraph_sparsemat_iterator_col
3189
 * \brief Return the column of the iterator.
3190
 *
3191
 * \param it A pointer to the sparse matrix iterator.
3192
 * \return The column of the element at the current iterator position.
3193
 *
3194
 * Time complexity: O(1).
3195
 */
3196
3197
942k
igraph_int_t igraph_sparsemat_iterator_col(const igraph_sparsemat_iterator_t *it) {
3198
942k
    if (igraph_sparsemat_is_triplet(it->mat)) {
3199
0
        return it->mat->cs->p[it->pos];
3200
942k
    } else {
3201
942k
        return it->col;
3202
942k
    }
3203
942k
}
3204
3205
/**
3206
 * \function igraph_sparsemat_iterator_get
3207
 * \brief Return the element at the current iterator position.
3208
 *
3209
 * \param it A pointer to the sparse matrix iterator.
3210
 * \return The value of the element at the current iterator position.
3211
 *
3212
 * Time complexity: O(1).
3213
 */
3214
3215
igraph_real_t
3216
1.57M
igraph_sparsemat_iterator_get(const igraph_sparsemat_iterator_t *it) {
3217
1.57M
    return it->mat->cs->x[it->pos];
3218
1.57M
}
3219
3220
/**
3221
 * \function igraph_sparsemat_iterator_next
3222
 * \brief Let a sparse matrix iterator go to the next element.
3223
 *
3224
 * \param it A pointer to the sparse matrix iterator.
3225
 * \return The position of the iterator in the element vector.
3226
 *
3227
 * Time complexity: O(n), the number of columns of the sparse matrix.
3228
 */
3229
3230
1.57M
igraph_int_t igraph_sparsemat_iterator_next(igraph_sparsemat_iterator_t *it) {
3231
1.57M
    it->pos += 1;
3232
2.84M
    while (it->col < it->mat->cs->n &&
3233
2.81M
           it->mat->cs->p[it->col + 1] == it->pos) {
3234
1.27M
        it->col++;
3235
1.27M
    }
3236
1.57M
    return it->pos;
3237
1.57M
}
3238
3239
/**
3240
 * \function igraph_sparsemat_iterator_idx
3241
 * \brief Returns the element vector index of a sparse matrix iterator.
3242
 *
3243
 * \param it A pointer to the sparse matrix iterator.
3244
 * \return The position of the iterator in the element vector.
3245
 *
3246
 * Time complexity: O(1).
3247
 */
3248
3249
0
igraph_int_t igraph_sparsemat_iterator_idx(const igraph_sparsemat_iterator_t *it) {
3250
0
    return it->pos;
3251
0
}