From 2849c9fad6a477b4f47492e2f6b661063e90a781 Mon Sep 17 00:00:00 2001 From: Spbd1 <148923621+Spbd1@users.noreply.github.com> Date: Thu, 7 May 2026 02:47:10 +0000 Subject: [PATCH] Add trait tradeoff registry --- docs/trait_tradeoff_matrix.md | 34 +++++ pishiegen/fitness/__init__.py | 9 +- pishiegen/fitness/tradeoffs.py | 254 +++++++++++++++++++++++++++++++++ tests/test_trait_tradeoffs.py | 47 ++++++ 4 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 docs/trait_tradeoff_matrix.md create mode 100644 pishiegen/fitness/tradeoffs.py create mode 100644 tests/test_trait_tradeoffs.py diff --git a/docs/trait_tradeoff_matrix.md b/docs/trait_tradeoff_matrix.md new file mode 100644 index 0000000..9e7604c --- /dev/null +++ b/docs/trait_tradeoff_matrix.md @@ -0,0 +1,34 @@ +# Trait trade-off matrix + +This matrix documents PishieGen's formal trait trade-off assumptions. These are **simulation assumptions inspired by biology**, not direct veterinary claims, diagnostic guidance, or measured ecological effect sizes. The weights in `pishiegen.fitness.tradeoffs` are intentionally small, transparent modeling defaults that should be calibrated before publication-quality biological interpretation. + +## Modeling rules + +- No major trait is treated as universally good: every listed trait has at least one modeled benefit and at least one modeled cost. +- Effects are context-dependent. A trait that helps in snow, forest, heat, hunting, or climbing can be costly in another biome or life-history context. +- Fantasy explanations are intentionally excluded. Rationales use conservative heat exchange, camouflage, movement, sensory, health-risk, or energy-budget assumptions. +- `confidence_level` separates evidence-grounded assumptions from more speculative simulation choices: + - `high`: broad biological or health-risk reasoning is relatively direct. + - `moderate`: plausible ecological trade-off, but exact magnitude is model-specific. + - `speculative`: useful for simulation diversity, but especially in need of calibration. + +## Matrix + +| Trait | Potential advantages | Potential disadvantages | Main contexts | Confidence | Rationale summary | +| --- | --- | --- | --- | --- | --- | +| Thick/long fur | Cold tolerance; snow biome survival | Heat stress in desert; stamina penalty in hot climates | Cold, snow, desert, hot climate | High | Insulation helps retain heat in cold settings but impedes heat shedding in hot open environments. | +| Short/hairless coat | Heat dissipation; lower heat stress | Cold vulnerability; lower snow survival | Hot climate, desert, cold, snow | High | Sparse covering supports heat loss but provides less insulation against cold exposure. | +| Dark coat | Night camouflage; dense forest concealment | Heat absorption in open hot biomes; visibility on snow | Night, dense forest, open hot biomes, snow | Moderate | Dark coloration can blend with shadowed backgrounds but may absorb more solar radiation and contrast with snow. | +| Light/white coat | Snow camouflage; lower heat absorption in open sun | Poor forest/night camouflage unless snow or moonlight context applies | Snow, open sun, forest, night | Moderate | Light coloration can match snow and reflect sunlight, while standing out in darker habitats. | +| Tabby/disruptive pattern | Forest/grassland camouflage; outline disruption | Less effective in uniform snow/desert backgrounds | Forest, grassland, snow, desert | Moderate | Patterning can break outlines in textured vegetation but may be conspicuous on uniform backgrounds. | +| Large ears | Heat dissipation; prey detection | Cold exposure penalty; potential injury exposure in dense terrain | Hot climate, hunting, cold, dense terrain | Moderate | Larger ears increase heat-exchange and sound-collection surface area, but expose more tissue. | +| Folded ears | Aesthetic only or negligible ecological advantage | Health-risk penalty; reduced sensory efficiency | Health, sensory, human preference | High | Folded ears are modeled as ecologically negligible except for possible human preference, with health and sensory costs. | +| Long tail | Balance; climbing; sharp turns | Minor injury/exposure risk in dense terrain | Climbing, pursuit, escape, dense terrain | Moderate | Tail length can help body control but creates a small snagging or injury exposure cost. | +| Tailless/bobtail | Lower tail injury risk | Balance/agility penalty | Dense terrain, climbing, pursuit, escape | Moderate | Less tail tissue can reduce injury exposure, while reduced tail leverage can affect balance. | +| Polydactyly | Grip; climbing; mud/wetland stability | Small locomotion cost or neutral effect | Climbing, wetland, mud, general locomotion | Speculative | Extra toes may improve contact with some substrates, but the net effect may be neutral or slightly costly. | +| High intelligence | Flexible behavior; better biome/time choice; reduced failed hunts | Metabolic cost; slower maturation or higher energy demand | Foraging, hunting, migration, development | Speculative | Behavioral flexibility helps in variable environments but is modeled as energetically and developmentally costly. | +| High agility | Hunting success; escape success | Energy demand; lower endurance if not paired with stamina | Hunting, escape, energy budget, endurance | Moderate | Maneuverability improves short-term pursuits and escapes, but costs energy and may not imply endurance. | + +## Implementation link + +The canonical machine-readable matrix lives in `pishiegen/fitness/tradeoffs.py`. Tests in `tests/test_trait_tradeoffs.py` audit the registry so that future additions fail if they are one-sided. diff --git a/pishiegen/fitness/__init__.py b/pishiegen/fitness/__init__.py index d485bfa..9b02de1 100644 --- a/pishiegen/fitness/__init__.py +++ b/pishiegen/fitness/__init__.py @@ -1,5 +1,12 @@ """Fitness scoring for biome-specific adaptation.""" from pishiegen.fitness.scoring import FitnessResult, score_fitness +from pishiegen.fitness.tradeoffs import TRAIT_TRADEOFFS, TraitEffect, all_trait_effects -__all__ = ["FitnessResult", "score_fitness"] +__all__ = [ + "FitnessResult", + "TRAIT_TRADEOFFS", + "TraitEffect", + "all_trait_effects", + "score_fitness", +] diff --git a/pishiegen/fitness/tradeoffs.py b/pishiegen/fitness/tradeoffs.py new file mode 100644 index 0000000..46c0642 --- /dev/null +++ b/pishiegen/fitness/tradeoffs.py @@ -0,0 +1,254 @@ +"""Trait trade-off assumptions for ecological fitness modeling. + +The entries in this module are simulation assumptions inspired by broad +biological reasoning. They are not veterinary claims and should be calibrated +before being used for empirical inference. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Literal + +ConfidenceLevel = Literal["high", "moderate", "speculative"] + + +@dataclass(frozen=True, slots=True) +class TraitEffect: + """Context-dependent benefits and costs associated with a trait. + + Effect weights use a small normalized modeling scale where positive values + indicate advantages and negative values indicate costs in the named context. + They are intended for transparent simulation configuration, not as measured + veterinary or ecological effect sizes. + """ + + trait_name: str + positive_effects: dict[str, float] + negative_effects: dict[str, float] + affected_contexts: list[str] + biological_rationale: str + confidence_level: ConfidenceLevel + + +TRAIT_TRADEOFFS: dict[str, TraitEffect] = { + "thick_long_fur": TraitEffect( + trait_name="Thick/long fur", + positive_effects={ + "cold_tolerance": 0.35, + "snow_biome_survival": 0.25, + }, + negative_effects={ + "heat_stress_in_desert": -0.30, + "hot_climate_stamina": -0.18, + }, + affected_contexts=["cold", "snow", "desert", "hot_climate"], + biological_rationale=( + "Dense insulation can reduce heat loss in cold and snowy settings, " + "but the same insulation can impede heat shedding during hot, open, " + "or desert activity." + ), + confidence_level="high", + ), + "short_hairless_coat": TraitEffect( + trait_name="Short/hairless coat", + positive_effects={ + "heat_dissipation": 0.30, + "heat_stress_reduction": 0.25, + }, + negative_effects={ + "cold_vulnerability": -0.30, + "snow_biome_survival": -0.25, + }, + affected_contexts=["hot_climate", "desert", "cold", "snow"], + biological_rationale=( + "Sparse covering can make heat loss easier in warm climates, while " + "providing less insulation against cold, wind, and snow exposure." + ), + confidence_level="high", + ), + "dark_coat": TraitEffect( + trait_name="Dark coat", + positive_effects={ + "night_camouflage": 0.24, + "dense_forest_concealment": 0.20, + }, + negative_effects={ + "heat_absorption_open_hot_biomes": -0.18, + "snow_visibility": -0.26, + }, + affected_contexts=["night", "dense_forest", "open_hot_biome", "snow"], + biological_rationale=( + "Dark coloration can conceal outlines in shadowed or nocturnal " + "contexts, but may absorb more solar radiation and contrast strongly " + "against snow." + ), + confidence_level="moderate", + ), + "light_white_coat": TraitEffect( + trait_name="Light/white coat", + positive_effects={ + "snow_camouflage": 0.32, + "lower_heat_absorption_open_sun": 0.16, + }, + negative_effects={ + "forest_camouflage": -0.22, + "night_camouflage": -0.18, + }, + affected_contexts=["snow", "open_sun", "forest", "night"], + biological_rationale=( + "Light coats can blend with snowy backgrounds and reflect more open " + "sunlight, yet may be conspicuous in darker forest or night settings " + "unless snow cover or bright moonlight changes the background." + ), + confidence_level="moderate", + ), + "tabby_disruptive_pattern": TraitEffect( + trait_name="Tabby/disruptive pattern", + positive_effects={ + "forest_grassland_camouflage": 0.26, + "outline_disruption": 0.22, + }, + negative_effects={ + "uniform_snow_camouflage": -0.16, + "uniform_desert_camouflage": -0.14, + }, + affected_contexts=["forest", "grassland", "snow", "desert"], + biological_rationale=( + "Striping and mottling can break up body outlines in textured " + "vegetation, but may stand out on visually uniform snow or sand." + ), + confidence_level="moderate", + ), + "large_ears": TraitEffect( + trait_name="Large ears", + positive_effects={ + "heat_dissipation": 0.22, + "prey_detection": 0.18, + }, + negative_effects={ + "cold_exposure": -0.18, + "dense_terrain_injury_exposure": -0.10, + }, + affected_contexts=["hot_climate", "hunting", "cold", "dense_terrain"], + biological_rationale=( + "Larger external ears can increase surface area for heat exchange " + "and sound collection, while exposing more tissue to cold or snags " + "in dense terrain." + ), + confidence_level="moderate", + ), + "folded_ears": TraitEffect( + trait_name="Folded ears", + positive_effects={ + "aesthetic_or_negligible_ecological_effect": 0.01, + }, + negative_effects={ + "health_risk": -0.25, + "sensory_efficiency": -0.12, + }, + affected_contexts=["health", "sensory", "human_preference"], + biological_rationale=( + "Folded ears are modeled as having no meaningful ecological benefit " + "beyond possible human aesthetic preference, with penalties reserved " + "for health-risk and altered sensory assumptions." + ), + confidence_level="high", + ), + "long_tail": TraitEffect( + trait_name="Long tail", + positive_effects={ + "balance": 0.22, + "climbing": 0.16, + "sharp_turns": 0.14, + }, + negative_effects={ + "dense_terrain_injury_exposure": -0.08, + }, + affected_contexts=["climbing", "pursuit", "escape", "dense_terrain"], + biological_rationale=( + "A long tail can aid balance and turning during climbing or rapid " + "movement, but adds a small exposure cost where vegetation or tight " + "spaces increase snagging or injury risk." + ), + confidence_level="moderate", + ), + "tailless_bobtail": TraitEffect( + trait_name="Tailless/bobtail", + positive_effects={ + "tail_injury_risk_reduction": 0.12, + }, + negative_effects={ + "balance": -0.18, + "agility": -0.14, + }, + affected_contexts=["dense_terrain", "climbing", "pursuit", "escape"], + biological_rationale=( + "A reduced tail leaves less tail tissue exposed to injury, but is " + "modeled as reducing balance and agility in contexts where tails aid " + "body control." + ), + confidence_level="moderate", + ), + "polydactyly": TraitEffect( + trait_name="Polydactyly", + positive_effects={ + "grip": 0.14, + "climbing": 0.12, + "mud_wetland_stability": 0.12, + }, + negative_effects={ + "locomotion_cost": -0.04, + }, + affected_contexts=["climbing", "wetland", "mud", "general_locomotion"], + biological_rationale=( + "Extra toes are modeled as potentially improving contact and grip in " + "some substrates, with only a small locomotion cost because the effect " + "may be neutral in many settings." + ), + confidence_level="speculative", + ), + "high_intelligence": TraitEffect( + trait_name="High intelligence", + positive_effects={ + "flexible_behavior": 0.28, + "biome_time_choice": 0.20, + "failed_hunt_reduction": 0.18, + }, + negative_effects={ + "metabolic_cost": -0.18, + "slower_maturation_or_energy_demand": -0.14, + }, + affected_contexts=["foraging", "hunting", "migration", "development"], + biological_rationale=( + "Flexible behavior can improve decisions across variable conditions, " + "but larger or more active nervous-system demands are modeled as " + "requiring more energy and development time." + ), + confidence_level="speculative", + ), + "high_agility": TraitEffect( + trait_name="High agility", + positive_effects={ + "hunting_success": 0.24, + "escape_success": 0.24, + }, + negative_effects={ + "energy_demand": -0.16, + "endurance_without_stamina": -0.12, + }, + affected_contexts=["hunting", "escape", "energy_budget", "endurance"], + biological_rationale=( + "Rapid acceleration and maneuvering can improve hunting and escape, " + "but are modeled as energetically costly and less useful over long " + "efforts without matching stamina." + ), + confidence_level="moderate", + ), +} + + +def all_trait_effects() -> tuple[TraitEffect, ...]: + """Return all configured trait trade-offs in stable registry order.""" + + return tuple(TRAIT_TRADEOFFS.values()) diff --git a/tests/test_trait_tradeoffs.py b/tests/test_trait_tradeoffs.py new file mode 100644 index 0000000..4892760 --- /dev/null +++ b/tests/test_trait_tradeoffs.py @@ -0,0 +1,47 @@ +from __future__ import annotations + +from pishiegen.fitness import TRAIT_TRADEOFFS, TraitEffect, all_trait_effects + + +REQUIRED_TRADEOFF_KEYS = { + "thick_long_fur", + "short_hairless_coat", + "dark_coat", + "light_white_coat", + "tabby_disruptive_pattern", + "large_ears", + "folded_ears", + "long_tail", + "tailless_bobtail", + "polydactyly", + "high_intelligence", + "high_agility", +} + + +def test_required_trait_tradeoffs_are_registered() -> None: + assert set(TRAIT_TRADEOFFS) == REQUIRED_TRADEOFF_KEYS + assert all(isinstance(effect, TraitEffect) for effect in all_trait_effects()) + + +def test_every_trait_has_at_least_one_benefit_and_cost() -> None: + one_sided_traits = [ + effect.trait_name + for effect in all_trait_effects() + if not effect.positive_effects or not effect.negative_effects + ] + + assert one_sided_traits == [] + + +def test_tradeoff_effect_directions_are_explicit() -> None: + for effect in all_trait_effects(): + assert all(value > 0.0 for value in effect.positive_effects.values()) + assert all(value < 0.0 for value in effect.negative_effects.values()) + + +def test_tradeoffs_include_context_rationale_and_confidence() -> None: + for effect in all_trait_effects(): + assert effect.affected_contexts + assert effect.biological_rationale + assert effect.confidence_level in {"high", "moderate", "speculative"}