/* * 疎行列を扱うためのコード * * (1) 行列(sparse_matrix)のインスタンスを作成し行列の要素を設定する * (2) 行列から行列イメージ(matrix_image)を作成する * * 行列イメージをnetwork byteorderでファイルに書き出す * (3) 行列イメージを読み込み(or mmapする)要素にアクセスする * */ /* * sparse matrix crammer * * sparse matrix storage uses following 2 sparse arrays * *array of row * *array of cells in a row * *(1/2) * sparse row crammed row * 0:0 1:1 * 1:1 ---->> 3:1 * 2:0 hash(h)%m 7:1 * 3:1 / * 4:0 / * 5:0 / * 6:0 * 7:1 * 8:0 * (?:1 means non-all 0 row) *(2/2) * crammed row cram shift count * 1:1 . . -> .. shift 0 * 3:1 . . -> .. shift 2 * 7:1 . . . -> ... shift 4 * * contents of | * matrix \|/ * * ....... unified array of (value.column) pair * * matrix image * image[0] : length of hashed row array * image[1] : length of crammed cell array * image[2 ~ 2+image[0]-1] : hashed row array * image[2+image[0] ~ 2+image[0]+image[1]-1] : hashed row array * * Copyright (C) 2005 TABATA Yusuke * */ /* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include /* public APIs */ #include /* maximum length allowed for hash chain */ #define MAX_FAILURE 50 struct list_elm { int index; int value; void *ptr; struct list_elm *next; /* bypass to mitigate O(n) insertion cost */ struct list_elm *orig_next; }; struct array_elm { int index; int value; void *ptr; }; /* * sparse array has two representation * * (1) list and (2) hashed array * build list first and sparse_array_make_array() to build hashed array * this stores one value and one pointer * */ struct sparse_array { /* list representation */ int elm_count; /* sorted */ struct list_elm head; /* array representation */ int array_len; struct array_elm *array; }; static struct sparse_array * sparse_array_new(void) { struct sparse_array *a = malloc(sizeof(struct sparse_array)); /**/ a->elm_count = 0; a->head.next = NULL; a->head.orig_next = NULL; a->head.index = -1; /**/ a->array_len = 0; a->array = NULL; return a; } static void insert_elm_after(struct list_elm *elm, int idx, int val, void *ptr) { struct list_elm *new_elm = malloc(sizeof(struct list_elm)); new_elm->value = val; new_elm->index = idx; new_elm->ptr = ptr; /**/ new_elm->next = elm->next; new_elm->orig_next = elm->next; elm->next = new_elm; } static void sparse_array_set(struct sparse_array *sa, int idx, int val, void *ptr) { struct list_elm *e; e = &sa->head; while (e) { if (e->index == idx) { /* find same index and update */ e->value = val; e->ptr = ptr; return ; } /* search */ if (e->index < idx && (!e->next || idx < e->next->index)) { insert_elm_after(e, idx, val, ptr); /**/ sa->elm_count ++; return ; } /* go next */ if (e->orig_next && e->orig_next->index < idx) { /* leap ahead */ e = e->orig_next; } else { e = e->next; } } } static int hash(int val, int max, int nth) { val += nth * 113; if (val < 0) { val = -val; } if (max == 0) { return 0; } return val % max; } static int sparse_array_try_make_array(struct sparse_array *s) { int i; struct list_elm *e; /* initialize */ free(s->array); s->array = malloc(sizeof(struct array_elm) * s->array_len); for (i = 0; i < s->array_len; i++) { s->array[i].index = -1; } /* push */ for (e = s->head.next; e; e = e->next) { int ok = 0; int n = 0; do { int h = hash(e->index, s->array_len, n); if (s->array[h].index == -1) { /* find unused element in this array */ ok = 1; s->array[h].index = e->index; s->array[h].value = e->value; s->array[h].ptr = e->ptr; } else { /* collision */ n ++; if (n > MAX_FAILURE) { /* too much collision */ return 1; } } } while (!ok); } return 0; } static void sparse_array_make_array(struct sparse_array *s) { /* estimate length */ if (s->elm_count == 1) { s->array_len = 1; } else { s->array_len = s->elm_count; } while (sparse_array_try_make_array(s)) { /* expand a little */ s->array_len ++; s->array_len *= 9; s->array_len /= 8; } } static struct array_elm * sparse_array_get(struct sparse_array *s, int index, struct array_elm *arg) { if (s->array) { int n = 0; while (1) { int h = hash(index, s->array_len, n); if (s->array[h].index == index) { *arg = s->array[h]; return arg; } n ++; if (n == MAX_FAILURE) { return NULL; } } } else { struct list_elm *e = e = s->head.next; while (e) { if (e->index == index) { arg->value = e->value; arg->ptr = e->ptr; return arg; } /* go next */ if (e->orig_next && e->orig_next->index < index) { /* leap ahead */ e = e->orig_next; } else { e = e->next; } } return NULL; } } static int sparse_array_get_int(struct sparse_array *s, int index) { struct array_elm elm; if (sparse_array_get(s, index, &elm)) { return elm.value; } return 0; } static void * sparse_array_get_ptr(struct sparse_array *s, int index) { struct array_elm elm; if (sparse_array_get(s, index, &elm)) { return elm.ptr; } return NULL; } /**/ struct sparse_matrix { /**/ struct sparse_array *row_array; /* image information */ int nr_rows; int array_length; }; /* API */ struct sparse_matrix * anthy_sparse_matrix_new() { struct sparse_matrix *m = malloc(sizeof(struct sparse_matrix)); m->row_array = sparse_array_new(); m->nr_rows = 0; return m; } static struct sparse_array * find_row(struct sparse_matrix *m, int row, int create) { struct sparse_array *a; a = sparse_array_get_ptr(m->row_array, row); if (a) { return a; } if (!create) { return NULL; } /* allocate a new row */ a = sparse_array_new(); sparse_array_set(m->row_array, row, 0, a); m->nr_rows ++; return a; } /* API */ void anthy_sparse_matrix_set(struct sparse_matrix *m, int row, int column, int value, void *ptr) { struct sparse_array *a; a = find_row(m, row, 1); sparse_array_set(a, column, value, ptr); } /* API */ int anthy_sparse_matrix_get_int(struct sparse_matrix *m, int row, int column) { struct sparse_array *a; struct list_elm *e; a = find_row(m, row, 1); if (!a) { return 0; } for (e = &a->head; e; e = e->next) { if (e->index == column) { return e->value; } } return 0; } /* API */ void anthy_sparse_matrix_make_matrix(struct sparse_matrix *m) { struct array_elm *ae; int i; int offset = 0; /**/ sparse_array_make_array(m->row_array); /**/ for (i = 0; i < m->row_array->array_len; i++) { struct sparse_array *row; ae = &m->row_array->array[i]; /**/ ae->value = offset; if (ae->index == -1) { continue; } /**/ row = ae->ptr; sparse_array_make_array(row); offset += row->array_len; } m->array_length = offset; } /* API */ struct matrix_image * anthy_matrix_image_new(struct sparse_matrix *s) { struct matrix_image *mi; int i; int offset; /**/ mi = malloc(sizeof(struct matrix_image)); mi->size = 2 + s->row_array->array_len * 2 + s->array_length * 2; mi->image = malloc(sizeof(int) * mi->size); mi->image[0] = s->row_array->array_len; mi->image[1] = s->array_length; /* row index */ offset = 2; for (i = 0; i < s->row_array->array_len; i++) { struct array_elm *ae; ae = &s->row_array->array[i]; mi->image[offset + i*2] = ae->index; mi->image[offset + i*2 + 1] = ae->value; } /* cells */ offset = 2 + s->row_array->array_len * 2; for (i = 0; i < s->row_array->array_len; i++) { struct array_elm *ae; struct sparse_array *sa; int j; ae = &s->row_array->array[i]; if (ae->index == -1) { continue; } sa = ae->ptr; if (!sa) { continue; } for (j = 0; j < sa->array_len; j++) { struct array_elm *cell = &sa->array[j]; mi->image[offset] = cell->index; if (cell->index == -1) { mi->image[offset + 1] = -1; } else { mi->image[offset + 1] = cell->value; } offset += 2; } } /**/ return mi; } static int read_int(int *image, int idx, int en) { if (en) { return anthy_dic_ntohl(image[idx]); } return image[idx]; } static int do_matrix_peek(int *image, int row, int col, int en) { int n, h, shift, next_shift; int row_array_len = read_int(image, 0, en); int column_array_len; int cell_offset; /* find row */ if (row_array_len == 0) { return 0; } for (n = 0; ; n++) { h = hash(row, row_array_len, n); if (read_int(image, 2+ h * 2, en) == row) { shift = read_int(image, 2+h*2+1, en); break; } if (read_int(image, 2+ h * 2, en) == -1) { return 0; } if (n > MAX_FAILURE) { return 0; } } /* find shift count of next row */ if (h == row_array_len - 1) { /* last one */ next_shift = read_int(image, 1, en); } else { /* not last one */ next_shift = read_int(image, 2+h*2+2+1, en); } /* crammed width of this row */ column_array_len = next_shift - shift; /* cells in this image */ cell_offset = 2 + row_array_len * 2; for (n = 0; ; n++) { h = hash(col, column_array_len, n); if (read_int(image, cell_offset + shift * 2+ h * 2, en) == col) { return read_int(image, cell_offset + shift * 2 + h*2+1, en); } if (read_int(image, cell_offset + shift * 2+ h * 2, en) == -1) { /* not exist */ return 0; } if (n > MAX_FAILURE) { return 0; } } return 0; } /* API */ int anthy_matrix_image_peek(int *image, int row, int col) { if (!image) { return 0; } return do_matrix_peek(image, row, col, 1); } #ifdef DEBUG /* for debug purpose */ static void sparse_array_dump(struct sparse_array *s) { struct list_elm *e; int i; printf("list(%d):", s->elm_count); for (e = s->head.next; e; e = e->next) { printf(" %d:%d(%x)", e->index, e->value, (unsigned long)e->ptr); } printf("\n"); if (!s->array) { return ; } printf("array(%d):", s->array_len); for (i = 0; i < s->array_len; i ++) { struct array_elm *ae = &s->array[i]; if (ae->index != -1) { printf(" %d:%d,%d(%x)", i, ae->index, ae->value, (unsigned long)ae->ptr); } } printf("\n"); return ; /**/ } /* for debug purpose */ void sparse_matrix_dump(struct sparse_matrix *m) { struct list_elm *e; struct array_elm *ae; int i, offset; if (!m->row_array) { for (e = m->row_array->head.next; e; e = e->next) { sparse_array_dump(e->ptr); } return ; } printf("\nnumber of row=%d, row array size=%d, cell array size=%d\n\n", m->nr_rows, m->row_array->array_len, m->array_length); /* row part */ for (i = 0; i < m->row_array->array_len; i++) { struct array_elm *ae; ae = &m->row_array->array[i]; if (ae->index != -1) { printf(" [%d] row=%d, shift=%d\n", i, ae->index, ae->value); } } printf("\n"); offset = 0; for (i = 0; i < m->row_array->array_len; i++) { struct array_elm *ae; struct sparse_array *sa; int j; ae = &m->row_array->array[i]; sa = ae->ptr; if (!sa) { continue; } for (j = 0; j < sa->array_len; j++) { struct array_elm *cell = &sa->array[j]; if (cell->index != -1) { printf(" [%d] column=%d, value=%d\n", offset, cell->index, cell->value); } offset ++; } } printf("\n"); } #endif /* DEBUG */