Download tests/test_simulation.py from ImAnouar1/SocialDynamics_Lab: direct link, hf CLI and curl.
- Browser
- Download file 5.88 kB
-
https://huggingface.co/spaces/ImAnouar1/SocialDynamics_Lab/resolve/main/tests/test_simulation.py
- Command line
-
hf download hf://spaces/ImAnouar1/SocialDynamics_Lab/tests/test_simulation.py
-
curl -L -o test_simulation.py https://huggingface.co/spaces/ImAnouar1/SocialDynamics_Lab/resolve/main/tests/test_simulation.py
5.88 kB
| from dataclasses import replace | |
| import networkx as nx | |
| import numpy as np | |
| from socialdynamics.experiments import aggregate, beta_sweep, run_ensemble, summarise_sweep | |
| from socialdynamics.presets import PRESETS | |
| from socialdynamics.simulation import OpinionSimulation, SimulationConfig | |
| def _preset_config(name: str, **overrides) -> SimulationConfig: | |
| p = dict(PRESETS[name]) | |
| p["misinformation_direction"] = 1.0 if p["misinformation_direction"].startswith("Positive") else -1.0 | |
| p.update(overrides) | |
| return SimulationConfig(**p) | |
| def _reference_step_v11(sim: OpinionSimulation, old: np.ndarray) -> np.ndarray: | |
| """The v1.1 per-agent loop, kept verbatim as a regression oracle.""" | |
| cfg = sim.config | |
| new = old.copy() | |
| beta = 6.0 * cfg.confirmation_bias | |
| for agent in range(cfg.n_agents): | |
| if sim.stubborn_mask[agent]: | |
| continue | |
| neighbors = list(sim.graph.neighbors(agent)) | |
| if neighbors: | |
| w = np.exp(-beta * np.abs(old[agent] - old[neighbors])) | |
| total = float(np.sum(w)) | |
| target = float(np.sum(w * old[neighbors]) / total) if total > 1e-12 else float(old[agent]) | |
| else: | |
| target = float(old[agent]) | |
| delta = cfg.social_influence * (target - old[agent]) | |
| if sim.misinformation_mask[agent] and cfg.misinformation_strength > 0: | |
| delta += 0.22 * cfg.misinformation_strength * (cfg.misinformation_direction - old[agent]) | |
| if cfg.noise > 0: | |
| delta += float(sim._ref_rng.normal(0.0, cfg.noise)) | |
| new[agent] = float(np.clip(old[agent] + delta, -1.0, 1.0)) | |
| return new | |
| def test_vectorised_update_matches_v11_loop(): | |
| for kw in ({}, {"network": "Scale Free", "initial_opinion": "Polarized"}, {"stubborn_fraction": 0.2}): | |
| cfg = SimulationConfig(steps=40, seed=17, **kw) | |
| fast = OpinionSimulation(cfg) | |
| ref = OpinionSimulation(cfg) | |
| ref._ref_rng = ref.rng | |
| x = ref.opinions.copy() | |
| for _ in range(cfg.steps): | |
| fast.step() | |
| x = _reference_step_v11(ref, x) | |
| assert np.max(np.abs(fast.opinions - x)) < 1e-12 | |
| def test_simulation_is_seed_deterministic(): | |
| for rate in (0.0, 0.2): | |
| cfg = SimulationConfig(n_agents=100, steps=20, seed=123, rewiring_rate=rate) | |
| a, b = OpinionSimulation(cfg), OpinionSimulation(cfg) | |
| a.run() | |
| b.run() | |
| assert np.array_equal(a.history_array, b.history_array) | |
| assert set(map(frozenset, a.graph.edges())) == set(map(frozenset, b.graph.edges())) | |
| def test_opinions_stay_bounded(): | |
| cfg = SimulationConfig(n_agents=100, steps=50, seed=3, misinformation_exposure=1.0, | |
| misinformation_strength=1.0, social_influence=1.0, noise=0.2) | |
| sim = OpinionSimulation(cfg) | |
| sim.run() | |
| assert np.all(sim.history_array >= -1.0) | |
| assert np.all(sim.history_array <= 1.0) | |
| def test_stubborn_agents_never_move(): | |
| cfg = SimulationConfig(n_agents=100, steps=30, seed=4, stubborn_fraction=0.20, stubborn_mode="Split extremes", | |
| misinformation_exposure=0.50, misinformation_strength=0.8, rewiring_rate=0.3) | |
| sim = OpinionSimulation(cfg) | |
| initial = sim.opinions.copy() | |
| mask = sim.stubborn_mask.copy() | |
| sim.run() | |
| assert np.array_equal(sim.opinions[mask], initial[mask]) | |
| def test_misinformation_can_shift_mean_positive(): | |
| base = dict(n_agents=100, network="Random", initial_opinion="Neutral", stubborn_fraction=0.0, | |
| confirmation_bias=0.2, social_influence=0.4, noise=0.0, steps=30, seed=11) | |
| control = OpinionSimulation(SimulationConfig(**base, misinformation_exposure=0.0, misinformation_strength=0.0)) | |
| treatment = OpinionSimulation(SimulationConfig(**base, misinformation_exposure=1.0, misinformation_strength=0.8)) | |
| control.run() | |
| treatment.run() | |
| assert treatment.opinions.mean() > control.opinions.mean() + 0.1 | |
| def test_rewiring_conserves_edges_and_creates_no_isolates(): | |
| sim = OpinionSimulation(_preset_config("Echo Chamber")) | |
| edges0 = sim.graph.number_of_edges() | |
| degree0 = dict(sim.graph.degree()) | |
| sim.run() | |
| assert sim.rewired_ties > 0 | |
| assert sim.graph.number_of_edges() == edges0 | |
| assert not list(nx.isolates(sim.graph)) | |
| assert nx.number_of_selfloops(sim.graph) == 0 | |
| assert sum(dict(sim.graph.degree()).values()) == sum(degree0.values()) | |
| def test_rewiring_does_not_perturb_the_noise_stream(): | |
| cfg = SimulationConfig(steps=1, seed=8) | |
| a = OpinionSimulation(cfg) | |
| b = OpinionSimulation(replace(cfg, rewiring_rate=0.5)) | |
| a.step() | |
| b.step() | |
| assert np.array_equal(a.opinions, b.opinions) # first update precedes any rewiring | |
| def test_filter_alone_does_not_segregate_but_rewiring_does(): | |
| seeds = [42, 43, 44] | |
| filtered = run_ensemble(_preset_config("Filtered, Not Segregated"), seeds, n_permutations=100) | |
| chamber = run_ensemble(_preset_config("Echo Chamber"), seeds, n_permutations=100) | |
| for run in filtered: | |
| assert run["selective_exposure"] > 0.9 # strong filter... | |
| assert abs(run["assortativity"]) < 0.2 # ...but no clustering | |
| assert run["assortativity_z"] < 3.0 | |
| for run in chamber: | |
| assert run["assortativity"] > 0.9 | |
| assert run["assortativity_z"] > 10.0 | |
| # and the old index would have ranked this *below* the unsegregated case | |
| assert run["selective_exposure"] < min(r["selective_exposure"] for r in filtered) | |
| def test_sweep_and_aggregate_shapes(): | |
| base = SimulationConfig(steps=20) | |
| rows = beta_sweep(base, (0.0, 1.0), (0.0, 0.1), seeds=[1, 2], n_permutations=20) | |
| assert len(rows) == 8 | |
| summary = summarise_sweep(rows, ("assortativity", "selective_exposure")) | |
| assert len(summary) == 4 | |
| assert all(r["n"] == 2 for r in summary) | |
| agg = aggregate(rows, ("assortativity",)) | |
| assert agg["assortativity_ci95"] >= 0.0 | |