gplu/copy.rs
1use crate::internal::{dordstat, OFF};
2use crate::gplu::PivotPolicy;
3use crate::Scalar;
4
5/// Copy dense column to sparse structure, pivot, and divide.
6///
7/// Copy column jcol from the dense vector to the sparse data structure,
8/// zeroing out the dense vector. Then find the diagonal element (either
9/// by partial or threshold pivoting or by looking for row number=col
10/// number), move it from `L` into `U`, and divide the column of `L` by it.
11///
12/// # Input variables
13///
14/// ```txt
15/// pivot = -1 for columns with no diagonal element
16/// = 0 for no pivoting
17/// = 1 for partial (row) pivoting
18/// = 2 for threshold (row) pivoting
19/// pthresh fraction of max pivot candidate acceptable for pivoting
20/// jcol Current column number.
21/// ncol Total number of columns; upper bound on row counts.
22/// ```
23///
24/// # Modified variables
25///
26/// ```txt
27/// lastlu Index of last nonzero in lu, updated here.
28/// lu On entry, cols 1 through jcol-1 of Pt(L-I+U).
29/// On exit, cols 1 through jcol.
30/// lurow, lcolst, ucolst Nonzero structure of columns 1 through jcol
31/// of PtL and PtU.
32/// No pivoting has been done, so on entry the
33/// element that will be U(jcol,jcol) is still
34/// somewhere in L; on exit, it is the last
35/// nonzero in column jcol of U.
36/// rperm The row permutation P.
37/// rperm(r) = s > 0 means row r of A is row s of PA.
38/// rperm(r) = 0 means row r of A has not yet been used
39/// as a pivot. On input, perm reflects rows 1 through
40/// jcol-1 of PA; on output, rows 1 through jcol.
41/// cperm The column permutation.
42/// dense On entry, column jcol of Pt(U(jcol,jcol)*(L-I)+U).
43/// On exit, zero.
44/// ```
45///
46/// # Output variable
47///
48/// ```txt
49/// zpivot > 0 for success (pivot row), -1 for zero pivot element.
50/// ```
51pub fn lucopy<S: Scalar>(
52 pivot: &PivotPolicy,
53 pthresh: f64,
54 dthresh: f64,
55 nzcount: isize,
56 jcol1: usize,
57 _ncol: usize,
58 lastlu: &mut usize,
59 lu: &mut [S],
60 lurow: &mut [isize],
61 lcolst: &mut [usize],
62 ucolst: &mut [usize],
63 rperm: &mut [usize],
64 cperm: &[usize],
65 dense: &mut [S],
66 pattern: &[usize],
67 twork: &mut [f64],
68) -> Result<isize, String> {
69 // Local variables:
70 // nzptr Index into lurow of current nonzero.
71 // nzst, nzend Loop bounds for nzptr.
72 // irow Row number of current nonzero (according to A, not PA).
73 // pivrow Pivot row number (according to A, not PA).
74 // maxpiv Temporary to find maximum element in column for pivoting.
75 // utemp Temporary for computing maxpiv.
76 // ujj Diagonal element U(jcol,jcol) = PtU(pivrow,jcol).
77 // ujjptr Index into lu and lurow of diagonal element.
78 // dptr Temporary index into lu and lurow.
79 // diagptr Index to diagonal element of QAQt
80 // diagpiv Value of diagonal element
81
82 let jcol = jcol1 - 1; // zero based column
83
84 // Copy column jcol from dense to sparse, recording the position of
85 // the diagonal element.
86 let mut ujjptr: usize = 0;
87
88 if *pivot == PivotPolicy::NoPivoting || *pivot == PivotPolicy::NoDiagonalElement {
89 // No pivoting, diagonal element has irow = jcol.
90 // Copy the column elements of U and L, throwing out zeros.
91
92 if ucolst[jcol + 1] - 1 < ucolst[jcol] {
93 return Err("zero length (U-I+L) column".to_string());
94 }
95
96 // Start with U.
97 let mut nzcpy = ucolst[jcol] - 1;
98 for nzptr in ucolst[jcol] - 1..lcolst[jcol] - 1 {
99 let irow = lurow[nzptr] as usize - 1;
100
101 if pattern[irow] != 0 || irow == cperm[jcol] - 1 {
102 lurow[nzcpy] = irow as isize + 1;
103 lu[nzcpy] = dense[irow];
104 dense[irow] = S::zero();
105 nzcpy += 1;
106 } else {
107 dense[irow] = S::zero();
108 }
109 }
110 let lastu = nzcpy;
111
112 // Now do L. Same action as U, except that we search for diagonal.
113 for nzptr in lcolst[jcol] - 1..ucolst[jcol + 1] - 1 {
114 let irow = lurow[nzptr] as usize - 1;
115 //if irow == cperm[jcol-off] {
116 if pattern[irow] == 2 {
117 ujjptr = nzcpy + 1
118 }
119 if pattern[irow] != 0 || /*irow == cperm(jcol-off)) then*/ pattern[irow] == 2 {
120 lurow[nzcpy] = irow as isize + 1;
121 lu[nzcpy] = dense[irow];
122 dense[irow] = S::zero();
123 nzcpy += 1;
124 } else {
125 dense[irow] = S::zero();
126 }
127 }
128
129 lcolst[jcol] = lastu + 1;
130 ucolst[jcol + 1] = nzcpy + 1;
131 *lastlu = nzcpy;
132
133 if *pivot == PivotPolicy::NoDiagonalElement {
134 let zpivot = 0; //pivrow
135 return Ok(zpivot);
136 }
137 } else {
138 let udthreshabs: f64;
139 let ldthreshabs: f64;
140 let mut rnd: usize = 0;
141
142 // Partial and threshold pivoting.
143 if ucolst[jcol + 1] - 1 < lcolst[jcol] {
144 return Err("zero length L column".to_string());
145 }
146
147 // Partial pivoting, diagonal elt. has max. magnitude in L.
148 // Compute the drop threshold for the column
149 if nzcount <= 0 {
150 let mut maxpivglb = -1.0;
151 for nzptr in ucolst[jcol] - 1..lcolst[jcol] - 1 {
152 let irow = lurow[nzptr] as usize;
153 let utemp = dense[irow - OFF].abs();
154 if utemp > maxpivglb {
155 maxpivglb = utemp;
156 }
157 }
158 udthreshabs = dthresh * maxpivglb;
159
160 maxpivglb = -1.0;
161 for nzptr in lcolst[jcol] - 1..ucolst[jcol + 1] - 1 {
162 let irow = lurow[nzptr] as usize;
163 let utemp = dense[irow - OFF].abs();
164 if utemp > maxpivglb {
165 maxpivglb = utemp;
166 }
167 }
168 ldthreshabs = dthresh * maxpivglb;
169 } else {
170 let mut i: usize = 0;
171 for nzptr in ucolst[jcol] - 1..lcolst[jcol] - 1 {
172 let irow = lurow[nzptr] as usize;
173 twork[i] = dense[irow - OFF].abs();
174 i += 1;
175 }
176 if (nzcount as usize) < i {
177 let mut kth: f64 = 0.0;
178 let mut info: isize = 0;
179 dordstat(
180 &mut rnd,
181 i,
182 i - (nzcount as usize) + 1,
183 twork,
184 &mut kth,
185 &mut info,
186 );
187 udthreshabs = kth;
188 } else {
189 udthreshabs = 0.0;
190 }
191
192 let mut i: usize = 0;
193 for nzptr in lcolst[jcol] - 1..ucolst[jcol + 1] - 1 {
194 let irow = lurow[nzptr] as usize;
195 twork[i] = dense[irow - OFF].abs();
196 i += 1;
197 }
198 if (nzcount as usize) < i {
199 let mut kth: f64 = 0.0;
200 let mut info: isize = 0;
201 dordstat(
202 &mut rnd,
203 i,
204 i - (nzcount as usize) + 1,
205 twork,
206 &mut kth,
207 &mut info,
208 );
209 ldthreshabs = kth;
210 } else {
211 ldthreshabs = 0.0;
212 }
213 }
214
215 // Copy the column elements of U, throwing out zeros.
216 let mut nzcpy = ucolst[jcol] - 1;
217 if lcolst[jcol] - 1 >= ucolst[jcol] {
218 for nzptr in ucolst[jcol] - 1..lcolst[jcol] - 1 {
219 let irow = lurow[nzptr] as usize - 1;
220
221 //if (pattern(irow) .ne. 0 .or. pattern(irow) .eq. 2) then
222 if pattern[irow] != 0 || dense[irow].abs() >= udthreshabs {
223 lurow[nzcpy] = irow as isize + 1;
224 lu[nzcpy] = dense[irow];
225 dense[irow] = S::zero();
226 nzcpy += 1;
227 } else {
228 dense[irow] = S::zero();
229 }
230 }
231 }
232 let lastu = nzcpy;
233
234 // Copy the column elements of L, throwing out zeros.
235 // Keep track of maximum magnitude element for pivot.
236
237 if ucolst[jcol + 1] - 1 < lcolst[jcol] {
238 return Err("zero length L column".to_string());
239 }
240
241 // Partial pivoting, diagonal elt. has max. magnitude in L.
242 let mut diagptr: usize = 0;
243 let mut diagpiv: f64 = 0.0;
244
245 ujjptr = 0;
246 let mut maxpiv = -1.0;
247 let mut maxpivglb = -1.0;
248
249 for nzptr in lcolst[jcol] - 1..ucolst[jcol + 1] - 1 {
250 let irow = lurow[nzptr] as usize - 1;
251 let utemp = dense[irow].abs();
252
253 //if irow == cperm[jcol-off] {
254 if pattern[irow] == 2 {
255 diagptr = irow + 1;
256 diagpiv = utemp;
257 //if diagpiv == 0 { print*, 'WARNING: Numerically zero diagonal element at col', jcol }
258 }
259
260 // original
261 //if utemp > maxpiv {
262
263 // do not pivot outside the pattern
264 // if utemp > maxpiv && pattern[irow] != 0 {
265
266 // Pivot outside pattern.
267 if utemp > maxpiv {
268 ujjptr = irow + 1;
269 maxpiv = utemp;
270 }
271
272 // Global pivot outside pattern.
273 if utemp > maxpivglb {
274 maxpivglb = utemp;
275 }
276 }
277
278 // Threshold pivoting.
279 if diagptr != 0 && diagpiv >= (pthresh * maxpiv) {
280 ujjptr = diagptr
281 }
282
283 if diagptr == 0 && ujjptr == 0 {
284 print!("error: {}", ucolst[jcol + 1] - lcolst[jcol]);
285 }
286
287 //if diagptr != ujjptr {
288 // print("pivoting", pthresh, maxpiv, diagpiv, diagptr)
289 //}
290
291 diagptr = ujjptr;
292 ujjptr = 0;
293
294 for nzptr in lcolst[jcol] - 1..ucolst[jcol + 1] - 1 {
295 let irow = lurow[nzptr] as usize - 1;
296 let utemp = dense[irow].abs();
297
298 //if irow == cperm[jcol-off] {
299 // diagptr = nzcpy
300 // diagpiv = utemp
301 //}
302
303 //if utemp > maxpiv {
304 // ujjptr = nzcpy
305 // maxpiv = utemp
306 //}
307
308 // Pattern dropping.
309 // if pattern[irow] == 0 && irow != diagptr - 1 {
310
311 // Pattern + threshold dropping.
312
313 if pattern[irow] == 0 && irow != diagptr - 1 && utemp < ldthreshabs {
314 dense[irow] = S::zero();
315 } else {
316 if irow == diagptr - 1 {
317 ujjptr = nzcpy + 1;
318 }
319
320 lurow[nzcpy] = irow as isize + 1;
321 lu[nzcpy] = dense[irow];
322 dense[irow] = S::zero();
323 nzcpy += 1;
324 }
325 }
326
327 lcolst[jcol] = lastu + 1;
328 ucolst[jcol + 1] = nzcpy + 1;
329 *lastlu = nzcpy;
330 }
331
332 // Diagonal element has been found. Swap U(jcol,jcol) from L into U.
333
334 if ujjptr == 0 {
335 return Err(format!(
336 "ujjptr not set (1) {} {} {}", /*diagptr*/
337 ujjptr,
338 lcolst[jcol],
339 ucolst[jcol + 1] - 1
340 ));
341 }
342
343 let pivrow = lurow[ujjptr - OFF];
344 let ujj = lu[ujjptr - OFF];
345
346 if ujj == S::zero() {
347 return Err(format!(
348 "numerically zero diagonal element at column {}",
349 jcol1
350 ));
351 }
352 let dptr = lcolst[jcol];
353 lurow[ujjptr - OFF] = lurow[dptr - OFF];
354 lu[ujjptr - OFF] = lu[dptr - OFF];
355 lurow[dptr - OFF] = pivrow;
356 lu[dptr - OFF] = ujj;
357 lcolst[jcol] = dptr + 1;
358
359 // Record the pivot in P.
360
361 rperm[pivrow as usize - OFF] = jcol1;
362 //if pivrow == 38 {
363 // print("exchanging", jcol, pivrow)
364 //}
365
366 // Divide column jcol of L by U(jcol,jcol).
367
368 let nzst = lcolst[jcol];
369 let nzend = ucolst[jcol + 1] - 1;
370 if nzst > nzend {
371 let zpivot = pivrow;
372 return Ok(zpivot);
373 }
374 for nzptr in nzst..=nzend {
375 lu[nzptr - OFF] = lu[nzptr - OFF] / ujj;
376 }
377
378 let zpivot = pivrow;
379 return Ok(zpivot);
380}