-module(viva_tensor@backend@dispatch). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/viva_tensor/backend/dispatch.gleam"). -export([capabilities/16, available_backends/1, is_available/2, plan_backend/5]). -export_type([backend_set/1, operation_kind/0, capability/4, rejection/1, plan/2]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC(false). -type backend_set(KBG) :: {backend_set, KBG, KBG, KBG, KBG, KBG, KBG, KBG}. -type operation_kind() :: elementwise | broadcast | reduction | softmax | {matmul, integer(), integer(), integer()}. -type capability(KBH, KBI, KBJ, KBK) :: {capability, KBH, boolean(), KBI, list(KBJ), list(KBK), binary()}. -type rejection(KBL) :: {rejection, KBL, binary()}. -type plan(KBM, KBN) :: {plan, KBM, KBN, list(KBN), list(rejection(KBN)), binary()}. -file("src/viva_tensor/backend/dispatch.gleam", 52). ?DOC(false). -spec capabilities( backend_set(KBO), KBQ, KBQ, KBQ, KBR, KBR, KBR, KBR, KBR, KBS, KBS, KBS, KBS, KBS, boolean(), list(viva_tensor@native@tflops:backend()) ) -> list(capability(KBO, KBQ, KBR, KBS)). capabilities( Backends, Beam_cpu, Native_cpu, Cuda, Float64, Float32, Float16, Int8, Sparse_float16, Elementwise, Broadcast, Reduction, Softmax, Matmul, Zig_loaded, Detected ) -> [{capability, erlang:element(2, Backends), true, Beam_cpu, [Float64], [Elementwise, Broadcast, Reduction, Softmax, Matmul], <<"Always available fallback."/utf8>>}, {capability, erlang:element(3, Backends), Zig_loaded, Native_cpu, [Float64], [Elementwise, Reduction, Matmul], <<"Portable SIMD NIF for CPU hot paths."/utf8>>}, {capability, erlang:element(4, Backends), Zig_loaded, Native_cpu, [Float64, Float32], [Matmul], <<"Native BLAS path exposed through the loaded Zig NIF."/utf8>>}, {capability, erlang:element(5, Backends), gleam@list:contains(Detected, cuda_f_p32), Cuda, [Float32], [Matmul], <<"CUDA FP32/cuBLAS dense matrix multiplication."/utf8>>}, {capability, erlang:element(6, Backends), gleam@list:contains(Detected, cuda_f_p16), Cuda, [Float16], [Matmul], <<"CUDA FP16 Tensor Core dense matrix multiplication."/utf8>>}, {capability, erlang:element(7, Backends), gleam@list:contains(Detected, cuda_i_n_t8), Cuda, [Int8], [Matmul], <<"CUDA INT8 IMMA Tensor Core matrix multiplication."/utf8>>}, {capability, erlang:element(8, Backends), gleam@list:contains(Detected, cuda_sparse), Cuda, [Sparse_float16], [Matmul], <<"CUDA 2:4 sparse Tensor Core matrix multiplication."/utf8>>}]. -file("src/viva_tensor/backend/dispatch.gleam", 130). ?DOC(false). -spec available_backends(list(capability(KBZ, any(), any(), any()))) -> list(KBZ). available_backends(Capabilities) -> _pipe = Capabilities, _pipe@1 = gleam@list:filter( _pipe, fun(Capability) -> erlang:element(3, Capability) end ), gleam@list:map( _pipe@1, fun(Capability@1) -> erlang:element(2, Capability@1) end ). -file("src/viva_tensor/backend/dispatch.gleam", 138). ?DOC(false). -spec is_available(KCJ, list(capability(KCJ, any(), any(), any()))) -> boolean(). is_available(Backend, Capabilities) -> _pipe = Capabilities, gleam@list:any( _pipe, fun(Capability) -> (erlang:element(2, Capability) =:= Backend) andalso erlang:element( 3, Capability ) end ). -file("src/viva_tensor/backend/dispatch.gleam", 327). ?DOC(false). -spec operation_specific_rejection( operation_kind(), KDY, backend_set(KDY), binary() ) -> binary(). operation_specific_rejection(Kind, Backend, Backends, Fallback_reason) -> case Kind of {matmul, _, _, _} when Backend =:= erlang:element(8, Backends) -> <<"Sparse Tensor Core dispatch requires an explicit sparse tensor."/utf8>>; {matmul, _, _, _} when Backend =:= erlang:element(7, Backends) -> <<"INT8 Tensor Core dispatch requires explicit quantized tensors."/utf8>>; {matmul, M, N, K} when Backend =:= erlang:element(6, Backends) -> case (((M rem 16) =:= 0) andalso ((N rem 16) =:= 0)) andalso ((K rem 16) =:= 0) of true -> Fallback_reason; false -> <<"FP16 Tensor Core matmul requires dimensions aligned to 16."/utf8>> end; _ -> Fallback_reason end. -file("src/viva_tensor/backend/dispatch.gleam", 311). ?DOC(false). -spec rejection_reason( operation_kind(), KDU, list(KDU), list(KDU), backend_set(KDU), binary() ) -> binary(). rejection_reason( Kind, Backend, Available, Candidates, Backends, Unsupported_reason ) -> case {gleam@list:contains(Candidates, Backend), gleam@list:contains(Available, Backend)} of {false, _} -> operation_specific_rejection( Kind, Backend, Backends, Unsupported_reason ); {true, false} -> <<"Backend is not available in this VM."/utf8>>; {true, true} -> <<"A higher-priority backend was selected."/utf8>> end. -file("src/viva_tensor/backend/dispatch.gleam", 347). ?DOC(false). -spec all_backends(backend_set(KEA)) -> list(KEA). all_backends(Backends) -> [erlang:element(8, Backends), erlang:element(6, Backends), erlang:element(7, Backends), erlang:element(5, Backends), erlang:element(4, Backends), erlang:element(3, Backends), erlang:element(2, Backends)]. -file("src/viva_tensor/backend/dispatch.gleam", 286). ?DOC(false). -spec backend_rejections( operation_kind(), KDO, list(KDO), list(KDO), backend_set(KDO), binary() ) -> list(rejection(KDO)). backend_rejections( Kind, Selected, Available, Candidates, Backends, Unsupported_reason ) -> _pipe = all_backends(Backends), _pipe@1 = gleam@list:filter(_pipe, fun(Backend) -> Backend /= Selected end), gleam@list:map( _pipe@1, fun(Backend@1) -> {rejection, Backend@1, rejection_reason( Kind, Backend@1, Available, Candidates, Backends, Unsupported_reason )} end ). -file("src/viva_tensor/backend/dispatch.gleam", 276). ?DOC(false). -spec select_backend(list(KDL), list(KDL), KDL) -> KDL. select_backend(Available, Candidates, Fallback) -> _pipe = Candidates, _pipe@1 = gleam@list:find( _pipe, fun(Candidate) -> gleam@list:contains(Available, Candidate) end ), gleam@result:unwrap(_pipe@1, Fallback). -file("src/viva_tensor/backend/dispatch.gleam", 249). ?DOC(false). -spec plan_first_available( KDE, operation_kind(), list(KDF), backend_set(KDF), list(KDF), binary(), binary() ) -> plan(KDE, KDF). plan_first_available( Operation, Kind, Available, Backends, Candidates, Reason, Unsupported_reason ) -> Selected = select_backend( Available, Candidates, erlang:element(2, Backends) ), {plan, Operation, Selected, Candidates, backend_rejections( Kind, Selected, Available, Candidates, Backends, Unsupported_reason ), Reason}. -file("src/viva_tensor/backend/dispatch.gleam", 201). ?DOC(false). -spec plan_matmul( KCY, integer(), integer(), integer(), list(KCZ), backend_set(KCZ), boolean() ) -> plan(KCY, KCZ). plan_matmul(Operation, M, N, K, Available, Backends, Nif_loaded) -> Tensor_core_aligned = (((M rem 16) =:= 0) andalso ((N rem 16) =:= 0)) andalso ((K rem 16) =:= 0), Candidates = case Tensor_core_aligned of true -> [erlang:element(8, Backends), erlang:element(6, Backends), erlang:element(7, Backends), erlang:element(5, Backends), erlang:element(4, Backends), erlang:element(3, Backends), erlang:element(2, Backends)]; false -> [erlang:element(5, Backends), erlang:element(4, Backends), erlang:element(3, Backends), erlang:element(2, Backends)] end, Reason = case Nif_loaded of true -> case Tensor_core_aligned of true -> <<"Matmul dimensions are Tensor Core aligned; CUDA is preferred."/utf8>>; false -> <<"Matmul dimensions are not Tensor Core aligned; dense CUDA/CPU fallback is preferred."/utf8>> end; false -> <<"Native NIF is not loaded; pure Gleam fallback is selected."/utf8>> end, plan_first_available( Operation, {matmul, M, N, K}, Available, Backends, Candidates, Reason, <<"Backend is not part of the stable matmul dispatch path for this shape."/utf8>> ). -file("src/viva_tensor/backend/dispatch.gleam", 148). ?DOC(false). -spec plan_backend( KCS, operation_kind(), list(KCT), backend_set(KCT), boolean() ) -> plan(KCS, KCT). plan_backend(Operation, Kind, Available, Backends, Nif_loaded) -> case Kind of {matmul, M, N, K} -> plan_matmul(Operation, M, N, K, Available, Backends, Nif_loaded); elementwise -> plan_first_available( Operation, Kind, Available, Backends, [erlang:element(3, Backends), erlang:element(4, Backends), erlang:element(2, Backends)], <<"Element-wise ops prefer SIMD, then native CPU, then pure Gleam."/utf8>>, <<"Backend does not support stable element-wise dispatch."/utf8>> ); broadcast -> plan_first_available( Operation, Kind, Available, Backends, [erlang:element(3, Backends), erlang:element(2, Backends)], <<"Broadcasting preserves views and only needs native compute when materialized."/utf8>>, <<"Backend does not support stable broadcast dispatch."/utf8>> ); reduction -> plan_first_available( Operation, Kind, Available, Backends, [erlang:element(3, Backends), erlang:element(4, Backends), erlang:element(2, Backends)], <<"Reductions prefer SIMD/native CPU and fall back to pure Gleam."/utf8>>, <<"Backend does not support stable reduction dispatch."/utf8>> ); softmax -> plan_first_available( Operation, Kind, Available, Backends, [erlang:element(2, Backends)], <<"Softmax currently uses the stable Gleam implementation."/utf8>>, <<"Softmax currently only has stable pure Gleam dispatch."/utf8>> ) end.