# -*- coding: utf-8 -*- """ 参考模型(ref_model)的纯 Python 单元测试 — Phase 2 cleaned. 本文件不涉及任何 RTL / Verilator 仿真,仅验证 Python 参考模型的正确性。 Phase 2 后只保留 pure matching 函数;所有 grouped/read-noise helpers 已删除。 测试覆盖: 1. XNOR 评分语义 — 确认是「匹配位数」而非「汉明距离」 2. Top-1 matching — 纯匹配,正确选出最高分索引 3. Top-K matching — 返回排序后的行索引列表 4. Top-K 排序规则 — 分数降序、平局行号升序 5. Top-K k 超过行数时 clamp """ from __future__ import annotations from model.ref_model import ( match_top1, match_topk, match_topk_from_scores, xnor_popcount_score, ) import numpy as np # ============================================================================== # 测试 1:评分函数语义 — 确认是「XNOR 匹配位数」而非「汉明距离」 # ============================================================================== def test_score_is_bit_match_popcount_not_hamming_distance(): """ 关键语义验证:xnor_popcount_score 计算的是匹配位的数量,不是汉明距离。 示例: query = 0b1010 stored = 0b1000 XNOR = 0b1101 → popcount = 3(有 3 个位匹配) 汉明距离 = 1 → 只有一个位不同 为什么这个区分很重要: - 如果 RTL 或模型错误地使用了汉明距离作为分数,则: 完全匹配的分数会是 0 而非 hash_bits(512) Top-K 排序会颠倒(分数低的反而排前面) - 这会导致整个 CAM 检索系统返回错误结果 """ query = 0b1010 stored = 0b1000 assert xnor_popcount_score(query, stored, width=4) == 3 # ============================================================================== # 测试 2:Top-1 matching # ============================================================================== def test_match_top1_selects_highest_xnor_score_with_row_index_tiebreak(): """Top-1 应选出 XNOR 分最高的行;平局时选最小行号。""" rows = [0b0000, 0b1111, 0b0011, 0b0101] query = 0b0000 result = match_top1(query, rows, width=4) assert result.top1_index == 0 assert result.top1_score == 4 assert result.scores.tolist() == [4, 0, 2, 2] # ============================================================================== # 测试 3:Top-K matching # ============================================================================== def test_match_topk_scores_rows_by_xnor_popcount(): """match_topk 通过 xnor_popcount 计算分数,返回排序后的行索引和分数数组。""" rows = [0b0000, 0b1111, 0b0011, 0b0101] query = 0b0000 indices, scores = match_topk(query, rows, width=4, k=3) assert scores.tolist() == [4, 0, 2, 2] assert indices == [0, 2, 3] # ============================================================================== # 测试 4:Top-K 排序 — 分数降序、平局行号升序 # ============================================================================== def test_match_topk_from_scores_uses_score_desc_then_row_asc(): """Top-K 排序规则:分数越大越优先;分数相同时行号越小越优先。""" scores = np.array([7, 9, 9, 2, 7], dtype=np.int32) assert match_topk_from_scores(scores, 4) == [1, 2, 0, 4] # ============================================================================== # 测试 5:Top-K k 超过行数时 clamp # ============================================================================== def test_match_topk_clamps_k_to_row_count(): """当 k 超过实际行数时,返回所有行(按排序)。""" indices, scores = match_topk(0, [0, 1], width=1, k=5) assert scores.tolist() == [1, 0] assert indices == [0, 1]