1 // =============================================================================
2 // === SuiteSparseQR_qmult =====================================================
3 // =============================================================================
4
5 // Applies Q in Householder form to a sparse or dense matrix X. These functions
6 // use the Householder form in plain sparse column form, as returned by
7 // the SuiteSparseQR function.
8 //
9 // The result Y is sparse if X is sparse, or dense if X is dense.
10 // The sparse/dense cases are handled via overloaded functions.
11 //
12 // method SPQR_QTX (0): Y = Q'*X
13 // method SPQR_QX (1): Y = Q*X
14 // method SPQR_XQT (2): Y = X*Q'
15 // method SPQR_XQ (3): Y = X*Q
16 //
17 // Q is held in its Householder form, as the mh-by-nh sparse matrix H,
18 // a vector HTau of length nh, and a permutation vector HPinv of length mh.
19 // mh is m for methods 0 and 1, and n for methods 2 and 3. If HPinv is
20 // NULL, the identity permutation is used.
21
22 #include "spqr.hpp"
23
24 // =============================================================================
25 // === SuiteSparseQR_qmult (dense) =============================================
26 // =============================================================================
27
28 #define HCHUNK_DENSE 32 // FUTURE: make this an input parameter
29
30 // returns Y of size m-by-n, or NULL on failure
SuiteSparseQR_qmult(int method,cholmod_sparse * H,cholmod_dense * HTau,Long * HPinv,cholmod_dense * Xdense,cholmod_common * cc)31 template <typename Entry> cholmod_dense *SuiteSparseQR_qmult
32 (
33 // inputs, not modified
34 int method, // 0,1,2,3
35 cholmod_sparse *H, // either m-by-nh or n-by-nh
36 cholmod_dense *HTau, // size 1-by-nh
37 Long *HPinv, // size mh, identity permutation if NULL
38 cholmod_dense *Xdense, // size m-by-n with leading dimension ldx
39
40 // workspace and parameters
41 cholmod_common *cc
42 )
43 {
44 cholmod_dense *Ydense ;
45 Entry *X, *Y, *X1, *Y1, *Z1, *Hx, *C, *V, *Z, *CV, *Tau ;
46 Long *Hp, *Hi, *Wi, *Wmap ;
47 Long i, k, zsize, nh, mh, vmax, hchunk, vsize, csize, cvsize, wisize, ldx,
48 m, n ;
49 int ok = TRUE ;
50
51 // -------------------------------------------------------------------------
52 // get inputs
53 // -------------------------------------------------------------------------
54
55 RETURN_IF_NULL_COMMON (NULL) ;
56 RETURN_IF_NULL (H, NULL) ;
57 RETURN_IF_NULL (HTau, NULL) ;
58 RETURN_IF_NULL (Xdense, NULL) ;
59 Long xtype = spqr_type <Entry> ( ) ;
60 RETURN_IF_XTYPE_INVALID (H, NULL) ;
61 RETURN_IF_XTYPE_INVALID (HTau, NULL) ;
62 RETURN_IF_XTYPE_INVALID (Xdense, NULL) ;
63 cc->status = CHOLMOD_OK ;
64
65 Hp = (Long *) H->p ;
66 Hi = (Long *) H->i ;
67 Hx = (Entry *) H->x ;
68 nh = H->ncol ;
69 mh = H->nrow ;
70
71 X = (Entry *) Xdense->x ;
72 m = Xdense->nrow ;
73 n = Xdense->ncol ;
74 ldx = Xdense->d ;
75 ASSERT (ldx >= m) ;
76
77 if (method == SPQR_QTX || method == SPQR_QX)
78 {
79 // rows of H and X must be the same
80 if (mh != m)
81 {
82 ERROR (CHOLMOD_INVALID, "mismatched dimensions") ;
83 return (NULL) ;
84 }
85 }
86 else if (method == SPQR_XQT || method == SPQR_XQ)
87 {
88 // rows of H and columns of X must be the same
89 if (mh != n)
90 {
91 ERROR (CHOLMOD_INVALID, "mismatched dimensions") ;
92 return (NULL) ;
93 }
94 }
95 else
96 {
97 ERROR (CHOLMOD_INVALID, "invalid method") ;
98 return (NULL) ;
99 }
100
101 // -------------------------------------------------------------------------
102 // allocate result Y
103 // -------------------------------------------------------------------------
104
105 Ydense = cholmod_l_allocate_dense (m, n, m, xtype, cc) ;
106 if (cc->status < CHOLMOD_OK)
107 {
108 // out of memory
109 return (NULL) ;
110 }
111 Y = (Entry *) Ydense->x ;
112
113 if (m == 0 || n == 0)
114 {
115 // nothing to do
116 return (Ydense) ;
117 }
118
119 // -------------------------------------------------------------------------
120 // allocate workspace
121 // -------------------------------------------------------------------------
122
123 Z = NULL ;
124 zsize = m*n ;
125 if (method == SPQR_QX || method == SPQR_XQT)
126 {
127 // Z is needed only for methods SPQR_QX and SPQR_XQT
128 Z = (Entry *) cholmod_l_malloc (zsize, sizeof (Entry), cc) ;
129 }
130
131 hchunk = MIN (HCHUNK_DENSE, nh) ;
132 ok = spqr_happly_work (method, m, n, nh, Hp, hchunk, &vmax, &vsize, &csize);
133
134 ASSERT (vmax <= mh) ;
135
136 wisize = mh + vmax ;
137 Wi = (Long *) cholmod_l_malloc (wisize, sizeof (Long), cc) ;
138 Wmap = Wi + vmax ; // Wmap is of size mh, Wi of size vmax
139
140 if (cc->status < CHOLMOD_OK)
141 {
142 // out of memory; free workspace and result Y
143 cholmod_l_free_dense (&Ydense, cc) ;
144 cholmod_l_free (zsize, sizeof (Entry), Z, cc) ;
145 cholmod_l_free (wisize, sizeof (Long), Wi, cc) ;
146 return (NULL) ;
147 }
148
149 if (method == SPQR_QX || method == SPQR_XQT)
150 {
151 // Z = X
152 Z1 = Z ;
153 X1 = X ;
154 for (k = 0 ; k < n ; k++)
155 {
156 for (i = 0 ; i < m ; i++)
157 {
158 Z1 [i] = X1 [i] ;
159 }
160 X1 += ldx ;
161 Z1 += m ;
162 }
163 }
164
165 for (i = 0 ; i < mh ; i++)
166 {
167 Wmap [i] = EMPTY ;
168 }
169
170 // -------------------------------------------------------------------------
171 // allocate O(hchunk) workspace
172 // -------------------------------------------------------------------------
173
174 // cvsize = csize + vsize ;
175 cvsize = spqr_add (csize, vsize, &ok) ;
176 CV = NULL ;
177
178 if (ok)
179 {
180 CV = (Entry *) cholmod_l_malloc (cvsize, sizeof (Entry), cc) ;
181 }
182
183 // -------------------------------------------------------------------------
184 // punt if out of memory
185 // -------------------------------------------------------------------------
186
187 if (!ok || cc->status < CHOLMOD_OK)
188 {
189 // PUNT: out of memory; try again with hchunk = 1
190 cc->status = CHOLMOD_OK ;
191 ok = TRUE ;
192 hchunk = 1 ;
193 ok = spqr_happly_work (method, m, n, nh, Hp, hchunk,
194 &vmax, &vsize, &csize) ;
195 // note that vmax has changed, but wisize is left as-is
196 // cvsize = csize + vsize ;
197 cvsize = spqr_add (csize, vsize, &ok) ;
198 if (ok)
199 {
200 CV = (Entry *) cholmod_l_malloc (cvsize, sizeof (Entry), cc) ;
201 }
202 if (!ok || cc->status < CHOLMOD_OK)
203 {
204 // out of memory (or problem too large); free workspace and result Y
205 cholmod_l_free_dense (&Ydense, cc) ;
206 cholmod_l_free (zsize, sizeof (Entry), Z, cc) ;
207 cholmod_l_free (wisize, sizeof (Long), Wi, cc) ;
208 ERROR (CHOLMOD_OUT_OF_MEMORY, "out of memory") ;
209 return (NULL) ;
210 }
211 }
212
213 // -------------------------------------------------------------------------
214 // split up workspace
215 // -------------------------------------------------------------------------
216
217 C = CV ; // size csize
218 V = C + csize ; // size vsize
219
220 // -------------------------------------------------------------------------
221 // Y = Q'*X, Q*X, X*Q, or X*Q'
222 // -------------------------------------------------------------------------
223
224 PR (("Method %d m %ld n %ld X %p Y %p P %p\n", method, m, n, X, Y, HPinv)) ;
225 PR (("Hp %p Hi %p Hx %p\n", Hp, Hi, Hx)) ;
226 ASSERT (IMPLIES ((nh > 0), Hp != NULL && Hi != NULL && Hx != NULL)) ;
227 Tau = (Entry *) HTau->x ;
228 PR (("Tau %p\n", Tau)) ;
229 ASSERT (Tau != NULL) ;
230
231 if (method == SPQR_QTX)
232 {
233
234 // ---------------------------------------------------------------------
235 // Y = Q'*X
236 // ---------------------------------------------------------------------
237
238 // Y (P,:) = X
239 X1 = X ;
240 Y1 = Y ;
241 for (k = 0 ; k < n ; k++)
242 {
243 for (i = 0 ; i < m ; i++)
244 {
245 Y1 [HPinv ? HPinv [i] : i] = X1 [i] ;
246 }
247 X1 += ldx ;
248 Y1 += m ;
249 }
250
251 // apply H to Y
252 spqr_happly (method, m, n, nh, Hp, Hi, Hx, Tau, Y,
253 vmax, hchunk, Wi, Wmap, C, V, cc) ;
254
255 }
256 else if (method == SPQR_QX)
257 {
258
259 // ---------------------------------------------------------------------
260 // Y = Q*X
261 // ---------------------------------------------------------------------
262
263 // apply H to Z
264 spqr_happly (method, m, n, nh, Hp, Hi, Hx, Tau, Z,
265 vmax, hchunk, Wi, Wmap, C, V, cc) ;
266
267 // Y = Z (P,:)
268 Z1 = Z ;
269 Y1 = Y ;
270 for (k = 0 ; k < n ; k++)
271 {
272 for (i = 0 ; i < m ; i++)
273 {
274 Y1 [i] = Z1 [HPinv ? HPinv [i] : i] ;
275 }
276 Z1 += m ;
277 Y1 += m ;
278 }
279
280 }
281 else if (method == SPQR_XQT)
282 {
283
284 // ---------------------------------------------------------------------
285 // Y = X*Q'
286 // ---------------------------------------------------------------------
287
288 // apply H to Z
289 spqr_happly (method, m, n, nh, Hp, Hi, Hx, Tau, Z,
290 vmax, hchunk, Wi, Wmap, C, V, cc) ;
291
292 // Y = Z (:,P)
293 Y1 = Y ;
294 for (k = 0 ; k < n ; k++)
295 {
296 ASSERT (IMPLIES (HPinv != NULL, HPinv [k] >= 0 && HPinv [k] < n)) ;
297 Z1 = Z + (HPinv ? HPinv [k] : k) * m ; // m = leading dim. of Z
298 for (i = 0 ; i < m ; i++)
299 {
300 Y1 [i] = Z1 [i] ;
301 }
302 Y1 += m ;
303 }
304
305 }
306 else if (method == SPQR_XQ)
307 {
308
309 // ---------------------------------------------------------------------
310 // Y = X*Q
311 // ---------------------------------------------------------------------
312
313 // Y (:,P) = X
314 X1 = X ;
315 for (k = 0 ; k < n ; k++)
316 {
317 ASSERT (IMPLIES (HPinv != NULL, HPinv [k] >= 0 && HPinv [k] < n)) ;
318 Y1 = Y + (HPinv ? HPinv [k] : k) * m ; // m = leading dim. of Y
319 for (i = 0 ; i < m ; i++)
320 {
321 Y1 [i] = X1 [i] ;
322 }
323 X1 += ldx ;
324 }
325
326 // apply H to Y
327 spqr_happly (method, m, n, nh, Hp, Hi, Hx, Tau, Y,
328 vmax, hchunk, Wi, Wmap, C, V, cc) ;
329 }
330
331 // -------------------------------------------------------------------------
332 // free workspace and return Y
333 // -------------------------------------------------------------------------
334
335 cholmod_l_free (cvsize, sizeof (Entry), CV, cc) ;
336 cholmod_l_free (zsize, sizeof (Entry), Z, cc) ;
337 cholmod_l_free (wisize, sizeof (Long), Wi, cc) ;
338
339 if (CHECK_BLAS_INT && !cc->blas_ok)
340 {
341 ERROR (CHOLMOD_INVALID, "problem too large for the BLAS") ;
342 cholmod_l_free_dense (&Ydense, cc) ;
343 return (NULL) ;
344 }
345
346 return (Ydense) ;
347 }
348
349
350 // =============================================================================
351
352 template cholmod_dense *SuiteSparseQR_qmult <double>
353 (
354 // inputs, not modified
355 int method, // 0,1,2,3
356 cholmod_sparse *H, // either m-by-nh or n-by-nh
357 cholmod_dense *HTau, // size 1-by-nh
358 Long *HPinv, // size mh
359 cholmod_dense *Xdense, // size m-by-n with leading dimension ldx
360
361 // workspace and parameters
362 cholmod_common *cc
363 ) ;
364
365 // =============================================================================
366
367 template cholmod_dense *SuiteSparseQR_qmult <Complex>
368 (
369 // inputs, not modified
370 int method, // 0,1,2,3
371 cholmod_sparse *H, // either m-by-nh or n-by-nh
372 cholmod_dense *HTau, // size 1-by-nh
373 Long *HPinv, // size mh
374 cholmod_dense *Xdense, // size m-by-n with leading dimension ldx
375
376 // workspace and parameters
377 cholmod_common *cc
378 ) ;
379
380
381 // =============================================================================
382 // === SuiteSparseQR_qmult (sparse) ============================================
383 // =============================================================================
384
385 // Applies Q in Householder form to a sparse matrix X>
386
387 #define XCHUNK 4 // FUTURE: make a parameter
388 #define HCHUNK_SPARSE 4 // FUTURE: make a parameter
389
SuiteSparseQR_qmult(int method,cholmod_sparse * H,cholmod_dense * HTau,Long * HPinv,cholmod_sparse * Xsparse,cholmod_common * cc)390 template <typename Entry> cholmod_sparse *SuiteSparseQR_qmult
391 (
392 // inputs, not modified
393 int method, // 0,1,2,3
394 cholmod_sparse *H, // size m-by-nh or n-by-nh
395 cholmod_dense *HTau, // size 1-by-nh
396 Long *HPinv, // size mh, identity permutation if NULL
397 cholmod_sparse *Xsparse,
398
399 // workspace and parameters
400 cholmod_common *cc
401 )
402 {
403 cholmod_sparse *Ysparse ;
404 Entry *W, *W1, *Hx, *Xx, *C, *V, *CVW, *Tau ;
405 Long *Hp, *Hi, *Xp, *Xi, *Wi, *Wmap ;
406 Long i, k, ny, k1, xchunk, p, k2, m, n, nh, vmax, hchunk, vsize,
407 csize, cvwsize, wsize, wisize ;
408 int ok = TRUE ;
409
410 // -------------------------------------------------------------------------
411 // get inputs
412 // -------------------------------------------------------------------------
413
414 RETURN_IF_NULL_COMMON (NULL) ;
415 RETURN_IF_NULL (H, NULL) ;
416 RETURN_IF_NULL (HTau, NULL) ;
417 RETURN_IF_NULL (Xsparse, NULL) ;
418 Long xtype = spqr_type <Entry> ( ) ;
419 RETURN_IF_XTYPE_INVALID (H, NULL) ;
420 RETURN_IF_XTYPE_INVALID (HTau, NULL) ;
421 RETURN_IF_XTYPE_INVALID (Xsparse, NULL) ;
422 cc->status = CHOLMOD_OK ;
423
424 if (method == SPQR_QTX || method == SPQR_QX)
425 {
426 // rows of H and X must be the same
427 if (H->nrow != Xsparse->nrow)
428 {
429 ERROR (CHOLMOD_INVALID, "mismatched dimensions") ;
430 return (NULL) ;
431 }
432 }
433 else if (method == SPQR_XQT || method == SPQR_XQ)
434 {
435 // rows of H and columns of X must be the same
436 if (H->nrow != Xsparse->ncol)
437 {
438 ERROR (CHOLMOD_INVALID, "mismatched dimensions") ;
439 return (NULL) ;
440 }
441 }
442 else
443 {
444 ERROR (CHOLMOD_INVALID, "invalid method") ;
445 return (NULL) ;
446 }
447
448 // -------------------------------------------------------------------------
449 // methods 2 or 3: Y = X*Q' = (Q*X')' or Y = X*Q = (Q'*X')'
450 // -------------------------------------------------------------------------
451
452 if (method == SPQR_XQT || method == SPQR_XQ)
453 {
454 cholmod_sparse *XT, *YT ;
455 XT = cholmod_l_transpose (Xsparse, 2, cc) ;
456 YT = SuiteSparseQR_qmult <Entry>
457 ((method == SPQR_XQT) ? SPQR_QX : SPQR_QTX, H, HTau, HPinv, XT, cc);
458 cholmod_l_free_sparse (&XT, cc) ;
459 Ysparse = cholmod_l_transpose (YT, 2, cc) ;
460 cholmod_l_free_sparse (&YT, cc) ;
461 return (Ysparse) ;
462 }
463
464 // -------------------------------------------------------------------------
465 // get H and X
466 // -------------------------------------------------------------------------
467
468 Hp = (Long *) H->p ;
469 Hi = (Long *) H->i ;
470 Hx = (Entry *) H->x ;
471 m = H->nrow ;
472 nh = H->ncol ;
473
474 Xp = (Long *) Xsparse->p ;
475 Xi = (Long *) Xsparse->i ;
476 Xx = (Entry *) Xsparse->x ;
477 n = Xsparse->ncol ;
478
479 Tau = (Entry *) HTau->x ;
480
481 // -------------------------------------------------------------------------
482 // allocate Long workspace
483 // -------------------------------------------------------------------------
484
485 xchunk = MIN (XCHUNK, n) ;
486 hchunk = MIN (HCHUNK_SPARSE, nh) ;
487
488 ok = spqr_happly_work (method, m, xchunk, nh, Hp, hchunk,
489 &vmax, &vsize, &csize) ;
490
491 wisize = m + vmax ;
492 Wi = (Long *) cholmod_l_malloc (wisize, sizeof (Long), cc) ;
493 Wmap = Wi + vmax ; // Wmap is of size m, Wi is of size vmax
494
495 if (cc->status < CHOLMOD_OK)
496 {
497 // out of memory
498 return (NULL) ;
499 }
500
501 for (i = 0 ; i < m ; i++)
502 {
503 Wmap [i] = EMPTY ;
504 }
505
506 // -------------------------------------------------------------------------
507 // allocate O(xchunk + hchunk) workspace
508 // -------------------------------------------------------------------------
509
510 // wsize = xchunk * m ;
511 wsize = spqr_mult (xchunk, m, &ok) ;
512
513 // cvwsize = wsize + csize + vsize ;
514 cvwsize = spqr_add (wsize, csize, &ok) ;
515 cvwsize = spqr_add (cvwsize, vsize, &ok) ;
516 CVW = NULL ;
517
518 if (ok)
519 {
520 CVW = (Entry *) cholmod_l_malloc (cvwsize, sizeof (Entry), cc) ;
521 }
522
523 // -------------------------------------------------------------------------
524 // punt if out of memory
525 // -------------------------------------------------------------------------
526
527 if (!ok || cc->status < CHOLMOD_OK)
528 {
529 // PUNT: out of memory; try again with xchunk = 1 and hchunk = 1
530 cc->status = CHOLMOD_OK ;
531 xchunk = 1 ;
532 hchunk = 1 ;
533 ok = spqr_happly_work (method, m, xchunk, nh, Hp, hchunk,
534 &vmax, &vsize, &csize) ;
535 wsize = m ;
536
537 // cvwsize = wsize + csize + vsize ;
538 cvwsize = spqr_add (wsize, csize, &ok) ;
539 cvwsize = spqr_add (cvwsize, vsize, &ok) ;
540 if (ok)
541 {
542 CVW = (Entry *) cholmod_l_malloc (cvwsize, sizeof (Entry), cc) ;
543 }
544 if (!ok || cc->status < CHOLMOD_OK)
545 {
546 // still out of memory (or problem too large)
547 ERROR (CHOLMOD_OUT_OF_MEMORY, "out of memory") ;
548 cholmod_l_free (wisize, sizeof (Long), Wi, cc) ;
549 return (NULL) ;
550 }
551 }
552
553 // -------------------------------------------------------------------------
554 // split up workspace
555 // -------------------------------------------------------------------------
556
557 C = CVW ; // C is of size csize Entry's
558 V = C + csize ; // V is of size vsize Entry's
559 W = V + vsize ; // W is of size wsize Entry's
560
561 // -------------------------------------------------------------------------
562 // allocate result Y
563 // -------------------------------------------------------------------------
564
565 // Y is a sparse matrix of size m-ny-n with space for m+1 entries
566 Ysparse = cholmod_l_allocate_sparse (m, n, m+1, TRUE, TRUE, 0, xtype, cc) ;
567 if (cc->status < CHOLMOD_OK)
568 {
569 // out of memory
570 cholmod_l_free (cvwsize, sizeof (Entry), CVW, cc) ;
571 cholmod_l_free (wisize, sizeof (Long), Wi, cc) ;
572 return (NULL) ;
573 }
574 ny = 0 ;
575
576 // -------------------------------------------------------------------------
577 // methods 0 or 1: Y = Q'*X or Q*X
578 // -------------------------------------------------------------------------
579
580 if (method == SPQR_QTX)
581 {
582
583 // ---------------------------------------------------------------------
584 // Y = Q'*X
585 // ---------------------------------------------------------------------
586
587 // apply in blocks of xchunk columns each
588 for (k1 = 0 ; k1 < n ; k1 += xchunk)
589 {
590 // scatter W: W (P,0:3) = X (:, k:k+3)
591 W1 = W ;
592 k2 = MIN (k1+xchunk, n) ;
593 for (k = k1 ; k < k2 ; k++)
594 {
595 // W1 = 0
596 for (i = 0 ; i < m ; i++)
597 {
598 W1 [i] = 0 ;
599 }
600 for (p = Xp [k] ; p < Xp [k+1] ; p++)
601 {
602 i = Xi [p] ;
603 W1 [HPinv ? HPinv [i] : i] = Xx [p] ;
604 }
605 W1 += m ;
606 }
607
608 // apply H to W
609
610 spqr_happly (method, m, k2-k1, nh, Hp, Hi, Hx, Tau, W,
611 vmax, hchunk, Wi, Wmap, C, V, cc) ;
612
613 // append W onto Y
614 W1 = W ;
615 for (k = k1 ; k < k2 ; k++)
616 {
617 spqr_append (W1, NULL, Ysparse, &ny, cc) ;
618
619 if (cc->status < CHOLMOD_OK)
620 {
621 // out of memory
622 cholmod_l_free_sparse (&Ysparse, cc) ;
623 cholmod_l_free (cvwsize, sizeof (Entry), CVW, cc) ;
624 cholmod_l_free (wisize, sizeof (Long), Wi, cc) ;
625 return (NULL) ;
626 }
627
628 W1 += m ;
629 }
630 }
631
632 }
633 else // if (method == SPQR_QX)
634 {
635
636 // ---------------------------------------------------------------------
637 // Y = Q*X
638 // ---------------------------------------------------------------------
639
640 // apply in blocks of xchunk columns each
641 for (k1 = 0 ; k1 < n ; k1 += xchunk)
642 {
643 // scatter W: W (:,0:3) = X (:, k:k+3)
644 W1 = W ;
645 k2 = MIN (k1+xchunk, n) ;
646 for (k = k1 ; k < k2 ; k++)
647 {
648 // W1 = 0
649 for (i = 0 ; i < m ; i++)
650 {
651 W1 [i] = 0 ;
652 }
653 for (p = Xp [k] ; p < Xp [k+1] ; p++)
654 {
655 i = Xi [p] ;
656 W1 [i] = Xx [p] ;
657 }
658 W1 += m ;
659 }
660
661 // apply H to W
662 spqr_happly (method, m, k2-k1, nh, Hp, Hi, Hx, Tau, W,
663 vmax, hchunk, Wi, Wmap, C, V, cc) ;
664
665 // append W (P,:) onto Y
666 W1 = W ;
667 for (k = k1 ; k < k2 ; k++)
668 {
669 spqr_append (W1, HPinv, Ysparse, &ny, cc) ;
670 if (cc->status < CHOLMOD_OK)
671 {
672 // out of memory
673 cholmod_l_free_sparse (&Ysparse, cc) ;
674 cholmod_l_free (cvwsize, sizeof (Entry), CVW, cc) ;
675 cholmod_l_free (wisize, sizeof (Long), Wi, cc) ;
676 return (NULL) ;
677 }
678 W1 += m ;
679 }
680 }
681 }
682
683 // -------------------------------------------------------------------------
684 // free workspace and reduce Y in size so that nnz (Y) == nzmax (Y)
685 // -------------------------------------------------------------------------
686
687 cholmod_l_free (cvwsize, sizeof (Entry), CVW, cc) ;
688 cholmod_l_free (wisize, sizeof (Long), Wi, cc) ;
689 cholmod_l_reallocate_sparse (cholmod_l_nnz (Ysparse,cc), Ysparse, cc) ;
690
691 if (CHECK_BLAS_INT && !cc->blas_ok)
692 {
693 ERROR (CHOLMOD_INVALID, "problem too large for the BLAS") ;
694 cholmod_l_free_sparse (&Ysparse, cc) ;
695 return (NULL) ;
696 }
697
698 return (Ysparse) ;
699 }
700
701
702 // =============================================================================
703
704 template cholmod_sparse *SuiteSparseQR_qmult <double>
705 (
706 // inputs, not modified
707 int method, // 0,1,2,3
708 cholmod_sparse *H, // size m-by-nh or n-by-nh
709 cholmod_dense *HTau, // size 1-by-nh
710 Long *HPinv, // size mh
711 cholmod_sparse *Xsparse,
712
713 // workspace and parameters
714 cholmod_common *cc
715 ) ;
716
717 // =============================================================================
718
719 template cholmod_sparse *SuiteSparseQR_qmult <Complex>
720 (
721 // inputs, not modified
722 int method, // 0,1,2,3
723 cholmod_sparse *H, // size m-by-nh or n-by-nh
724 cholmod_dense *HTau, // size 1-by-nh
725 Long *HPinv, // size mh
726 cholmod_sparse *Xsparse,
727
728 // workspace and parameters
729 cholmod_common *cc
730 ) ;
731