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)
91 return sizeof(entry_t);
95 * Returns the new size for an array of size old_size,
96 * which must at least store an entry at position min.
98 static inline int m_new_size(int old_size, int min)
101 assert(min >= old_size);
106 assert(bits < sizeof(min) * 8 - 1);
111 * Allocates space for @p count entries in the rows array and
112 * initializes all entries from @p start to the end.
114 static inline void m_alloc_row(sp_matrix_t *m, int start, int count)
119 m->rows = XREALLOC(m->rows, sp_matrix_list_head_t *, m->rowc);
120 m->last_row_el = XREALLOC(m->last_row_el, sp_matrix_list_head_t *, m->rowc);
122 for (p = start; p < m->rowc; ++p) {
123 m->rows[p] = XMALLOC(sp_matrix_list_head_t);
124 SP_MATRIX_INIT_LIST_HEAD(m->rows[p]);
125 m->last_row_el[p] = m->rows[p];
130 * Allocates space for @p count entries in the cols array and
131 * initializes all entries from @p start to the end.
133 static inline void m_alloc_col(sp_matrix_t *m, int start, int count)
138 m->cols = XREALLOC(m->cols, sp_matrix_list_head_t*, m->colc);
139 m->last_col_el = XREALLOC(m->last_col_el, sp_matrix_list_head_t*, m->colc);
141 for (p = start; p < m->colc; ++p) {
142 m->cols[p] = XMALLOC(sp_matrix_list_head_t);
143 SP_MATRIX_INIT_LIST_HEAD(m->cols[p]);
144 m->last_col_el[p] = m->cols[p];
149 * Searches in row @p row for the matrix element m[row, col], starting at element @p start.
150 * @return If the element exists:
151 * Element m[row, col] and @p prev points to the sp_matrix_list_head in the entry_t holding the element.
152 * Else: NULL and @p prev points to the sp_matrix_list_head after which the element would be inserted.
153 * @p prev_prev always points to the previous element of @p prev
155 static inline matrix_elem_t *m_search_in_row_from(const sp_matrix_t *m,
156 int row, int col, sp_matrix_list_head_t *start, sp_matrix_list_head_t **prev, sp_matrix_list_head_t **prev_prev)
158 sp_matrix_list_head_t *row_start;
159 matrix_elem_t *res = NULL;
161 row_start = m->rows[row];
164 while ((*prev)->next != NULL && list_entry_by_row((*prev)->next)->col <= col) {
165 (*prev_prev) = (*prev);
166 *prev = (*prev)->next;
169 if (*prev != row_start) {
170 matrix_elem_t *me = list_entry_by_row(*prev);
172 if (me->row == row && me->col == col)
177 m->last_row_el[row] = *prev;
184 * Searches in row @p row for the matrix element m[row, col].
185 * @return If the element exists:
186 * Element m[row, col] and @p prev points to the sp_matrix_list_head in the entry_t holding the element.
187 * Else: NULL and @p prev points to the sp_matrix_list_head after which the element would be inserted.
188 * @p prev_prev always points to the previous element of @p prev
190 static inline matrix_elem_t *m_search_in_row(const sp_matrix_t *m,
191 int row, int col, sp_matrix_list_head_t **prev, sp_matrix_list_head_t **prev_prev)
193 sp_matrix_list_head_t *start = m->rows[row];
197 if (m->last_row_el[row] != start) {
198 matrix_elem_t *el = list_entry_by_row(m->last_row_el[row]);
200 *prev_prev = start = m->last_row_el[row];
204 return m_search_in_row_from(m, row, col, start, prev, prev_prev);
208 * Searches in col @p col for the matrix element m[row, col], starting at @p start.
209 * @return If the element exists:
210 * Element m[row, col] and @p prev points to the sp_matrix_list_head in the entry_t holding the element.
211 * Else: NULL and @p prev points to the sp_matrix_list_head after which the element would be inserted.
212 * @p prev_prev always points to the previous element of @p prev
214 static inline matrix_elem_t *m_search_in_col_from(const sp_matrix_t *m,
215 int row, int col, sp_matrix_list_head_t *start, sp_matrix_list_head_t **prev, sp_matrix_list_head_t **prev_prev)
217 sp_matrix_list_head_t *col_start;
218 matrix_elem_t *res = NULL;
220 col_start = m->cols[col];
223 while ((*prev)->next != NULL && list_entry_by_col((*prev)->next)->row <= row) {
224 *prev_prev = (*prev);
225 *prev = (*prev)->next;
228 if (*prev != col_start) {
229 matrix_elem_t *me = list_entry_by_col(*prev);
231 if (me->row == row && me->col == col)
236 m->last_col_el[col] = *prev;
243 * Searches in col @p col for the matrix element m[row, col].
244 * @return If the element exists:
245 * Element m[row, col] and @p prev points to the sp_matrix_list_head in the entry_t holding the element.
246 * Else: NULL and @p prev points to the sp_matrix_list_head after which the element would be inserted.
247 * @p prev_prev always points to the previous element of @p prev
249 static inline matrix_elem_t *m_search_in_col(const sp_matrix_t *m,
250 int row, int col, sp_matrix_list_head_t **prev, sp_matrix_list_head_t **prev_prev)
252 sp_matrix_list_head_t *start = m->cols[col];
256 if (m->last_col_el[col] != start) {
257 matrix_elem_t *el = list_entry_by_col(m->last_col_el[col]);
259 *prev_prev = start = m->last_col_el[col];
263 return m_search_in_col_from(m, row, col, start, prev, prev_prev);
266 sp_matrix_t *new_matrix(int row_init, int col_init)
268 sp_matrix_t *res = XMALLOCZ(sp_matrix_t);
271 m_alloc_row(res, 0, MAX(0, row_init));
272 m_alloc_col(res, 0, MAX(0, col_init));
276 void del_matrix(sp_matrix_t *m)
280 for (i = 0; i < m->rowc; ++i) {
281 if (! is_empty_row(i)) {
283 sp_matrix_list_head_t *n;
285 n = m->rows[i]->next;
287 /* get current matrix element */
288 e = _container_of(n, entry_t, row_chain);
296 for (i = 0; i < m->colc; ++i)
298 xfree(m->last_col_el);
299 xfree(m->last_row_el);
305 void matrix_set(sp_matrix_t *m, int row, int col, double val)
307 matrix_elem_t *me = NULL;
309 sp_matrix_list_head_t *leftof, *above;
310 sp_matrix_list_head_t *prev_leftof, *prev_above;
312 /* if necessary enlarge the matrix */
313 if (row > m->maxrow) {
316 m_alloc_row(m, m->rowc, m_new_size(m->rowc, row));
318 if (col > m->maxcol) {
321 m_alloc_col(m, m->colc, m_new_size(m->colc, col));
324 /* search for existing entry */
325 if (m->maxrow < m->maxcol)
326 me = m_search_in_col(m, row, col, &above, &prev_above);
328 me = m_search_in_row(m, row, col, &leftof, &prev_leftof);
330 /* if it exists, set the value and return */
333 me->val = (float)val;
335 entr = _container_of(me, entry_t, e);
337 /* remove row_chain entry */
339 prev_leftof->next = entr->row_chain.next;
341 m->rows[row]->next = entr->row_chain.next;
343 /* remove col_chain entry */
345 prev_above->next = entr->col_chain.next;
347 m->cols[col]->next = entr->col_chain.next;
349 entr->row_chain.next = NULL;
350 entr->col_chain.next = NULL;
352 /* set the last pointer to the "previous" element */
353 if (m->last_col_el[col] == &entr->col_chain ||
354 m->last_row_el[row] == &entr->row_chain)
356 m->last_col_el[col] = prev_above ? prev_above : m->cols[col];
357 m->last_row_el[row] = prev_leftof ? prev_leftof : m->rows[row];
366 /* if it does not exist and 0 should be set just quit */
370 /* if it does not exist and val != 0 search the other direction */
371 if (m->maxrow >= m->maxcol)
372 m_search_in_col(m, row, col, &above, &prev_above);
374 m_search_in_row(m, row, col, &leftof, &prev_leftof);
375 /* now leftof and above are the entry_t's prior the new one in each direction */
377 /* insert new entry */
378 entr = XMALLOC(entry_t);
381 entr->e.val = (float)val;
383 /* add row_chain entry */
384 entr->row_chain.next = leftof->next;
385 leftof->next = &entr->row_chain;
387 /* add col_chain entry */
388 entr->col_chain.next = above->next;
389 above->next = &entr->col_chain;
391 m->last_col_el[col] = &entr->col_chain;
392 m->last_row_el[row] = &entr->row_chain;
397 void matrix_set_row_bulk(sp_matrix_t *m, int row, int *cols, int num_cols, double val)
399 matrix_elem_t *me = NULL;
402 sp_matrix_list_head_t *leftof, *above;
403 sp_matrix_list_head_t *prev_leftof, *prev_above;
405 /* if necessary enlarge the matrix */
406 if (row > m->maxrow) {
409 m_alloc_row(m, m->rowc, m_new_size(m->rowc, row));
411 if (cols[num_cols - 1] > m->maxcol) {
412 m->maxcol = cols[num_cols - 1];
413 if (cols[num_cols - 1] >= m->colc)
414 m_alloc_col(m, m->colc, m_new_size(m->colc, cols[num_cols - 1]));
417 /* set start values */
421 for (i = 0; i < num_cols; ++i) {
422 /* search for existing entry */
423 me = m_search_in_row(m, row, cols[i], &leftof, &prev_leftof);
425 /* if it exists, set the value and return */
428 me->val = (float)val;
430 entr = _container_of(me, entry_t, e);
432 /* remove row_chain entry */
434 prev_leftof->next = entr->row_chain.next;
436 m->rows[row]->next = entr->row_chain.next;
438 /* remove col_chain entry */
440 prev_above->next = entr->col_chain.next;
442 m->cols[cols[i]]->next = entr->col_chain.next;
444 entr->row_chain.next = NULL;
445 entr->col_chain.next = NULL;
447 /* set the last pointer to the "previous" element */
448 if (m->last_col_el[cols[i]] == &entr->col_chain ||
449 m->last_row_el[row] == &entr->row_chain)
451 m->last_col_el[cols[i]] = prev_above ? prev_above : m->cols[cols[i]];
452 m->last_row_el[row] = prev_leftof ? prev_leftof : m->rows[row];
462 /* if it does not exist and 0 should be set just quit */
466 /* we have to search the col list as well, to get the above pointer */
467 m_search_in_col(m, row, cols[i], &above, &prev_above);
469 /* now leftof and above are the entry_t's prior the new one in each direction */
471 /* insert new entry */
472 entr = XMALLOC(entry_t);
474 entr->e.col = cols[i];
475 entr->e.val = (float)val;
477 m->last_col_el[cols[i]] = &entr->col_chain;
478 m->last_row_el[row] = &entr->row_chain;
480 /* add row_chain entry */
481 entr->row_chain.next = leftof->next;
482 leftof->next = &entr->row_chain;
484 /* add col_chain entry */
485 entr->col_chain.next = above->next;
486 above->next = &entr->col_chain;
492 double matrix_get(const sp_matrix_t *m, int row, int col)
494 sp_matrix_list_head_t *dummy, *dummy2;
497 if (is_empty_row(row) || is_empty_col(col))
500 if (m->maxrow < m->maxcol)
501 me = m_search_in_col(m, row, col, &dummy, &dummy2);
503 me = m_search_in_row(m, row, col, &dummy, &dummy2);
506 assert(me->col == col && me->row == row);
508 return me ? me->val : 0.0;
511 int matrix_get_entries(const sp_matrix_t *m)
516 int matrix_get_rowcount(const sp_matrix_t *m)
518 return m->maxrow + 1;
521 int matrix_get_colcount(const sp_matrix_t *m)
523 return m->maxcol + 1;
526 const matrix_elem_t *matrix_row_first(sp_matrix_t *m, int row)
528 if (is_empty_row(row))
532 m->first = m->rows[row];
533 m->last = m->first->next;
534 m->next = m->last ? m->last->next : NULL;
536 assert (list_entry_by_row(m->last)->row == row);
538 return list_entry_by_row(m->last);
541 const matrix_elem_t *matrix_col_first(sp_matrix_t *m, int col)
543 if (is_empty_col(col))
547 m->first = m->cols[col];
548 m->last = m->first->next;
549 m->next = m->last ? m->last->next : NULL;
551 assert (list_entry_by_col(m->last)->col == col);
553 return list_entry_by_col(m->last);
556 static inline const matrix_elem_t *matrix_first_from(sp_matrix_t *m, int startrow)
558 const matrix_elem_t *res;
561 for (i = startrow; i <= m->maxrow; ++i) {
562 res = matrix_row_first(m, i);
573 const matrix_elem_t *matrix_first(sp_matrix_t *m)
575 return matrix_first_from(m, 0);
578 const matrix_elem_t *matrix_next(sp_matrix_t *m)
580 assert(m->first && "Start iteration with matrix_???_first, before calling me!");
582 if (m->next == NULL) {
584 return matrix_first_from(m, ++m->iter_row);
590 m->next = m->next->next;
593 return list_entry_by_col(m->last);
594 else /* right or all */
595 return list_entry_by_row(m->last);
598 static int cmp_count(const void *e1, const void *e2)
600 return (int *)e2 - (int *)e1;
603 static inline void matrix_fill_row(sp_matrix_t *m, int row, bitset_t *fullrow)
605 const matrix_elem_t *e;
606 bitset_set(fullrow, row);
607 matrix_foreach_in_col(m, row, e) {
608 if (! bitset_is_set(fullrow, e->row)) {
609 assert(0.0 == matrix_get(m, e->col, e->row));
610 matrix_set(m, e->col, e->row, e->val);
611 matrix_set(m, e->row, e->col, 0.0);
616 void matrix_optimize(sp_matrix_t *m)
620 const matrix_elem_t *e;
623 size = MAX(m->maxcol, m->maxrow)+1;
625 /* kill all double-entries (Mij and Mji are set) */
626 matrix_foreach(m, e) {
629 assert(e->row != e->col && "Root has itself as arg. Ok. But the arg (=root) will always have the same color as root");
630 t_val = matrix_get(m, e->col, e->row);
631 if (fabs(t_val) > 1e-10) {
632 matrix_set(m, e->col, e->row, 0);
633 matrix_set(m, e->row, e->col, e->val + t_val);
637 c = alloca(size * sizeof(*c));
639 fullrow = bitset_alloca(size);
641 /* kill 'all' rows containing only 1 entry */
644 /* count elements in rows */
645 memset(c, 0, size * sizeof(*c));
650 for (i = 0; i<size; ++i)
651 if (c[i] == 1 && ! bitset_is_set(fullrow, i)) {
653 /* if the other row isn't empty move the e in there, else fill e's row */
654 if (e = matrix_row_first(m, i), e) {
656 matrix_fill_row(m, e->col, fullrow);
658 matrix_fill_row(m, e->row, fullrow);
664 memset(c, 0, size * sizeof(*c));
668 qsort(c, size, sizeof(*c), cmp_count);
670 for (i = 0; i < size; ++i) {
671 if (! bitset_is_set(fullrow, i))
672 matrix_fill_row(m, i, fullrow);
676 void matrix_dump(sp_matrix_t *m, FILE *out, int factor)
679 const matrix_elem_t *e;
681 for (i = 0; i <= m->maxrow; ++i) {
683 matrix_foreach_in_row(m, i, e) {
684 for (o = last_idx + 1; o < e->col; ++o)
685 fprintf(out, " %4.1f" , 0.0);
687 fprintf(out, " %4.1f", factor * e->val);
691 for (o = last_idx + 1; o <= m->maxcol; ++o)
692 fprintf(out, " %4.1f" , 0.0);
698 void matrix_self_test(int d)
701 const matrix_elem_t *e;
702 sp_matrix_t *m = new_matrix(10, 10);
704 for (i = 0; i < d; ++i)
705 for (o = 0; o < d; ++o)
706 matrix_set(m, i, o, i*o);
708 for (i = 0; i < d; ++i)
709 for (o = 0; o<d; ++o)
710 assert(matrix_get(m, i, o) == i*o);
713 matrix_foreach_in_row(m,1,e) {
717 assert(!matrix_next(m)); /*iter must finish */
720 matrix_foreach_in_col(m,d-1,e) {
724 assert(!matrix_next(m));
726 m = new_matrix(16,16);
727 matrix_set(m, 1,1,9);
728 matrix_set(m, 1,2,8);
729 matrix_set(m, 1,3,7);
731 matrix_set(m, 1,3,6);
732 matrix_set(m, 1,2,5);
733 matrix_set(m, 1,1,4);
735 matrix_foreach_in_row(m, 1, e) {
736 assert(e->row == 1 && e->col == i && e->val == i+3);
743 matrix_set(m, 1,1,1);
744 matrix_set(m, 2,2,2);
745 matrix_set(m, 3,3,3);
746 matrix_set(m, 3,5,4);
747 matrix_set(m, 4,4,5);
748 matrix_set(m, 5,5,6);
749 for (i=1, e = matrix_first(m); e; ++i, e=matrix_next(m))
752 matrix_set(m, 1,1,0);
753 assert(5 == matrix_get_entries(m));