runmat-runtime 0.6.0

Core runtime for RunMat with builtins, BLAS/LAPACK integration, and execution APIs
Documentation
use runmat_builtins::{CellArray, IntValue, IntegerStorage, StructValue, Tensor, Value};
use runmat_runtime::data::{DataArrayPayload, DataArrayValues};

fn empty_cell() -> Value {
    Value::Cell(CellArray::new(Vec::new(), 1, 0).expect("empty cell"))
}

fn create_array(path: String, dtype: &str, shape: Vec<usize>, chunk: Vec<usize>) -> Value {
    let mut array = StructValue::new();
    array
        .fields
        .insert("dtype".to_string(), Value::String(dtype.to_string()));
    array.fields.insert(
        "shape".to_string(),
        Value::Tensor(
            Tensor::new(
                shape.iter().map(|&value| value as f64).collect(),
                vec![1, shape.len()],
            )
            .expect("shape tensor"),
        ),
    );
    array.fields.insert(
        "chunk".to_string(),
        Value::Tensor(
            Tensor::new(
                chunk.iter().map(|&value| value as f64).collect(),
                vec![1, chunk.len()],
            )
            .expect("chunk tensor"),
        ),
    );
    let mut arrays = StructValue::new();
    arrays.insert("samples", Value::Struct(array));
    let mut schema = StructValue::new();
    schema.insert("arrays", Value::Struct(arrays));
    let dataset = runmat_runtime::call_builtin(
        "data.create",
        &[Value::String(path), Value::Struct(schema), empty_cell()],
    )
    .expect("create dataset");
    runmat_runtime::call_builtin(
        "Dataset.array",
        &[dataset, Value::String("samples".to_string())],
    )
    .expect("open data array")
}

#[test]
fn data_payloads_roundtrip_all_integer_classes_and_legacy_json() {
    let cases = vec![
        ("int8", DataArrayValues::I8(vec![i8::MIN, i8::MAX])),
        ("int16", DataArrayValues::I16(vec![i16::MIN, i16::MAX])),
        ("int32", DataArrayValues::I32(vec![i32::MIN, i32::MAX])),
        ("int64", DataArrayValues::I64(vec![i64::MIN, i64::MAX])),
        ("uint8", DataArrayValues::U8(vec![0, u8::MAX])),
        ("uint16", DataArrayValues::U16(vec![0, u16::MAX])),
        ("uint32", DataArrayValues::U32(vec![0, u32::MAX])),
        ("uint64", DataArrayValues::U64(vec![0, u64::MAX])),
    ];
    for (dtype, values) in cases {
        let payload = DataArrayPayload {
            dtype: dtype.to_string(),
            shape: vec![1, 2],
            values: values.clone(),
        };
        let bytes = serde_json::to_vec(&payload).expect("encode payload");
        let decoded: DataArrayPayload = serde_json::from_slice(&bytes).expect("decode payload");
        assert_eq!(decoded.values, values, "{dtype}");
    }

    let legacy = br#"{"dtype":"f64","shape":[1,2],"values":[1,2]}"#;
    let decoded: DataArrayPayload = serde_json::from_slice(legacy).expect("decode legacy payload");
    assert_eq!(decoded.values, DataArrayValues::F64(vec![1.0, 2.0]));
}

#[test]
fn data_arrays_keep_exact_integer_storage_through_chunked_api_paths() {
    let cases = vec![
        ("int8", IntegerStorage::I8(vec![i8::MIN, i8::MAX])),
        ("int16", IntegerStorage::I16(vec![i16::MIN, i16::MAX])),
        ("int32", IntegerStorage::I32(vec![i32::MIN, i32::MAX])),
        ("int64", IntegerStorage::I64(vec![i64::MIN, i64::MAX])),
        ("uint8", IntegerStorage::U8(vec![0, u8::MAX])),
        ("uint16", IntegerStorage::U16(vec![0, u16::MAX])),
        ("uint32", IntegerStorage::U32(vec![0, u32::MAX])),
        ("uint64", IntegerStorage::U64(vec![0, u64::MAX])),
    ];
    for (dtype, storage) in cases {
        let dir = tempfile::tempdir().expect("tempdir");
        let array = create_array(
            dir.path()
                .join(format!("{dtype}.data"))
                .display()
                .to_string(),
            dtype,
            vec![2, 1],
            vec![1, 1],
        );
        let input = Tensor::new_integer(storage.clone(), vec![2, 1]).expect("integer tensor");
        runmat_runtime::call_builtin("DataArray.write", &[array.clone(), Value::Tensor(input)])
            .expect("write array");
        let Value::Tensor(read_back) =
            runmat_runtime::call_builtin("DataArray.read", &[array]).expect("read array")
        else {
            panic!("expected tensor");
        };
        assert_eq!(read_back.integer_storage(), Some(&storage), "{dtype}");
    }
}

#[test]
fn uint64_data_array_slice_fill_and_transaction_paths_remain_exact() {
    let dir = tempfile::tempdir().expect("tempdir");
    let dataset_path = dir.path().join("uint64.data").display().to_string();
    let array = create_array(dataset_path.clone(), "uint64", vec![2, 2], vec![1, 1]);
    runmat_runtime::call_builtin(
        "DataArray.fill",
        &[array.clone(), Value::Int(IntValue::U64(u64::MAX))],
    )
    .expect("fill array");

    let slice = Value::Cell(
        CellArray::new(
            vec![Value::Int(IntValue::I32(1)), Value::String(":".to_string())],
            1,
            2,
        )
        .expect("slice"),
    );
    let replacement = Tensor::new_integer(
        IntegerStorage::U64(vec![1_u64 << 63, u64::MAX - 1]),
        vec![1, 2],
    )
    .expect("replacement");
    runmat_runtime::call_builtin(
        "DataArray.write",
        &[array.clone(), slice, Value::Tensor(replacement)],
    )
    .expect("slice write");

    let dataset =
        runmat_runtime::call_builtin("data.open", &[Value::String(dataset_path), empty_cell()])
            .expect("open dataset");
    let tx = runmat_runtime::call_builtin("Dataset.begin", &[dataset]).expect("begin transaction");
    runmat_runtime::call_builtin(
        "DataTransaction.fill",
        &[
            tx.clone(),
            Value::String("samples".to_string()),
            Value::Int(IntValue::U64(1_u64 << 63)),
        ],
    )
    .expect("queue transaction fill");
    runmat_runtime::call_builtin("DataTransaction.commit", &[tx]).expect("commit transaction");

    let Value::Tensor(read_back) =
        runmat_runtime::call_builtin("DataArray.read", &[array]).expect("read array")
    else {
        panic!("expected tensor");
    };
    assert_eq!(
        read_back.integer_storage(),
        Some(&IntegerStorage::U64(vec![1_u64 << 63; 4]))
    );
}