sciforge 0.0.3

A comprehensive scientific computing library in pure Rust with zero dependencies
Documentation
use sciforge::hub::prelude::*;

fn run_maths(name: &str, params: Vec<(&str, ParameterValue)>) -> RunOutput {
    let mut exp = Experiment::new(DomainType::Maths, name);
    for (k, v) in params {
        exp = exp.param(k, v);
    }
    ExperimentRunner::new()
        .run(&exp)
        .unwrap_or_else(|_| panic!("dispatch '{name}' failed"))
}

fn scalar(o: RunOutput) -> f64 {
    match o {
        RunOutput::Scalar(v) => v,
        _ => panic!("expected Scalar, got {o:?}"),
    }
}

#[test]
fn tensor_reshape() {
    let out = run_maths(
        "tensor_reshape",
        vec![
            (
                "t",
                ParameterValue::Tensor {
                    data: vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0],
                    shape: vec![6],
                },
            ),
            ("new_shape", ParameterValue::IntVector(vec![2, 3])),
        ],
    );
    match out {
        RunOutput::TensorOut { data, shape } => {
            assert_eq!(data.len(), 6, "reshaped tensor same element count");
            assert_eq!(shape, vec![2, 3]);
        }
        other => panic!("expected TensorOut, got {other:?}"),
    }
}

#[test]
fn tensor_contract() {
    let out = run_maths(
        "tensor_contract",
        vec![
            (
                "a",
                ParameterValue::Tensor {
                    data: vec![1.0, 2.0, 3.0],
                    shape: vec![3],
                },
            ),
            (
                "b",
                ParameterValue::Tensor {
                    data: vec![4.0, 5.0, 6.0],
                    shape: vec![3],
                },
            ),
            ("axis_a", ParameterValue::Integer(0)),
            ("axis_b", ParameterValue::Integer(0)),
        ],
    );
    match out {
        RunOutput::TensorOut { data, .. } => {
            assert!(!data.is_empty(), "contraction should produce data");
        }
        RunOutput::Scalar(v) => assert!((v - 32.0).abs() < 1e-8, "dot=32, got {v}"),
        other => panic!("expected TensorOut or Scalar, got {other:?}"),
    }
}

#[test]
fn tensor_determinant() {
    let v = scalar(run_maths(
        "tensor_determinant",
        vec![(
            "t",
            ParameterValue::Tensor {
                data: vec![1.0, 0.0, 0.0, 1.0],
                shape: vec![2, 2],
            },
        )],
    ));
    assert!((v - 1.0).abs() < 1e-8, "det(I)=1, got {v}");
}