Skip to main content

singe_cusolver/dense/legacy/
bidiagonal.rs

1use singe_cuda::{
2    memory::DeviceMemory,
3    types::{Complex32, Complex64},
4};
5
6use crate::{
7    context::Context,
8    dense::validation::{
9        require_info_buffer, require_workspace, validate_bidiagonal_buffers,
10        validate_bidiagonal_dims, validate_orgbr_inputs,
11    },
12    error::Result,
13    sys, try_ffi,
14    types::SideMode,
15    utility::{to_i32, to_usize},
16};
17
18pub fn sgebrd_buffer_size(ctx: &Context, m: usize, n: usize) -> Result<usize> {
19    ctx.bind()?;
20    validate_bidiagonal_dims(m, n)?;
21    let mut lwork = 0;
22    unsafe {
23        try_ffi!(sys::cusolverDnSgebrd_bufferSize(
24            ctx.as_raw(),
25            to_i32(m, "m")?,
26            to_i32(n, "n")?,
27            &raw mut lwork,
28        ))?;
29    }
30    to_usize(lwork, "lwork")
31}
32
33pub fn dgebrd_buffer_size(ctx: &Context, m: usize, n: usize) -> Result<usize> {
34    ctx.bind()?;
35    validate_bidiagonal_dims(m, n)?;
36    let mut lwork = 0;
37    unsafe {
38        try_ffi!(sys::cusolverDnDgebrd_bufferSize(
39            ctx.as_raw(),
40            to_i32(m, "m")?,
41            to_i32(n, "n")?,
42            &raw mut lwork,
43        ))?;
44    }
45    to_usize(lwork, "lwork")
46}
47
48pub fn cgebrd_buffer_size(ctx: &Context, m: usize, n: usize) -> Result<usize> {
49    ctx.bind()?;
50    validate_bidiagonal_dims(m, n)?;
51    let mut lwork = 0;
52    unsafe {
53        try_ffi!(sys::cusolverDnCgebrd_bufferSize(
54            ctx.as_raw(),
55            to_i32(m, "m")?,
56            to_i32(n, "n")?,
57            &raw mut lwork,
58        ))?;
59    }
60    to_usize(lwork, "lwork")
61}
62
63pub fn zgebrd_buffer_size(ctx: &Context, m: usize, n: usize) -> Result<usize> {
64    ctx.bind()?;
65    validate_bidiagonal_dims(m, n)?;
66    let mut lwork = 0;
67    unsafe {
68        try_ffi!(sys::cusolverDnZgebrd_bufferSize(
69            ctx.as_raw(),
70            to_i32(m, "m")?,
71            to_i32(n, "n")?,
72            &raw mut lwork,
73        ))?;
74    }
75    to_usize(lwork, "lwork")
76}
77
78/// Use the matching buffer-size helper to calculate the required workspace size.
79///
80/// The S and D data types are real valued single and double precision, respectively.
81///
82/// The C and Z data types are complex valued single and double precision, respectively.
83///
84/// Reduces a general $m \times n$ matrix `A` to a real upper or lower
85/// bidiagonal form `B` by an orthogonal transformation:
86/// $Q^{H}\cdot A\cdot P = B$.
87///
88/// If `m >= n`, `B` is upper bidiagonal; if `m < n`, `B` is lower
89/// bidiagonal.
90///
91/// The matrix `Q` and `P` are overwritten into matrix `A` in the following sense:
92///
93/// - If `m >= n`, the diagonal and first superdiagonal are overwritten with
94///   the upper bidiagonal matrix `B`. Elements below the diagonal, together
95///   with `tauq`, represent `Q`; elements above the first superdiagonal,
96///   together with `taup`, represent `P`.
97/// - If `m < n`, the diagonal and first subdiagonal are overwritten with the
98///   lower bidiagonal matrix `B`. Elements below the first subdiagonal,
99///   together with `tauq`, represent `Q`; elements above the diagonal,
100///   together with `taup`, represent `P`.
101///
102/// Provide workspace through `workspace`.
103/// Use the corresponding `*_buffer_size` helper to query the required workspace length.
104///
105/// If the reported `dev_info` value is `-i`, the `i`th parameter is invalid.
106///
107/// `gebrd` only supports `m >= n`.
108///
109/// # Errors
110///
111/// Returns an error if cuSOLVER has not been initialized, if the
112/// matrix dimensions or leading dimension are invalid, if the current GPU
113/// architecture is unsupported, or if cuSOLVER reports an internal failure.
114pub fn sgebrd(
115    ctx: &Context,
116    m: usize,
117    n: usize,
118    a: &mut DeviceMemory<f32>,
119    lda: usize,
120    d: &mut DeviceMemory<f32>,
121    e: &mut DeviceMemory<f32>,
122    tauq: &mut DeviceMemory<f32>,
123    taup: &mut DeviceMemory<f32>,
124    workspace: &mut DeviceMemory<f32>,
125    dev_info: &mut DeviceMemory<i32>,
126) -> Result<()> {
127    ctx.bind()?;
128    validate_bidiagonal_buffers(m, n, a.len(), lda, d.len(), e.len(), tauq.len(), taup.len())?;
129    require_info_buffer(dev_info)?;
130    let lwork = sgebrd_buffer_size(ctx, m, n)?;
131    require_workspace(workspace.len(), lwork)?;
132    unsafe {
133        try_ffi!(sys::cusolverDnSgebrd(
134            ctx.as_raw(),
135            to_i32(m, "m")?,
136            to_i32(n, "n")?,
137            a.as_mut_ptr().cast(),
138            to_i32(lda, "lda")?,
139            d.as_mut_ptr().cast(),
140            e.as_mut_ptr().cast(),
141            tauq.as_mut_ptr().cast(),
142            taup.as_mut_ptr().cast(),
143            workspace.as_mut_ptr().cast(),
144            to_i32(lwork, "lwork")?,
145            dev_info.as_mut_ptr().cast(),
146        ))?;
147    }
148    Ok(())
149}
150
151/// Use the matching buffer-size helper to calculate the required workspace size.
152///
153/// The S and D data types are real valued single and double precision, respectively.
154///
155/// The C and Z data types are complex valued single and double precision, respectively.
156///
157/// Reduces a general $m \times n$ matrix `A` to a real upper or lower
158/// bidiagonal form `B` by an orthogonal transformation:
159/// $Q^{H}\cdot A\cdot P = B$.
160///
161/// If `m >= n`, `B` is upper bidiagonal; if `m < n`, `B` is lower
162/// bidiagonal.
163///
164/// The matrix `Q` and `P` are overwritten into matrix `A` in the following sense:
165///
166/// - If `m >= n`, the diagonal and first superdiagonal are overwritten with
167///   the upper bidiagonal matrix `B`. Elements below the diagonal, together
168///   with `tauq`, represent `Q`; elements above the first superdiagonal,
169///   together with `taup`, represent `P`.
170/// - If `m < n`, the diagonal and first subdiagonal are overwritten with the
171///   lower bidiagonal matrix `B`. Elements below the first subdiagonal,
172///   together with `tauq`, represent `Q`; elements above the diagonal,
173///   together with `taup`, represent `P`.
174///
175/// Provide workspace through `workspace`.
176/// Use the corresponding `*_buffer_size` helper to query the required workspace length.
177///
178/// If the reported `dev_info` value is `-i`, the `i`th parameter is invalid.
179///
180/// `gebrd` only supports `m >= n`.
181///
182/// # Errors
183///
184/// Returns an error if cuSOLVER has not been initialized, if the
185/// matrix dimensions or leading dimension are invalid, if the current GPU
186/// architecture is unsupported, or if cuSOLVER reports an internal failure.
187pub fn dgebrd(
188    ctx: &Context,
189    m: usize,
190    n: usize,
191    a: &mut DeviceMemory<f64>,
192    lda: usize,
193    d: &mut DeviceMemory<f64>,
194    e: &mut DeviceMemory<f64>,
195    tauq: &mut DeviceMemory<f64>,
196    taup: &mut DeviceMemory<f64>,
197    workspace: &mut DeviceMemory<f64>,
198    dev_info: &mut DeviceMemory<i32>,
199) -> Result<()> {
200    ctx.bind()?;
201    validate_bidiagonal_buffers(m, n, a.len(), lda, d.len(), e.len(), tauq.len(), taup.len())?;
202    require_info_buffer(dev_info)?;
203    let lwork = dgebrd_buffer_size(ctx, m, n)?;
204    require_workspace(workspace.len(), lwork)?;
205    unsafe {
206        try_ffi!(sys::cusolverDnDgebrd(
207            ctx.as_raw(),
208            to_i32(m, "m")?,
209            to_i32(n, "n")?,
210            a.as_mut_ptr().cast(),
211            to_i32(lda, "lda")?,
212            d.as_mut_ptr().cast(),
213            e.as_mut_ptr().cast(),
214            tauq.as_mut_ptr().cast(),
215            taup.as_mut_ptr().cast(),
216            workspace.as_mut_ptr().cast(),
217            to_i32(lwork, "lwork")?,
218            dev_info.as_mut_ptr().cast(),
219        ))?;
220    }
221    Ok(())
222}
223
224/// Use the matching buffer-size helper to calculate the required workspace size.
225///
226/// The S and D data types are real valued single and double precision, respectively.
227///
228/// The C and Z data types are complex valued single and double precision, respectively.
229///
230/// Reduces a general $m \times n$ matrix `A` to a real upper or lower
231/// bidiagonal form `B` by an orthogonal transformation:
232/// $Q^{H}\cdot A\cdot P = B$.
233///
234/// If `m >= n`, `B` is upper bidiagonal; if `m < n`, `B` is lower
235/// bidiagonal.
236///
237/// The matrix `Q` and `P` are overwritten into matrix `A` in the following sense:
238///
239/// - If `m >= n`, the diagonal and first superdiagonal are overwritten with
240///   the upper bidiagonal matrix `B`. Elements below the diagonal, together
241///   with `tauq`, represent `Q`; elements above the first superdiagonal,
242///   together with `taup`, represent `P`.
243/// - If `m < n`, the diagonal and first subdiagonal are overwritten with the
244///   lower bidiagonal matrix `B`. Elements below the first subdiagonal,
245///   together with `tauq`, represent `Q`; elements above the diagonal,
246///   together with `taup`, represent `P`.
247///
248/// Provide workspace through `workspace`.
249/// Use the corresponding `*_buffer_size` helper to query the required workspace length.
250///
251/// If the reported `dev_info` value is `-i`, the `i`th parameter is invalid.
252///
253/// `gebrd` only supports `m >= n`.
254///
255/// # Errors
256///
257/// Returns an error if cuSOLVER has not been initialized, if the
258/// matrix dimensions or leading dimension are invalid, if the current GPU
259/// architecture is unsupported, or if cuSOLVER reports an internal failure.
260pub fn cgebrd(
261    ctx: &Context,
262    m: usize,
263    n: usize,
264    a: &mut DeviceMemory<Complex32>,
265    lda: usize,
266    d: &mut DeviceMemory<f32>,
267    e: &mut DeviceMemory<f32>,
268    tauq: &mut DeviceMemory<Complex32>,
269    taup: &mut DeviceMemory<Complex32>,
270    workspace: &mut DeviceMemory<Complex32>,
271    dev_info: &mut DeviceMemory<i32>,
272) -> Result<()> {
273    ctx.bind()?;
274    validate_bidiagonal_buffers(m, n, a.len(), lda, d.len(), e.len(), tauq.len(), taup.len())?;
275    require_info_buffer(dev_info)?;
276    let lwork = cgebrd_buffer_size(ctx, m, n)?;
277    require_workspace(workspace.len(), lwork)?;
278    unsafe {
279        try_ffi!(sys::cusolverDnCgebrd(
280            ctx.as_raw(),
281            to_i32(m, "m")?,
282            to_i32(n, "n")?,
283            a.as_mut_ptr().cast(),
284            to_i32(lda, "lda")?,
285            d.as_mut_ptr().cast(),
286            e.as_mut_ptr().cast(),
287            tauq.as_mut_ptr().cast(),
288            taup.as_mut_ptr().cast(),
289            workspace.as_mut_ptr().cast(),
290            to_i32(lwork, "lwork")?,
291            dev_info.as_mut_ptr().cast(),
292        ))?;
293    }
294    Ok(())
295}
296
297/// Use the matching buffer-size helper to calculate the required workspace size.
298///
299/// The S and D data types are real valued single and double precision, respectively.
300///
301/// The C and Z data types are complex valued single and double precision, respectively.
302///
303/// Reduces a general $m \times n$ matrix `A` to a real upper or lower
304/// bidiagonal form `B` by an orthogonal transformation:
305/// $Q^{H}\cdot A\cdot P = B$.
306///
307/// If `m >= n`, `B` is upper bidiagonal; if `m < n`, `B` is lower
308/// bidiagonal.
309///
310/// The matrix `Q` and `P` are overwritten into matrix `A` in the following sense:
311///
312/// - If `m >= n`, the diagonal and first superdiagonal are overwritten with
313///   the upper bidiagonal matrix `B`. Elements below the diagonal, together
314///   with `tauq`, represent `Q`; elements above the first superdiagonal,
315///   together with `taup`, represent `P`.
316/// - If `m < n`, the diagonal and first subdiagonal are overwritten with the
317///   lower bidiagonal matrix `B`. Elements below the first subdiagonal,
318///   together with `tauq`, represent `Q`; elements above the diagonal,
319///   together with `taup`, represent `P`.
320///
321/// Provide workspace through `workspace`.
322/// Use the corresponding `*_buffer_size` helper to query the required workspace length.
323///
324/// If the reported `dev_info` value is `-i`, the `i`th parameter is invalid.
325///
326/// `gebrd` only supports `m >= n`.
327///
328/// # Errors
329///
330/// Returns an error if cuSOLVER has not been initialized, if the
331/// matrix dimensions or leading dimension are invalid, if the current GPU
332/// architecture is unsupported, or if cuSOLVER reports an internal failure.
333pub fn zgebrd(
334    ctx: &Context,
335    m: usize,
336    n: usize,
337    a: &mut DeviceMemory<Complex64>,
338    lda: usize,
339    d: &mut DeviceMemory<f64>,
340    e: &mut DeviceMemory<f64>,
341    tauq: &mut DeviceMemory<Complex64>,
342    taup: &mut DeviceMemory<Complex64>,
343    workspace: &mut DeviceMemory<Complex64>,
344    dev_info: &mut DeviceMemory<i32>,
345) -> Result<()> {
346    ctx.bind()?;
347    validate_bidiagonal_buffers(m, n, a.len(), lda, d.len(), e.len(), tauq.len(), taup.len())?;
348    require_info_buffer(dev_info)?;
349    let lwork = zgebrd_buffer_size(ctx, m, n)?;
350    require_workspace(workspace.len(), lwork)?;
351    unsafe {
352        try_ffi!(sys::cusolverDnZgebrd(
353            ctx.as_raw(),
354            to_i32(m, "m")?,
355            to_i32(n, "n")?,
356            a.as_mut_ptr().cast(),
357            to_i32(lda, "lda")?,
358            d.as_mut_ptr().cast(),
359            e.as_mut_ptr().cast(),
360            tauq.as_mut_ptr().cast(),
361            taup.as_mut_ptr().cast(),
362            workspace.as_mut_ptr().cast(),
363            to_i32(lwork, "lwork")?,
364            dev_info.as_mut_ptr().cast(),
365        ))?;
366    }
367    Ok(())
368}
369
370pub fn sorgbr_buffer_size(
371    ctx: &Context,
372    side: SideMode,
373    m: usize,
374    n: usize,
375    k: usize,
376    a: &DeviceMemory<f32>,
377    lda: usize,
378    tau: &DeviceMemory<f32>,
379) -> Result<usize> {
380    ctx.bind()?;
381    validate_orgbr_inputs(side, m, n, k, a.len(), lda, tau.len())?;
382    let mut lwork = 0;
383    unsafe {
384        try_ffi!(sys::cusolverDnSorgbr_bufferSize(
385            ctx.as_raw(),
386            side.into(),
387            to_i32(m, "m")?,
388            to_i32(n, "n")?,
389            to_i32(k, "k")?,
390            a.as_ptr().cast(),
391            to_i32(lda, "lda")?,
392            tau.as_ptr().cast(),
393            &raw mut lwork,
394        ))?;
395    }
396    to_usize(lwork, "lwork")
397}
398
399pub fn dorgbr_buffer_size(
400    ctx: &Context,
401    side: SideMode,
402    m: usize,
403    n: usize,
404    k: usize,
405    a: &DeviceMemory<f64>,
406    lda: usize,
407    tau: &DeviceMemory<f64>,
408) -> Result<usize> {
409    ctx.bind()?;
410    validate_orgbr_inputs(side, m, n, k, a.len(), lda, tau.len())?;
411    let mut lwork = 0;
412    unsafe {
413        try_ffi!(sys::cusolverDnDorgbr_bufferSize(
414            ctx.as_raw(),
415            side.into(),
416            to_i32(m, "m")?,
417            to_i32(n, "n")?,
418            to_i32(k, "k")?,
419            a.as_ptr().cast(),
420            to_i32(lda, "lda")?,
421            tau.as_ptr().cast(),
422            &raw mut lwork,
423        ))?;
424    }
425    to_usize(lwork, "lwork")
426}
427
428pub fn cungbr_buffer_size(
429    ctx: &Context,
430    side: SideMode,
431    m: usize,
432    n: usize,
433    k: usize,
434    a: &DeviceMemory<Complex32>,
435    lda: usize,
436    tau: &DeviceMemory<Complex32>,
437) -> Result<usize> {
438    ctx.bind()?;
439    validate_orgbr_inputs(side, m, n, k, a.len(), lda, tau.len())?;
440    let mut lwork = 0;
441    unsafe {
442        try_ffi!(sys::cusolverDnCungbr_bufferSize(
443            ctx.as_raw(),
444            side.into(),
445            to_i32(m, "m")?,
446            to_i32(n, "n")?,
447            to_i32(k, "k")?,
448            a.as_ptr().cast(),
449            to_i32(lda, "lda")?,
450            tau.as_ptr().cast(),
451            &raw mut lwork,
452        ))?;
453    }
454    to_usize(lwork, "lwork")
455}
456
457pub fn zungbr_buffer_size(
458    ctx: &Context,
459    side: SideMode,
460    m: usize,
461    n: usize,
462    k: usize,
463    a: &DeviceMemory<Complex64>,
464    lda: usize,
465    tau: &DeviceMemory<Complex64>,
466) -> Result<usize> {
467    ctx.bind()?;
468    validate_orgbr_inputs(side, m, n, k, a.len(), lda, tau.len())?;
469    let mut lwork = 0;
470    unsafe {
471        try_ffi!(sys::cusolverDnZungbr_bufferSize(
472            ctx.as_raw(),
473            side.into(),
474            to_i32(m, "m")?,
475            to_i32(n, "n")?,
476            to_i32(k, "k")?,
477            a.as_ptr().cast(),
478            to_i32(lda, "lda")?,
479            tau.as_ptr().cast(),
480            &raw mut lwork,
481        ))?;
482    }
483    to_usize(lwork, "lwork")
484}
485
486/// Use the matching buffer-size helper to calculate the required workspace size.
487///
488/// The S and D data types are real valued single and double precision, respectively.
489///
490/// The C and Z data types are complex valued single and double precision, respectively.
491///
492/// Generates one of the unitary matrices `Q` or $P^{H}$ determined by `gebrd`
493/// when reducing matrix `A` to bidiagonal form:
494/// $Q^{H}\cdot A\cdot P = B$.
495///
496/// `Q` and $P^{H}$ are defined as products of elementary reflectors `H(i)`
497/// or `G(i)`, respectively.
498///
499/// Provide workspace through `workspace`.
500/// Use the corresponding `*_buffer_size` helper to query the required workspace length.
501/// The workspace size in bytes is `size_of::<T>() * lwork`.
502///
503/// If the reported `dev_info` value is `-i`, the `i`th parameter is invalid.
504///
505/// # Errors
506///
507/// Returns an error if cuSOLVER has not been initialized, if the
508/// matrix dimensions or leading dimension are invalid, if the current GPU
509/// architecture is unsupported, or if cuSOLVER reports an internal failure.
510pub fn sorgbr(
511    ctx: &Context,
512    side: SideMode,
513    m: usize,
514    n: usize,
515    k: usize,
516    a: &mut DeviceMemory<f32>,
517    lda: usize,
518    tau: &DeviceMemory<f32>,
519    workspace: &mut DeviceMemory<f32>,
520    dev_info: &mut DeviceMemory<i32>,
521) -> Result<()> {
522    ctx.bind()?;
523    validate_orgbr_inputs(side, m, n, k, a.len(), lda, tau.len())?;
524    require_info_buffer(dev_info)?;
525    let lwork = sorgbr_buffer_size(ctx, side, m, n, k, a, lda, tau)?;
526    require_workspace(workspace.len(), lwork)?;
527    unsafe {
528        try_ffi!(sys::cusolverDnSorgbr(
529            ctx.as_raw(),
530            side.into(),
531            to_i32(m, "m")?,
532            to_i32(n, "n")?,
533            to_i32(k, "k")?,
534            a.as_mut_ptr().cast(),
535            to_i32(lda, "lda")?,
536            tau.as_ptr().cast(),
537            workspace.as_mut_ptr().cast(),
538            to_i32(lwork, "lwork")?,
539            dev_info.as_mut_ptr().cast(),
540        ))?;
541    }
542    Ok(())
543}
544
545/// Use the matching buffer-size helper to calculate the required workspace size.
546///
547/// The S and D data types are real valued single and double precision, respectively.
548///
549/// The C and Z data types are complex valued single and double precision, respectively.
550///
551/// Generates one of the unitary matrices `Q` or $P^{H}$ determined by `gebrd`
552/// when reducing matrix `A` to bidiagonal form:
553/// $Q^{H}\cdot A\cdot P = B$.
554///
555/// `Q` and $P^{H}$ are defined as products of elementary reflectors `H(i)`
556/// or `G(i)`, respectively.
557///
558/// Provide workspace through `workspace`.
559/// Use the corresponding `*_buffer_size` helper to query the required workspace length.
560/// The workspace size in bytes is `size_of::<T>() * lwork`.
561///
562/// If the reported `dev_info` value is `-i`, the `i`th parameter is invalid.
563///
564/// # Errors
565///
566/// Returns an error if cuSOLVER has not been initialized, if the
567/// matrix dimensions or leading dimension are invalid, if the current GPU
568/// architecture is unsupported, or if cuSOLVER reports an internal failure.
569pub fn dorgbr(
570    ctx: &Context,
571    side: SideMode,
572    m: usize,
573    n: usize,
574    k: usize,
575    a: &mut DeviceMemory<f64>,
576    lda: usize,
577    tau: &DeviceMemory<f64>,
578    workspace: &mut DeviceMemory<f64>,
579    dev_info: &mut DeviceMemory<i32>,
580) -> Result<()> {
581    ctx.bind()?;
582    validate_orgbr_inputs(side, m, n, k, a.len(), lda, tau.len())?;
583    require_info_buffer(dev_info)?;
584    let lwork = dorgbr_buffer_size(ctx, side, m, n, k, a, lda, tau)?;
585    require_workspace(workspace.len(), lwork)?;
586    unsafe {
587        try_ffi!(sys::cusolverDnDorgbr(
588            ctx.as_raw(),
589            side.into(),
590            to_i32(m, "m")?,
591            to_i32(n, "n")?,
592            to_i32(k, "k")?,
593            a.as_mut_ptr().cast(),
594            to_i32(lda, "lda")?,
595            tau.as_ptr().cast(),
596            workspace.as_mut_ptr().cast(),
597            to_i32(lwork, "lwork")?,
598            dev_info.as_mut_ptr().cast(),
599        ))?;
600    }
601    Ok(())
602}
603
604pub fn cungbr(
605    ctx: &Context,
606    side: SideMode,
607    m: usize,
608    n: usize,
609    k: usize,
610    a: &mut DeviceMemory<Complex32>,
611    lda: usize,
612    tau: &DeviceMemory<Complex32>,
613    workspace: &mut DeviceMemory<Complex32>,
614    dev_info: &mut DeviceMemory<i32>,
615) -> Result<()> {
616    ctx.bind()?;
617    validate_orgbr_inputs(side, m, n, k, a.len(), lda, tau.len())?;
618    require_info_buffer(dev_info)?;
619    let lwork = cungbr_buffer_size(ctx, side, m, n, k, a, lda, tau)?;
620    require_workspace(workspace.len(), lwork)?;
621    unsafe {
622        try_ffi!(sys::cusolverDnCungbr(
623            ctx.as_raw(),
624            side.into(),
625            to_i32(m, "m")?,
626            to_i32(n, "n")?,
627            to_i32(k, "k")?,
628            a.as_mut_ptr().cast(),
629            to_i32(lda, "lda")?,
630            tau.as_ptr().cast(),
631            workspace.as_mut_ptr().cast(),
632            to_i32(lwork, "lwork")?,
633            dev_info.as_mut_ptr().cast(),
634        ))?;
635    }
636    Ok(())
637}
638
639pub fn zungbr(
640    ctx: &Context,
641    side: SideMode,
642    m: usize,
643    n: usize,
644    k: usize,
645    a: &mut DeviceMemory<Complex64>,
646    lda: usize,
647    tau: &DeviceMemory<Complex64>,
648    workspace: &mut DeviceMemory<Complex64>,
649    dev_info: &mut DeviceMemory<i32>,
650) -> Result<()> {
651    ctx.bind()?;
652    validate_orgbr_inputs(side, m, n, k, a.len(), lda, tau.len())?;
653    require_info_buffer(dev_info)?;
654    let lwork = zungbr_buffer_size(ctx, side, m, n, k, a, lda, tau)?;
655    require_workspace(workspace.len(), lwork)?;
656    unsafe {
657        try_ffi!(sys::cusolverDnZungbr(
658            ctx.as_raw(),
659            side.into(),
660            to_i32(m, "m")?,
661            to_i32(n, "n")?,
662            to_i32(k, "k")?,
663            a.as_mut_ptr().cast(),
664            to_i32(lda, "lda")?,
665            tau.as_ptr().cast(),
666            workspace.as_mut_ptr().cast(),
667            to_i32(lwork, "lwork")?,
668            dev_info.as_mut_ptr().cast(),
669        ))?;
670    }
671    Ok(())
672}