Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,22 @@ env:
CARGO_TERM_COLOR: always

jobs:
lint:

runs-on: macos-latest

steps:
- uses: actions/checkout@v4

- name: Install rustfmt and clippy
run: rustup component add rustfmt clippy

- name: Check formatting
run: cargo fmt --all -- --check

- name: Clippy
run: cargo clippy --all-targets --features sdp-accelerate,faer-sparse,serde -- -D warnings

build:

runs-on: macos-latest
Expand Down
19 changes: 19 additions & 0 deletions .github/workflows/msrv.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,24 @@ jobs:
steps:
- uses: actions/checkout@v4
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}

# Cargo.lock is not committed, so every run resolves dependencies afresh and
# picks up releases that have raised their own MSRV past ours: serde_json
# >=1.0.150, proc-macro2 >=1.0.107, unicode-ident >=1.0.23 and syn 3.x all
# now require rustc 1.71. Resolve with a current cargo using MSRV-aware
# resolution, which honours rust-version in Cargo.toml, then build and test
# with the pinned toolchain.
- name: Resolve dependencies compatible with the declared MSRV
if: matrix.toolchain == '1.70.0'
env:
CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS: fallback
run: |
rustup toolchain install stable --profile minimal
cargo +stable generate-lockfile
# serde pins serde_derive with an exact `=` requirement, and serde_derive
# 1.0.229 depends on syn 3.x; the fallback resolver cannot see through
# that pin, so step serde back one release explicitly.
cargo +stable update -p serde --precise 1.0.228

- run: cargo build --verbose
- run: cargo test --verbose
4 changes: 3 additions & 1 deletion .github/workflows/pypi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ jobs:
test: false

# macOS Intel build
- os: macos-13
# macos-13 was retired on 2025-12-04; macos-15-intel is the
# replacement x86_64 image, available until August 2027.
- os: macos-15-intel
target: x86_64
features: "python,pardiso"
test: true
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/testpypi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ jobs:
test: false

