use std::cmp::max;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WorkspaceQuery {
pub optimal: usize,
pub minimum: usize,
}
impl WorkspaceQuery {
#[inline]
pub const fn new(optimal: usize, minimum: usize) -> Self {
Self { optimal, minimum }
}
#[inline]
pub const fn fixed(size: usize) -> Self {
Self {
optimal: size,
minimum: size,
}
}
#[inline]
pub const fn optimal(&self) -> usize {
self.optimal
}
#[inline]
pub const fn minimum(&self) -> usize {
self.minimum
}
#[inline]
pub const fn optimal_bytes<T>(&self) -> usize {
self.optimal * std::mem::size_of::<T>()
}
#[inline]
pub const fn minimum_bytes<T>(&self) -> usize {
self.minimum * std::mem::size_of::<T>()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WorkspaceQueryWithInt {
pub real_work: WorkspaceQuery,
pub int_work: WorkspaceQuery,
}
impl WorkspaceQueryWithInt {
#[inline]
pub const fn new(real_work: WorkspaceQuery, int_work: WorkspaceQuery) -> Self {
Self {
real_work,
int_work,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SvdWorkspaceQuery {
pub work: WorkspaceQuery,
pub iwork: Option<usize>,
}
impl SvdWorkspaceQuery {
#[inline]
pub const fn new(work: WorkspaceQuery, iwork: Option<usize>) -> Self {
Self { work, iwork }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EvdWorkspaceQuery {
pub work: WorkspaceQuery,
pub iwork: Option<usize>,
pub rwork: Option<usize>,
}
impl EvdWorkspaceQuery {
#[inline]
pub const fn new(work: WorkspaceQuery, iwork: Option<usize>, rwork: Option<usize>) -> Self {
Self { work, iwork, rwork }
}
}
#[inline]
pub fn lu_workspace(m: usize, n: usize) -> WorkspaceQuery {
let k = m.min(n);
let nb = optimal_block_size_lu(m, n);
let optimal = nb * k;
let minimum = 1; WorkspaceQuery::new(optimal, minimum)
}
#[inline]
pub fn lu_solve_workspace(n: usize, nrhs: usize) -> WorkspaceQuery {
let nb = optimal_block_size_trsm(n, nrhs);
let optimal = nb * nrhs;
let minimum = 1;
WorkspaceQuery::new(optimal, minimum)
}
#[inline]
pub fn band_lu_workspace(_n: usize, kl: usize, ku: usize) -> WorkspaceQuery {
let bandwidth = 2 * kl + ku + 1;
let optimal = bandwidth;
let minimum = bandwidth;
WorkspaceQuery::new(optimal, minimum)
}
#[inline]
pub fn cholesky_workspace(n: usize) -> WorkspaceQuery {
let nb = optimal_block_size_cholesky(n);
let optimal = nb * n;
let minimum = 1;
WorkspaceQuery::new(optimal, minimum)
}
#[inline]
pub fn cholesky_solve_workspace(n: usize, nrhs: usize) -> WorkspaceQuery {
let nb = optimal_block_size_trsm(n, nrhs);
let optimal = nb * nrhs;
let minimum = 1;
WorkspaceQuery::new(optimal, minimum)
}
#[inline]
pub fn ldlt_workspace(n: usize) -> WorkspaceQuery {
let nb = optimal_block_size_cholesky(n);
let optimal = n * nb;
let minimum = n;
WorkspaceQuery::new(optimal, minimum)
}
#[inline]
pub fn qr_workspace(m: usize, n: usize) -> WorkspaceQuery {
let _k = m.min(n);
let nb = optimal_block_size_qr(m, n);
let optimal = nb * n + nb * m;
let minimum = n.max(1);
WorkspaceQuery::new(optimal, minimum)
}
#[inline]
pub fn orgqr_workspace(m: usize, n: usize, _k: usize) -> WorkspaceQuery {
let nb = optimal_block_size_qr(m, n);
let optimal = n * nb;
let minimum = n.max(1);
WorkspaceQuery::new(optimal, minimum)
}
#[inline]
pub fn ormqr_workspace(side: char, m: usize, n: usize, _k: usize) -> WorkspaceQuery {
let nw = if side == 'L' || side == 'l' { n } else { m };
let nb = optimal_block_size_qr(m, n);
let optimal = nw * nb;
let minimum = nw.max(1);
WorkspaceQuery::new(optimal, minimum)
}
#[inline]
pub fn qr_pivot_workspace(m: usize, n: usize) -> WorkspaceQuery {
let nb = optimal_block_size_qr(m, n);
let optimal = 2 * n + (n + 1) * nb;
let minimum = 3 * n + 1;
WorkspaceQuery::new(optimal, minimum)
}
#[inline]
pub fn svd_workspace(m: usize, n: usize, compute_u: bool, compute_vt: bool) -> SvdWorkspaceQuery {
let k = m.min(n);
let mut optimal = k * k;
if compute_u {
optimal += m * k;
}
if compute_vt {
optimal += n * k;
}
optimal += 2 * n;
let minimum = max(1, 3 * k + max(m, n));
SvdWorkspaceQuery::new(WorkspaceQuery::new(optimal, minimum), None)
}
#[inline]
pub fn svd_dc_workspace(m: usize, n: usize, job: char) -> SvdWorkspaceQuery {
let mn = m.min(n);
let mx = m.max(n);
let (optimal, minimum) = match job {
'N' | 'n' => {
let opt = 3 * mn + max(mx, 7 * mn);
let min = 3 * mn + max(mx, 6 * mn);
(opt, min)
}
'O' | 'o' => {
let opt = 3 * mn * mn + max(mx, 5 * mn * mn + 4 * mn);
let min = 3 * mn + max(mx, 5 * mn * mn + 4 * mn);
(opt, min)
}
'S' | 's' => {
let opt = 4 * mn * mn + max(mx, 5 * mn * mn + 4 * mn);
let min = 3 * mn + max(mx, 5 * mn * mn + 4 * mn);
(opt, min)
}
_ => {
let opt = 4 * mn * mn + max(mx, 5 * mn * mn + 4 * mn);
let min = 3 * mn + max(mx, 5 * mn * mn + 4 * mn);
(opt, min)
}
};
let iwork = 8 * mn;
SvdWorkspaceQuery::new(WorkspaceQuery::new(optimal, minimum), Some(iwork))
}
#[inline]
pub fn bidiag_workspace(m: usize, n: usize) -> WorkspaceQuery {
let _k = m.min(n);
let nb = optimal_block_size_bidiag(m, n);
let optimal = (m + n) * nb;
let minimum = max(m, n);
WorkspaceQuery::new(optimal, minimum)
}
#[inline]
pub fn symmetric_evd_workspace(n: usize, compute_vectors: bool) -> EvdWorkspaceQuery {
let nb = optimal_block_size_evd(n);
let (optimal, minimum) = if compute_vectors {
let opt = (nb + 2) * n;
let min = 3 * n - 1;
(opt, min)
} else {
let opt = (nb + 1) * n;
let min = max(1, 3 * n - 1);
(opt, min)
};
EvdWorkspaceQuery::new(WorkspaceQuery::new(optimal, minimum), None, None)
}
#[inline]
pub fn symmetric_evd_dc_workspace(n: usize, compute_vectors: bool) -> EvdWorkspaceQuery {
let (work_opt, work_min, iwork) = if compute_vectors {
let opt = 1 + 6 * n + 2 * n * n;
let min = 1 + 6 * n + 2 * n * n;
let iw = 3 + 5 * n;
(opt, min, Some(iw))
} else {
let opt = 2 * n + 1;
let min = 2 * n + 1;
(opt, min, Some(1))
};
EvdWorkspaceQuery::new(WorkspaceQuery::new(work_opt, work_min), iwork, None)
}
#[inline]
pub fn hermitian_evd_workspace(n: usize, compute_vectors: bool) -> EvdWorkspaceQuery {
let nb = optimal_block_size_evd(n);
let (optimal, minimum) = if compute_vectors {
let opt = (nb + 1) * n;
let min = 2 * n - 1;
(opt, min)
} else {
let opt = nb * n;
let min = max(1, 2 * n - 1);
(opt, min)
};
let rwork = 3 * n - 2;
EvdWorkspaceQuery::new(WorkspaceQuery::new(optimal, minimum), None, Some(rwork))
}
#[inline]
pub fn general_evd_workspace(
n: usize,
compute_left: bool,
compute_right: bool,
) -> EvdWorkspaceQuery {
let optimal: usize;
let minimum: usize;
if compute_left || compute_right {
optimal = (2 + optimal_block_size_evd(n)) * n;
minimum = 4 * n;
} else {
optimal = (2 + optimal_block_size_evd(n)) * n;
minimum = 3 * n;
}
EvdWorkspaceQuery::new(WorkspaceQuery::new(optimal, minimum), None, None)
}
#[inline]
pub fn schur_workspace(n: usize, compute_vectors: bool) -> EvdWorkspaceQuery {
let nb = optimal_block_size_hessenberg(n);
let (optimal, minimum) = if compute_vectors {
let opt = n * (1 + nb);
let min = max(1, 3 * n);
(opt, min)
} else {
let opt = n * (1 + nb);
let min = max(1, 2 * n);
(opt, min)
};
EvdWorkspaceQuery::new(WorkspaceQuery::new(optimal, minimum), None, None)
}
#[inline]
pub fn hessenberg_workspace(n: usize) -> WorkspaceQuery {
let nb = optimal_block_size_hessenberg(n);
let optimal = n * nb;
let minimum = max(1, n);
WorkspaceQuery::new(optimal, minimum)
}
#[inline]
pub fn generalized_evd_workspace(
n: usize,
_compute_left: bool,
_compute_right: bool,
) -> EvdWorkspaceQuery {
let optimal = max(1, 2 * n + max(6 * n, n * n));
let minimum = max(1, 8 * n);
EvdWorkspaceQuery::new(WorkspaceQuery::new(optimal, minimum), None, None)
}
#[inline]
pub fn qz_workspace(n: usize, _compute_left: bool, _compute_right: bool) -> EvdWorkspaceQuery {
let optimal = max(1, 8 * n + 16);
let minimum = max(1, 8 * n);
EvdWorkspaceQuery::new(WorkspaceQuery::new(optimal, minimum), None, None)
}
#[inline]
pub fn least_squares_workspace(m: usize, n: usize, nrhs: usize) -> WorkspaceQuery {
let k = m.min(n);
let nb = optimal_block_size_qr(m, n);
let optimal = k + max(k, nrhs) * nb;
let minimum = k + max(1, max(m, max(n, nrhs)));
WorkspaceQuery::new(optimal, minimum)
}
#[inline]
pub fn triangular_solve_workspace(n: usize, nrhs: usize) -> WorkspaceQuery {
let nb = optimal_block_size_trsm(n, nrhs);
let optimal = nb * nrhs;
let minimum = 1;
WorkspaceQuery::new(optimal, minimum)
}
#[inline]
pub fn tridiagonal_solve_workspace(n: usize, _nrhs: usize) -> WorkspaceQuery {
let optimal = 2 * n;
let minimum = n;
WorkspaceQuery::new(optimal, minimum)
}
#[inline]
pub fn optimal_block_size_lu(m: usize, n: usize) -> usize {
let k = m.min(n);
if k < 64 {
k.max(1)
} else if k < 256 {
32
} else if k < 1024 {
64
} else {
128
}
}
#[inline]
pub fn optimal_block_size_cholesky(n: usize) -> usize {
if n < 64 {
n.max(1)
} else if n < 256 {
32
} else if n < 1024 {
64
} else {
128
}
}
#[inline]
pub fn optimal_block_size_qr(m: usize, n: usize) -> usize {
let k = m.min(n);
if k < 32 {
k.max(1)
} else if k < 128 {
32
} else if k < 512 {
48
} else {
64
}
}
#[inline]
pub fn optimal_block_size_bidiag(m: usize, n: usize) -> usize {
let k = m.min(n);
if k < 32 {
k.max(1)
} else if k < 256 {
32
} else {
48
}
}
#[inline]
pub fn optimal_block_size_evd(n: usize) -> usize {
if n < 32 {
n.max(1)
} else if n < 256 {
32
} else if n < 1024 {
48
} else {
64
}
}
#[inline]
pub fn optimal_block_size_hessenberg(n: usize) -> usize {
if n < 32 {
n.max(1)
} else if n < 256 {
32
} else {
48
}
}
#[inline]
pub fn optimal_block_size_trsm(n: usize, nrhs: usize) -> usize {
let k = n.min(nrhs);
if k < 32 {
k.max(1)
} else if k < 128 {
32
} else {
64
}
}
pub trait Workspace {
type Element;
fn as_mut_slice(&mut self) -> &mut [Self::Element];
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<T> Workspace for Vec<T> {
type Element = T;
fn as_mut_slice(&mut self) -> &mut [T] {
self.as_mut_slice()
}
fn len(&self) -> usize {
Vec::len(self)
}
}
impl<T> Workspace for [T] {
type Element = T;
fn as_mut_slice(&mut self) -> &mut [T] {
self
}
fn len(&self) -> usize {
<[T]>::len(self)
}
}
impl<T, const N: usize> Workspace for [T; N] {
type Element = T;
fn as_mut_slice(&mut self) -> &mut [T] {
self.as_mut_slice()
}
fn len(&self) -> usize {
N
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_workspace_query_new() {
let ws = WorkspaceQuery::new(100, 50);
assert_eq!(ws.optimal(), 100);
assert_eq!(ws.minimum(), 50);
}
#[test]
fn test_workspace_query_fixed() {
let ws = WorkspaceQuery::fixed(64);
assert_eq!(ws.optimal(), 64);
assert_eq!(ws.minimum(), 64);
}
#[test]
fn test_workspace_query_bytes() {
let ws = WorkspaceQuery::new(100, 50);
assert_eq!(ws.optimal_bytes::<f64>(), 800);
assert_eq!(ws.minimum_bytes::<f64>(), 400);
assert_eq!(ws.optimal_bytes::<f32>(), 400);
}
#[test]
fn test_lu_workspace() {
let ws = lu_workspace(100, 100);
assert!(ws.optimal >= ws.minimum);
assert!(ws.minimum >= 1);
}
#[test]
fn test_qr_workspace() {
let ws = qr_workspace(100, 50);
assert!(ws.optimal >= ws.minimum);
assert!(ws.minimum >= 1);
let ws_large = qr_workspace(1000, 500);
assert!(ws_large.optimal > ws.optimal);
}
#[test]
fn test_svd_workspace() {
let ws = svd_workspace(100, 50, true, true);
assert!(ws.work.optimal >= ws.work.minimum);
assert!(ws.iwork.is_none()); }
#[test]
fn test_svd_dc_workspace() {
let ws = svd_dc_workspace(100, 50, 'A');
assert!(ws.work.optimal >= ws.work.minimum);
assert!(ws.iwork.is_some()); assert!(ws.iwork.unwrap() > 0);
}
#[test]
fn test_symmetric_evd_workspace() {
let ws_novecs = symmetric_evd_workspace(100, false);
let ws_vecs = symmetric_evd_workspace(100, true);
assert!(ws_vecs.work.optimal >= ws_novecs.work.optimal);
}
#[test]
fn test_symmetric_evd_dc_workspace() {
let ws = symmetric_evd_dc_workspace(100, true);
assert!(ws.work.optimal >= ws.work.minimum);
assert!(ws.iwork.is_some());
}
#[test]
fn test_general_evd_workspace() {
let ws = general_evd_workspace(100, true, true);
assert!(ws.work.optimal >= ws.work.minimum);
}
#[test]
fn test_schur_workspace() {
let ws = schur_workspace(100, true);
assert!(ws.work.optimal >= ws.work.minimum);
}
#[test]
fn test_hessenberg_workspace() {
let ws = hessenberg_workspace(100);
assert!(ws.optimal >= ws.minimum);
}
#[test]
fn test_cholesky_workspace() {
let ws = cholesky_workspace(100);
assert!(ws.optimal >= ws.minimum);
}
#[test]
fn test_least_squares_workspace() {
let ws = least_squares_workspace(100, 50, 10);
assert!(ws.optimal >= ws.minimum);
}
#[test]
fn test_block_sizes() {
assert!(optimal_block_size_lu(10, 10) <= 32);
assert!(optimal_block_size_qr(10, 10) <= 32);
assert!(optimal_block_size_lu(2000, 2000) >= 64);
assert!(optimal_block_size_qr(2000, 2000) >= 48);
}
#[test]
fn test_workspace_trait() {
let mut vec_work: Vec<f64> = vec![0.0; 100];
assert_eq!(vec_work.len(), 100);
assert!(!vec_work.is_empty());
let slice = vec_work.as_mut_slice();
slice[0] = 1.0;
assert_eq!(vec_work[0], 1.0);
}
#[test]
fn test_workspace_trait_array() {
let mut arr_work: [f64; 64] = [0.0; 64];
assert_eq!(Workspace::len(&arr_work), 64);
let slice = arr_work.as_mut_slice();
slice[0] = 2.0;
assert_eq!(arr_work[0], 2.0);
}
#[test]
fn test_small_matrix_workspace() {
let ws = qr_workspace(1, 1);
assert!(ws.minimum >= 1);
let ws = svd_workspace(1, 1, true, true);
assert!(ws.work.minimum >= 1);
let ws = symmetric_evd_workspace(1, true);
assert!(ws.work.minimum >= 1);
}
#[test]
fn test_qr_pivot_workspace() {
let ws = qr_pivot_workspace(100, 50);
assert!(ws.optimal >= ws.minimum);
assert!(ws.minimum > 3 * 50);
}
#[test]
fn test_orgqr_workspace() {
let ws = orgqr_workspace(100, 50, 50);
assert!(ws.optimal >= ws.minimum);
}
#[test]
fn test_ormqr_workspace() {
let ws_left = ormqr_workspace('L', 100, 50, 50);
let ws_right = ormqr_workspace('R', 100, 50, 50);
assert!(ws_left.optimal >= ws_left.minimum);
assert!(ws_right.optimal >= ws_right.minimum);
}
#[test]
fn test_generalized_evd_workspace() {
let ws = generalized_evd_workspace(100, true, true);
assert!(ws.work.optimal >= ws.work.minimum);
}
#[test]
fn test_qz_workspace() {
let ws = qz_workspace(100, true, true);
assert!(ws.work.optimal >= ws.work.minimum);
}
#[test]
fn test_band_lu_workspace() {
let ws = band_lu_workspace(100, 3, 3);
assert_eq!(ws.optimal, 2 * 3 + 3 + 1); }
#[test]
fn test_bidiag_workspace() {
let ws = bidiag_workspace(100, 50);
assert!(ws.optimal >= ws.minimum);
}
#[test]
fn test_tridiagonal_solve_workspace() {
let ws = tridiagonal_solve_workspace(100, 1);
assert!(ws.optimal >= ws.minimum);
}
#[test]
fn test_hermitian_evd_workspace() {
let ws = hermitian_evd_workspace(100, true);
assert!(ws.work.optimal >= ws.work.minimum);
assert!(ws.rwork.is_some()); }
}