1 // =============================================================================
2 // === spqr_happly =============================================================
3 // =============================================================================
4 
5 // Applies a set n of sparse Householder vectors to a dense m-by-n matrix X.
6 //
7 //  Let H(k) = I - Tau(k) * V(k) * V(k)', then either of the following is done:
8 //
9 //  method 0: X = Q'*X
10 //      X = H(nh-1) * ... * H(1) * H(0) * X         where H is m-by-nh
11 //
12 //  method 1: X = Q*X
13 //      X = H(0)' * H(1)' * ... * H(nh-1)' * X      where H is m-by-nh
14 //
15 //  method 2: X = X*Q'
16 //      X = X * H(nh-1) * ... * H(1) * H(0)         where H is n-by-nh
17 //
18 //  method 3: X = X*Q
19 //      X = X * H(0)' * H(1) * ... * H(nh-1)'       where H is n-by-nh
20 //
21 //  The first nonzero entry in each column of H is assumed to be equal to 1.
22 //  This function does not apply the row permutation vector (the Q.P part of
23 //  the Q struct in the MATLAB interface).
24 
25 #include "spqr.hpp"
26 
27 // =============================================================================
28 // === spqr_private_do_panel ===================================================
29 // =============================================================================
30 
31 // Loads V with a panel of Householder vectors and applies them to X
32 
spqr_private_do_panel(int method,Long m,Long n,Long v,Long * Wi,Long h1,Long h2,Long * Hp,Long * Hi,Entry * Hx,Entry * Tau,Long * Wmap,Entry * X,Entry * V,Entry * C,Entry * W,cholmod_common * cc)33 template <typename Entry> void spqr_private_do_panel
34 (
35     // inputs, not modified
36     int method,         // which method to use (0,1,2,3)
37     Long m,
38     Long n,
39     Long v,             // number of Householder vectors in the panel
40     Long *Wi,           // Wi [0:v-1] defines the pattern of the panel
41     Long h1,            // load H (h1) to H (h2-1) into V
42     Long h2,
43 
44     // FUTURE : make H cholmod_sparse:
45     Long *Hp,           // Householder vectors: mh-by-nh sparse matrix
46     Long *Hi,
47     Entry *Hx,
48 
49     Entry *Tau,         // Householder coefficients (size nh)
50 
51     // input/output
52     Long *Wmap,         // inverse of Wi on input, set to all EMPTY on output
53     Entry *X,           // m-by-n with leading dimension m
54 
55     // workspace, undefined on input and output
56     Entry *V,           // dense panel
57     Entry *C,           // workspace
58     Entry *W,           // workspace
59     cholmod_common *cc
60 )
61 {
62     Entry *V1 ;
63     Long h, k, p, i ;
64 
65     // -------------------------------------------------------------------------
66     // load the panel with Householder vectors h1 ... h2-1
67     // -------------------------------------------------------------------------
68 
69     // Wi [0 .. v-1] defines the pattern of the panel, and Wmap gives its
70     // inverse (Wmap [Wi [k]] == k for k = 0 to v-1).  The V matrix is v-by-k.
71 
72 #ifndef NDEBUG
73     for (k = 0 ; k < v ; k++) ASSERT (Wmap [Wi [k]] == k) ;
74 #endif
75 
76     V1 = V ;
77     for (h = h1 ; h < h2 ; h++)
78     {
79         PR (("loading %ld\n", h)) ;
80         for (k = 0 ; k < v ; k++)
81         {
82             V1 [k] = 0 ;
83         }
84         for (p = Hp [h] ; p < Hp [h+1] ; p++)
85         {
86             i = Hi [p] ;
87             ASSERT (Wmap [i] >= 0 && Wmap [i] < v) ;
88             V1 [Wmap [i]] = Hx [p] ;
89         }
90         V1 += v ;
91     }
92 
93     // -------------------------------------------------------------------------
94     // apply the panel
95     // -------------------------------------------------------------------------
96 
97     spqr_panel (method, m, n, v, h2-h1, Wi, V, Tau+h1, m, X, C, W, cc) ;
98 
99     // -------------------------------------------------------------------------
100     // clear the panel mapping
101     // -------------------------------------------------------------------------
102 
103     for (k = 0 ; k < v ; k++)
104     {
105         i = Wi [k] ;
106         Wmap [i] = EMPTY ;
107     }
108 }
109 
110 
111 // =============================================================================
112 // === spqr_happly =============================================================
113 // =============================================================================
114 
spqr_happly(int method,Long m,Long n,Long nh,Long * Hp,Long * Hi,Entry * Hx,Entry * Tau,Entry * X,Long vmax,Long hchunk,Long * Wi,Long * Wmap,Entry * C,Entry * V,cholmod_common * cc)115 template <typename Entry> void spqr_happly
116 (
117     // input
118     int method,     // 0,1,2,3
119 
120     Long m,         // X is m-by-n with leading dimension m
121     Long n,
122 
123     // FUTURE : make H cholmod_sparse:
124     Long nh,        // number of Householder vectors
125     Long *Hp,       // size nh+1, column pointers for H
126     Long *Hi,       // size hnz = Hp [nh], row indices of H
127     Entry *Hx,      // size hnz, Householder values.  Note that the first
128                     // entry in each column must be equal to 1.0
129 
130     Entry *Tau,     // size nh
131 
132     // input/output
133     Entry *X,       // size m-by-n with leading dimension m
134 
135     // workspace
136     Long vmax,
137     Long hchunk,
138     Long *Wi,       // size vmax
139     Long *Wmap,     // size MAX(mh,1) where H is mh-by-nh; all EMPTY
140     Entry *C,       // size csize
141     Entry *V,       // size vsize
142     cholmod_common *cc
143 )
144 {
145     Entry *W ;
146     Long h, h1, h2, i, k, hmax, hmin, v, v1, p, done, v2, mh ;
147 
148     // -------------------------------------------------------------------------
149     // get inputs
150     // -------------------------------------------------------------------------
151 
152     if (m == 0 || n == 0 || nh == 0)
153     {
154         // nothing to do
155         return ;
156     }
157 
158     // number of rows of H
159     mh = (method == 0 || method == 1) ? m : n ;
160 
161     W = V + vmax * hchunk ;
162 
163     // -------------------------------------------------------------------------
164     // apply the Householder vectors
165     // -------------------------------------------------------------------------
166 
167     if (method == 0 || method == 3)
168     {
169 
170         // ---------------------------------------------------------------------
171         // apply in forward direction
172         // ---------------------------------------------------------------------
173 
174         PR (("\nHAPPLY Forward, method %d\n", method)) ;
175 
176         for (h1 = 0 ; h1 < nh ; h1 = h2)
177         {
178 
179             // -----------------------------------------------------------------
180             // start the panel with Householder vector h1
181             // -----------------------------------------------------------------
182 
183 #ifndef NDEBUG
184             for (i = 0 ; i < mh ; i++) ASSERT (Wmap [i] == EMPTY) ;
185             PR (("\n ------ h1 %ld\n", h1)) ;
186 #endif
187 
188             v = 0 ;
189             for (p = Hp [h1] ; p < Hp [h1+1] ; p++)
190             {
191                 i = Hi [p] ;
192                 Wmap [i] = v ;
193                 Wi [v] = i ;
194                 v++ ;
195             }
196             Long this_vmax = 2*v + 8 ;               // max # rows in this panel
197             this_vmax = MIN (this_vmax, mh) ;
198             ASSERT (this_vmax <= vmax) ;
199 
200             // -----------------------------------------------------------------
201             // acquire pattern of panel of Householder vectors
202             // -----------------------------------------------------------------
203 
204             hmax = MIN (h1 + hchunk, nh) ;   // at most h1 .. hmax-1 in panel
205             done = FALSE ;
206             for (h2 = h1+1 ; h2 < hmax ; h2++)
207             {
208                 PR (("try %ld\n", h2)) ;
209                 p = Hp [h2] ;
210                 i = Hi [p] ;
211                 // check to see that this vector fits in the lower triangle
212                 if (h2-h1 >= v || Wi [h2-h1] != i)
213                 {
214                     // h2 will not be part of this panel
215                     PR (("triangle broken\n")) ;
216                     break ;
217                 }
218                 v1 = v ;      // save this in case h2 is not part of panel
219                 for ( ; p < Hp [h2+1] ; p++)
220                 {
221                     i = Hi [p] ;
222                     if (Wmap [i] == EMPTY)
223                     {
224                         if (v >= this_vmax)
225                         {
226                             // h2 is not part of this panel
227                             for (v2 = v1 ; v2 < v ; v2++)
228                             {
229                                 // clear the partial column h2 from the panel
230                                 Wmap [Wi [v2]] = EMPTY ;
231                             }
232                             v = v1 ;
233                             done = TRUE ;
234                             PR (("too long\n")) ;
235                             break ;
236                         }
237                         Wmap [i] = v ;
238                         Wi [v] = i ;
239                         v++ ;
240                     }
241                 }
242                 if (done)
243                 {
244                     break ;
245                 }
246             }
247 
248             // -----------------------------------------------------------------
249             // load and apply the panel
250             // -----------------------------------------------------------------
251 
252             spqr_private_do_panel (method, m, n, v, Wi, h1, h2, Hp, Hi, Hx, Tau,
253                 Wmap, X, V, C, W, cc) ;
254         }
255 
256     }
257     else
258     {
259 
260         // ---------------------------------------------------------------------
261         // apply in backward direction
262         // ---------------------------------------------------------------------
263 
264         PR (("\nHAPPLY Backward, method %d\n", method)) ;
265 
266         for (h2 = nh ; h2 > 0 ; h2 = h1)
267         {
268 
269             // -----------------------------------------------------------------
270             // start the panel with Householder vector h2-1 as the last one
271             // -----------------------------------------------------------------
272 
273 #ifndef NDEBUG
274             for (i = 0 ; i < mh ; i++) ASSERT (Wmap [i] == EMPTY) ;
275             PR (("\n ------ h2 %ld\n", h2)) ;
276 #endif
277 
278             // use Wi as a stack, growing upwards starting at Wi [vmax-1]
279             h = h2-1 ;
280             v = vmax ;
281             for (p = Hp [h+1]-1 ; p >= Hp [h] ; p--)
282             {
283                 i = Hi [p] ;
284                 v-- ;
285                 Wmap [i] = v ;              // this will be shifted later
286                 Wi [v] = i ;
287             }
288             Long this_vmin = v - 32 ;
289             this_vmin = MAX (this_vmin, 0) ;
290 
291             // -----------------------------------------------------------------
292             // acquire pattern of panel of Householder vectors
293             // -----------------------------------------------------------------
294 
295             hmin = MAX (h2 - hchunk, 0) ;    // at most hmin .. h2-1 in panel
296             done = FALSE ;
297 
298             for (h1 = h2-2 ; h1 >= hmin ; h1--)
299             {
300                 // try to add h1 to the panel
301                 PR (("try %ld\n", h1)) ;
302 
303                 p = Hp [h1] ;
304 
305                 // check to see that this vector fits in the lower triangle
306                 Long hlen = Hp [h1+1] - p ;
307                 if (hlen > 1 && Hi [p+1] != Wi [v])
308                 {
309                     // h1 will not be part of this panel
310                     h1++ ;
311                     PR (("triangle broken\n")) ;
312                     break ;
313                 }
314 
315                 // ensure that the first entry of h1 is not in Wi
316                 i = Hi [p] ;
317                 if (Wmap [i] != EMPTY)
318                 {
319                     h1++ ;
320                     PR (("first entry %ld present; triangle broken\n", i)) ;
321                     break ;
322                 }
323 
324                 // ensure that all of h1 is already in Wi (except first entry)
325                 for (p++ ; p < Hp [h1+1] ; p++)
326                 {
327                     i = Hi [p] ;
328                     if (Wmap [i] == EMPTY)
329                     {
330                         // h1 is not in the panel
331                         done = TRUE ;
332                         PR (("pattern broken\n")) ;
333                         h1++ ;
334                         break ;
335                     }
336                 }
337 
338                 if (done)
339                 {
340                     break;
341                 }
342 
343                 // h1 is added to the panel
344                 p = Hp [h1] ;
345                 i = Hi [p] ;
346                 v-- ;
347                 Wi [v] = i ;
348                 Wmap [i] = v ;              // this will be shifted later
349 
350 #ifndef NDEBUG
351                 for (k = v ; k < vmax ; k++) ASSERT (Wmap [Wi [k]] == k) ;
352 #endif
353             }
354 
355             h1 = MAX (h1, hmin) ;
356 
357             // shift Wi upwards from Wi [v..vmax-1] to Wi [0...], and
358             // recompute Wmap
359 
360             v2 = 0 ;
361             for (k = v ; k < vmax ; k++)
362             {
363                 Wi [v2++] = Wi [k] ;
364             }
365             v = v2 ;
366 
367             for (k = 0 ; k < v ; k++)
368             {
369                 Wmap [Wi [k]] = k ;
370             }
371 
372             // -----------------------------------------------------------------
373             // load and apply the panel
374             // -----------------------------------------------------------------
375 
376             spqr_private_do_panel (method, m, n, v, Wi, h1, h2, Hp, Hi, Hx, Tau,
377                 Wmap, X, V, C, W, cc) ;
378         }
379     }
380 }
381 
382 
383 // =============================================================================
384 
385 template void spqr_happly <double>
386 (
387     // input
388     int method,     // 0,1,2,3
389 
390     Long m,         // X is m-by-n
391     Long n,
392 
393     Long nh,        // number of Householder vectors
394     Long *Hp,       // size nh+1, column pointers for H
395     Long *Hi,       // size hnz = Hp [nh], row indices of H
396     double *Hx,     // size hnz, Householder values.  Note that the first
397                     // entry in each column must be equal to 1.0
398 
399     double *Tau,    // size nh
400 
401     // input/output
402     double *X,      // size m-by-n with leading dimension m
403 
404     // workspace
405     Long vmax,
406     Long hchunk,
407     Long *Wi,       // size vmax
408     Long *Wmap,     // size MAX(mh,1) where H is mh-by-nh
409     double *C,      // size csize
410     double *V,      // size vsize
411     cholmod_common *cc
412 ) ;
413 
414 // =============================================================================
415 
416 template void spqr_happly <Complex>
417 (
418     // input
419     int method,     // 0,1,2,3
420 
421     Long m,         // X is m-by-n
422     Long n,
423 
424     Long nh,        // number of Householder vectors
425     Long *Hp,       // size nh+1, column pointers for H
426     Long *Hi,       // size hnz = Hp [nh], row indices of H
427     Complex *Hx,    // size hnz, Householder values.  Note that the first
428                     // entry in each column must be equal to 1.0
429 
430     Complex *Tau,   // size nh
431 
432     // input/output
433     Complex *X,     // size m-by-n with leading dimension m
434 
435     // workspace
436     Long vmax,
437     Long hchunk,
438     Long *Wi,       // size vmax
439     Long *Wmap,     // size MAX(mh,1) where H is mh-by-nh
440     Complex *C,     // size csize
441     Complex *V,     // size vsize
442     cholmod_common *cc
443 ) ;
444