conspire 0.7.2

The Rust interface to conspire.
Documentation
use super::{TensorRank2, TensorRank2SparseVec2D};
use crate::math::{Hessian, HessianAccumulate, HessianAccumulateGeneral, Rank2, SquareMatrix};

fn block(value: f64) -> TensorRank2<2, 1, 1> {
    TensorRank2::from([[value, 2.0 * value], [3.0 * value, 4.0 * value]])
}

fn accumulator() -> TensorRank2SparseVec2D<2, 1, 1> {
    let mut stiffnesses = TensorRank2SparseVec2D::<2, 1, 1>::zero(3);
    stiffnesses[2][0] += block(1.0);
    stiffnesses[0][0] += block(2.0);
    stiffnesses[0][2] += block(3.0);
    stiffnesses[0][0] += block(4.0);
    stiffnesses[1][1] += block(5.0);
    stiffnesses
}

fn dense() -> SquareMatrix {
    let mut square_matrix = SquareMatrix::zero(6);
    accumulator().fill_into(&mut square_matrix);
    square_matrix
}

#[test]
fn insertion_sums_and_sorts() {
    let stiffnesses = accumulator();
    assert_eq!(stiffnesses[0][0][0][0], 6.0);
    assert_eq!(stiffnesses[0][2][1][0], 9.0);
    assert_eq!(stiffnesses[2][0][1][1], 4.0);
    let columns: Vec<usize> = stiffnesses[0].entries().map(|(column, _)| column).collect();
    assert_eq!(columns, [0, 2]);
}

#[test]
fn fill_into_scatters_blocks() {
    let square_matrix = dense();
    assert_eq!(square_matrix[0][0], 6.0);
    assert_eq!(square_matrix[1][0], 18.0);
    assert_eq!(square_matrix[0][5], 6.0);
    assert_eq!(square_matrix[4][0], 1.0);
    assert_eq!(square_matrix[2][2], 5.0);
    assert_eq!(square_matrix[0][2], 0.0);
}

#[test]
fn retain_from_filters_and_remaps() {
    let retained = [true, false, true, true, true, false];
    let square_matrix = accumulator().retain_from(&retained);
    let full = dense();
    let kept: Vec<usize> = (0..6).filter(|&p| retained[p]).collect();
    kept.iter().enumerate().for_each(|(p, &full_p)| {
        kept.iter()
            .enumerate()
            .for_each(|(q, &full_q)| assert_eq!(square_matrix[p][q], full[full_p][full_q]))
    });
}

#[test]
fn rotation_preserves_structure() {
    let rotation = TensorRank2::<2, 1, 1>::from([[0.0, -1.0], [1.0, 0.0]]);
    let rotated = rotation.transpose() * accumulator() * rotation;
    let mut square_matrix = SquareMatrix::zero(6);
    rotated.fill_into(&mut square_matrix);
    let full = dense();
    assert_eq!(square_matrix[0][0], full[1][1]);
    assert_eq!(square_matrix[1][0], -full[0][1]);
    assert_eq!(square_matrix[0][5], -full[1][4]);
}

#[test]
fn merge_add_and_subtract() {
    let mut other = TensorRank2SparseVec2D::<2, 1, 1>::zero(3);
    other[0][1] += block(7.0);
    other[0][0] += block(1.0);
    let sum: Vec<_> = accumulator()
        .into_iter()
        .zip(other.clone())
        .map(|(row, other_row)| row + other_row)
        .collect();
    assert_eq!(sum[0][0][0][0], 7.0);
    assert_eq!(sum[0][1][0][0], 7.0);
    assert_eq!(sum[0][2][0][0], 3.0);
    let difference: Vec<_> = accumulator()
        .into_iter()
        .zip(other)
        .map(|(row, other_row)| row - other_row)
        .collect();
    assert_eq!(difference[0][0][0][0], 5.0);
    assert_eq!(difference[0][1][0][0], -7.0);
}

#[test]
fn accumulate_mirrors_off_diagonal_blocks() {
    let mut stiffnesses = TensorRank2SparseVec2D::<2, 1, 1>::zero(3);
    stiffnesses.accumulate(0, 2, block(1.0));
    stiffnesses.accumulate(1, 1, block(5.0));
    assert_eq!(stiffnesses[0][2][0][0], 1.0);
    assert_eq!(stiffnesses[0][2][1][0], 3.0);
    assert_eq!(stiffnesses[2][0][0][0], 1.0);
    assert_eq!(stiffnesses[2][0][0][1], 3.0);
    assert_eq!(stiffnesses[1][1][0][0], 5.0);
}

#[test]
fn accumulate_general_inserts_without_mirroring() {
    let mut stiffnesses = TensorRank2SparseVec2D::<2, 1, 1>::zero(3);
    stiffnesses.accumulate_general(0, 2, block(1.0));
    assert_eq!(stiffnesses[0][2][0][0], 1.0);
    assert_eq!(
        stiffnesses[0].entries().map(|(c, _)| c).collect::<Vec<_>>(),
        [2]
    );
    assert_eq!(stiffnesses[2].entries().count(), 0);
}