use super::abi::{DLDataType, dtype_code};
use super::codes::*;
use super::sidecar::{FDXAffine, FDXAffineTerm, FDXExtent};
use crate::backend::SubstrateClass;
use crate::dtype::DType;
use crate::probe::BackendId;
use crate::quant_scale::ScaleGranularity;
use crate::quantized::GgmlDType;
use crate::shape::Extent;
use crate::symbol::SymId;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FdxDtype {
Coded(u16),
RidesBase(u16),
}
impl FdxDtype {
pub fn namespace_code(self) -> u16 {
match self {
FdxDtype::Coded(c) | FdxDtype::RidesBase(c) => c,
}
}
}
pub fn dtype_to_fdx(d: DType) -> crate::Result<FdxDtype> {
Ok(match d {
DType::F8E5M2 => {
return Err(crate::Error::UnsupportedDTypeForOp(
DType::F8E5M2,
"no FDX logical-dtype code has been assigned to F8E5M2 yet (GAP-097); declining rather than inferring one from adjacency",
));
}
DType::F8E6M2 => {
return Err(crate::Error::UnsupportedDTypeForOp(
DType::F8E6M2,
"FDX logical-dtype encoding is not authored yet (GAP-045); \
declining rather than emitting an invented wire code",
));
}
DType::Bool => FdxDtype::RidesBase(FDX_DTYPE_BOOL),
DType::U8 => FdxDtype::Coded(FDX_DTYPE_U8),
DType::I8 => FdxDtype::Coded(FDX_DTYPE_I8),
DType::U32 => FdxDtype::Coded(FDX_DTYPE_U32),
DType::I16 => FdxDtype::Coded(FDX_DTYPE_I16),
DType::I32 => FdxDtype::Coded(FDX_DTYPE_I32),
DType::I64 => FdxDtype::Coded(FDX_DTYPE_I64),
DType::BF16 => FdxDtype::Coded(FDX_DTYPE_BF16),
DType::F16 => FdxDtype::Coded(FDX_DTYPE_F16),
DType::F32 => FdxDtype::Coded(FDX_DTYPE_F32),
DType::F64 => FdxDtype::Coded(FDX_DTYPE_F64),
DType::F8E4M3 => FdxDtype::Coded(FDX_DTYPE_F8E4M3),
DType::F6E2M3 => FdxDtype::Coded(FDX_DTYPE_F6E2M3),
DType::F6E3M2 => FdxDtype::Coded(FDX_DTYPE_F6E3M2),
DType::F4 => FdxDtype::Coded(FDX_DTYPE_F4),
DType::F8E8M0 => FdxDtype::Coded(FDX_DTYPE_F8E8M0),
})
}
pub fn fdx_to_dtype(code: u16) -> Option<DType> {
let d = match code {
FDX_DTYPE_U8 => DType::U8,
FDX_DTYPE_I8 => DType::I8,
FDX_DTYPE_U32 => DType::U32,
FDX_DTYPE_I16 => DType::I16,
FDX_DTYPE_I32 => DType::I32,
FDX_DTYPE_I64 => DType::I64,
FDX_DTYPE_BF16 => DType::BF16,
FDX_DTYPE_F16 => DType::F16,
FDX_DTYPE_F32 => DType::F32,
FDX_DTYPE_F64 => DType::F64,
FDX_DTYPE_F8E4M3 => DType::F8E4M3,
FDX_DTYPE_F6E2M3 => DType::F6E2M3,
FDX_DTYPE_F6E3M2 => DType::F6E3M2,
FDX_DTYPE_F4 => DType::F4,
FDX_DTYPE_F8E8M0 => DType::F8E8M0,
_ => return None,
};
Some(d)
}
pub fn dl_dtype(d: DType) -> crate::Result<DLDataType> {
use dtype_code::*;
let (code, bits): (u8, u8) = match d {
DType::F8E5M2 => {
return Err(crate::Error::UnsupportedDTypeForOp(
DType::F8E5M2,
"F8E5M2 has no assigned FDX code, and DLPack's `(float, 8)` cannot distinguish it from F8E4M3; declining rather than exporting an fp8 a consumer would mis-read (GAP-097)",
));
}
DType::F8E6M2 => {
return Err(crate::Error::UnsupportedDTypeForOp(
DType::F8E6M2,
"DLPack element width is not authored yet (GAP-045); declining \
rather than asserting a bit-width a consumer would stride on",
));
}
DType::Bool => (K_DL_BOOL, 8),
DType::U8 => (K_DL_UINT, 8),
DType::I8 => (K_DL_INT, 8),
DType::U32 => (K_DL_UINT, 32),
DType::I16 => (K_DL_INT, 16),
DType::I32 => (K_DL_INT, 32),
DType::I64 => (K_DL_INT, 64),
DType::BF16 => (K_DL_BFLOAT, 16),
DType::F16 => (K_DL_FLOAT, 16),
DType::F32 => (K_DL_FLOAT, 32),
DType::F64 => (K_DL_FLOAT, 64),
DType::F8E4M3 => (K_DL_FLOAT, 8),
DType::F8E8M0 => (K_DL_FLOAT, 8),
DType::F6E2M3 => (K_DL_FLOAT, 6),
DType::F6E3M2 => (K_DL_FLOAT, 6),
DType::F4 => (K_DL_FLOAT, 4),
};
Ok(DLDataType {
code,
bits,
lanes: 1,
})
}
pub fn ggml_to_fdx(g: GgmlDType) -> u16 {
match g {
GgmlDType::F32 => FDX_GGML_F32,
GgmlDType::F16 => FDX_GGML_F16,
GgmlDType::BF16 => FDX_GGML_BF16,
GgmlDType::Q4_0 => FDX_GGML_Q4_0,
GgmlDType::Q4_1 => FDX_GGML_Q4_1,
GgmlDType::Q5_0 => FDX_GGML_Q5_0,
GgmlDType::Q5_1 => FDX_GGML_Q5_1,
GgmlDType::Q8_0 => FDX_GGML_Q8_0,
GgmlDType::Q8_1 => FDX_GGML_Q8_1,
GgmlDType::Q2K => FDX_GGML_Q2K,
GgmlDType::Q3K => FDX_GGML_Q3K,
GgmlDType::Q4K => FDX_GGML_Q4K,
GgmlDType::Q5K => FDX_GGML_Q5K,
GgmlDType::Q6K => FDX_GGML_Q6K,
GgmlDType::Q8K => FDX_GGML_Q8K,
GgmlDType::Iq4Nl => FDX_GGML_IQ4_NL,
GgmlDType::Iq4Xs => FDX_GGML_IQ4_XS,
GgmlDType::Iq2Xxs => FDX_GGML_IQ2_XXS,
GgmlDType::Iq2Xs => FDX_GGML_IQ2_XS,
GgmlDType::Iq3Xxs => FDX_GGML_IQ3_XXS,
GgmlDType::Iq1S => FDX_GGML_IQ1_S,
GgmlDType::Iq3S => FDX_GGML_IQ3_S,
GgmlDType::Iq2S => FDX_GGML_IQ2_S,
GgmlDType::Iq1M => FDX_GGML_IQ1_M,
GgmlDType::Tq1_0 => FDX_GGML_TQ1_0,
GgmlDType::Tq2_0 => FDX_GGML_TQ2_0,
GgmlDType::Mxfp4 => FDX_GGML_MXFP4,
GgmlDType::Nvfp4 => FDX_GGML_NVFP4,
}
}
pub fn fdx_to_ggml(code: u16) -> Option<GgmlDType> {
let g = match code {
FDX_GGML_F32 => GgmlDType::F32,
FDX_GGML_F16 => GgmlDType::F16,
FDX_GGML_BF16 => GgmlDType::BF16,
FDX_GGML_Q4_0 => GgmlDType::Q4_0,
FDX_GGML_Q4_1 => GgmlDType::Q4_1,
FDX_GGML_Q5_0 => GgmlDType::Q5_0,
FDX_GGML_Q5_1 => GgmlDType::Q5_1,
FDX_GGML_Q8_0 => GgmlDType::Q8_0,
FDX_GGML_Q8_1 => GgmlDType::Q8_1,
FDX_GGML_Q2K => GgmlDType::Q2K,
FDX_GGML_Q3K => GgmlDType::Q3K,
FDX_GGML_Q4K => GgmlDType::Q4K,
FDX_GGML_Q5K => GgmlDType::Q5K,
FDX_GGML_Q6K => GgmlDType::Q6K,
FDX_GGML_Q8K => GgmlDType::Q8K,
FDX_GGML_IQ4_NL => GgmlDType::Iq4Nl,
FDX_GGML_IQ4_XS => GgmlDType::Iq4Xs,
FDX_GGML_IQ2_XXS => GgmlDType::Iq2Xxs,
FDX_GGML_IQ2_XS => GgmlDType::Iq2Xs,
FDX_GGML_IQ3_XXS => GgmlDType::Iq3Xxs,
FDX_GGML_IQ1_S => GgmlDType::Iq1S,
FDX_GGML_IQ3_S => GgmlDType::Iq3S,
FDX_GGML_IQ2_S => GgmlDType::Iq2S,
FDX_GGML_IQ1_M => GgmlDType::Iq1M,
FDX_GGML_TQ1_0 => GgmlDType::Tq1_0,
FDX_GGML_TQ2_0 => GgmlDType::Tq2_0,
FDX_GGML_MXFP4 => GgmlDType::Mxfp4,
FDX_GGML_NVFP4 => GgmlDType::Nvfp4,
_ => return None,
};
Some(g)
}
pub fn scale_granularity_to_fdx(g: ScaleGranularity) -> u8 {
match g {
ScaleGranularity::PerTensor => FDX_SCALE_GRAN_PER_TENSOR,
ScaleGranularity::PerToken => FDX_SCALE_GRAN_PER_TOKEN,
ScaleGranularity::PerChannel => FDX_SCALE_GRAN_PER_CHANNEL,
}
}
pub fn fdx_to_scale_granularity(code: u8) -> Option<ScaleGranularity> {
let g = match code {
FDX_SCALE_GRAN_PER_TENSOR => ScaleGranularity::PerTensor,
FDX_SCALE_GRAN_PER_TOKEN => ScaleGranularity::PerToken,
FDX_SCALE_GRAN_PER_CHANNEL => ScaleGranularity::PerChannel,
_ => return None,
};
Some(g)
}
pub fn backend_to_fdx(b: BackendId) -> u8 {
match b {
BackendId::Cpu => FDX_BACKEND_CPU,
BackendId::Cuda => FDX_BACKEND_CUDA,
BackendId::Vulkan => FDX_BACKEND_VULKAN,
BackendId::Metal => FDX_BACKEND_METAL,
}
}
pub fn fdx_to_backend(code: u8) -> Option<BackendId> {
let b = match code {
FDX_BACKEND_CPU => BackendId::Cpu,
FDX_BACKEND_CUDA => BackendId::Cuda,
FDX_BACKEND_VULKAN => BackendId::Vulkan,
FDX_BACKEND_METAL => BackendId::Metal,
_ => return None,
};
Some(b)
}
pub fn substrate_to_fdx(s: SubstrateClass) -> u8 {
match s {
SubstrateClass::HostBytes => FDX_SUBSTRATE_HOST_BYTES,
SubstrateClass::CudaUntyped => FDX_SUBSTRATE_CUDA_UNTYPED,
SubstrateClass::VulkanBuffer => FDX_SUBSTRATE_VULKAN_BUFFER,
SubstrateClass::MetalBuffer => FDX_SUBSTRATE_METAL_BUFFER,
}
}
pub fn fdx_to_substrate(code: u8) -> Option<SubstrateClass> {
let s = match code {
FDX_SUBSTRATE_HOST_BYTES => SubstrateClass::HostBytes,
FDX_SUBSTRATE_CUDA_UNTYPED => SubstrateClass::CudaUntyped,
FDX_SUBSTRATE_VULKAN_BUFFER => SubstrateClass::VulkanBuffer,
FDX_SUBSTRATE_METAL_BUFFER => SubstrateClass::MetalBuffer,
_ => return None,
};
Some(s)
}
pub fn extent_to_fdx(e: Extent) -> FDXExtent {
let zero_affine = FDXAffine {
c0: 0,
term_count: 0,
_pad: [0; 7],
terms: [FDXAffineTerm {
coeff: 0,
sym_id: FDX_SYM_NONE,
_pad: 0,
}; FDX_AFFINE_MAX_TERMS],
};
let (kind, min, capacity, sym_id) = match e {
Extent::Scalar(v) => (FDX_EXTENT_SCALAR as u8, v as u64, v as u64, FDX_SYM_NONE),
Extent::Range { min, max, sym } => (FDX_EXTENT_RANGE as u8, min as u64, max as u64, sym.0),
};
FDXExtent {
kind,
_pad: [0; 3],
min,
capacity,
sym_id,
sym_scope: 0,
_pad2: [0; 3],
cap_kind: FDX_CAP_KIND_EXPLICIT as u8,
_pad3: [0; 3],
_pad4: 0,
affine: zero_affine,
reserved: [0; 2],
}
}
pub fn fdx_to_extent(x: &FDXExtent) -> Option<Extent> {
match x.kind as u16 {
FDX_EXTENT_SCALAR => Some(Extent::Scalar(x.capacity as usize)),
FDX_EXTENT_RANGE => Some(Extent::Range {
min: x.min as usize,
max: x.capacity as usize,
sym: SymId(x.sym_id),
}),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dtype_roundtrips_for_every_variant() {
for &d in DType::ALL {
match d {
DType::F8E5M2 | DType::F8E6M2 => assert!(
dtype_to_fdx(d).is_err(),
"{d:?} must decline until its FDX code is authored, never round-trip through an invented code"
),
DType::Bool => assert!(
matches!(dtype_to_fdx(d), Ok(FdxDtype::RidesBase(_))),
"Bool must ride the base as RidesBase, not a coded round-trip"
),
_ => {
let code = dtype_to_fdx(d)
.expect("every coded dtype encodes")
.namespace_code();
assert_eq!(
fdx_to_dtype(code),
Some(d),
"DType {d:?} (code {code}) must round-trip"
);
}
}
}
}
#[test]
fn dtype_anchor_codes_match_spec_6_1() {
assert_eq!(
dtype_to_fdx(DType::U8).unwrap().namespace_code(),
FDX_DTYPE_U8
);
assert_eq!(dtype_to_fdx(DType::U8).unwrap().namespace_code(), 0);
assert_eq!(dtype_to_fdx(DType::I8).unwrap().namespace_code(), 1);
assert_eq!(
dtype_to_fdx(DType::F32).unwrap().namespace_code(),
FDX_DTYPE_F32
);
assert_eq!(dtype_to_fdx(DType::F32).unwrap().namespace_code(), 8);
assert_eq!(dtype_to_fdx(DType::F64).unwrap().namespace_code(), 9);
assert_eq!(dtype_to_fdx(DType::F8E4M3).unwrap().namespace_code(), 10);
assert_eq!(dtype_to_fdx(DType::F6E2M3).unwrap().namespace_code(), 11);
assert_eq!(dtype_to_fdx(DType::F6E3M2).unwrap().namespace_code(), 12);
assert_eq!(dtype_to_fdx(DType::F4).unwrap().namespace_code(), 13);
assert_eq!(dtype_to_fdx(DType::F8E8M0).unwrap().namespace_code(), 14);
}
#[test]
fn bool_rides_the_base_as_a_third_disposition_distinct_from_a_decline() {
let bool_result = dtype_to_fdx(DType::Bool);
assert!(
bool_result.is_ok(),
"Bool rides the base and must not decline; got {bool_result:?}"
);
let bool_disp = bool_result.expect("asserted Ok directly above");
assert_eq!(
bool_disp,
FdxDtype::RidesBase(FDX_DTYPE_BOOL),
"Bool must be RidesBase(0x0201) — a Coded value is a wire claim the base already makes"
);
assert_eq!(
FDX_DTYPE_BOOL, 0x0201,
"the reserved BOOL namespace code is 0x0201 (§6.1)"
);
assert!(
!matches!(bool_disp, FdxDtype::Coded(_)),
"Bool must never be Coded — a plain Bool tensor emits no standalone FDXDTypeExt"
);
assert!(
matches!(dtype_to_fdx(DType::F32), Ok(FdxDtype::Coded(_))),
"a normal dtype is Coded — distinct from RidesBase and from a decline"
);
assert!(
dtype_to_fdx(DType::F8E5M2).is_err(),
"F8E5M2's code is unassigned (GAP-097) — a decline, distinct from Bool's RidesBase"
);
assert_eq!(dtype_to_fdx(DType::Bool).unwrap().namespace_code(), 0x0201);
}
#[test]
fn dl_dtype_faithful_descriptors() {
use dtype_code::*;
assert_eq!(
dl_dtype(DType::F32).unwrap(),
DLDataType {
code: K_DL_FLOAT,
bits: 32,
lanes: 1
}
);
assert_eq!(
dl_dtype(DType::F16).unwrap(),
DLDataType {
code: K_DL_FLOAT,
bits: 16,
lanes: 1
}
);
assert_eq!(
dl_dtype(DType::BF16).unwrap(),
DLDataType {
code: K_DL_BFLOAT,
bits: 16,
lanes: 1
}
);
assert_eq!(
dl_dtype(DType::F64).unwrap(),
DLDataType {
code: K_DL_FLOAT,
bits: 64,
lanes: 1
}
);
assert_eq!(
dl_dtype(DType::I32).unwrap(),
DLDataType {
code: K_DL_INT,
bits: 32,
lanes: 1
}
);
assert_eq!(
dl_dtype(DType::I64).unwrap(),
DLDataType {
code: K_DL_INT,
bits: 64,
lanes: 1
}
);
assert_eq!(
dl_dtype(DType::U8).unwrap(),
DLDataType {
code: K_DL_UINT,
bits: 8,
lanes: 1
}
);
assert_eq!(
dl_dtype(DType::U32).unwrap(),
DLDataType {
code: K_DL_UINT,
bits: 32,
lanes: 1
}
);
assert_eq!(
dl_dtype(DType::I8).unwrap(),
DLDataType {
code: K_DL_INT,
bits: 8,
lanes: 1
}
);
assert_eq!(
dl_dtype(DType::I16).unwrap(),
DLDataType {
code: K_DL_INT,
bits: 16,
lanes: 1
}
);
assert_eq!(dl_dtype(DType::F4).unwrap().bits, 4);
assert_eq!(dl_dtype(DType::F6E2M3).unwrap().bits, 6);
assert_eq!(dl_dtype(DType::F6E3M2).unwrap().bits, 6);
assert_eq!(dl_dtype(DType::F8E4M3).unwrap().bits, 8);
assert_eq!(dl_dtype(DType::F8E8M0).unwrap().bits, 8);
for &d in DType::ALL {
match d {
DType::F8E5M2 | DType::F8E6M2 => assert!(
dl_dtype(d).is_err(),
"{d:?} must decline a base width, not assert one"
),
_ => assert_eq!(
dl_dtype(d).unwrap().lanes,
1,
"DType {d:?} must be scalar (lanes=1)"
),
}
}
}
#[test]
fn dtype_codes_have_no_duplicates() {
let mut codes: Vec<u16> = DType::ALL
.iter()
.copied()
.flat_map(dtype_to_fdx)
.map(FdxDtype::namespace_code)
.collect();
let n = codes.len();
codes.sort_unstable();
codes.dedup();
assert_eq!(
codes.len(),
n,
"FDX_DTYPE_* set must have no duplicate codes"
);
}
#[test]
fn fdx_to_dtype_rejects_escape_and_reserved_codes() {
assert_eq!(fdx_to_dtype(0x0100), None); assert_eq!(fdx_to_dtype(0x0101), None); assert_eq!(fdx_to_dtype(FDX_DTYPE_I4), None); assert_eq!(fdx_to_dtype(FDX_DTYPE_U4), None); assert_eq!(fdx_to_dtype(FDX_DTYPE_B1), None); assert_eq!(fdx_to_dtype(0x0200), None); assert_eq!(fdx_to_dtype(0x0201), None); assert_eq!(fdx_to_dtype(FDX_DTYPE_NONE), None); assert_eq!(fdx_to_dtype(15), None); }
const ALL_GGML: [GgmlDType; 28] = [
GgmlDType::F32,
GgmlDType::F16,
GgmlDType::BF16,
GgmlDType::Q4_0,
GgmlDType::Q4_1,
GgmlDType::Q5_0,
GgmlDType::Q5_1,
GgmlDType::Q8_0,
GgmlDType::Q8_1,
GgmlDType::Q2K,
GgmlDType::Q3K,
GgmlDType::Q4K,
GgmlDType::Q5K,
GgmlDType::Q6K,
GgmlDType::Q8K,
GgmlDType::Iq4Nl,
GgmlDType::Iq4Xs,
GgmlDType::Iq2Xxs,
GgmlDType::Iq2Xs,
GgmlDType::Iq3Xxs,
GgmlDType::Iq1S,
GgmlDType::Iq3S,
GgmlDType::Iq2S,
GgmlDType::Iq1M,
GgmlDType::Tq1_0,
GgmlDType::Tq2_0,
GgmlDType::Mxfp4,
GgmlDType::Nvfp4,
];
#[test]
fn ggml_roundtrips_for_every_variant() {
for g in ALL_GGML {
let code = ggml_to_fdx(g);
assert_eq!(
fdx_to_ggml(code),
Some(g),
"GgmlDType {g:?} (code {code}) must round-trip"
);
}
}
#[test]
fn ggml_codes_match_source_numbering_and_anchors() {
for g in ALL_GGML {
assert_eq!(
ggml_to_fdx(g) as u32,
g.to_u32(),
"FDX ggml code must equal GgmlDType::to_u32 for {g:?}"
);
}
assert_eq!(ggml_to_fdx(GgmlDType::Q4K), FDX_GGML_Q4K);
assert_eq!(ggml_to_fdx(GgmlDType::Q4K), 12);
assert_eq!(ggml_to_fdx(GgmlDType::Q4_0), 2);
assert_eq!(ggml_to_fdx(GgmlDType::Q5_0), 6);
assert_eq!(ggml_to_fdx(GgmlDType::Q8K), 15);
assert_eq!(ggml_to_fdx(GgmlDType::Iq4Nl), 20);
assert_eq!(ggml_to_fdx(GgmlDType::Iq4Xs), 23);
assert_eq!(ggml_to_fdx(GgmlDType::Iq2Xxs), 16);
assert_eq!(ggml_to_fdx(GgmlDType::Iq2Xs), 17);
assert_eq!(ggml_to_fdx(GgmlDType::Iq3Xxs), 18);
assert_eq!(ggml_to_fdx(GgmlDType::Iq1S), 19);
assert_eq!(ggml_to_fdx(GgmlDType::Iq3S), 21);
assert_eq!(ggml_to_fdx(GgmlDType::Iq2S), 22);
assert_eq!(ggml_to_fdx(GgmlDType::Iq1M), 29);
assert_eq!(ggml_to_fdx(GgmlDType::Tq1_0), 34);
assert_eq!(ggml_to_fdx(GgmlDType::Tq2_0), 35);
assert_eq!(ggml_to_fdx(GgmlDType::Mxfp4), 39);
assert_eq!(ggml_to_fdx(GgmlDType::Nvfp4), 40);
assert_eq!(ggml_to_fdx(GgmlDType::F16), 1);
assert_eq!(ggml_to_fdx(GgmlDType::BF16), 30);
}
#[test]
fn fdx_to_ggml_rejects_gaps_and_none() {
assert_eq!(fdx_to_ggml(FDX_QUANT_NONE), None); assert_eq!(fdx_to_ggml(4), None); assert_eq!(fdx_to_ggml(5), None); assert_eq!(fdx_to_ggml(24), None); assert_eq!(fdx_to_ggml(41), None); }
const ALL_GRAN: [ScaleGranularity; 3] = [
ScaleGranularity::PerTensor,
ScaleGranularity::PerToken,
ScaleGranularity::PerChannel,
];
#[test]
fn scale_granularity_roundtrips_for_every_variant() {
for g in ALL_GRAN {
let code = scale_granularity_to_fdx(g);
assert_eq!(
fdx_to_scale_granularity(code),
Some(g),
"gran {g:?} round-trip"
);
}
assert_eq!(scale_granularity_to_fdx(ScaleGranularity::PerTensor), 0);
assert_eq!(scale_granularity_to_fdx(ScaleGranularity::PerToken), 1);
assert_eq!(scale_granularity_to_fdx(ScaleGranularity::PerChannel), 2);
}
#[test]
fn per_block_granularity_has_no_fuel_variant() {
assert_eq!(fdx_to_scale_granularity(FDX_SCALE_GRAN_PER_BLOCK), None);
assert_eq!(fdx_to_scale_granularity(4), None);
}
const ALL_BACKENDS: [BackendId; 4] = [
BackendId::Cpu,
BackendId::Cuda,
BackendId::Vulkan,
BackendId::Metal,
];
#[test]
fn backend_roundtrips_for_every_variant() {
for b in ALL_BACKENDS {
let code = backend_to_fdx(b);
assert_eq!(fdx_to_backend(code), Some(b), "backend {b:?} round-trip");
}
assert_eq!(backend_to_fdx(BackendId::Cpu), 0);
assert_eq!(backend_to_fdx(BackendId::Cuda), 1);
assert_eq!(backend_to_fdx(BackendId::Vulkan), 2);
assert_eq!(backend_to_fdx(BackendId::Metal), 3);
assert_eq!(fdx_to_backend(4), None);
}
const ALL_SUBSTRATES: [SubstrateClass; 4] = [
SubstrateClass::HostBytes,
SubstrateClass::CudaUntyped,
SubstrateClass::VulkanBuffer,
SubstrateClass::MetalBuffer,
];
#[test]
fn substrate_roundtrips_for_every_variant() {
for s in ALL_SUBSTRATES {
let code = substrate_to_fdx(s);
assert_eq!(
fdx_to_substrate(code),
Some(s),
"substrate {s:?} round-trip"
);
}
assert_eq!(substrate_to_fdx(SubstrateClass::HostBytes), 0);
assert_eq!(substrate_to_fdx(SubstrateClass::CudaUntyped), 1);
assert_eq!(substrate_to_fdx(SubstrateClass::VulkanBuffer), 2);
assert_eq!(substrate_to_fdx(SubstrateClass::MetalBuffer), 3);
assert_eq!(fdx_to_substrate(4), None);
}
#[test]
fn extent_scalar_roundtrips() {
let e = Extent::Scalar(4096);
let x = extent_to_fdx(e);
assert_eq!(x.kind as u16, FDX_EXTENT_SCALAR);
assert_eq!(x.capacity, 4096);
assert_eq!(x.min, 4096);
assert_eq!(x.sym_id, FDX_SYM_NONE);
assert_eq!(x.affine.term_count, 0);
assert_eq!(fdx_to_extent(&x), Some(e));
}
#[test]
fn extent_range_roundtrips() {
let e = Extent::Range {
min: 1,
max: 8192,
sym: SymId(7),
};
let x = extent_to_fdx(e);
assert_eq!(x.kind as u16, FDX_EXTENT_RANGE);
assert_eq!(x.min, 1);
assert_eq!(x.capacity, 8192); assert_eq!(x.sym_id, 7);
assert_eq!(fdx_to_extent(&x), Some(e));
}
#[test]
fn affine_extent_has_no_fuel_variant() {
let mut x = extent_to_fdx(Extent::Scalar(1));
x.kind = FDX_EXTENT_AFFINE as u8;
assert_eq!(fdx_to_extent(&x), None);
x.kind = 99;
assert_eq!(fdx_to_extent(&x), None);
}
#[test]
fn every_source_variant_maps_to_a_distinct_real_code() {
let dt: Vec<u16> = DType::ALL
.iter()
.copied()
.filter_map(|d| match dtype_to_fdx(d) {
Ok(FdxDtype::Coded(c)) => Some(c),
_ => None,
})
.collect();
for &c in &dt {
assert_ne!(c, FDX_DTYPE_NONE);
assert!(fdx_to_dtype(c).is_some());
}
assert_eq!(
{
let mut v = dt.clone();
v.sort_unstable();
v.dedup();
v.len()
},
dt.len()
);
let gg: Vec<u16> = ALL_GGML.iter().copied().map(ggml_to_fdx).collect();
for &c in &gg {
assert_ne!(c, FDX_QUANT_NONE);
assert!(fdx_to_ggml(c).is_some());
}
assert_eq!(
{
let mut v = gg.clone();
v.sort_unstable();
v.dedup();
v.len()
},
gg.len()
);
let gr: Vec<u8> = ALL_GRAN
.iter()
.copied()
.map(scale_granularity_to_fdx)
.collect();
assert!(gr.iter().all(|&c| fdx_to_scale_granularity(c).is_some()));
let bk: Vec<u8> = ALL_BACKENDS.iter().copied().map(backend_to_fdx).collect();
assert!(bk.iter().all(|&c| fdx_to_backend(c).is_some()));
let su: Vec<u8> = ALL_SUBSTRATES
.iter()
.copied()
.map(substrate_to_fdx)
.collect();
assert!(su.iter().all(|&c| fdx_to_substrate(c).is_some()));
}
}