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}