physics_in_parallel 3.0.3

High-performance infrastructure for numerical simulations in physics
Documentation
use ndarray::{Array1, Array2, array};

use physics_in_parallel::math::scalar::Complex;
use physics_in_parallel::math::tensor::{Dense, Tensor};

fn dense_from_array2(array: &Array2<f64>) -> Tensor<f64, Dense> {
    let shape = array.shape();
    let mut tensor = Tensor::<f64, Dense>::empty(shape);
    for ((i, j), value) in array.indexed_iter() {
        tensor.set(&[i as isize, j as isize], *value);
    }
    tensor
}

fn dense_from_array1(array: &Array1<f64>) -> Tensor<f64, Dense> {
    let mut tensor = Tensor::<f64, Dense>::empty(&[array.len()]);
    for (i, value) in array.indexed_iter() {
        tensor.set(&[i as isize], *value);
    }
    tensor
}

fn assert_tensor_eq_array2(tensor: &Tensor<f64, Dense>, expected: &Array2<f64>) {
    assert_eq!(tensor.shape(), expected.shape());
    for ((i, j), value) in expected.indexed_iter() {
        assert_eq!(tensor.get(&[i as isize, j as isize]), *value);
    }
}

fn assert_tensor_eq_array1(tensor: &Tensor<f64, Dense>, expected: &Array1<f64>) {
    assert_eq!(tensor.shape(), expected.shape());
    for (i, value) in expected.indexed_iter() {
        assert_eq!(tensor.get(&[i as isize]), *value);
    }
}

#[test]
fn transpose_and_matmul_match_ndarray() {
    let a_ref = array![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]];
    let b_ref = array![[7.0, 8.0], [9.0, 10.0], [11.0, 12.0]];
    let a = dense_from_array2(&a_ref);
    let b = dense_from_array2(&b_ref);

    let transpose = a.transpose();
    assert_tensor_eq_array2(&transpose, &a_ref.t().to_owned());

    let product = a.matmul(&b);
    assert_tensor_eq_array2(&product, &a_ref.dot(&b_ref));
}

#[test]
fn dot_cross_wedge_and_norm_match_ndarray_reference_calculations() {
    let a_ref = array![1.0, 2.0, 3.0];
    let b_ref = array![4.0, 5.0, 6.0];
    let a = dense_from_array1(&a_ref);
    let b = dense_from_array1(&b_ref);

    assert_eq!(a.dot(&b), a_ref.dot(&b_ref));
    assert_eq!(a.hermitian_dot(&b), a_ref.dot(&b_ref));
    assert_eq!(a.norm_sqr_real(), a_ref.dot(&a_ref));
    assert_eq!(a.norm(), a_ref.dot(&a_ref).sqrt());

    let cross_ref = array![
        a_ref[1] * b_ref[2] - a_ref[2] * b_ref[1],
        a_ref[2] * b_ref[0] - a_ref[0] * b_ref[2],
        a_ref[0] * b_ref[1] - a_ref[1] * b_ref[0],
    ];
    assert_tensor_eq_array1(&a.cross(&b), &cross_ref);

    let wedge_ref =
        Array2::from_shape_fn((3, 3), |(i, j)| a_ref[i] * b_ref[j] - a_ref[j] * b_ref[i]);
    assert_tensor_eq_array2(&a.wedge(&b), &wedge_ref);
}

#[test]
fn hermitian_transpose_and_dot_match_ndarray_style_reference() {
    let mut a = Tensor::<Complex<f64>, Dense>::empty(&[2, 2]);
    let a_ref = [
        [Complex::new(1.0, 1.0), Complex::new(2.0, -3.0)],
        [Complex::new(4.0, 5.0), Complex::new(6.0, -7.0)],
    ];
    for i in 0..2 {
        for j in 0..2 {
            a.set(&[i, j], a_ref[i as usize][j as usize]);
        }
    }

    let ah = a.hermitian_transpose();
    for i in 0..2 {
        for j in 0..2 {
            assert_eq!(ah.get(&[i, j]), a_ref[j as usize][i as usize].conj());
        }
    }

    let mut x = Tensor::<Complex<f64>, Dense>::empty(&[2]);
    let x_ref = [Complex::new(1.0, 1.0), Complex::new(2.0, -1.0)];
    x.set(&[0], x_ref[0]);
    x.set(&[1], x_ref[1]);

    let mut y = Tensor::<Complex<f64>, Dense>::empty(&[2]);
    let y_ref = [Complex::new(3.0, 0.0), Complex::new(0.0, 4.0)];
    y.set(&[0], y_ref[0]);
    y.set(&[1], y_ref[1]);

    let dot_ref = x_ref[0] * y_ref[0] + x_ref[1] * y_ref[1];
    let hermitian_dot_ref = x_ref[0].conj() * y_ref[0] + x_ref[1].conj() * y_ref[1];
    let norm_sqr_ref = x_ref[0].norm_sqr() + x_ref[1].norm_sqr();

    assert_eq!(x.dot(&y), dot_ref);
    assert_eq!(x.hermitian_dot(&y), hermitian_dot_ref);
    assert_eq!(x.norm_sqr_real(), norm_sqr_ref);
    assert_eq!(x.norm(), Complex::new(norm_sqr_ref.sqrt(), 0.0));
}