mirror of
https://github.com/wassname/moral-maps.git
synced 2026-09-09 11:27:22 +08:00
Import name tinymfv -> moralmaps, pip name tiny-mfv -> moral-maps, GitHub URLs wassname/tinymfv -> wassname/moral-maps. HuggingFace dataset id wassname/tiny-mfv left as-is (separate namespace, published data artifact). Historical docs/spec/* and RESEARCH_JOURNAL keep their dated paths. Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
97 lines
3.9 KiB
Python
97 lines
3.9 KiB
Python
"""Minimal functional tests for the pure instrument layer.
|
|
|
|
The model smoke test is the real integration gate. These tests cover the pure pieces the smoke test
|
|
does not isolate cleanly: nominal vs ordinal reducers, frame canonicalization/keying, readout names,
|
|
and packaged data integrity.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import unittest
|
|
|
|
import numpy as np
|
|
|
|
from moralmaps.data import CONFIGS, CONDITIONS, load_vignettes
|
|
from moralmaps.instrument import (
|
|
Instrument,
|
|
canonicalize_to_forward,
|
|
per_item_categorical,
|
|
reduce_nominal,
|
|
reduce_ordinal,
|
|
)
|
|
from moralmaps.readouts import expected_score, logit_contrast, logodds_agree
|
|
|
|
|
|
class InstrumentFlowTest(unittest.TestCase):
|
|
def test_nominal_flow_reduces_answer_categories_to_profile(self) -> None:
|
|
instr = Instrument(
|
|
"mfv", "salience", "nominal",
|
|
["care", "fairness", "social"],
|
|
["Care", "Fairness", "SocialNorms"],
|
|
items=[],
|
|
prefill='This is wrong because {"violation": "',
|
|
answer_to_dim={"care": "Care", "fairness": "Fairness", "social": "SocialNorms"},
|
|
)
|
|
rows = [
|
|
{"id": "a", "frame": "forward", "lp": np.log([0.80, 0.15, 0.05]),
|
|
"p": np.array([0.80, 0.15, 0.05]), "pmass_allowed": 0.95,
|
|
"dimension": None, "sign": 1, "human_label": None},
|
|
{"id": "b", "frame": "forward", "lp": np.log([0.20, 0.50, 0.30]),
|
|
"p": np.array([0.20, 0.50, 0.30]), "pmass_allowed": 0.90,
|
|
"dimension": None, "sign": 1, "human_label": None},
|
|
]
|
|
|
|
items = per_item_categorical(rows, instr.kind)
|
|
|
|
self.assertTrue(np.allclose(reduce_nominal(items, instr), [0.50, 0.325, 0.175]))
|
|
self.assertAlmostEqual(items["a"]["pmass"], 0.95)
|
|
self.assertEqual(items["a"]["n_frames"], 1)
|
|
|
|
def test_ordinal_flow_canonicalizes_frames_then_keys_profile(self) -> None:
|
|
instr = Instrument(
|
|
"likert", "endorsement", "ordinal",
|
|
["1", "2", "3", "4", "5"],
|
|
["care", "authority"],
|
|
items=[],
|
|
prefill="(",
|
|
)
|
|
onehot = {d: np.eye(5)[d - 1] for d in range(1, 6)}
|
|
rows = [
|
|
{"id": "care", "frame": "forward", "lp": np.log(onehot[4] + 1e-9),
|
|
"p": onehot[4], "pmass_allowed": 1.0,
|
|
"dimension": "care", "sign": 1, "human_label": None},
|
|
{"id": "authority", "frame": "inverted", "lp": np.log(onehot[5] + 1e-9),
|
|
"p": onehot[5], "pmass_allowed": 1.0,
|
|
"dimension": "authority", "sign": -1, "human_label": None},
|
|
]
|
|
|
|
items = per_item_categorical(rows, instr.kind)
|
|
profile = reduce_ordinal(items, instr)
|
|
|
|
self.assertTrue(np.array_equal(canonicalize_to_forward(onehot[5], "inverted", "ordinal"), onehot[1]))
|
|
self.assertAlmostEqual(expected_score(items["care"]["p"], 5), 4.0)
|
|
self.assertAlmostEqual(profile[0], 4.0)
|
|
self.assertAlmostEqual(profile[1], 5.0)
|
|
self.assertAlmostEqual(
|
|
logit_contrast(items["care"]["lp"] + 100.0, 5),
|
|
logit_contrast(items["care"]["lp"], 5),
|
|
)
|
|
self.assertTrue(np.isfinite(logodds_agree(items["care"]["lp"], 5)))
|
|
|
|
def test_packaged_vignettes_have_aligned_conditions_and_labels(self) -> None:
|
|
required_human = {
|
|
"human_Care", "human_Fairness", "human_Loyalty", "human_Authority",
|
|
"human_Sanctity", "human_Liberty", "human_SocialNorms",
|
|
}
|
|
for cfg in CONFIGS:
|
|
rows = load_vignettes(cfg)
|
|
self.assertGreaterEqual(len(rows), 100)
|
|
self.assertEqual({r["set"] for r in rows}, {cfg})
|
|
for row in rows:
|
|
self.assertTrue({"id", "foundation", "foundation_coarse", *CONDITIONS} <= set(row))
|
|
self.assertTrue(required_human <= set(row))
|
|
self.assertGreater(sum(float(row[k]) for k in required_human), 0.0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|