nu_plugin_vulkan_matrix 0.1.2

GPU matrix operations for Nushell using Vulkan
plugin use vulkan_matrix
$env.config.plugin_gc = {default: {enabled: true, stop_after: 100ms}, plugins: {}}

def check [condition: bool, message: string] {
    if not $condition { error make {msg: $message} }
}
def export-native [value: any]: nothing -> any { $value | matrix into-nu }
def export-gpu [value: any]: nothing -> any { $value | vulkan matrix into-nu }
def equal [actual: any, expected: any] {
    if ($expected | describe) =~ '^list' {
        if ($actual | length) != ($expected | length) { return false }
        $expected | enumerate | all {|v| equal ($actual | get $v.index) $v.item }
    } else if ($expected | describe) == float and ($expected | into string) in ['NaN' 'inf' '-inf'] {
        ($actual | into string) == ($expected | into string)
    } else { $actual == $expected }
}
def error-labels [run: closure] {
    try { do $run | ignore; [] } catch {|e| $e.details.labels.text }
}
def same-error [native: closure, gpu: closure, cause: string] {
    let expected = error-labels $native
    check (($expected | str join ' ') | str contains $cause) $'native error: ($cause)'
    check ((error-labels $gpu) == $expected) $'GPU error: ($cause)'
}
def close-mean [actual: float, expected: float, dtype: string, data: list] {
    if ($expected | into string) in ['NaN' 'inf' '-inf'] {
        return (equal $actual $expected)
    }
    let values = $data | flatten
    let n = $values | length
    let unit = if $dtype == f32 { 5.960464477539063e-8 } else { 1.1102230246251565e-16 }
    let gamma = $n * $unit / (1.0 - $n * $unit)
    let magnitude = $values | each { math abs } | math sum
    let bound = 2.0 * $gamma * $magnitude / $n + 4.0 * $unit * ($expected | math abs)
    ($actual - $expected | math abs) <= $bound
}

def main [--device: string = 'd0'] {
    do {
    for dtype in [f64 f32] {
        for data in [
            [[1.0 2.0 3.0] [4.0 5.0 6.0]]
            [[NaN NaN NaN] [NaN 2.0 -1.0] [1.0 2.0 3.0] [inf -inf 3.0]]
            [[-inf -inf]]
            []
        ] {
            let native = $data | into matrix
            let gpu = $data | vulkan matrix from-nu --device $device --dtype $dtype --shape $native.shape
            for transpose in [false true] {
                let a = if $transpose { $native | matrix transpose } else { $native }
                let g = if $transpose { $gpu | vulkan matrix transpose } else { $gpu }
                check (equal ($g | vulkan matrix sum) ($a | matrix sum)) 'sum scalar'
                check (equal ($g | vulkan matrix max) ($a | matrix max)) 'max scalar'
                if ($a.shape | any {|n| $n == 0}) {
                    same-error { $a | matrix mean } { $g | vulkan matrix mean } 'cannot compute mean of an empty matrix'
                } else {
                    let actual = $g | vulkan matrix mean
                    let expected = $a | matrix mean
                    check (close-mean $actual $expected $dtype $data) $'mean scalar ($dtype), transpose=($transpose): ($actual) vs ($expected)'
                }
                for axis in [0 1] {
                    let ns = $a | matrix sum --axis $axis
                    let gs = $g | vulkan matrix sum --axis $axis
                    let nm = $a | matrix max --axis $axis
                    let gm = $g | vulkan matrix max --axis $axis
                    check ($gs.shape == $ns.shape and $gm.shape == $nm.shape) 'axis shape'
                    check ($gs.dtype == $dtype and $gs.device == $device) 'axis metadata'
                    check (equal (export-gpu $gs) (export-native $ns | get 0)) 'axis sum'
                    check (equal (export-gpu $gm) (export-native $nm | get 0)) 'axis max'
                }
                for axis in [-1 2] {
                    same-error { $a | matrix sum --axis $axis } { $g | vulkan matrix sum --axis $axis } 'out of bounds for a 2-dimensional array'
                    same-error { $a | matrix max --axis $axis } { $g | vulkan matrix max --axis $axis } 'out of bounds for a 2-dimensional array'
                }
            }
        }
        let vector = [1 2 3] | vulkan matrix from-nu --device $device --dtype $dtype
        let scalar = $vector | vulkan matrix sum --axis 0
        check ($scalar.shape == [] and (export-gpu $scalar) == 6.0) 'axis scalar remains a GPU value'
        check (($scalar | vulkan matrix sum) == 6.0) 'scalar GPU input'
    }
    }
    do {
        let result = do {
            let a = [[1 2] [3 4]] | vulkan matrix from-nu --device $device
            let identity = vulkan matrix identity 2 --device $device
            $a | vulkan matrix multiply $identity | vulkan matrix scale 2 | vulkan matrix sum --axis 1
        }
        let cloned = $result
        sleep 300ms
        check ((plugin list | where name == vulkan_matrix | get 0.pid) != null) 'GC while reduction is held'
        check ((export-gpu $cloned) == [6.0 14.0]) 'chain result survives input drop'
    }
    for attempt in 0..20 {
        if (plugin list | where name == vulkan_matrix | get 0.pid) == null {
            print $'reduction Nu tests passed: ($device)'
            return
        }
        sleep 100ms
    }
    error make {msg: 'GC after reduction final drop'}
}