1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
//---------------------------------------------------------
// low-level internal utilities for counting / filling entries
// in block partitioned sparse matrices.
//---------------------------------------------------------
use crate::algebra::{CscMatrix, MatrixShape, MatrixTriangle};
use num_traits::Num;
use std::iter::zip;
impl<T> CscMatrix<T>
where
T: Num + Copy,
{
// increment self.colptr by the number of nonzeros
// in a dense upper/lower triangle on the diagonal.
pub(crate) fn colcount_dense_triangle(
&mut self,
initcol: usize,
blockcols: usize,
shape: MatrixTriangle,
) {
let cols = self.colptr[(initcol)..(initcol + blockcols)].iter_mut();
let counts = 1..(blockcols + 1);
match shape {
MatrixTriangle::Triu => {
zip(cols, counts).for_each(|(x, c)| *x += c);
}
MatrixTriangle::Tril => {
zip(cols, counts.rev()).for_each(|(x, c)| *x += c);
}
}
}
// increment the self.colptr by the number of nonzeros
// in a square diagonal matrix placed on the diagonal.
pub(crate) fn colcount_diag(&mut self, initcol: usize, blockcols: usize) {
let cols = self.colptr[initcol..(initcol + blockcols)].iter_mut();
cols.for_each(|x| *x += 1);
}
// same as kkt_count_diag, but counts places
// where the input matrix M has a missing
// diagonal entry. M must be square and TRIU
pub(crate) fn colcount_missing_diag(&mut self, M: &CscMatrix<T>, initcol: usize) {
assert_eq!(M.colptr.len(), M.n + 1);
assert!(self.colptr.len() >= M.n + initcol);
for i in 0..M.n {
if M.colptr[i] == M.colptr[i+1] || // completely empty column
M.rowval[M.colptr[i+1]-1] != i
// last element is not on diagonal
{
self.colptr[i + initcol] += 1;
}
}
}
// increment the self.colptr by the a number of nonzeros.
// used to account for the placement of a column
// vector that partially populates the column
pub(crate) fn colcount_colvec(&mut self, n: usize, _firstrow: usize, firstcol: usize) {
// just add the vector length to this column
self.colptr[firstcol] += n;
}
// increment the self.colptr by 1 for every element
// used to account for the placement of a column
// vector that partially populates the column
pub(crate) fn colcount_rowvec(&mut self, n: usize, _firstrow: usize, firstcol: usize) {
// add one element to each of n consective columns
// starting from initcol. The row index doesn't
// matter here.
let cols = self.colptr[firstcol..(firstcol + n)].iter_mut();
cols.for_each(|x| *x += 1);
}
// increment the self.colptr by the number of nonzeros in M
pub(crate) fn colcount_block(&mut self, M: &CscMatrix<T>, initcol: usize, shape: MatrixShape) {
match shape {
MatrixShape::T => {
for row in M.rowval.iter() {
self.colptr[initcol + row] += 1;
}
}
MatrixShape::N => {
// just add the column count
for i in 0..M.n {
self.colptr[initcol + i] += M.colptr[i + 1] - M.colptr[i];
}
}
}
}
//populate a partial column with zeros using the self.colptr as the indicator
// the next fill location in each row.
pub(crate) fn fill_colvec(&mut self, vtoKKT: &mut [usize], initrow: usize, initcol: usize) {
for (i, v) in vtoKKT.iter_mut().enumerate() {
let dest = self.colptr[initcol];
self.rowval[dest] = initrow + i;
self.nzval[dest] = T::zero();
*v = dest;
self.colptr[initcol] += 1;
}
}
// populate a partial row with zeros using the self.colptr as indicator of
// next fill location in each row.
pub(crate) fn fill_rowvec(&mut self, vtoKKT: &mut [usize], initrow: usize, initcol: usize) {
for (i, v) in vtoKKT.iter_mut().enumerate() {
let col = initcol + i;
let dest = self.colptr[col];
self.rowval[dest] = initrow;
self.nzval[dest] = T::zero();
*v = dest;
self.colptr[col] += 1;
}
}
// populate values from M using the self.colptr as indicator of
// next fill location in each row.
#[allow(clippy::needless_range_loop)]
pub(crate) fn fill_block(
&mut self,
M: &CscMatrix<T>,
MtoKKT: &mut [usize],
initrow: usize,
initcol: usize,
shape: MatrixShape,
) {
for i in 0..M.n {
let start = M.colptr[i];
let stop = M.colptr[i + 1];
for j in start..stop {
let (col, row);
match shape {
MatrixShape::T => {
col = M.rowval[j] + initcol;
row = i + initrow;
}
MatrixShape::N => {
col = i + initcol;
row = M.rowval[j] + initrow;
}
};
let dest = self.colptr[col];
self.rowval[dest] = row;
self.nzval[dest] = M.nzval[j];
MtoKKT[j] = dest;
self.colptr[col] += 1;
}
}
}
// Populate the upper or lower triangle with 0s using the self.colptr
// as indicator of next fill location in each row
pub(crate) fn fill_dense_triangle(
&mut self,
blocktoKKT: &mut [usize],
offset: usize,
blockdim: usize,
shape: MatrixTriangle,
) {
// data will always be supplied as triu, so when filling it into
// a tril shape we also need to transpose it. Just write two
// separate functions for clarity here
match shape {
MatrixTriangle::Triu => {
self._fill_dense_triangle_triu(blocktoKKT, offset, blockdim);
}
MatrixTriangle::Tril => {
self._fill_dense_triangle_tril(blocktoKKT, offset, blockdim);
}
}
}
pub(crate) fn _fill_dense_triangle_triu(
&mut self,
blocktoKKT: &mut [usize],
offset: usize,
blockdim: usize,
) {
let mut kidx = 0;
for col in offset..(offset + blockdim) {
for row in offset..=col {
let dest = self.colptr[col];
self.rowval[dest] = row;
self.nzval[dest] = T::zero(); //structural zero
self.colptr[col] += 1;
blocktoKKT[kidx] = dest;
kidx += 1;
}
}
}
pub(crate) fn _fill_dense_triangle_tril(
&mut self,
blocktoKKT: &mut [usize],
offset: usize,
blockdim: usize,
) {
let mut kidx = 0;
for row in offset..(offset + blockdim) {
for col in offset..=row {
let dest = self.colptr[col];
self.rowval[dest] = row;
self.nzval[dest] = T::zero(); //structural zero
self.colptr[col] += 1;
blocktoKKT[kidx] = dest;
kidx += 1;
}
}
}
// Populate the diagonal with 0s using the K.colptr as indicator of
// next fill location in each row
pub(crate) fn fill_diag(&mut self, diagtoKKT: &mut [usize], offset: usize, blockdim: usize) {
for (i, col) in (offset..(offset + blockdim)).enumerate() {
let dest = self.colptr[col];
self.rowval[dest] = col;
self.nzval[dest] = T::zero(); //structural zero
self.colptr[col] += 1;
diagtoKKT[i] = dest;
}
}
// same as fill_diag, but only places zero
// entries where the input matrix M has a missing
// diagonal entry. M must be square and TRIU
pub(crate) fn fill_missing_diag(&mut self, M: &CscMatrix<T>, initcol: usize) {
for i in 0..M.n {
// fill out missing diagonal terms only
if M.colptr[i] == M.colptr[i+1] || // completely empty column
M.rowval[M.colptr[i+1]-1] != i
{
// last element is not on diagonal
let dest = self.colptr[i + initcol];
self.rowval[dest] = i + initcol;
self.nzval[dest] = T::zero(); //structural zero
self.colptr[i + initcol] += 1;
}
}
}
// treats self.colptr as a vector of counts for entries in each column,
// and rebuilds a valid colptr from it
pub(crate) fn colcount_to_colptr(&mut self) {
let mut currentptr = 0;
for p in &mut self.colptr {
let count = *p;
*p = currentptr;
currentptr += count;
}
}
// converts the colptr vector a vector of entry counts for each column
// the final entry is set to zero
pub(crate) fn colptr_to_colcount(&mut self) {
for i in 0..self.n {
self.colptr[i] = self.colptr[i + 1] - self.colptr[i];
}
self.colptr[self.n] = 0;
}
pub(crate) fn backshift_colptrs(&mut self) {
self.colptr.rotate_right(1);
self.colptr[0] = 0;
}
pub(crate) fn count_diagonal_entries(&self, shape: MatrixTriangle) -> usize {
let mut count = 0;
match shape {
MatrixTriangle::Triu => {
for i in 0..self.n {
// compare last entry in each column with
// its row number to identify diagonal entries
if self.colptr[i+1] != self.colptr[i] && // nonempty column
self.rowval[self.colptr[i+1]-1] == i
{
// last element is on diagonal
count += 1;
}
}
}
MatrixTriangle::Tril => {
for i in 0..self.n {
// compare first entry in each column with
// its row number to identify diagonal entries
if self.colptr[i+1] != self.colptr[i] && // nonempty column
self.rowval[self.colptr[i]] == i
{
// first element is on diagonal
count += 1;
}
}
}
}
count
}
}
#[test]
fn test_count_diagonal_entries_triu() {
let shape = MatrixTriangle::Triu;
let A = CscMatrix::from(&[
[1., 2., 3.], //
[0., 0., 4.], //
[0., 0., 1.],
]); //
assert_eq!(A.count_diagonal_entries(shape), 2);
let A = CscMatrix::from(&[
[1., 0., 3.], //
[0., 0., 4.], //
[0., 0., 1.],
]); //
assert_eq!(A.count_diagonal_entries(shape), 2);
let A = CscMatrix::from(&[
[1., 0., 3.], //
[0., 4., 4.], //
[0., 0., 1.],
]); //
assert_eq!(A.count_diagonal_entries(shape), 3);
let A = CscMatrix::<f64>::zeros((5, 5)); //
assert_eq!(A.count_diagonal_entries(shape), 0);
}
#[test]
fn test_count_diagonal_entries_tril() {
let shape = MatrixTriangle::Tril;
let A = CscMatrix::from(&[
[1., 0., 0.], //
[2., 0., 0.], //
[3., 4., 1.],
]); //
assert_eq!(A.count_diagonal_entries(shape), 2);
let A = CscMatrix::from(&[
[1., 0., 0.], //
[0., 0., 0.], //
[3., 4., 1.],
]); //
assert_eq!(A.count_diagonal_entries(shape), 2);
let A = CscMatrix::from(&[
[1., 0., 0.], //
[0., 4., 0.], //
[3., 4., 1.],
]); //
assert_eq!(A.count_diagonal_entries(shape), 3);
let A = CscMatrix::<f64>::zeros((5, 5)); //
assert_eq!(A.count_diagonal_entries(shape), 0);
}