ReNunney/tests/test_track1_analysis.py

180 lines
5.7 KiB
Python

import sys
from dataclasses import dataclass
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SRC_DIR = ROOT / "src"
if str(SRC_DIR) not in sys.path:
sys.path.insert(0, str(SRC_DIR))
import renunney.track1_analysis as analysis
import renunney.track1_api as api
from track1_reference import GenerationSummary, Track1Parameters
def test_fit_linear_cost_by_loci_recovers_line():
rows = [
analysis.LocusThresholdRow(n=1, threshold_T=12.0, accepted=True),
analysis.LocusThresholdRow(n=2, threshold_T=14.0, accepted=True),
analysis.LocusThresholdRow(n=3, threshold_T=16.0, accepted=True),
]
fit = analysis.fit_linear_cost_by_loci(rows)
assert fit is not None
assert abs(fit.intercept_c0 - 10.0) < 1e-9
assert abs(fit.slope_c1 - 2.0) < 1e-9
def test_sweep_number_of_loci_uses_search_results(monkeypatch):
params = Track1Parameters(K=5000, N0=20, n=1, u=5e-6, R=10.0, T=20)
class Dummy:
def __init__(self, threshold_T):
self.threshold_T = threshold_T
def fake_search(params, candidate_T_values, runs=20, seed_start=0, cache_path=None, jobs=1):
return Dummy(threshold_T=10.0 + params.n)
monkeypatch.setattr(analysis, "search_threshold_over_candidates", fake_search)
sweep = analysis.sweep_number_of_loci(params, [1, 2, 3], [10, 20, 30], runs=2, seed_start=1, jobs=3)
assert [row.threshold_T for row in sweep.rows] == [11.0, 12.0, 13.0]
assert sweep.fit is not None
def test_run_config_loci_regression_mode(monkeypatch):
@dataclass(frozen=True)
class DummyFit:
intercept_c0: float = 5.0
slope_c1: float = 2.0
r_squared: float = 1.0
points_used: int = 3
class DummySweep:
rows = [
analysis.LocusThresholdRow(n=1, threshold_T=7.0, accepted=True),
analysis.LocusThresholdRow(n=2, threshold_T=9.0, accepted=True),
analysis.LocusThresholdRow(n=3, threshold_T=11.0, accepted=True),
]
fit = DummyFit()
monkeypatch.setattr(api, "sweep_number_of_loci", lambda *args, **kwargs: DummySweep())
config = api.Track1RunConfig(
mode="loci_regression",
loci_values=[1, 2, 3],
t_start=10,
t_stop=30,
t_step=10,
runs=2,
)
payload = api.run_config(config)
assert payload["mode"] == "loci_regression"
assert payload["loci_values"] == [1, 2, 3]
assert payload["fit"]["slope_c1"] == 2.0
def test_summarize_tracking_detects_post_initial_nonzero_alleles():
summaries = [
GenerationSummary(
t=-2,
N=10,
female_fraction=0.5,
male_count=5,
female_count=5,
fecundity=1.0,
mean_fitness=1.0,
mean_expected_female_productivity=1.0,
target_value=-0.1,
mean_allele_value=0.0,
mean_genotype_value=0.0,
mean_tracking_gap=0.1,
paper_M=0.05,
expected_mutations_current_N=0.0001,
realized_mutation_count=0,
realized_mutation_rate_per_allele=0.0,
birth_count=0,
surviving_offspring_count=0,
ne_approx=5.0,
extinct=False,
),
GenerationSummary(
t=-1,
N=10,
female_fraction=0.5,
male_count=5,
female_count=5,
fecundity=1.0,
mean_fitness=1.0,
mean_expected_female_productivity=1.0,
target_value=-0.05,
mean_allele_value=0.2,
mean_genotype_value=0.2,
mean_tracking_gap=0.25,
paper_M=0.05,
expected_mutations_current_N=0.0001,
realized_mutation_count=1,
realized_mutation_rate_per_allele=0.05,
birth_count=2,
surviving_offspring_count=1,
ne_approx=5.0,
extinct=False,
),
]
tracking = analysis.summarize_tracking(summaries)
assert tracking.extinction_occurred is False
assert tracking.first_extinction_t is None
assert tracking.first_nonzero_allele_t == -1
assert tracking.last_nonzero_allele_t == -1
assert tracking.stayed_zero_after_initialization is False
def test_summarize_tracking_detects_extinction_time():
summaries = [
GenerationSummary(
t=0,
N=10,
female_fraction=0.5,
male_count=5,
female_count=5,
fecundity=1.0,
mean_fitness=1.0,
mean_expected_female_productivity=1.0,
target_value=0.0,
mean_allele_value=0.0,
mean_genotype_value=0.0,
mean_tracking_gap=0.0,
paper_M=0.05,
expected_mutations_current_N=0.0001,
realized_mutation_count=0,
realized_mutation_rate_per_allele=0.0,
birth_count=0,
surviving_offspring_count=0,
ne_approx=5.0,
extinct=False,
),
GenerationSummary(
t=1,
N=0,
female_fraction=0.0,
male_count=0,
female_count=0,
fecundity=0.0,
mean_fitness=0.0,
mean_expected_female_productivity=0.0,
target_value=0.1,
mean_allele_value=0.0,
mean_genotype_value=0.0,
mean_tracking_gap=-0.1,
paper_M=0.05,
expected_mutations_current_N=0.0,
realized_mutation_count=0,
realized_mutation_rate_per_allele=0.0,
birth_count=0,
surviving_offspring_count=0,
ne_approx=0.0,
extinct=True,
),
]
tracking = analysis.summarize_tracking(summaries)
assert tracking.extinction_occurred is True
assert tracking.first_extinction_t == 1