2 * Author: Daniel Grund, Christian Wuerdig
4 * Copyright: (c) Universitaet Karlsruhe
5 * Licence: This file protected by GPL - GNU GENERAL PUBLIC LICENSE.
6 * CVS-Id: $Id: sp_matrix.c 24123 2008-11-28 15:08:27Z mallon $
8 * Sparse matrix storage with linked lists for rows and cols.
9 * Matrix is optimized for left-to-right and top-to-bottom access.
10 * Complexity is O(1) then.
11 * Random access or right-to-left and bottom-to-top is O(m*n).
26 #include "sp_matrix.h"
32 typedef enum _iter_direction_t {
37 * Embedded list pointer.
39 typedef struct _sp_matrix_list_head_t {
40 struct _sp_matrix_list_head_t *next;
41 } sp_matrix_list_head_t;
46 typedef struct _entry_t {
47 sp_matrix_list_head_t col_chain; /**< points to next element in same column */
48 sp_matrix_list_head_t row_chain; /**< points to next element in same row */
49 matrix_elem_t e; /**< The actual element */
53 /* These specify the dimensions of the matrix.
54 * They equal the largest values ever used in matrix_set */
56 /* These are the dimensions of allocated arrays below.
57 * rowc >= maxrow and colc >= maxcol hold. */
59 /* number of entries */
61 /* arrays of sp_matrix_list_head* as entry-points to rows and cols */
62 sp_matrix_list_head_t **rows, **cols;
63 /* for iteration: first is to remember start-point;
64 * last was returned just before
65 * next is used in case last was removed from list */
67 sp_matrix_list_head_t *first, *last, *next;
68 int iter_row; /* used for iteration over all elements */
69 /* for each column the last inserted element in col list */
70 sp_matrix_list_head_t **last_col_el;
71 /* for each row the last inserted element in row list */
72 sp_matrix_list_head_t **last_row_el;
75 #define SP_MATRIX_INIT_LIST_HEAD(ptr) do { (ptr)->next = NULL; } while (0)
77 #define _offsetof(type,member) ((char *) &(((type *) 0)->member) - (char *) 0)
78 #define _container_of(ptr,type,member) ((type *) ((char *) (ptr) - _offsetof(type, member)))
80 #define is_empty_row(row) (row > m->maxrow || (m->rows[row])->next == NULL)
81 #define is_empty_col(col) (col > m->maxcol || (m->cols[col])->next == NULL)
83 #define list_entry_by_col(h) (&_container_of(h, entry_t, col_chain)->e)
84 #define list_entry_by_row(h) (&_container_of(h, entry_t, row_chain)->e)
87 * Returns the size of a single matrix element.
89 unsigned matrix_get_elem_size(void) {
90 return sizeof(entry_t);
94 * Returns the new size for an array of size old_size,
95 * which must at least store an entry at position min.
97 static inline int _m_new_size(int old_size, int min) {
99 assert(min >= old_size);
104 assert(bits < sizeof(min) * 8 - 1);
109 * Allocates space for @p count entries in the rows array and
110 * initializes all entries from @p start to the end.
112 static inline void _m_alloc_row(sp_matrix_t *m, int start, int count) {
116 m->rows = XREALLOC(m->rows, sp_matrix_list_head_t *, m->rowc);
117 m->last_row_el = XREALLOC(m->last_row_el, sp_matrix_list_head_t *, m->rowc);
119 for (p = start; p < m->rowc; ++p) {
120 m->rows[p] = XMALLOC(sp_matrix_list_head_t);
121 SP_MATRIX_INIT_LIST_HEAD(m->rows[p]);
122 m->last_row_el[p] = m->rows[p];
127 * Allocates space for @p count entries in the cols array and
128 * initializes all entries from @p start to the end.
130 static inline void _m_alloc_col(sp_matrix_t *m, int start, int count) {
134 m->cols = XREALLOC(m->cols, sp_matrix_list_head_t*, m->colc);
135 m->last_col_el = XREALLOC(m->last_col_el, sp_matrix_list_head_t*, m->colc);
137 for (p = start; p < m->colc; ++p) {
138 m->cols[p] = XMALLOC(sp_matrix_list_head_t);
139 SP_MATRIX_INIT_LIST_HEAD(m->cols[p]);
140 m->last_col_el[p] = m->cols[p];
145 * Searches in row @p row for the matrix element m[row, col], starting at element @p start.
146 * @return If the element exists:
147 * Element m[row, col] and @p prev points to the sp_matrix_list_head in the entry_t holding the element.
148 * Else: NULL and @p prev points to the sp_matrix_list_head after which the element would be inserted.
149 * @p prev_prev always points to the previous element of @p prev
151 static inline matrix_elem_t *_m_search_in_row_from(const sp_matrix_t *m,
152 int row, int col, sp_matrix_list_head_t *start, sp_matrix_list_head_t **prev, sp_matrix_list_head_t **prev_prev)
154 sp_matrix_list_head_t *row_start;
155 matrix_elem_t *res = NULL;
157 row_start = m->rows[row];
160 while ((*prev)->next != NULL && list_entry_by_row((*prev)->next)->col <= col) {
161 (*prev_prev) = (*prev);
162 *prev = (*prev)->next;
165 if (*prev != row_start) {
166 matrix_elem_t *me = list_entry_by_row(*prev);
168 if (me->row == row && me->col == col)
173 m->last_row_el[row] = *prev;
180 * Searches in row @p row for the matrix element m[row, col].
181 * @return If the element exists:
182 * Element m[row, col] and @p prev points to the sp_matrix_list_head in the entry_t holding the element.
183 * Else: NULL and @p prev points to the sp_matrix_list_head after which the element would be inserted.
184 * @p prev_prev always points to the previous element of @p prev
186 static inline matrix_elem_t *_m_search_in_row(const sp_matrix_t *m,
187 int row, int col, sp_matrix_list_head_t **prev, sp_matrix_list_head_t **prev_prev)
189 sp_matrix_list_head_t *start = m->rows[row];
193 if (m->last_row_el[row] != start) {
194 matrix_elem_t *el = list_entry_by_row(m->last_row_el[row]);
196 *prev_prev = start = m->last_row_el[row];
200 return _m_search_in_row_from(m, row, col, start, prev, prev_prev);
204 * Searches in col @p col for the matrix element m[row, col], starting at @p start.
205 * @return If the element exists:
206 * Element m[row, col] and @p prev points to the sp_matrix_list_head in the entry_t holding the element.
207 * Else: NULL and @p prev points to the sp_matrix_list_head after which the element would be inserted.
208 * @p prev_prev always points to the previous element of @p prev
210 static inline matrix_elem_t *_m_search_in_col_from(const sp_matrix_t *m,
211 int row, int col, sp_matrix_list_head_t *start, sp_matrix_list_head_t **prev, sp_matrix_list_head_t **prev_prev)
213 sp_matrix_list_head_t *col_start;
214 matrix_elem_t *res = NULL;
216 col_start = m->cols[col];
219 while ((*prev)->next != NULL && list_entry_by_col((*prev)->next)->row <= row) {
220 *prev_prev = (*prev);
221 *prev = (*prev)->next;
224 if (*prev != col_start) {
225 matrix_elem_t *me = list_entry_by_col(*prev);
227 if (me->row == row && me->col == col)
232 m->last_col_el[col] = *prev;
239 * Searches in col @p col for the matrix element m[row, col].
240 * @return If the element exists:
241 * Element m[row, col] and @p prev points to the sp_matrix_list_head in the entry_t holding the element.
242 * Else: NULL and @p prev points to the sp_matrix_list_head after which the element would be inserted.
243 * @p prev_prev always points to the previous element of @p prev
245 static inline matrix_elem_t *_m_search_in_col(const sp_matrix_t *m,
246 int row, int col, sp_matrix_list_head_t **prev, sp_matrix_list_head_t **prev_prev)
248 sp_matrix_list_head_t *start = m->cols[col];
252 if (m->last_col_el[col] != start) {
253 matrix_elem_t *el = list_entry_by_col(m->last_col_el[col]);
255 *prev_prev = start = m->last_col_el[col];
259 return _m_search_in_col_from(m, row, col, start, prev, prev_prev);
262 sp_matrix_t *new_matrix(int row_init, int col_init) {
263 sp_matrix_t *res = XMALLOCZ(sp_matrix_t);
266 _m_alloc_row(res, 0, MAX(0, row_init));
267 _m_alloc_col(res, 0, MAX(0, col_init));
271 void del_matrix(sp_matrix_t *m) {
274 for (i = 0; i < m->rowc; ++i) {
275 if (! is_empty_row(i)) {
277 sp_matrix_list_head_t *n;
279 n = m->rows[i]->next;
281 /* get current matrix element */
282 e = _container_of(n, entry_t, row_chain);
290 for (i = 0; i < m->colc; ++i)
292 xfree(m->last_col_el);
293 xfree(m->last_row_el);
299 void matrix_set(sp_matrix_t *m, int row, int col, double val) {
300 matrix_elem_t *me = NULL;
302 sp_matrix_list_head_t *leftof, *above;
303 sp_matrix_list_head_t *prev_leftof, *prev_above;
305 /* if necessary enlarge the matrix */
306 if (row > m->maxrow) {
309 _m_alloc_row(m, m->rowc, _m_new_size(m->rowc, row));
311 if (col > m->maxcol) {
314 _m_alloc_col(m, m->colc, _m_new_size(m->colc, col));
317 /* search for existing entry */
318 if (m->maxrow < m->maxcol)
319 me = _m_search_in_col(m, row, col, &above, &prev_above);
321 me = _m_search_in_row(m, row, col, &leftof, &prev_leftof);
323 /* if it exists, set the value and return */
326 me->val = (float)val;
328 entr = _container_of(me, entry_t, e);
330 /* remove row_chain entry */
332 prev_leftof->next = entr->row_chain.next;
334 m->rows[row]->next = entr->row_chain.next;
336 /* remove col_chain entry */
338 prev_above->next = entr->col_chain.next;
340 m->cols[col]->next = entr->col_chain.next;
342 entr->row_chain.next = NULL;
343 entr->col_chain.next = NULL;
345 /* set the last pointer to the "previous" element */
346 if (m->last_col_el[col] == &entr->col_chain ||
347 m->last_row_el[row] == &entr->row_chain)
349 m->last_col_el[col] = prev_above ? prev_above : m->cols[col];
350 m->last_row_el[row] = prev_leftof ? prev_leftof : m->rows[row];
359 /* if it does not exist and 0 should be set just quit */
363 /* if it does not exist and val != 0 search the other direction */
364 if (m->maxrow >= m->maxcol)
365 _m_search_in_col(m, row, col, &above, &prev_above);
367 _m_search_in_row(m, row, col, &leftof, &prev_leftof);
368 /* now leftof and above are the entry_t's prior the new one in each direction */
370 /* insert new entry */
371 entr = XMALLOC(entry_t);
374 entr->e.val = (float)val;
376 /* add row_chain entry */
377 entr->row_chain.next = leftof->next;
378 leftof->next = &entr->row_chain;
380 /* add col_chain entry */
381 entr->col_chain.next = above->next;
382 above->next = &entr->col_chain;
384 m->last_col_el[col] = &entr->col_chain;
385 m->last_row_el[row] = &entr->row_chain;
390 void matrix_set_row_bulk(sp_matrix_t *m, int row, int *cols, int num_cols, double val) {
391 matrix_elem_t *me = NULL;
394 sp_matrix_list_head_t *leftof, *above;
395 sp_matrix_list_head_t *prev_leftof, *prev_above;
397 /* if necessary enlarge the matrix */
398 if (row > m->maxrow) {
401 _m_alloc_row(m, m->rowc, _m_new_size(m->rowc, row));
403 if (cols[num_cols - 1] > m->maxcol) {
404 m->maxcol = cols[num_cols - 1];
405 if (cols[num_cols - 1] >= m->colc)
406 _m_alloc_col(m, m->colc, _m_new_size(m->colc, cols[num_cols - 1]));
409 /* set start values */
413 for (i = 0; i < num_cols; ++i) {
414 /* search for existing entry */
415 me = _m_search_in_row(m, row, cols[i], &leftof, &prev_leftof);
417 /* if it exists, set the value and return */
420 me->val = (float)val;
422 entr = _container_of(me, entry_t, e);
424 /* remove row_chain entry */
426 prev_leftof->next = entr->row_chain.next;
428 m->rows[row]->next = entr->row_chain.next;
430 /* remove col_chain entry */
432 prev_above->next = entr->col_chain.next;
434 m->cols[cols[i]]->next = entr->col_chain.next;
436 entr->row_chain.next = NULL;
437 entr->col_chain.next = NULL;
439 /* set the last pointer to the "previous" element */
440 if (m->last_col_el[cols[i]] == &entr->col_chain ||
441 m->last_row_el[row] == &entr->row_chain)
443 m->last_col_el[cols[i]] = prev_above ? prev_above : m->cols[cols[i]];
444 m->last_row_el[row] = prev_leftof ? prev_leftof : m->rows[row];
454 /* if it does not exist and 0 should be set just quit */
458 /* we have to search the col list as well, to get the above pointer */
459 _m_search_in_col(m, row, cols[i], &above, &prev_above);
461 /* now leftof and above are the entry_t's prior the new one in each direction */
463 /* insert new entry */
464 entr = XMALLOC(entry_t);
466 entr->e.col = cols[i];
467 entr->e.val = (float)val;
469 m->last_col_el[cols[i]] = &entr->col_chain;
470 m->last_row_el[row] = &entr->row_chain;
472 /* add row_chain entry */
473 entr->row_chain.next = leftof->next;
474 leftof->next = &entr->row_chain;
476 /* add col_chain entry */
477 entr->col_chain.next = above->next;
478 above->next = &entr->col_chain;
484 double matrix_get(const sp_matrix_t *m, int row, int col) {
485 sp_matrix_list_head_t *dummy, *dummy2;
488 if (is_empty_row(row) || is_empty_col(col))
491 if (m->maxrow < m->maxcol)
492 me = _m_search_in_col(m, row, col, &dummy, &dummy2);
494 me = _m_search_in_row(m, row, col, &dummy, &dummy2);
497 assert(me->col == col && me->row == row);
499 return me ? me->val : 0.0;
502 int matrix_get_entries(const sp_matrix_t *m) {
506 int matrix_get_rowcount(const sp_matrix_t *m) {
507 return m->maxrow + 1;
510 int matrix_get_colcount(const sp_matrix_t *m) {
511 return m->maxcol + 1;
514 const matrix_elem_t *matrix_row_first(sp_matrix_t *m, int row) {
515 if (is_empty_row(row))
519 m->first = m->rows[row];
520 m->last = m->first->next;
521 m->next = m->last ? m->last->next : NULL;
523 assert (list_entry_by_row(m->last)->row == row);
525 return list_entry_by_row(m->last);
528 const matrix_elem_t *matrix_col_first(sp_matrix_t *m, int col) {
529 if (is_empty_col(col))
533 m->first = m->cols[col];
534 m->last = m->first->next;
535 m->next = m->last ? m->last->next : NULL;
537 assert (list_entry_by_col(m->last)->col == col);
539 return list_entry_by_col(m->last);
542 static inline const matrix_elem_t *matrix_first_from(sp_matrix_t *m, int startrow) {
543 const matrix_elem_t *res;
546 for (i = startrow; i <= m->maxrow; ++i) {
547 res = matrix_row_first(m, i);
558 const matrix_elem_t *matrix_first(sp_matrix_t *m) {
559 return matrix_first_from(m, 0);
562 const matrix_elem_t *matrix_next(sp_matrix_t *m) {
563 assert(m->first && "Start iteration with matrix_???_first, before calling me!");
565 if (m->next == NULL) {
567 return matrix_first_from(m, ++m->iter_row);
573 m->next = m->next->next;
576 return list_entry_by_col(m->last);
577 else /* right or all */
578 return list_entry_by_row(m->last);
581 static int cmp_count(const void *e1, const void *e2) {
582 return (int *)e2 - (int *)e1;
585 static inline void matrix_fill_row(sp_matrix_t *m, int row, bitset_t *fullrow) {
586 const matrix_elem_t *e;
587 bitset_set(fullrow, row);
588 matrix_foreach_in_col(m, row, e) {
589 if (! bitset_is_set(fullrow, e->row)) {
590 assert(0.0 == matrix_get(m, e->col, e->row));
591 matrix_set(m, e->col, e->row, e->val);
592 matrix_set(m, e->row, e->col, 0.0);
597 void matrix_optimize(sp_matrix_t *m) {
600 const matrix_elem_t *e;
603 size = MAX(m->maxcol, m->maxrow)+1;
605 /* kill all double-entries (Mij and Mji are set) */
606 matrix_foreach(m, e) {
609 assert(e->row != e->col && "Root has itself as arg. Ok. But the arg (=root) will always have the same color as root");
610 t_val = matrix_get(m, e->col, e->row);
611 if (fabs(t_val) > 1e-10) {
612 matrix_set(m, e->col, e->row, 0);
613 matrix_set(m, e->row, e->col, e->val + t_val);
617 c = alloca(size * sizeof(*c));
619 fullrow = bitset_alloca(size);
621 /* kill 'all' rows containing only 1 entry */
624 /* count elements in rows */
625 memset(c, 0, size * sizeof(*c));
630 for (i = 0; i<size; ++i)
631 if (c[i] == 1 && ! bitset_is_set(fullrow, i)) {
633 /* if the other row isn't empty move the e in there, else fill e's row */
634 if (e = matrix_row_first(m, i), e) {
636 matrix_fill_row(m, e->col, fullrow);
638 matrix_fill_row(m, e->row, fullrow);
644 memset(c, 0, size * sizeof(*c));
648 qsort(c, size, sizeof(*c), cmp_count);
650 for (i = 0; i < size; ++i) {
651 if (! bitset_is_set(fullrow, i))
652 matrix_fill_row(m, i, fullrow);
656 void matrix_dump(sp_matrix_t *m, FILE *out, int factor) {
658 const matrix_elem_t *e;
660 for (i = 0; i <= m->maxrow; ++i) {
662 matrix_foreach_in_row(m, i, e) {
663 for (o = last_idx + 1; o < e->col; ++o)
664 fprintf(out, " %4.1f" , 0.0);
666 fprintf(out, " %4.1f", factor * e->val);
670 for (o = last_idx + 1; o <= m->maxcol; ++o)
671 fprintf(out, " %4.1f" , 0.0);
677 void matrix_self_test(int d) {
679 const matrix_elem_t *e;
680 sp_matrix_t *m = new_matrix(10, 10);
682 for (i = 0; i < d; ++i)
683 for (o = 0; o < d; ++o)
684 matrix_set(m, i, o, i*o);
686 for (i = 0; i < d; ++i)
687 for (o = 0; o<d; ++o)
688 assert(matrix_get(m, i, o) == i*o);
691 matrix_foreach_in_row(m,1,e) {
695 assert(!matrix_next(m)); /*iter must finish */
698 matrix_foreach_in_col(m,d-1,e) {
702 assert(!matrix_next(m));
704 m = new_matrix(16,16);
705 matrix_set(m, 1,1,9);
706 matrix_set(m, 1,2,8);
707 matrix_set(m, 1,3,7);
709 matrix_set(m, 1,3,6);
710 matrix_set(m, 1,2,5);
711 matrix_set(m, 1,1,4);
713 matrix_foreach_in_row(m, 1, e) {
714 assert(e->row == 1 && e->col == i && e->val == i+3);
721 matrix_set(m, 1,1,1);
722 matrix_set(m, 2,2,2);
723 matrix_set(m, 3,3,3);
724 matrix_set(m, 3,5,4);
725 matrix_set(m, 4,4,5);
726 matrix_set(m, 5,5,6);
727 for (i=1, e = matrix_first(m); e; ++i, e=matrix_next(m))
730 matrix_set(m, 1,1,0);
731 assert(5 == matrix_get_entries(m));