use super::*;
use crate::{
AxisRange, FeatureInfo, ShapeConstraint,
embeddings::face::align::TEMPLATE_BYTES,
model::{RawShapeConstraint, contract::check_load_contract},
};
const DIM: usize = 512;
fn model(dim: usize) -> FaceModel {
FaceModel::new("data", "embedding", dim)
}
fn manifest(layout: TensorLayout) -> FaceModel {
let arcface = Preprocessing::ARCFACE;
model(DIM).with_preprocessing(Preprocessing::new(
arcface.order(),
layout,
arcface.scale(),
arcface.bias(),
))
}
fn multi_array(
name: &str,
shape: &[usize],
dtype: DataType,
optional: bool,
raw_type: isize,
enumerated: Vec<Vec<usize>>,
ranges: Vec<AxisRange>,
) -> FeatureInfo {
FeatureInfo::from_parts(
name.to_string(),
shape.to_vec(),
Some(dtype),
optional,
Some(RawShapeConstraint::new(raw_type, enumerated, ranges)),
)
}
fn pinned(shape: &[usize]) -> Vec<AxisRange> {
shape.iter().map(|d| AxisRange::new(*d, 1)).collect()
}
fn fixed(name: &str, shape: &[usize], dtype: DataType) -> FeatureInfo {
multi_array(
name,
shape,
dtype,
false,
2,
vec![shape.to_vec()],
pinned(shape),
)
}
fn undeclared(name: &str) -> FeatureInfo {
multi_array(name, &[], DataType::F32, false, 1, Vec::new(), Vec::new())
}
fn graph(input_shape: &[usize], output_shape: &[usize]) -> ModelDescription {
ModelDescription::from_parts(
vec![fixed("data", input_shape, DataType::F32)],
vec![fixed("embedding", output_shape, DataType::F32)],
Vec::new(),
)
}
fn check(
description: &ModelDescription,
manifest: &FaceModel,
) -> Result<(InputContract, OutputContract)> {
let resolved = load_contract(description, manifest)?;
check_load_contract(description, &resolved.contract).map_err(contract_violation)?;
Ok((
InputContract::read_back(description, manifest.input(), resolved.rank),
resolved.output,
))
}
fn contract_of(shape: &[usize], layout: TensorLayout) -> Option<(usize, InputRank)> {
let batch = if shape.len() == 4 { shape[0] } else { 1 };
check(&graph(shape, &[batch, DIM]), &manifest(layout))
.ok()
.map(|(input, _)| (input.batch, input.rank))
}
fn artifact(tag: u8) -> ArtifactDigest {
ArtifactDigest::from_raw([tag; 32])
}
fn space_of(tag: u8, manifest: &FaceModel) -> EmbeddingSpace {
EmbeddingSpace::of(artifact(tag), manifest)
}
fn space(dim: usize) -> EmbeddingSpace {
space_of(1, &model(dim))
}
fn ramp_face() -> AlignedFace {
let mut pixels = vec![0u8; TEMPLATE_BYTES];
for pixel in 0..TEMPLATE_SIZE * TEMPLATE_SIZE {
for channel in 0..3 {
pixels[pixel * 3 + channel] =
u8::try_from((pixel + channel) % 256).expect("modulo keeps it in range");
}
}
AlignedFace::from_template_pixels(&pixels).expect("exact template length")
}
#[test]
fn arcface_preprocessing_maps_bytes_onto_minus_one_to_one() {
let p = Preprocessing::ARCFACE;
assert_eq!(p.order(), ChannelOrder::Rgb);
assert_eq!(p.layout(), TensorLayout::Nchw);
assert!((0.0f32.mul_add(p.scale(), p.bias()[0]) + 1.0).abs() < 1e-6);
assert!((255.0f32.mul_add(p.scale(), p.bias()[0]) - 1.0).abs() < 1e-6);
assert!(127.5f32.mul_add(p.scale(), p.bias()[0]).abs() < 1e-6);
}
#[test]
fn mean_and_divisor_is_the_same_preprocessing_written_the_other_way() {
let from_table = Preprocessing::from_mean_and_divisor(
ChannelOrder::Rgb,
TensorLayout::Nchw,
[127.5, 127.5, 127.5],
127.5,
);
assert!((from_table.scale() - Preprocessing::ARCFACE.scale()).abs() < 1e-9);
for (got, want) in from_table
.bias()
.into_iter()
.zip(Preprocessing::ARCFACE.bias())
{
assert!((got - want).abs() < 1e-6);
}
let dlib = Preprocessing::from_mean_and_divisor(
ChannelOrder::Rgb,
TensorLayout::Nchw,
[122.782, 117.001, 104.298],
256.0,
);
assert!((dlib.bias()[0] + 122.782 / 256.0).abs() < 1e-6);
assert!((dlib.bias()[2] + 104.298 / 256.0).abs() < 1e-6);
assert!(
dlib.bias()[0] < dlib.bias()[2],
"the per-channel means must not collapse"
);
}
#[test]
fn nchw_rgb_writes_planes_in_channel_order() {
let face = ramp_face();
let pixels = TEMPLATE_SIZE * TEMPLATE_SIZE;
let mut row = vec![0.0f32; 3 * pixels];
let identity = Preprocessing::new(ChannelOrder::Rgb, TensorLayout::Nchw, 1.0, [0.0; 3]);
write_row(&mut row, &face, identity);
for pixel in [0usize, 1, 17, pixels - 1] {
for channel in 0..3 {
let expected = f32::from(face.pixels()[pixel * 3 + channel]);
assert_eq!(
row[channel * pixels + pixel],
expected,
"NCHW plane {channel}, pixel {pixel}"
);
}
}
}
#[test]
fn bgr_reads_the_opposite_channel_of_the_rgb_template() {
let face = ramp_face();
let pixels = TEMPLATE_SIZE * TEMPLATE_SIZE;
let identity_rgb = Preprocessing::new(ChannelOrder::Rgb, TensorLayout::Nchw, 1.0, [0.0; 3]);
let identity_bgr = Preprocessing::new(ChannelOrder::Bgr, TensorLayout::Nchw, 1.0, [0.0; 3]);
let mut rgb = vec![0.0f32; 3 * pixels];
let mut bgr = vec![0.0f32; 3 * pixels];
write_row(&mut rgb, &face, identity_rgb);
write_row(&mut bgr, &face, identity_bgr);
assert_eq!(&bgr[0..pixels], &rgb[2 * pixels..3 * pixels]);
assert_eq!(&bgr[2 * pixels..3 * pixels], &rgb[0..pixels]);
assert_eq!(&bgr[pixels..2 * pixels], &rgb[pixels..2 * pixels]);
assert_ne!(
&bgr[0..pixels],
&rgb[0..pixels],
"the ramp must distinguish the channel orders, or this gate proves nothing"
);
}
#[test]
fn a_written_zero_is_positive_zero_whichever_sign_the_bias_carries() {
let black =
AlignedFace::from_template_pixels(&vec![0u8; TEMPLATE_BYTES]).expect("exact template length");
let signed_zero =
|bias: f32| Preprocessing::new(ChannelOrder::Rgb, TensorLayout::Nchw, -1.0, [bias; 3]);
assert_eq!(
signed_zero(0.0),
signed_zero(-0.0),
"the space cannot see the sign of a zero, which is why the tensor must not"
);
for bias in [0.0f32, -0.0] {
let mut row = vec![f32::NAN; 3 * TEMPLATE_SIZE * TEMPLATE_SIZE];
write_row(&mut row, &black, signed_zero(bias));
for (index, value) in row.iter().enumerate() {
assert_eq!(
value.to_bits(),
0x0000_0000,
"bias {bias:?} wrote {value:?} (bits {:#010x}) at {index}, not `+0.0`",
value.to_bits()
);
}
}
}
#[test]
fn a_manifest_whose_preprocessing_is_not_finite_is_refused_at_load() {
let arcface = Preprocessing::ARCFACE;
let cases = [
(f32::NAN, [-1.0f32, -1.0, -1.0], PreprocessingField::Scale),
(f32::INFINITY, [-1.0, -1.0, -1.0], PreprocessingField::Scale),
(
arcface.scale(),
[-1.0, f32::NAN, -1.0],
PreprocessingField::Bias(1),
),
(
arcface.scale(),
[f32::NEG_INFINITY, -1.0, -1.0],
PreprocessingField::Bias(0),
),
];
for (scale, bias, want) in cases {
let broken = model(DIM).with_preprocessing(Preprocessing::new(
arcface.order(),
arcface.layout(),
scale,
bias,
));
let error = check(
&graph(&[1, 3, TEMPLATE_SIZE, TEMPLATE_SIZE], &[1, DIM]),
&broken,
)
.expect_err("a non-finite preprocessing parameter is not loadable");
assert!(
matches!(&error, Error::NonFinitePreprocessing(payload) if payload.field() == want),
"expected NonFinitePreprocessing({want}) for scale={scale:?} bias={bias:?}, got {error:?}"
);
}
assert!(
check(
&graph(&[1, 3, TEMPLATE_SIZE, TEMPLATE_SIZE], &[1, DIM]),
&model(DIM)
)
.is_ok(),
"a finite manifest must still load"
);
}
#[test]
fn a_preprocessing_map_that_leaves_f32_at_a_byte_endpoint_is_refused_at_load() {
let arcface = Preprocessing::ARCFACE;
let graph = graph(&[1, 3, TEMPLATE_SIZE, TEMPLATE_SIZE], &[1, DIM]);
assert!(
f32::MAX.is_finite() && 0.0f32.is_finite(),
"both fields are finite, which is why the per-field check admits this"
);
assert!(
!f32::from(255u8).mul_add(f32::MAX, 0.0).is_finite(),
"and the map at the far endpoint is not"
);
let overflowing = model(DIM).with_preprocessing(Preprocessing::new(
arcface.order(),
arcface.layout(),
f32::MAX,
[0.0; 3],
));
let error =
check(&graph, &overflowing).expect_err("a manifest whose map leaves `f32` is not loadable");
assert!(
matches!(
&error,
Error::NonFinitePreprocessing(payload)
if payload.field() == PreprocessingField::Map(PreprocessingMap::new(0, u8::MAX))
),
"expected the endpoint that overflows to be named, got {error:?}"
);
assert!(
error.to_string().contains("byte 255"),
"and a reader must be told WHICH end, got {error}"
);
let scale = f32::MAX / 256.0;
assert!(
f32::from(255u8).mul_add(scale, 0.0).is_finite(),
"this scale is safe over the whole byte range on its own"
);
let lopsided = model(DIM).with_preprocessing(Preprocessing::new(
arcface.order(),
arcface.layout(),
scale,
[0.0, 0.0, f32::MAX],
));
let error =
check(&graph, &lopsided).expect_err("one channel's bias carries that channel out of `f32`");
assert!(
matches!(
&error,
Error::NonFinitePreprocessing(payload)
if payload.field() == PreprocessingField::Map(PreprocessingMap::new(2, u8::MAX))
),
"expected channel 2 to be named, got {error:?}"
);
for (scale, bias) in [(f32::MAX, 0.0f32), (scale, f32::MAX)] {
assert_eq!(
f32::from(0u8).mul_add(scale, bias),
bias,
"the near endpoint is the bias itself, which the field check has already cleared"
);
}
let largest = f32::MAX / f32::from(255u8);
assert!(
f32::from(255u8).mul_add(largest, 0.0).is_finite(),
"the map at the far endpoint is finite here"
);
assert!(
check(
&graph,
&model(DIM).with_preprocessing(Preprocessing::new(
arcface.order(),
arcface.layout(),
largest,
[0.0; 3],
))
)
.is_ok(),
"an extreme but finite map must still load — the check is on the map, not on the magnitude"
);
}
#[test]
fn a_manifest_of_zero_width_is_refused_at_load() {
let input = [1, 3, TEMPLATE_SIZE, TEMPLATE_SIZE];
let error = check(&graph(&input, &[1, 0]), &model(0))
.expect_err("a zero-width manifest has no embedding to produce");
assert!(
matches!(&error, Error::ZeroEmbeddingWidth(payload) if payload.output() == "embedding"),
"{error}"
);
assert!(error.to_string().contains("embedding"), "{error}");
for output in [vec![0], vec![512]] {
assert!(
matches!(
check(&graph(&input, &output), &model(0)),
Err(Error::ZeroEmbeddingWidth(_))
),
"output {output:?} must be refused for the manifest's width, not for its own"
);
}
assert!(
check(&graph(&input, &[1, 1]), &model(1)).is_ok(),
"a one-wide manifest is degenerate but well defined"
);
}
#[test]
fn nhwc_interleaves_where_nchw_planes() {
let face = ramp_face();
let pixels = TEMPLATE_SIZE * TEMPLATE_SIZE;
let identity = Preprocessing::new(ChannelOrder::Rgb, TensorLayout::Nhwc, 1.0, [0.0; 3]);
let mut row = vec![0.0f32; 3 * pixels];
write_row(&mut row, &face, identity);
for pixel in [0usize, 5, pixels - 1] {
for channel in 0..3 {
assert_eq!(
row[pixel * 3 + channel],
f32::from(face.pixels()[pixel * 3 + channel]),
"NHWC pixel {pixel}, channel {channel}"
);
}
}
}
#[test]
fn preprocessing_is_scale_then_bias_with_the_bias_in_the_models_channel_space() {
let face = ramp_face();
let pixels = TEMPLATE_SIZE * TEMPLATE_SIZE;
let scale = 1.0f32 / 127.5;
let bias = [-1.0f32, -2.0, -3.0];
let mut row = vec![0.0f32; 3 * pixels];
write_row(
&mut row,
&face,
Preprocessing::new(ChannelOrder::Bgr, TensorLayout::Nchw, scale, bias),
);
for pixel in [0usize, 128, 255, pixels - 1] {
for channel in 0..3 {
let byte = f32::from(face.pixels()[pixel * 3 + (2 - channel)]);
let expected = byte * scale + bias[channel];
let got = row[channel * pixels + pixel];
assert!(
(got - expected).abs() < 1e-6,
"plane {channel}, pixel {pixel}: got {got}, expected {expected} (byte {byte} · {scale} + \
{})",
bias[channel]
);
let swapped = (byte + bias[channel]) * scale;
assert!(
(expected - swapped).abs() > 1e-3,
"this fixture cannot tell scale-then-bias from bias-then-scale, so it proves nothing"
);
}
}
}
#[test]
fn the_contract_accepts_the_forms_real_exports_declare() {
assert_eq!(
contract_of(&[3, TEMPLATE_SIZE, TEMPLATE_SIZE], TensorLayout::Nchw),
Some((1, InputRank::Unbatched))
);
assert_eq!(
contract_of(&[1, 3, TEMPLATE_SIZE, TEMPLATE_SIZE], TensorLayout::Nchw),
Some((1, InputRank::Batched))
);
assert_eq!(
contract_of(&[8, 3, TEMPLATE_SIZE, TEMPLATE_SIZE], TensorLayout::Nchw),
Some((8, InputRank::Batched))
);
assert_eq!(
contract_of(&[TEMPLATE_SIZE, TEMPLATE_SIZE, 3], TensorLayout::Nhwc),
Some((1, InputRank::Unbatched))
);
assert_eq!(
contract_of(&[4, TEMPLATE_SIZE, TEMPLATE_SIZE, 3], TensorLayout::Nhwc),
Some((4, InputRank::Batched))
);
}
#[test]
fn the_contract_reads_the_batch_and_requires_everything_else() {
let face_nchw = [
Dim::Exactly(3),
Dim::Exactly(TEMPLATE_SIZE),
Dim::Exactly(TEMPLATE_SIZE),
];
let face_nhwc = [
Dim::Exactly(TEMPLATE_SIZE),
Dim::Exactly(TEMPLATE_SIZE),
Dim::Exactly(3),
];
let resolved = load_contract(
&graph(&[4, 3, TEMPLATE_SIZE, TEMPLATE_SIZE], &[4, DIM]),
&manifest(TensorLayout::Nchw),
)
.expect("a batch-4 NCHW export is one of the accepted forms");
assert_eq!(
(resolved.rank, resolved.output),
(InputRank::Batched, OutputContract::Batched)
);
assert_eq!(
resolved.contract,
LoadContract::new(
vec![FeatureContract::new(
"data",
DataType::F32,
[Dim::AnyFixed].into_iter().chain(face_nchw).collect()
)],
vec![FeatureContract::new(
"embedding",
DataType::F32,
vec![Dim::Exactly(4), Dim::Exactly(DIM)]
)],
StateContract::None,
)
);
let resolved = load_contract(
&graph(&[2, TEMPLATE_SIZE, TEMPLATE_SIZE, 3], &[2, DIM]),
&manifest(TensorLayout::Nhwc),
)
.expect("a batch-2 NHWC export is one of the accepted forms");
assert_eq!(
(resolved.rank, resolved.output),
(InputRank::Batched, OutputContract::Batched)
);
assert_eq!(
resolved.contract,
LoadContract::new(
vec![FeatureContract::new(
"data",
DataType::F32,
[Dim::AnyFixed].into_iter().chain(face_nhwc).collect()
)],
vec![FeatureContract::new(
"embedding",
DataType::F32,
vec![Dim::Exactly(2), Dim::Exactly(DIM)]
)],
StateContract::None,
)
);
let resolved = load_contract(
&graph(&[3, TEMPLATE_SIZE, TEMPLATE_SIZE], &[DIM]),
&manifest(TensorLayout::Nchw),
)
.expect("the unbatched rank-3 form is one of the accepted forms");
assert_eq!(
(resolved.rank, resolved.output),
(InputRank::Unbatched, OutputContract::Flat)
);
assert_eq!(
resolved.contract,
LoadContract::new(
vec![FeatureContract::new(
"data",
DataType::F32,
face_nchw.to_vec()
)],
vec![FeatureContract::new(
"embedding",
DataType::F32,
vec![Dim::Exactly(DIM)]
)],
StateContract::None,
)
);
}
#[test]
fn the_contract_refuses_a_shape_that_is_not_a_template_face() {
assert_eq!(
contract_of(&[1, TEMPLATE_SIZE, TEMPLATE_SIZE, 3], TensorLayout::Nchw),
None
);
assert_eq!(
contract_of(&[1, 3, TEMPLATE_SIZE, TEMPLATE_SIZE], TensorLayout::Nhwc),
None
);
assert_eq!(
contract_of(&[1, 3, 96, TEMPLATE_SIZE], TensorLayout::Nchw),
None
);
assert_eq!(
contract_of(&[1, 1, 3, TEMPLATE_SIZE, TEMPLATE_SIZE], TensorLayout::Nchw),
None
);
assert_eq!(
contract_of(&[TEMPLATE_SIZE, TEMPLATE_SIZE], TensorLayout::Nchw),
None
);
assert_eq!(
contract_of(&[0, 3, TEMPLATE_SIZE, TEMPLATE_SIZE], TensorLayout::Nchw),
None
);
}
#[test]
fn the_contract_refuses_an_extra_required_input() {
let description = ModelDescription::from_parts(
vec![
fixed("data", &[4, 3, TEMPLATE_SIZE, TEMPLATE_SIZE], DataType::F32),
fixed("landmark_hint", &[4, 10], DataType::F32),
],
vec![fixed("embedding", &[4, DIM], DataType::F32)],
Vec::new(),
);
let error = check(&description, &manifest(TensorLayout::Nchw)).unwrap_err();
assert!(
matches!(&error, Error::UnsatisfiableInput(name) if name == "landmark_hint"),
"{error}"
);
}
#[test]
fn the_contract_accepts_an_extra_optional_input() {
let description = ModelDescription::from_parts(
vec![
fixed("data", &[4, 3, TEMPLATE_SIZE, TEMPLATE_SIZE], DataType::F32),
multi_array(
"landmark_hint",
&[4, 10],
DataType::F32,
true,
2,
vec![vec![4, 10]],
pinned(&[4, 10]),
),
],
vec![fixed("embedding", &[4, DIM], DataType::F32)],
Vec::new(),
);
assert!(check(&description, &manifest(TensorLayout::Nchw)).is_ok());
}
#[test]
fn the_contract_refuses_a_graph_that_declares_state() {
let description = ModelDescription::from_parts(
vec![fixed(
"data",
&[1, 3, TEMPLATE_SIZE, TEMPLATE_SIZE],
DataType::F32,
)],
vec![fixed("embedding", &[1, DIM], DataType::F32)],
vec![fixed("kv_cache", &[1, 8], DataType::F32)],
);
let error = check(&description, &manifest(TensorLayout::Nchw)).unwrap_err();
assert!(
matches!(&error, Error::UnsatisfiableState(name) if name == "kv_cache"),
"{error}"
);
}
#[test]
fn the_contract_refuses_a_flexible_input_declaring_its_exact_numbers() {
let shape = [4, 3, TEMPLATE_SIZE, TEMPLATE_SIZE];
let flexible = multi_array(
"data",
&shape,
DataType::F32,
false,
3,
Vec::new(),
pinned(&shape),
);
assert_eq!(
flexible.shape_constraint(),
Some(ShapeConstraint::Range),
"the fixture must be a flexible feature, not merely a differently spelled fixed one"
);
let description = ModelDescription::from_parts(
vec![flexible],
vec![fixed("embedding", &[4, DIM], DataType::F32)],
Vec::new(),
);
let error = check(&description, &manifest(TensorLayout::Nchw)).unwrap_err();
assert!(
matches!(&error, Error::ContractMismatch(m) if m.feature() == "data"),
"{error}"
);
assert!(error.to_string().contains("range"), "{error}");
}
#[test]
fn the_contract_refuses_an_export_that_declares_no_shape() {
let output = undeclared("embedding");
assert_eq!(
output.shape_constraint(),
Some(ShapeConstraint::Unspecified),
"the fixture must be what a `neuralnetwork` output actually reports"
);
let description = ModelDescription::from_parts(
vec![fixed(
"data",
&[1, 3, TEMPLATE_SIZE, TEMPLATE_SIZE],
DataType::F32,
)],
vec![output],
Vec::new(),
);
let error = check(&description, &manifest(TensorLayout::Nchw)).unwrap_err();
assert!(
matches!(&error, Error::ContractMismatch(m)
if m.feature() == "embedding" && m.actual() == "[]"),
"{error}"
);
let description = ModelDescription::from_parts(
vec![undeclared("data")],
vec![fixed("embedding", &[1, DIM], DataType::F32)],
Vec::new(),
);
let error = check(&description, &manifest(TensorLayout::Nchw)).unwrap_err();
assert!(
matches!(&error, Error::ContractMismatch(m)
if m.feature() == "data" && m.actual() == "[]"),
"{error}"
);
}
#[test]
fn the_contract_refuses_an_output_whose_batch_is_not_the_inputs() {
let error = check(
&graph(&[4, 3, TEMPLATE_SIZE, TEMPLATE_SIZE], &[2, DIM]),
&manifest(TensorLayout::Nchw),
)
.unwrap_err();
assert!(
matches!(&error, Error::ContractMismatch(m) if m.feature() == "embedding"),
"{error}"
);
}
#[test]
fn the_contract_refuses_an_output_of_a_different_width() {
let error = check(
&graph(&[4, 3, TEMPLATE_SIZE, TEMPLATE_SIZE], &[4, 128]),
&manifest(TensorLayout::Nchw),
)
.unwrap_err();
assert!(
matches!(&error, Error::ContractMismatch(m) if m.feature() == "embedding"),
"{error}"
);
}
#[test]
fn a_flat_output_is_a_batch_one_form_only() {
let nchw = manifest(TensorLayout::Nchw);
assert_eq!(
check(&graph(&[1, 3, TEMPLATE_SIZE, TEMPLATE_SIZE], &[DIM]), &nchw)
.expect("a batch-one graph may declare the bare form")
.1,
OutputContract::Flat
);
assert!(
check(&graph(&[4, 3, TEMPLATE_SIZE, TEMPLATE_SIZE], &[DIM]), &nchw).is_err(),
"[dim] must not resolve against a batch-4 graph"
);
}
#[test]
fn the_contract_refuses_a_differently_spelled_feature() {
let nchw = manifest(TensorLayout::Nchw);
let description = ModelDescription::from_parts(
vec![fixed(
"input_1",
&[1, 3, TEMPLATE_SIZE, TEMPLATE_SIZE],
DataType::F32,
)],
vec![fixed("embedding", &[1, DIM], DataType::F32)],
Vec::new(),
);
let error = check(&description, &nchw).unwrap_err();
assert!(
matches!(&error, Error::ContractMismatch(m)
if m.feature() == "data" && m.actual() == r#"inputs ["input_1"]"#),
"{error}"
);
let description = ModelDescription::from_parts(
vec![fixed(
"data",
&[1, 3, TEMPLATE_SIZE, TEMPLATE_SIZE],
DataType::F32,
)],
vec![fixed("output_1", &[1, DIM], DataType::F32)],
Vec::new(),
);
let error = check(&description, &nchw).unwrap_err();
assert!(
matches!(&error, Error::ContractMismatch(m)
if m.feature() == "embedding" && m.actual() == r#"outputs ["output_1"]"#),
"{error}"
);
}
#[test]
fn normalising_produces_a_unit_vector() {
let embedding = normalise_row(&[3.0, 4.0], 0, space(2)).expect("finite and nonzero");
assert_eq!(embedding.dim(), 2);
assert!((embedding.as_slice()[0] - 0.6).abs() < 1e-6);
assert!((embedding.as_slice()[1] - 0.8).abs() < 1e-6);
let norm: f32 = embedding.as_slice().iter().map(|v| v * v).sum();
assert!((norm - 1.0).abs() < 1e-6);
assert_eq!(embedding.to_vec(), embedding.as_slice().to_vec());
}
#[test]
fn a_zero_row_is_refused_and_names_the_callers_index() {
let error = normalise_row(&[0.0, 0.0, 0.0], 7, space(3)).expect_err("zero has no direction");
assert!(
matches!(error, Error::EmbeddingZero(payload) if payload.row() == 7),
"expected EmbeddingZero(7), got {error:?}"
);
}
#[test]
fn a_non_finite_row_names_the_row_and_the_component() {
let error =
normalise_row(&[1.0, f32::NAN, 2.0], 5, space(3)).expect_err("NaN is not an embedding");
assert!(
matches!(error, Error::NonFiniteOutput(payload) if payload.row() == 5 && payload.component() == 1),
"expected NonFiniteOutput(5, 1), got {error:?}"
);
}
#[test]
fn cosine_is_the_dot_product_of_unit_vectors() {
let a = normalise_row(&[1.0, 0.0], 0, space(2)).expect("unit");
let b = normalise_row(&[0.0, 1.0], 0, space(2)).expect("unit");
let c = normalise_row(&[1.0, 0.0], 0, space(2)).expect("unit");
assert!(a.cosine(&b).expect("one space").abs() < 1e-6);
assert!((a.cosine(&c).expect("one space") - 1.0).abs() < 1e-6);
assert_eq!(
a.dot(&b).expect("one space"),
a.cosine(&b).expect("one space")
);
}
const OVER_UNIT_ROW: [f32; 10] = [80.0, 36.0, 30.0, 40.0, 7.0, 12.0, 10.0, 4.0, 75.0, 70.0];
#[test]
fn a_unit_vector_never_scores_above_one_against_itself() {
let vector = normalise_row(&OVER_UNIT_ROW, 0, space(10)).expect("finite and nonzero");
let exact: f64 = vector
.as_slice()
.iter()
.map(|v| f64::from(*v) * f64::from(*v))
.sum();
assert!(
exact > 1.0,
"the witness must overshoot in `f64` too, got {exact:.17}"
);
assert!(
exact as f32 > 1.0,
"and the overshoot must survive narrowing to `f32`, or the clamp is untestable"
);
let self_score = vector.dot(&vector).expect("one vector is in one space");
assert!(
self_score <= 1.0,
"a unit vector scored {self_score:.10} against itself; a cosine that leaves [-1, 1] breaks \
`acos`, `1 - cos`, and every threshold a caller sets"
);
assert!(
self_score >= 1.0 - 1e-6,
"and clamping must not cost the answer: {self_score:.10}"
);
let opposite =
normalise_row(&OVER_UNIT_ROW.map(|v| -v), 0, space(10)).expect("finite and nonzero");
let against = vector.dot(&opposite).expect("one space");
assert!(
against >= -1.0,
"an antipodal pair scored {against:.10}, below the floor a cosine has"
);
}
#[test]
fn one_space_reached_through_two_separately_built_manifests_still_compares() {
let a = normalise_row(&[1.0, 0.0], 0, space(2)).expect("unit");
let b = normalise_row(
&[1.0, 0.0],
0,
space_of(1, &FaceModel::new("data", "embedding", 2)),
)
.expect("unit");
assert_eq!(
a.cosine(&b).expect("two equal manifests are one space"),
1.0,
"two equal manifests must name one space"
);
}
#[test]
fn embeddings_of_different_widths_are_refused_rather_than_scored_zero() {
let a = normalise_row(&[1.0, 0.0], 0, space(2)).expect("unit");
let wide = normalise_row(&[1.0, 0.0, 0.0], 0, space(3)).expect("unit");
let error = a.cosine(&wide).expect_err("two widths are two spaces");
assert!(
matches!(
error,
Error::IncomparableEmbeddings(p) if p.field() == EmbeddingSpaceField::Dim
),
"expected IncomparableEmbeddings(Dim), got {error:?}"
);
}
#[test]
fn embeddings_from_two_preprocessing_spaces_are_refused_not_scored() {
let rgb = model(2);
let bgr = rgb.with_preprocessing(Preprocessing::from_mean_and_divisor(
ChannelOrder::Bgr,
TensorLayout::Nchw,
[127.5, 127.5, 127.5],
127.5,
));
let a = normalise_row(&[1.0, 0.0], 0, space_of(1, &rgb)).expect("unit");
let b = normalise_row(&[1.0, 0.0], 0, space_of(1, &bgr)).expect("unit");
assert_eq!(
a.dot(&a).expect("one space"),
1.0,
"the same space must still score"
);
let error = a
.cosine(&b)
.expect_err("two preprocessing spaces are not one space");
assert!(
matches!(
error,
Error::IncomparableEmbeddings(p) if p.field() == EmbeddingSpaceField::ChannelOrder
),
"expected IncomparableEmbeddings(ChannelOrder), got {error:?}"
);
for (want, other) in [
(
EmbeddingSpaceField::TensorLayout,
rgb.with_preprocessing(Preprocessing::new(
ChannelOrder::Rgb,
TensorLayout::Nhwc,
1.0 / 127.5,
[-1.0, -1.0, -1.0],
)),
),
(
EmbeddingSpaceField::PreprocessingScale,
rgb.with_preprocessing(Preprocessing::new(
ChannelOrder::Rgb,
TensorLayout::Nchw,
1.0 / 255.0,
[-1.0, -1.0, -1.0],
)),
),
(
EmbeddingSpaceField::PreprocessingBias,
rgb.with_preprocessing(Preprocessing::new(
ChannelOrder::Rgb,
TensorLayout::Nchw,
1.0 / 127.5,
[0.0, -1.0, -1.0],
)),
),
] {
let far = normalise_row(&[1.0, 0.0], 0, space_of(1, &other)).expect("unit");
let error = a
.cosine(&far)
.expect_err("a different manifest is a different space");
assert!(
matches!(error, Error::IncomparableEmbeddings(p) if p.field() == want),
"expected IncomparableEmbeddings({want}), got {error:?}"
);
}
}
#[test]
fn a_non_finite_preprocessing_scale_still_names_one_space() {
let with_nan = |payload: f32| {
model(2).with_preprocessing(Preprocessing::new(
ChannelOrder::Rgb,
TensorLayout::Nchw,
payload,
[payload, -1.0, -1.0],
))
};
let nan = with_nan(f32::NAN);
let a = normalise_row(&[1.0, 0.0], 0, space_of(1, &nan)).expect("unit");
let other_payload = f32::from_bits(f32::NAN.to_bits() | 1);
assert!(other_payload.is_nan() && other_payload.to_bits() != f32::NAN.to_bits());
let b = normalise_row(&[1.0, 0.0], 0, space_of(1, &with_nan(other_payload))).expect("unit");
assert_eq!(
a.cosine(&b)
.expect("one manifest is one space, whatever it holds"),
1.0
);
assert!(
a.cosine(&normalise_row(&[1.0, 0.0], 0, space(2)).expect("unit"))
.is_err(),
"a NaN scale is still a DIFFERENT space from a finite one"
);
}
#[test]
fn an_embedding_carries_the_space_that_produced_it() {
let manifest = model(2).with_preprocessing(Preprocessing::from_mean_and_divisor(
ChannelOrder::Bgr,
TensorLayout::Nhwc,
[104.0, 117.0, 123.0],
58.0,
));
assert_ne!(manifest.preprocessing(), Preprocessing::ARCFACE);
let embedding = normalise_row(&[1.0, 0.0], 0, space_of(1, &manifest)).expect("unit");
assert_eq!(embedding.space(), space_of(1, &manifest));
assert_eq!(
embedding.space().preprocessing(),
manifest.preprocessing(),
"the preprocessing half of the space must travel with the vector too"
);
assert_eq!(embedding.space().dim(), embedding.dim());
}
#[test]
fn a_manifest_carries_its_own_preprocessing() {
let manifest = FaceModel::new("data", "embedding", 512);
assert_eq!(manifest.input(), "data");
assert_eq!(manifest.output(), "embedding");
assert_eq!(manifest.dim(), 512);
assert_eq!(manifest.preprocessing(), Preprocessing::ARCFACE);
let adaface = manifest.with_preprocessing(Preprocessing::from_mean_and_divisor(
ChannelOrder::Bgr,
TensorLayout::Nchw,
[127.5, 127.5, 127.5],
127.5,
));
assert_eq!(adaface.preprocessing().order(), ChannelOrder::Bgr);
assert_eq!(
manifest.preprocessing().order(),
ChannelOrder::Rgb,
"the builder must not mutate the manifest it was called on"
);
}
#[test]
fn options_default_to_the_module_default() {
assert_eq!(FaceEmbedderOptions::new().compute(), DEFAULT_FACE_COMPUTE);
assert_eq!(FaceEmbedderOptions::default(), FaceEmbedderOptions::new());
assert_eq!(
FaceEmbedderOptions::new()
.with_compute(crate::ComputeUnits::CpuOnly)
.compute(),
crate::ComputeUnits::CpuOnly
);
}
#[cfg(feature = "serde")]
#[test]
fn options_round_trip_through_serde() {
let options = FaceEmbedderOptions::new().with_compute(crate::ComputeUnits::CpuAndGpu);
let json = serde_json::to_string(&options).expect("serialisable");
let back: FaceEmbedderOptions = serde_json::from_str(&json).expect("deserialisable");
assert_eq!(back, options);
let defaulted: FaceEmbedderOptions = serde_json::from_str("{}").expect("compute defaults");
assert_eq!(defaulted.compute(), DEFAULT_FACE_COMPUTE);
}
#[cfg(feature = "serde")]
#[test]
fn preprocessing_round_trips_through_serde() {
let preprocessing =
Preprocessing::new(ChannelOrder::Bgr, TensorLayout::Nhwc, 0.5, [1.0, 2.0, 3.0]);
let json = serde_json::to_string(&preprocessing).expect("serialisable");
assert!(
json.contains("bgr"),
"the channel order should be kebab-case: {json}"
);
let back: Preprocessing = serde_json::from_str(&json).expect("deserialisable");
assert_eq!(back, preprocessing);
}
#[test]
fn the_tensor_built_has_the_rank_the_model_declared() {
for (declared, layout) in [
(
vec![3usize, TEMPLATE_SIZE, TEMPLATE_SIZE],
TensorLayout::Nchw,
),
(
vec![TEMPLATE_SIZE, TEMPLATE_SIZE, 3usize],
TensorLayout::Nhwc,
),
(
vec![8usize, 3, TEMPLATE_SIZE, TEMPLATE_SIZE],
TensorLayout::Nchw,
),
(
vec![4usize, TEMPLATE_SIZE, TEMPLATE_SIZE, 3],
TensorLayout::Nhwc,
),
] {
let batch = if declared.len() == 4 { declared[0] } else { 1 };
let (contract, _) = check(&graph(&declared, &[batch, DIM]), &manifest(layout))
.expect("a shape real exports declare must load");
assert_eq!(
input_shape(contract, layout),
declared,
"the loader accepted {declared:?} and would then feed the graph a different shape"
);
}
}
#[test]
fn a_transposed_output_tensor_is_refused() {
let error = check_predicted_shape(&[512, 4], 4 * 512, OutputContract::Batched, 4, 512, 4 * 512)
.expect_err("a [dim, batch] tensor is not a [batch, dim] tensor");
assert!(
matches!(&error, Error::OutputShape(payload) if payload.got() == [512, 4]),
"expected OutputShape([512, 4]), got {error:?}"
);
assert!(
check_predicted_shape(&[4, 512], 4 * 512, OutputContract::Batched, 4, 512, 4 * 512).is_ok()
);
assert!(check_predicted_shape(&[512], 512, OutputContract::Flat, 1, 512, 512).is_ok());
assert!(check_predicted_shape(&[512], 512, OutputContract::Batched, 1, 512, 512).is_err());
}
#[test]
fn a_diverging_element_count_is_not_reported_as_a_shape_mismatch() {
let error = check_predicted_shape(
&[4, 512],
4 * 512 - 1,
OutputContract::Batched,
4,
512,
4 * 512,
)
.expect_err("an element count short of the contract is a divergence");
let message = error.to_string();
assert!(
!message.contains("expected [4, 512], got [4, 512]"),
"the shapes are equal; reporting them as a mismatch is a falsehood, got {message:?}"
);
assert!(
message.contains("2047") && message.contains("2048"),
"the failure must name the counts that diverged, got {message:?}"
);
assert!(
matches!(
&error,
Error::OutputElementCount(payload) if payload.got() == 2047 && payload.expected() == 2048
),
"the payload must carry both counts, got {error:?}"
);
assert!(
matches!(
check_predicted_shape(&[512, 4], 4 * 512, OutputContract::Batched, 4, 512, 4 * 512),
Err(Error::OutputShape(_))
),
"a transposed tensor is still a shape mismatch"
);
}
#[test]
fn normalising_survives_components_an_f32_square_cannot_hold() {
let big =
normalise_row(&[1.8e38, 2.4e38], 0, space(2)).expect("a large but finite row has a direction");
assert!(
(big.as_slice()[0] - 0.6).abs() < 1e-6,
"got {:?}",
big.as_slice()
);
assert!(
(big.as_slice()[1] - 0.8).abs() < 1e-6,
"got {:?}",
big.as_slice()
);
let small =
normalise_row(&[3.0e-25, 4.0e-25], 1, space(2)).expect("a tiny but finite row has a direction");
assert!(
(small.as_slice()[0] - 0.6).abs() < 1e-6,
"got {:?}",
small.as_slice()
);
assert!(
(small.as_slice()[1] - 0.8).abs() < 1e-6,
"got {:?}",
small.as_slice()
);
assert!(matches!(
normalise_row(&[0.0, -0.0], 2, space(2)).expect_err("zero has no direction"),
Error::EmbeddingZero(_)
));
}
#[test]
fn the_load_time_contract_requires_an_f32_multi_array() {
let nchw = manifest(TensorLayout::Nchw);
let shape = [4, 3, TEMPLATE_SIZE, TEMPLATE_SIZE];
for wrong in [DataType::F16, DataType::F64, DataType::I32] {
let description = ModelDescription::from_parts(
vec![fixed("data", &shape, wrong)],
vec![fixed("embedding", &[4, DIM], DataType::F32)],
Vec::new(),
);
let error = check(&description, &nchw).unwrap_err();
assert!(
matches!(&error, Error::ContractMismatch(m) if m.feature() == "data"),
"a {wrong} input must not load against an f32 inference path: {error}"
);
let description = ModelDescription::from_parts(
vec![fixed("data", &shape, DataType::F32)],
vec![fixed("embedding", &[4, DIM], wrong)],
Vec::new(),
);
let error = check(&description, &nchw).unwrap_err();
assert!(
matches!(&error, Error::ContractMismatch(m) if m.feature() == "embedding"),
"a {wrong} output must not load against an f32 inference path: {error}"
);
}
let image = FeatureInfo::from_parts("data".to_string(), Vec::new(), None, false, None);
assert_eq!(image.data_type(), None);
assert_eq!(image.shape_constraint(), None);
let description = ModelDescription::from_parts(
vec![image],
vec![fixed("embedding", &[4, DIM], DataType::F32)],
Vec::new(),
);
assert!(
matches!(check(&description, &nchw), Err(Error::ContractMismatch(m)) if m.feature() == "data"),
"a feature that is not a multi-array must not resolve to batch 1"
);
assert!(check(&graph(&shape, &[4, DIM]), &nchw).is_ok());
}
#[test]
fn a_batch_whose_tensor_element_count_leaves_usize_is_refused_at_load() {
let batch = usize::MAX / 1000;
assert!(
batch.checked_mul(DIM).is_some(),
"this batch must overflow the INPUT count only, or the case below is the \
one being tested twice"
);
let description = graph(&[batch, 3, TEMPLATE_SIZE, TEMPLATE_SIZE], &[batch, DIM]);
let error = check(&description, &model(DIM))
.expect_err("a batch whose input tensor cannot be counted must not load");
assert!(
matches!(
&error,
Error::ElementCountOverflow(o)
if o.tensor() == PredictionTensor::Input
&& o.batch() == batch
&& o.per_row() == TEMPLATE_BYTES
),
"{error}"
);
let dim = 1 << 40;
let batch = usize::MAX / dim + 1;
assert!(
batch.checked_mul(TEMPLATE_BYTES).is_some(),
"this batch must overflow the OUTPUT count only"
);
let description = graph(&[batch, 3, TEMPLATE_SIZE, TEMPLATE_SIZE], &[batch, dim]);
let error = check(&description, &model(dim))
.expect_err("a batch and width whose output tensor cannot be counted must not load");
assert!(
matches!(
&error,
Error::ElementCountOverflow(o)
if o.tensor() == PredictionTensor::Output && o.batch() == batch && o.per_row() == dim
),
"{error}"
);
let (input, _) = check(
&graph(&[8, 3, TEMPLATE_SIZE, TEMPLATE_SIZE], &[8, DIM]),
&model(DIM),
)
.expect("a batch-8 export counts to 301056 input and 4096 output elements");
assert_eq!(input.batch, 8);
}
#[test]
fn the_tensor_allocator_refuses_rather_than_aborting() {
let error = zeroed_tensor(PredictionTensor::Output, usize::MAX)
.expect_err("a length whose byte size leaves `usize` has no buffer");
assert!(
matches!(
&error,
Error::AllocationFailed(a)
if a.tensor() == PredictionTensor::Output && a.elements() == usize::MAX
),
"{error}"
);
let beyond_memory = usize::MAX / 8;
let error = zeroed_tensor(PredictionTensor::Input, beyond_memory)
.expect_err("a buffer the allocator refuses is an error, not an abort");
assert!(
matches!(
&error,
Error::AllocationFailed(a)
if a.tensor() == PredictionTensor::Input && a.elements() == beyond_memory
),
"{error}"
);
assert!(
error.to_string().contains(&beyond_memory.to_string()),
"the refusal must name the length that was asked for, got {error}"
);
let data = zeroed_tensor(PredictionTensor::Input, FACE_ELEMENTS).expect("one face fits");
assert_eq!(data.len(), FACE_ELEMENTS);
assert!(data.iter().all(|v| v.to_bits() == 0.0f32.to_bits()));
}
#[test]
fn the_embedding_row_allocator_refuses_rather_than_aborting() {
let error =
embedding_buffer(usize::MAX).expect_err("a width whose byte size leaves `usize` has no row");
assert!(
matches!(
&error,
Error::AllocationFailed(a)
if a.tensor() == PredictionTensor::Output && a.elements() == usize::MAX
),
"{error}"
);
let beyond_memory = usize::MAX / 8;
let error = embedding_buffer(beyond_memory)
.expect_err("a row the allocator refuses is an error, not an abort");
assert!(
matches!(
&error,
Error::AllocationFailed(a)
if a.tensor() == PredictionTensor::Output && a.elements() == beyond_memory
),
"{error}"
);
assert!(
error.to_string().contains(&beyond_memory.to_string()),
"the refusal must name the width that was asked for, got {error}"
);
let embedding = normalise_row(&[3.0, 4.0], 0, space(2)).expect("finite and nonzero");
assert_eq!(embedding.dim(), 2);
assert!((embedding.as_slice()[0] - 0.6).abs() < 1e-6);
assert!((embedding.as_slice()[1] - 0.8).abs() < 1e-6);
}
#[test]
fn the_result_allocator_refuses_rather_than_aborting() {
let error = result_buffer(usize::MAX)
.expect_err("a face count whose byte size leaves `usize` has no result vector");
assert!(
matches!(
&error,
Error::ResultAllocationFailed(r) if r.faces() == usize::MAX
),
"{error}"
);
let beyond_memory = usize::MAX / 8;
let error = result_buffer(beyond_memory)
.expect_err("a result vector the allocator refuses is an error, not an abort");
assert!(
matches!(
&error,
Error::ResultAllocationFailed(r) if r.faces() == beyond_memory
),
"{error}"
);
assert!(
error.to_string().contains(&beyond_memory.to_string()),
"the refusal must name the count that was asked for, got {error}"
);
let out = result_buffer(9).expect("nine embeddings fit");
assert!(out.is_empty());
assert!(out.capacity() >= 9);
let empty = result_buffer(0).expect("no faces need no buffer");
assert!(empty.is_empty());
}
#[test]
fn the_face_door_refuses_the_vendored_silero_bundle() {
let bundle = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../Models/vadkit/silero-vad-unified-256ms-v6.2.1.mlmodelc");
assert!(
bundle.is_dir(),
"the vendored silero bundle is committed, so this gate is NOT model-gated; looked for {}",
bundle.display()
);
let options = FaceEmbedderOptions::new().with_compute(ComputeUnits::CpuOnly);
let error = FaceEmbedder::load(&bundle, model(DIM), options)
.expect_err("silero declares no `data` feature");
assert!(
matches!(&error, Error::ContractMismatch(m)
if m.feature() == "data" && m.actual().contains("audio_input")),
"{error}"
);
let error = FaceEmbedder::load(
&bundle,
FaceModel::new("audio_input", "vad_output", DIM),
options,
)
.expect_err("silero's audio window is not a template face");
assert!(
matches!(&error, Error::ContractMismatch(m)
if m.feature() == "audio_input" && m.actual() == "[1, 4160]"),
"{error}"
);
}
#[test]
fn a_load_that_cannot_open_the_artifact_never_walks_it() {
let temp = tempfile::tempdir().expect("tempdir");
let absent = temp.path().join("not-there.mlmodelc");
let options = FaceEmbedderOptions::new().with_compute(ComputeUnits::CpuOnly);
let error =
FaceEmbedder::load(&absent, model(DIM), options).expect_err("there is nothing to load");
assert!(
matches!(&error, Error::Load(crate::LoadError::NotFound(path)) if path == &absent),
"a path CoreML cannot open must fail as a LOAD, not as a digest of bytes the door never \
needed; got {error:?}"
);
}
#[test]
fn manifest_equality_and_space_identity_are_one_relation() {
let plus = model(2).with_preprocessing(Preprocessing::new(
ChannelOrder::Rgb,
TensorLayout::Nchw,
1.0 / 127.5,
[0.0, -1.0, -1.0],
));
let minus = model(2).with_preprocessing(Preprocessing::new(
ChannelOrder::Rgb,
TensorLayout::Nchw,
1.0 / 127.5,
[-0.0, -1.0, -1.0],
));
assert_ne!(
plus.preprocessing().bias().map(f32::to_bits),
minus.preprocessing().bias().map(f32::to_bits),
"the two must differ in their BITS, or the gate proves nothing"
);
let a = normalise_row(&[1.0, 0.0], 0, space_of(1, &plus)).expect("unit");
let b = normalise_row(&[1.0, 0.0], 0, space_of(1, &minus)).expect("unit");
for (name, related) in [
("FaceModel: PartialEq", plus == minus),
(
"Preprocessing: PartialEq",
plus.preprocessing() == minus.preprocessing(),
),
(
"EmbeddingSpace: PartialEq",
space_of(1, &plus) == space_of(1, &minus),
),
("FaceEmbedding::dot", a.dot(&b).is_ok()),
] {
assert!(
related,
"`{name}` says these two are different, but they preprocess identically — every relation \
on this manifest has to be the same relation"
);
}
let mut left = vec![0.0f32; 3 * TEMPLATE_SIZE * TEMPLATE_SIZE];
let mut right = left.clone();
write_row(&mut left, &ramp_face(), plus.preprocessing());
write_row(&mut right, &ramp_face(), minus.preprocessing());
assert_eq!(
left.iter().map(|v| v.to_bits()).collect::<Vec<_>>(),
right.iter().map(|v| v.to_bits()).collect::<Vec<_>>(),
"the two manifests preprocess to the same bits"
);
let other = model(2).with_preprocessing(Preprocessing::new(
ChannelOrder::Rgb,
TensorLayout::Nchw,
1.0 / 127.5,
[1e-45, -1.0, -1.0],
));
assert_ne!(
space_of(1, &plus),
space_of(1, &other),
"a subnormal bias is not zero"
);
assert!(
a.dot(&normalise_row(&[1.0, 0.0], 0, space_of(1, &other)).expect("unit"))
.is_err()
);
}
#[test]
fn a_space_hashes_by_the_relation_it_compares_by() {
let hash_of = |model: FaceModel| {
use core::hash::{Hash, Hasher};
let mut hasher = std::hash::DefaultHasher::new();
space_of(1, &model).hash(&mut hasher);
hasher.finish()
};
let with = |bias: [f32; 3], scale: f32| {
model(2).with_preprocessing(Preprocessing::new(
ChannelOrder::Rgb,
TensorLayout::Nchw,
scale,
bias,
))
};
for (left, right) in [
(with([0.0, -1.0, -1.0], 1.0), with([-0.0, -1.0, -1.0], 1.0)),
(
with([-1.0; 3], f32::NAN),
with([-1.0; 3], f32::from_bits(f32::NAN.to_bits() | 1)),
),
] {
assert_eq!(
space_of(1, &left),
space_of(1, &right),
"these name one space"
);
assert_eq!(
hash_of(left),
hash_of(right),
"equal spaces must hash equal, or a map keyed by one is broken"
);
}
let one = model(2);
let renamed = FaceModel::new("input_1", "var_9", 2);
for (name, left, right) in [
("artifact", space_of(1, &one), space_of(2, &one)),
("feature names", space_of(1, &one), space_of(1, &renamed)),
] {
assert_ne!(left, right, "{name}: these are two spaces");
let digest = |space: EmbeddingSpace| {
use core::hash::{Hash, Hasher};
let mut hasher = std::hash::DefaultHasher::new();
space.hash(&mut hasher);
hasher.finish()
};
assert_ne!(
digest(left),
digest(right),
"{name}: two spaces that differ only here must reach the hasher, or a map keyed by a space \
buckets unrelated artifacts together"
);
}
}
#[test]
fn two_heads_of_one_artifact_are_two_spaces() {
let one_artifact = artifact(1);
let embedding = FaceModel::new("data", "embedding", 2);
let projection = FaceModel::new("data", "projection", 2);
assert_eq!(
(embedding.input(), embedding.dim()),
(projection.input(), projection.dim()),
"the two heads must differ ONLY in the output name, or this gate proves nothing"
);
let head =
normalise_row(&[1.0, 0.0], 0, EmbeddingSpace::of(one_artifact, &embedding)).expect("unit");
let other_head = normalise_row(
&[1.0, 0.0],
0,
EmbeddingSpace::of(one_artifact, &projection),
)
.expect("unit");
let error = head
.dot(&other_head)
.expect_err("two heads of one graph are two spaces");
assert!(
matches!(
error,
Error::IncomparableEmbeddings(p) if p.field() == EmbeddingSpaceField::OutputFeature
),
"expected IncomparableEmbeddings(OutputFeature), got {error:?}"
);
}
#[test]
fn two_artifacts_with_one_schema_are_two_spaces() {
let schema = model(2);
let first = normalise_row(&[1.0, 0.0], 0, space_of(1, &schema)).expect("unit");
let second = normalise_row(&[1.0, 0.0], 0, space_of(2, &schema)).expect("unit");
assert_ne!(
first.space().artifact(),
second.space().artifact(),
"the two artifacts must differ in their digest, or this gate proves nothing"
);
assert_eq!(
(first.space().dim(), first.space().preprocessing()),
(second.space().dim(), second.space().preprocessing()),
"and they must agree on everything else, or something weaker than the digest could refuse"
);
let error = first
.dot(&second)
.expect_err("two artifacts are two spaces whatever their schemas say");
assert!(
matches!(
error,
Error::IncomparableEmbeddings(p) if p.field() == EmbeddingSpaceField::Artifact
),
"expected IncomparableEmbeddings(Artifact), got {error:?}"
);
let same_bytes = normalise_row(&[1.0, 0.0], 0, space_of(1, &schema)).expect("unit");
assert_eq!(
first
.dot(&same_bytes)
.expect("the same artifact read twice is one space"),
1.0
);
}
#[test]
fn feature_names_select_which_tensor_and_therefore_do_decide_the_space() {
let renamed = FaceModel::new("input_1", "var_2011", 2);
let original = model(2);
let a = normalise_row(&[1.0, 0.0], 0, space_of(1, &original)).expect("unit");
let b = normalise_row(&[1.0, 0.0], 0, space_of(1, &renamed)).expect("unit");
let error = a
.dot(&b)
.expect_err("differently routed tensors are differently produced numbers");
assert!(
matches!(
error,
Error::IncomparableEmbeddings(p) if p.field() == EmbeddingSpaceField::InputFeature
),
"expected IncomparableEmbeddings(InputFeature) — the first field that differs — got {error:?}"
);
let re_exported = FaceModel::new("input_1", "var_2011", 2);
let same_weights_new_names =
normalise_row(&[1.0, 0.0], 0, space_of(1, &re_exported)).expect("unit");
assert!(
a.dot(&same_weights_new_names).is_err(),
"a re-export under other names is refused, not scored"
);
}
#[test]
fn the_space_is_half_the_callers_and_half_the_artifacts() {
let caller_built = FaceModel::new("data", "embedding", 2).with_preprocessing(Preprocessing::new(
ChannelOrder::Bgr,
TensorLayout::Nhwc,
1.0 / 128.0,
[-1.0, -1.0, -1.0],
));
let space = space_of(1, &caller_built);
let embedding = normalise_row(&[1.0, 0.0], 0, space).expect("unit");
assert_eq!(embedding.space().input(), caller_built.input());
assert_eq!(embedding.space().output(), caller_built.output());
assert_eq!(embedding.space().dim(), caller_built.dim());
assert_eq!(
embedding.space().preprocessing(),
caller_built.preprocessing()
);
let second_artifact = normalise_row(&[1.0, 0.0], 0, space_of(2, &caller_built)).expect("unit");
let error = embedding
.dot(&second_artifact)
.expect_err("schema equality is not artifact identity, and the space now knows it");
assert!(
matches!(
error,
Error::IncomparableEmbeddings(p) if p.field() == EmbeddingSpaceField::Artifact
),
"expected IncomparableEmbeddings(Artifact), got {error:?}"
);
assert_eq!(embedding.space(), space);
}