use std::ops::AddAssign;
use std::ops::Mul;
use faer::reborrow::ReborrowMut;
use faer_traits::ComplexField;
use crate::ComplexScalar;
use faer::MatMut;
use faer::MatRef;
pub unsafe fn kron_kernel_raw<C: Mul<Output = C> + Copy>(
dst: *mut C,
dst_rs: isize,
dst_cs: isize,
lhs: *const C,
lhs_nrows: usize,
lhs_ncols: usize,
lhs_rs: isize,
lhs_cs: isize,
rhs: *const C,
rhs_nrows: usize,
rhs_ncols: usize,
rhs_rs: isize,
rhs_cs: isize,
) {
unsafe {
for lhs_j in 0..lhs_ncols {
for lhs_i in 0..lhs_nrows {
let lhs_val = *lhs.offset(lhs_i as isize * lhs_rs + lhs_j as isize * lhs_cs);
let dst_major_row = lhs_i * rhs_nrows;
let dst_major_col = lhs_j * rhs_ncols;
for rhs_j in 0..rhs_ncols {
for rhs_i in 0..rhs_nrows {
let rhs_val =
*rhs.offset(rhs_i as isize * rhs_rs + rhs_j as isize * rhs_cs);
let dst_row = dst_major_row + rhs_i;
let dst_col = dst_major_col + rhs_j;
let dst_offset = dst_row as isize * dst_rs + dst_col as isize * dst_cs;
*dst.offset(dst_offset) = lhs_val * rhs_val;
}
}
}
}
}
}
pub unsafe fn kron_kernel_add_raw<C: Mul<Output = C> + Copy + AddAssign>(
dst: *mut C,
dst_rs: isize,
dst_cs: isize,
lhs: *const C,
lhs_nrows: usize,
lhs_ncols: usize,
lhs_rs: isize,
lhs_cs: isize,
rhs: *const C,
rhs_nrows: usize,
rhs_ncols: usize,
rhs_rs: isize,
rhs_cs: isize,
) {
unsafe {
for lhs_j in 0..lhs_ncols {
for lhs_i in 0..lhs_nrows {
let lhs_val = *lhs.offset(lhs_i as isize * lhs_rs + lhs_j as isize * lhs_cs);
let dst_major_row = lhs_i * rhs_nrows;
let dst_major_col = lhs_j * rhs_ncols;
for rhs_j in 0..rhs_ncols {
for rhs_i in 0..rhs_nrows {
let rhs_val =
*rhs.offset(rhs_i as isize * rhs_rs + rhs_j as isize * rhs_cs);
let dst_row = dst_major_row + rhs_i;
let dst_col = dst_major_col + rhs_j;
let dst_offset = dst_row as isize * dst_rs + dst_col as isize * dst_cs;
*dst.offset(dst_offset) += lhs_val * rhs_val;
}
}
}
}
}
}
unsafe fn kron_kernel<C: ComplexField>(
mut dst: MatMut<C>,
lhs: MatRef<C>,
rhs: MatRef<C>,
lhs_rows: usize,
lhs_cols: usize,
rhs_rows: usize,
rhs_cols: usize,
) {
unsafe {
for lhs_j in 0..lhs_cols {
for lhs_i in 0..lhs_rows {
let lhs_val = lhs.get_unchecked(lhs_i, lhs_j);
for rhs_j in 0..rhs_cols {
for rhs_i in 0..rhs_rows {
let rhs_val = rhs.get_unchecked(rhs_i, rhs_j);
*(dst.rb_mut().get_mut_unchecked(
lhs_i * rhs_rows + rhs_i,
lhs_j * rhs_cols + rhs_j,
)) = lhs_val.mul_by_ref(rhs_val);
}
}
}
}
}
}
unsafe fn kron_kernel_add<C: ComplexScalar>(
mut dst: MatMut<C>,
lhs: MatRef<C>,
rhs: MatRef<C>,
lhs_rows: usize,
lhs_cols: usize,
rhs_rows: usize,
rhs_cols: usize,
) {
unsafe {
for lhs_j in 0..lhs_cols {
for lhs_i in 0..lhs_rows {
let lhs_val = lhs.get_unchecked(lhs_i, lhs_j);
for rhs_j in 0..rhs_cols {
for rhs_i in 0..rhs_rows {
let rhs_val = rhs.get_unchecked(rhs_i, rhs_j);
*(dst.rb_mut().get_mut_unchecked(
lhs_i * rhs_rows + rhs_i,
lhs_j * rhs_cols + rhs_j,
)) += lhs_val.mul_by_ref(rhs_val);
}
}
}
}
}
}
pub unsafe fn kron_unchecked<C: ComplexField>(dst: MatMut<C>, lhs: MatRef<C>, rhs: MatRef<C>) {
unsafe {
let lhs_rows = lhs.nrows();
let lhs_cols = lhs.ncols();
let rhs_rows = rhs.nrows();
let rhs_cols = rhs.ncols();
cartesian_match!(
{ kron_kernel(dst, lhs, rhs, lhs_rows, lhs_cols, rhs_rows, rhs_cols) },
(lhs_rows, (lhs_cols, (rhs_rows, (rhs_cols, ())))),
(
(2, 3, 4, _),
((2, 3, 4, _), ((2, 3, 4, _), ((2, 3, 4, _), ())))
)
);
}
}
pub unsafe fn kron_sq_unchecked<C: ComplexField>(dst: MatMut<C>, lhs: MatRef<C>, rhs: MatRef<C>) {
unsafe {
let lhs_dim = lhs.nrows();
let rhs_dim = rhs.nrows();
cartesian_match!(
{ kron_kernel(dst, lhs, rhs, lhs_dim, lhs_dim, rhs_dim, rhs_dim) },
(lhs_dim, (rhs_dim, ())),
(
(2, 3, 4, 6, 8, 9, 16, 27, 32, 64, 81, _),
((2, 3, 4, 6, 8, 9, 16, 27, 32, 64, 81, _), ())
)
);
}
}
pub fn kron<C: ComplexField>(lhs: MatRef<C>, rhs: MatRef<C>, dst: MatMut<C>) {
let mut lhs = lhs;
let mut rhs = rhs;
let mut dst = dst;
if dst.col_stride().unsigned_abs() < dst.row_stride().unsigned_abs() {
dst = dst.transpose_mut();
lhs = lhs.transpose();
rhs = rhs.transpose();
}
assert!(Some(dst.nrows()) == lhs.nrows().checked_mul(rhs.nrows()));
assert!(Some(dst.ncols()) == lhs.ncols().checked_mul(rhs.ncols()));
if lhs.nrows() == lhs.ncols() && rhs.nrows() == rhs.ncols() {
unsafe { kron_sq_unchecked(dst, lhs, rhs) }
} else {
unsafe { kron_unchecked(dst, lhs, rhs) }
}
}
pub fn kron_add<C: ComplexScalar>(lhs: MatRef<C>, rhs: MatRef<C>, dst: MatMut<C>) {
let mut lhs = lhs;
let mut rhs = rhs;
let mut dst = dst;
if dst.col_stride().unsigned_abs() < dst.row_stride().unsigned_abs() {
dst = dst.transpose_mut();
lhs = lhs.transpose();
rhs = rhs.transpose();
}
assert!(Some(dst.nrows()) == lhs.nrows().checked_mul(rhs.nrows()));
assert!(Some(dst.ncols()) == lhs.ncols().checked_mul(rhs.ncols()));
unsafe {
kron_kernel_add(
dst,
lhs,
rhs,
lhs.nrows(),
lhs.ncols(),
rhs.nrows(),
rhs.ncols(),
);
}
}
#[cfg(test)]
mod kron_tests {
use super::*;
use faer::Mat;
use faer::mat;
#[test]
fn kron_test() {
let a = mat![[1.0, 2.0], [3.0, 4.0],];
let b = mat![[0.0, 5.0], [6.0, 7.0],];
let c = mat![
[0.0, 5.0, 0.0, 10.0],
[6.0, 7.0, 12.0, 14.0],
[0.0, 15.0, 0.0, 20.0],
[18.0, 21.0, 24.0, 28.0],
];
let mut dst = Mat::new();
dst.resize_with(4, 4, |_, _| 0f64);
kron(a.as_ref(), b.as_ref(), dst.as_mut());
assert_eq!(dst, c);
}
}