# macOS Intel build
- os: macos-13
# macos-13 was retired on 2025-12-04; macos-15-intel is the
# replacement x86_64 image, available until August 2027.
- os: macos-15-intel
target: x86_64
features: "python,pardiso"
test: true
Expand Down
42 changes: 14 additions & 28 deletions src/algebra/dense/blas/cholesky.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,7 @@ where
if A <= T::zero() {
// check for positive definite
Err(DenseFactorizationError::Cholesky(1))
}
else {
} else {
self.L[(0, 0)] = A.sqrt();
Ok(())
}
Expand Down Expand Up @@ -307,26 +306,17 @@ mod test {

fn test_data_2x2<T: FloatT>() -> (Matrix<T>, Matrix<T>, Matrix<T>) {
// Create a symmetric matrix S
let S = Matrix::<T>::from(&[
[(4.0).as_T(), (1.0).as_T()],
[(1.0).as_T(), (3.0).as_T()],
]);

let S = Matrix::<T>::from(&[[(4.0).as_T(), (1.0).as_T()], [(1.0).as_T(), (3.0).as_T()]]);

// Solution matrix X with 2 columns
let X = Matrix::<T>::from(&[
[(2.0).as_T(), (3.0).as_T()],
[(1.0).as_T(), (2.0).as_T()],
]);

let X = Matrix::<T>::from(&[[(2.0).as_T(), (3.0).as_T()], [(1.0).as_T(), (2.0).as_T()]]);

// Right-hand side B = S*X
let B = Matrix::<T>::from(&[
[(9.0).as_T(), (14.0).as_T()],
[(5.0).as_T(), (9.0).as_T()],
]);

let B = Matrix::<T>::from(&[[(9.0).as_T(), (14.0).as_T()], [(5.0).as_T(), (9.0).as_T()]]);

(S, X, B)
}

#[rustfmt::skip]
fn test_data_3x3<T: FloatT>() -> (Matrix<T>, Matrix<T>, Matrix<T>) {
let S = Matrix::<T>::from(&[
Expand All @@ -350,7 +340,6 @@ mod test {
(S, X, B)
}


#[rustfmt::skip]
fn test_data_4x4<T: FloatT>() -> (Matrix<T>, Matrix<T>, Matrix<T>) {
// Create a symmetric matrix S
Expand Down Expand Up @@ -441,9 +430,6 @@ mod test {
generate_test_cholesky_logdet!(f64, test_cholesky_logdet_f64, abs);
}




#[cfg(all(test, feature = "bench"))]
mod bench {

Expand All @@ -454,15 +440,15 @@ mod bench {

let v: Vec<f64> = (-100..=100).map(|i| i as f64).collect();

iproduct!(v.clone(), v.clone(), v.clone()).map(move |(b,d,e)| {
// new matrices that are positive definite,
// so choose a,c,e so that the matrix is
iproduct!(v.clone(), v.clone(), v.clone()).map(move |(b, d, e)| {
// new matrices that are positive definite,
// so choose a,c,e so that the matrix is
// diagonally dominant
let a = b.abs() + d.abs() + 0.1;
let c = b.abs() + e.abs() + 0.1;
let f = d.abs() + e.abs() + 0.1;
let data = [a,b,d,b,c,e,d,e,f];
Matrix::new_from_slice((3,3), &data)
let data = [a, b, d, b, c, e, d, e, f];
Matrix::new_from_slice((3, 3), &data)
})
}

Expand All @@ -478,4 +464,4 @@ mod bench {
let _ = eng.factorblas(&mut A);
}
}
}
}
74 changes: 26 additions & 48 deletions src/algebra/dense/blas/svd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,26 +366,17 @@ mod test {

fn test_solve_data_2x2<T: FloatT>() -> (Matrix<T>, Matrix<T>, Matrix<T>) {
// Create a symmetric matrix S
let A = Matrix::<T>::from(&[
[(4.0).as_T(), (1.0).as_T()],
[(1.0).as_T(), (3.0).as_T()],
]);

let A = Matrix::<T>::from(&[[(4.0).as_T(), (1.0).as_T()], [(1.0).as_T(), (3.0).as_T()]]);

// Solution matrix X with 2 columns
let X = Matrix::<T>::from(&[
[(2.0).as_T(), (3.0).as_T()],
[(1.0).as_T(), (2.0).as_T()],
]);

let X = Matrix::<T>::from(&[[(2.0).as_T(), (3.0).as_T()], [(1.0).as_T(), (2.0).as_T()]]);

// Right-hand side B = S*X
let B = Matrix::<T>::from(&[
[(9.0).as_T(), (14.0).as_T()],
[(5.0).as_T(), (9.0).as_T()],
]);

let B = Matrix::<T>::from(&[[(9.0).as_T(), (14.0).as_T()], [(5.0).as_T(), (9.0).as_T()]]);

(A, X, B)
}

#[rustfmt::skip]
fn test_solve_data_3x3<T: FloatT>() -> (Matrix<T>, Matrix<T>, Matrix<T>) {
let A = Matrix::<T>::from(&[
Expand All @@ -409,7 +400,6 @@ mod test {
(A, X, B)
}


#[rustfmt::skip]
fn test_solve_data_4x4<T: FloatT>() -> (Matrix<T>, Matrix<T>, Matrix<T>) {
// Create a symmetric matrix S
Expand Down Expand Up @@ -451,7 +441,6 @@ mod test {
];

for method in methods.iter() {

// A and B are modified inplace during factor/solve
let mut thisA = A.clone();
let mut thisB = B.clone();
Expand All @@ -471,31 +460,30 @@ mod test {
#[test]
fn $test_name() {
let (mut A, mut X, mut B) = test_solve_data_2x2::<$fxx>();
run_svd_solve_test(&mut A, &mut X, &mut B, |x| x.$tolfn());
run_svd_solve_test(&mut A, &mut X, &mut B, |x| x.$tolfn());

let (mut A, mut X, mut B) = test_solve_data_3x3::<$fxx>();
run_svd_solve_test(&mut A, &mut X, &mut B, |x| x.$tolfn());
let (mut A, mut X, mut B) = test_solve_data_3x3::<$fxx>();
run_svd_solve_test(&mut A, &mut X, &mut B, |x| x.$tolfn());

let (mut A, mut X, mut B) = test_solve_data_4x4::<$fxx>();
run_svd_solve_test(&mut A, &mut X, &mut B, |x| x.$tolfn());
run_svd_solve_test(&mut A, &mut X, &mut B, |x| x.$tolfn());
}
};
}

generate_test_svd_solve!(f32, test_svd_solve_f32, sqrt);
generate_test_svd_solve!(f64, test_svd_solve_f64, abs);


fn test_factor_data_2x2<T: FloatT>() ->Matrix<T> {
let (A,_,_) = test_solve_data_2x2::<T>();
fn test_factor_data_2x2<T: FloatT>() -> Matrix<T> {
let (A, _, _) = test_solve_data_2x2::<T>();
A
}
fn test_factor_data_3x3<T: FloatT>() ->Matrix<T> {
let (A,_,_) = test_solve_data_3x3::<T>();
fn test_factor_data_3x3<T: FloatT>() -> Matrix<T> {
let (A, _, _) = test_solve_data_3x3::<T>();
A
}
fn test_factor_data_4x4<T: FloatT>() ->Matrix<T> {
let (A,_,_) = test_solve_data_4x4::<T>();
fn test_factor_data_4x4<T: FloatT>() -> Matrix<T> {
let (A, _, _) = test_solve_data_4x4::<T>();
A
}

Expand All @@ -522,7 +510,6 @@ mod test {
s.windows(2).all(|w| w[0] >= w[1])
}


fn run_svd_factor_test<T>(A: &mut Matrix<T>, tolfn: fn(T) -> T)
where
T: FloatT,
Expand All @@ -535,7 +522,6 @@ mod test {
];

for method in methods.iter() {

let Acopy = A.clone(); //A is corrupted after factorization

let mut eng = SVDEngine::<T>::new(A.size());
Expand Down Expand Up @@ -564,57 +550,51 @@ mod test {
}
}


macro_rules! generate_test_svd_factor {
($fxx:ty, $test_name:ident, $tolfn:ident) => {
#[test]
fn $test_name() {
let mut A = test_factor_data_2x2::<$fxx>();
run_svd_factor_test(&mut A, |x| x.$tolfn());
run_svd_factor_test(&mut A, |x| x.$tolfn());

let mut A = test_factor_data_3x3::<$fxx>();
run_svd_factor_test(&mut A, |x| x.$tolfn());
let mut A = test_factor_data_3x3::<$fxx>();
run_svd_factor_test(&mut A, |x| x.$tolfn());

let mut A = test_factor_data_4x4::<$fxx>();
run_svd_factor_test(&mut A, |x| x.$tolfn());
run_svd_factor_test(&mut A, |x| x.$tolfn());

let mut A = test_factor_data_2x4::<$fxx>();
run_svd_factor_test(&mut A, |x| x.$tolfn());
run_svd_factor_test(&mut A, |x| x.$tolfn());

let mut A = test_factor_data_4x2::<$fxx>();
run_svd_factor_test(&mut A, |x| x.$tolfn());
run_svd_factor_test(&mut A, |x| x.$tolfn());
}
};
}

generate_test_svd_factor!(f32, test_svd_factor_f32, sqrt);
generate_test_svd_factor!(f64, test_svd_factor_f64, abs);

}



#[cfg(all(test, feature = "bench"))]
mod bench {

use super::*;

fn svd3_bench_iter() -> impl Iterator<Item = Matrix<f64>> {

use itertools::iproduct;

let v = [-4., -2., 0., 1., 5.];

iproduct!(v, v, v, v, v, v, v, v, v).map(move |(a, b, c, d, e, f, g, h, i)| {
let data = [a,b,c,d,e,f,g,h,i];
Matrix::new_from_slice((3,3), &data)
let data = [a, b, c, d, e, f, g, h, i];
Matrix::new_from_slice((3, 3), &data)
})
}

#[test]
fn bench_svd3_vs_blas() {

let mut eng = SVDEngine::<f64>::new((3,3));
let mut eng = SVDEngine::<f64>::new((3, 3));

for mut A in svd3_bench_iter() {
eng.factor3(&mut A).unwrap();
Expand All @@ -624,6 +604,4 @@ mod bench {
eng.factorblas(&mut A).unwrap();
}
}

}

14 changes: 6 additions & 8 deletions src/algebra/dense/blas/symv.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#![allow(non_snake_case)]

use crate::algebra::{
DenseMatrix, FloatT, Matrix, MultiplySYMV, ShapedMatrix, Symmetric,
};
use crate::algebra::{DenseMatrix, FloatT, Matrix, MultiplySYMV, ShapedMatrix, Symmetric};

impl<T> MultiplySYMV for Symmetric<'_, Matrix<T>>
where
Expand Down Expand Up @@ -31,8 +29,8 @@ macro_rules! generate_test_gsymv {
fn $test_name() {
#[rustfmt::skip]
let A = Matrix::<$fxx>::from(&[
[ 1., 2., 4.],
[ 0., 3., 5.],
[ 1., 2., 4.],
[ 0., 3., 5.],
[ 0., 0., 6.],
]);

Expand All @@ -43,16 +41,16 @@ macro_rules! generate_test_gsymv {

#[rustfmt::skip]
let A = Matrix::<$fxx>::from(&[
[ 1., 0., 0.],
[ 2., 3., 0.],
[ 1., 0., 0.],
[ 2., 3., 0.],
[ 4., 5., 6.],
]);

let x = vec![1., -2., 3.];
let mut y = vec![-4., -1., 3.];
A.sym_lo().symv(&x, &mut y, 2.0, 3.0);
assert_eq!(y, [6.0, 19.0, 33.0]);
}
}
};
}

Expand Down
Loading
Loading