LADEL main
Sparse LDL factorization package with rank 1 and rowadd/rowdel updates
ladel_rank1_mod.c
Go to the documentation of this file.
1#include "ladel_types.h"
2#include "ladel_global.h"
3#include "ladel_constants.h"
4#include "ladel_rank1_mod.h"
5#include "ladel_debug_print.h"
6
8{
9 ladel_int start = L->p[col], status;
10 ladel_set_set(col_set, L->i + start, L->nz[col], L->p[col+1] - L->p[col]);
11 status = ladel_set_union(col_set, set, difference, offset, insertions, col);
12
13 /* For now it is assumed the user has allocated enough space. If not, the error is passed on.
14 Technically, some reallocation code could go here to include cases where one doesn't know the
15 maximum number of nonzeros in each column of L. */
16 if (status == MAX_SET_SIZE_EXCEEDED) return MAX_SET_SIZE_EXCEEDED;
17 if (status == SET_HAS_NOT_CHANGED) return SET_HAS_NOT_CHANGED;
18
19 /* Move old nz values in L to correct positions and initialize new nz values in L to zero */
20 ladel_int index;
21 for (index = L->nz[col]-1; index >= 0; index--)
22 L->x[start+index+offset[index]] = L->x[start+index];
23
24 /* Initialize new nz values in L to zero */
25 for (index = 0; index < difference->size_set; index++)
26 L->x[start+insertions[index]] = 0;
27
28 L->nz[col] = col_set->size_set;
29
30 return status;
31}
32
33ladel_int ladel_set_union(ladel_set *first_set, ladel_set *second_set, ladel_set *difference,
34 ladel_int *offset, ladel_int *insertions, ladel_int threshold)
35{
36 ladel_int *set1 = first_set->set;
37 ladel_int size_set1 = first_set->size_set;
38 ladel_int max_size_set1 = first_set->max_size_set;
39 ladel_int *set2 = second_set->set;
40 ladel_int size_set2 = second_set->size_set;
41
42 ladel_int *dif = difference->set;
43 difference->size_set = 0;
44
45 ladel_int index1 = 0, index2, row1, row2, index_dif = 0;
46
47 /* Special case: second set is empty => do nothing */
48 if (size_set2 == 0) return SET_HAS_NOT_CHANGED;
49
50 /* Special case: first set is empty => just copy */
51 if (size_set1 == 0)
52 {
53 for (index2 = 0; index2 < size_set2; index2++)
54 {
55 row2 = set2[index2];
56 if (row2 <= threshold) continue;
57 insertions[index1] = index1;
58 set1[index1] = dif[index1] = row2;
59 index1++;
60 }
61 first_set->size_set = difference->size_set = index1;
62 if (index1 == 0) return SET_HAS_NOT_CHANGED;
63 else return SET_HAS_CHANGED;
64 }
65
66 row1 = set1[0];
67/* Construct difference set and offsets -----------------------------------*/
68 for (index2 = 0; index2 < size_set2; index2++)
69 {
70 row2 = set2[index2];
71 if (row2 <= threshold) continue;
72
73 for (; index1 < first_set->size_set && row1 < row2; index1++)
74 {
75 row1 = set1[index1];
76 offset[index1] = index_dif;
77 if (row1 >= row2) break;
78 }
79 if (row1 > row2)/*add elem of set2 in place index1 in set1*/
80 {
81 dif[index_dif] = row2;
82 index_dif++;
83
84 size_set1++;
85 if (size_set1 > max_size_set1) return MAX_SET_SIZE_EXCEEDED;
86 }
87 else if (row1 < row2) /*append the rest of set2 to the end of set1*/
88 {
89 for (; index2 < size_set2; index2++, size_set1++, index_dif++)
90 {
91 if (size_set1 == max_size_set1) return MAX_SET_SIZE_EXCEEDED;
92 dif[index_dif] = set2[index2];
93 insertions[index_dif] = index1+index_dif;
94 }
95 }
96 }
97
98 if (index_dif == 0) return SET_HAS_NOT_CHANGED;
99
100 for (; index1 < first_set->size_set; index1++) offset[index1] = index_dif;
101 difference->size_set = index_dif;
102
103/* Merge difference into the first set ------------------------------------*/
104
105 /* Move original set1 values to the correct positions */
106 for (index1 = first_set->size_set-1; index1 >= 0; index1--) set1[index1+offset[index1]] = set1[index1];
107
108 /* Compute the positions for the new elements */
109 index_dif = 0;
110 for (index1 = 0; index1 < first_set->size_set; index1++)
111 for (; index_dif < offset[index1]; index_dif++)
112 insertions[index_dif] = index1 + index_dif;
113
114 /* Insert the new elements */
115 for (index_dif = 0; index_dif < difference->size_set; index_dif++) set1[insertions[index_dif]] = dif[index_dif];
116
117 first_set->size_set = size_set1;
118 return SET_HAS_CHANGED;
119}
120
121
123{
124 if (!LD || !sym || !W || !work) return FAIL;
125
126 ladel_int *etree = sym->etree;
127 ladel_sparse_matrix *L = LD->L;
128 ladel_double *Dinv = LD->Dinv;
129
130 /* TODO: account for the permutation! */
131
132 ladel_int col, row, index, index_L, size_W = (W->nz == NULL) ? W->p[col_in_W+1] - W->p[col_in_W] : W->nz[col_in_W] ;
133 if (size_W == 0) return SUCCESS;
134 ladel_int changed = SET_HAS_NOT_CHANGED, changed_W, changed_child;
135 ladel_double sigma;
136 if (up_or_down == UPDATE) sigma = 1.0;
137 else if (up_or_down == DOWNDATE) sigma = -1.0;
138 else return FAIL;
139
140 ladel_set *set_W = work->set_unallocated_values1;
141 ladel_set_set(set_W, W->i + W->p[col_in_W], size_W, size_W);
142 ladel_set *set_L = work->set_unallocated_values2;
143 ladel_set *difference = work->set_preallocated1;
144 difference->size_set = 0;
145 ladel_set *difference2 = work->set_preallocated2;
146 difference2->size_set = 0;
147 ladel_set *difference_child = work->set_preallocated3;
148 difference_child->size_set = 0;
149 ladel_set *set_child = work->set_unallocated_values3;
150
151 ladel_int *offset = work->array_int_ncol1;
152 ladel_int *insertions = work->array_int_ncol2;
154
155 LADEL_FOR(index, W, col_in_W)
156 W_col[W->i[index]] = factor*W->x[index];
157
158 ladel_double alpha = 1, alpha_new, gamma, w, dinv;
159 ladel_int child, old_parent;
160
161 LADEL_FOR(index, W, col_in_W)
162 {
163 col = W->i[index];
164 changed = ladel_add_nonzero_pattern_to_col_of_L(L, col, set_L, set_W, difference_child, offset, insertions);
165 if (changed == MAX_SET_SIZE_EXCEEDED) return FAIL;
166 if (changed == SET_HAS_CHANGED)
167 {
168 child = col;
169 old_parent = etree[col];
170 col = etree[col] = L->i[L->p[col]]; /*new_parent, there must be one since the set has changed*/
171
172 /* prepare the difference in the next col due to the child */
173 if (col != old_parent)
174 ladel_set_set(set_child, L->i+L->p[child], L->nz[child], L->p[child+1] - L->p[child]);
175
176 break;
177 }
178 }
179
180 if (changed == SET_HAS_CHANGED)
181 {
182 while (TRUE)
183 {
184 if (col == old_parent)
185 changed_child = ladel_add_nonzero_pattern_to_col_of_L(L, col, set_L, difference_child, difference, offset, insertions);
186 else
187 changed_child = ladel_add_nonzero_pattern_to_col_of_L(L, col, set_L, set_child, difference, offset, insertions);
188
189 changed_W = ladel_add_nonzero_pattern_to_col_of_L(L, col, set_L, set_W, difference_child, offset, insertions);
190
191 if (changed_child == MAX_SET_SIZE_EXCEEDED || changed_W == MAX_SET_SIZE_EXCEEDED) return FAIL;
192
193 child = col;
194 old_parent = etree[col];
195 if (L->nz[col] == 0) break; /* There is no subdiag entry, so no new parent */
196 col = etree[col] = L->i[L->p[col]]; /* new_parent */
197
198 /* prepare the difference in the next col due to the child */
199 if (col == old_parent)
200 ladel_set_union(difference_child, difference, difference2, offset, insertions, 0);
201 else
202 ladel_set_set(set_child, L->i+L->p[child], L->nz[child], L->p[child+1] - L->p[child]);
203 }
204 }
205
206 /* numerical update */
207 for (col = W->i[W->p[col_in_W]]; col != NONE; col = etree[col])
208 {
209 w = W_col[col];
210 dinv = Dinv[col];
211 alpha_new = alpha + sigma*w*w*dinv;
212 gamma = w*dinv/alpha_new; /*if alpha_new == 0 then matrix not full rank */
213 Dinv[col] *= alpha/alpha_new;
214 alpha = alpha_new;
215 for (index_L = L->p[col]; index_L < L->p[col]+L->nz[col]; index_L++)
216 {
217 row = L->i[index_L];
218 W_col[row] -= w*L->x[index_L];
219 L->x[index_L] += sigma*gamma*W_col[row];
220 }
221 }
222
223 /* Return the double workspace to all zeros */
224 for (index = W->i[W->p[col_in_W]]; index != NONE; index = etree[index]) W_col[index] = 0;
225
226 return SUCCESS;
227
228}
#define NONE
Indicates a root (a node without parent), or an untouched node.
#define SET_HAS_NOT_CHANGED
Possible return value of ladel_set_union indicating the set has not changed.
#define TRUE
For booleans.
#define SUCCESS
For status returns.
#define FAIL
For status returns.
#define MAX_SET_SIZE_EXCEEDED
Possible return value of ladel_set_union indicating the set has grown beyond the maximum size.
#define UPDATE
Flag in rank1_update to perform an update.
#define DOWNDATE
Flag in rank1_update to perform a downdate.
#define SET_HAS_CHANGED
Possible return value of ladel_set_union indicating the set has changed.
#define LADEL_FOR(index, M, col)
Loop through a column of a sparse matrix.
ladel_int ladel_rank1_update(ladel_factor *LD, ladel_symbolics *sym, ladel_sparse_matrix *W, ladel_int col_in_W, ladel_double factor, ladel_int up_or_down, ladel_work *work)
Updates an factorization.
Constants and macros used in LADEL.
Routines to print out matrices and vectors.
Memory allocation routines.
void ladel_set_set(ladel_set *set, ladel_int *set_vals, ladel_int size_set, ladel_int max_size_set)
Fill in the fields of the given set.
Definition: ladel_global.c:247
ladel_int ladel_add_nonzero_pattern_to_col_of_L(ladel_sparse_matrix *L, ladel_int col, ladel_set *col_set, ladel_set *set, ladel_set *difference, ladel_int *offset, ladel_int *insertions)
Adds the nonzero pattern in set to the pattern of the col of L.
ladel_int ladel_set_union(ladel_set *first_set, ladel_set *second_set, ladel_set *difference, ladel_int *offset, ladel_int *insertions, ladel_int threshold)
Computes the set union of two index sets.
Helper routines for the rank1_update function.
Structures and types used in LADEL routines.
int64_t ladel_int
Type for integer numbers (default: int64_t)
Definition: ladel_types.h:24
double ladel_double
Type for floating point numbers (default: double)
Definition: ladel_types.h:20
Sparse matrix in compressed column storage.
Definition: ladel_types.h:35
ladel_double * x
numerical values (size nzmax)
Definition: ladel_types.h:42
ladel_int * p
column pointers (size ncol+1)
Definition: ladel_types.h:40
ladel_int * nz
(optional) number of elements in each column (size ncol)
Definition: ladel_types.h:43
ladel_int * i
row pointers (size nzmax)
Definition: ladel_types.h:41
Set of integers.
Definition: ladel_types.h:81
ladel_int max_size_set
Maximum (allocated) size of the list.
Definition: ladel_types.h:84
ladel_int * set
List of integers representing the set.
Definition: ladel_types.h:82
ladel_int size_set
Size of the list.
Definition: ladel_types.h:83
Factors of an factorization.
Definition: ladel_types.h:69
ladel_sparse_matrix * L
L in LDL' factorization.
Definition: ladel_types.h:71
ladel_double * Dinv
D^-1 in LDL' factorization (stored as vector)
Definition: ladel_types.h:73
Structure capturing symbolic information used for and during the factorization.
Definition: ladel_types.h:54
ladel_int * etree
eliminations tree
Definition: ladel_types.h:56
Workspace required for various routines in LADEL.
Definition: ladel_types.h:109
ladel_int * array_int_ncol2
An array of ncol integers.
Definition: ladel_types.h:117
ladel_set * set_unallocated_values3
An unallocated set structure.
Definition: ladel_types.h:115
ladel_set * set_preallocated2
A preallocated set structure.
Definition: ladel_types.h:111
ladel_int * array_int_ncol1
An array of ncol integers.
Definition: ladel_types.h:116
ladel_set * set_unallocated_values2
An unallocated set structure.
Definition: ladel_types.h:114
ladel_double * array_double_all_zeros_ncol1
An array of ncol doubles, on input and output this should be all zeros.
Definition: ladel_types.h:122
ladel_set * set_preallocated1
A preallocated set structure.
Definition: ladel_types.h:110
ladel_set * set_preallocated3
A preallocated set structure.
Definition: ladel_types.h:112
ladel_set * set_unallocated_values1
An unallocated set structure.
Definition: ladel_types.h:113